Update.
[glibc.git] / stdio / linewrap.c
blobd91f3a4bc232aebbdffa079b728858ded021cc41
1 /* Word-wrapping and line-truncating streams.
2 Copyright (C) 1996, 1997 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with the GNU C Library; see the file COPYING.LIB. If not,
17 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA. */
20 #include <stdio.h>
21 #include <ctype.h>
22 #include <string.h>
23 #include <stdlib.h>
25 #include <linewrap.h>
27 void __line_wrap_output (FILE *, int);
29 /* Install our hooks into a stream. */
30 static inline void
31 wrap_stream (FILE *stream, struct line_wrap_data *d)
33 static __io_close_fn lwclose;
34 static __io_fileno_fn lwfileno;
36 stream->__cookie = d;
37 stream->__room_funcs.__output = &__line_wrap_output;
38 stream->__io_funcs.__close = &lwclose;
39 stream->__io_funcs.__fileno = &lwfileno;
40 stream->__io_funcs.__seek = NULL; /* Cannot seek. */
43 /* Restore a stream to its original state. */
44 static inline void
45 unwrap_stream (FILE *stream, struct line_wrap_data *d)
47 stream->__cookie = d->cookie;
48 stream->__room_funcs.__output = d->output;
49 stream->__io_funcs.__close = d->close;
50 stream->__io_funcs.__fileno = d->fileno;
51 stream->__io_funcs.__seek = d->seek;
54 /* If WRAPPER_COOKIE points to a 0 pointer, then STREAM is assumed to be
55 wrapped, and will be unwrapped, storing the wrapper cookie into
56 WRAPPER_COOKIE. Otherwise, nothing is done. */
57 static inline void
58 ensure_unwrapped (FILE *stream, struct line_wrap_data **wrapper_cookie)
60 if (*wrapper_cookie == 0)
62 *wrapper_cookie = stream->__cookie;
63 unwrap_stream (stream, *wrapper_cookie);
67 /* If WRAPPER_COOKIE points to a non-0 pointer, then STREAM is assumed to
68 *have been unwrapped with ensure_unwrapped, will be wrapped with
69 *WRAPPER_COOKIE, and *WRAPPER_COOKIE zeroed. Otherwise, nothing is done. */
70 static inline void
71 ensure_wrapped (FILE *stream, struct line_wrap_data **wrapper_cookie)
73 if (*wrapper_cookie)
75 wrap_stream (stream, *wrapper_cookie);
76 *wrapper_cookie = 0;
80 /* Cookie io functions that might get called on a wrapped stream.
81 Must pass the original cookie to the original functions. */
83 static int
84 lwclose (void *cookie)
86 struct line_wrap_data *d = cookie;
87 return (*d->close) (d->cookie);
90 static int
91 lwfileno (void *cookie)
93 struct line_wrap_data *d = cookie;
94 return (*d->fileno) (d->cookie);
97 /* Process STREAM's buffer so that line wrapping is done from POINT_OFFS to
98 the end of its buffer. If WRAPPER_COOKIE is 0, and it's necessary to
99 flush some data, STREAM is unwrapped, and the line wrap stdio cookie
100 stored in WRAPPER_COOKIE; otherwise, stream is assumed to already be
101 unwrapped, and WRAPPER_COOKIE to point to the line wrap data. Returns C
102 or EOF if C was output. */
103 static inline int
104 lwupdate (FILE *stream, int c, struct line_wrap_data **wrapper_cookie)
106 char *buf, *nl;
107 size_t len;
108 struct line_wrap_data *d = *wrapper_cookie ?: stream->__cookie;
110 /* Scan the buffer for newlines. */
111 buf = stream->__buffer + d->point_offs;
112 while ((buf < stream->__bufp || (c != EOF && c != '\n')) && !stream->__error)
114 size_t r;
116 if (d->point_col == 0 && d->lmargin != 0)
118 /* We are starting a new line. Print spaces to the left margin. */
119 const size_t pad = d->lmargin;
120 if (stream->__bufp + pad < stream->__put_limit)
122 /* We can fit in them in the buffer by moving the
123 buffer text up and filling in the beginning. */
124 memmove (buf + pad, buf, stream->__bufp - buf);
125 stream->__bufp += pad; /* Compensate for bigger buffer. */
126 memset (buf, ' ', pad); /* Fill in the spaces. */
127 buf += pad; /* Don't bother searching them. */
129 else
131 /* No buffer space for spaces. Must flush. */
132 size_t i;
133 char *olimit;
135 ensure_unwrapped (stream, wrapper_cookie);
137 len = stream->__bufp - buf;
138 olimit = stream->__put_limit;
139 stream->__bufp = stream->__put_limit = buf;
140 for (i = 0; i < pad; ++i)
141 (*d->output) (stream, ' ');
142 stream->__put_limit = olimit;
143 memmove (stream->__bufp, buf, len);
144 stream->__bufp += len;
146 d->point_col = pad;
149 len = stream->__bufp - buf;
150 nl = memchr (buf, '\n', len);
152 if (d->point_col < 0)
153 d->point_col = 0;
155 if (!nl)
157 /* The buffer ends in a partial line. */
159 if (d->point_col + len + (c != EOF && c != '\n') < d->rmargin)
161 /* The remaining buffer text is a partial line and fits
162 within the maximum line width. Advance point for the
163 characters to be written and stop scanning. */
164 d->point_col += len;
165 break;
167 else
168 /* Set the end-of-line pointer for the code below to
169 the end of the buffer. */
170 nl = stream->__bufp;
172 else if ((size_t) d->point_col + (nl - buf) < d->rmargin)
174 /* The buffer contains a full line that fits within the maximum
175 line width. Reset point and scan the next line. */
176 d->point_col = 0;
177 buf = nl + 1;
178 continue;
181 /* This line is too long. */
182 r = d->rmargin - 1;
184 if (d->wmargin < 0)
186 /* Truncate the line by overwriting the excess with the
187 newline and anything after it in the buffer. */
188 if (nl < stream->__bufp)
190 memmove (buf + (r - d->point_col), nl, stream->__bufp - nl);
191 stream->__bufp -= buf + (r - d->point_col) - nl;
192 /* Reset point for the next line and start scanning it. */
193 d->point_col = 0;
194 buf += r + 1; /* Skip full line plus \n. */
196 else
198 /* The buffer ends with a partial line that is beyond the
199 maximum line width. Advance point for the characters
200 written, and discard those past the max from the buffer. */
201 d->point_col += len;
202 stream->__bufp -= d->point_col - r;
203 if (c != '\n')
204 /* Swallow the extra character too. */
205 c = EOF;
206 break;
209 else
211 /* Do word wrap. Go to the column just past the maximum line
212 width and scan back for the beginning of the word there.
213 Then insert a line break. */
215 char *p, *nextline;
216 int i;
218 p = buf + (r + 1 - d->point_col);
219 while (p >= buf && !isblank (*p))
220 --p;
221 nextline = p + 1; /* This will begin the next line. */
223 if (nextline > buf)
225 /* Swallow separating blanks. */
227 --p;
228 while (isblank (*p));
229 nl = p + 1; /* The newline will replace the first blank. */
231 else
233 /* A single word that is greater than the maximum line width.
234 Oh well. Put it on an overlong line by itself. */
235 p = buf + (r + 1 - d->point_col);
236 /* Find the end of the long word. */
238 ++p;
239 while (p < nl && !isblank (*p));
240 if (p == nl)
242 /* It already ends a line. No fussing required. */
243 d->point_col = 0;
244 buf = nl + 1;
245 continue;
247 /* We will move the newline to replace the first blank. */
248 nl = p;
249 /* Swallow separating blanks. */
251 ++p;
252 while (isblank (*p));
253 /* The next line will start here. */
254 nextline = p;
257 /* Temporarily reset bufp to include just the first line. */
258 stream->__bufp = nl;
259 if (nextline - (nl + 1) < d->wmargin)
260 /* The margin needs more blanks than we removed.
261 Output the first line so we can use the space. */
263 ensure_unwrapped (stream, wrapper_cookie);
264 (*d->output) (stream, '\n');
266 else
267 /* We can fit the newline and blanks in before
268 the next word. */
269 *stream->__bufp++ = '\n';
271 /* Reset the counter of what has been output this line. If wmargin
272 is 0, we want to avoid the lmargin getting added, so we set
273 point_col to a magic value of -1 in that case. */
274 d->point_col = d->wmargin ? d->wmargin : -1;
276 /* Add blanks up to the wrap margin column. */
277 for (i = 0; i < d->wmargin; ++i)
278 *stream->__bufp++ = ' ';
280 /* Copy the tail of the original buffer into the current buffer
281 position. */
282 if (stream->__bufp != nextline)
283 memmove (stream->__bufp, nextline, buf + len - nextline);
284 len -= nextline - buf;
286 /* Continue the scan on the remaining lines in the buffer. */
287 buf = stream->__bufp;
289 /* Restore bufp to include all the remaining text. */
290 stream->__bufp += len;
294 /* Remember that we've scanned as far as the end of the buffer. */
295 d->point_offs = stream->__bufp - stream->__buffer;
297 return c;
300 /* This function is called when STREAM must be flushed.
301 C is EOF or a character to be appended to the buffer contents. */
302 void
303 __line_wrap_output (FILE *stream, int c)
305 struct line_wrap_data *d = 0;
307 c = lwupdate (stream, c, &d);
309 if (!stream->__error)
311 ensure_unwrapped (stream, &d);
312 (*d->output) (stream, c);
313 d->point_offs = 0; /* The buffer now holds nothing. */
314 if (c == '\n')
315 d->point_col = 0;
316 else if (c != EOF)
317 ++d->point_col;
320 ensure_wrapped (stream, &d);
323 /* Modify STREAM so that it prefixes lines written on it with LMARGIN spaces
324 and limits them to RMARGIN columns total. If WMARGIN >= 0, words that
325 extend past RMARGIN are wrapped by replacing the whitespace before them
326 with a newline and WMARGIN spaces. Otherwise, chars beyond RMARGIN are
327 simply dropped until a newline. Returns STREAM after modifying it, or
328 NULL if there was an error. */
329 FILE *
330 line_wrap_stream (FILE *stream, size_t lmargin, size_t rmargin, ssize_t wmargin)
332 struct line_wrap_data *d = malloc (sizeof *d);
334 if (!d)
335 return NULL;
337 /* Ensure full setup before we start tweaking. */
338 fflush (stream);
340 /* Initialize our wrapping state. */
341 d->point_col = 0;
342 d->point_offs = 0;
344 /* Save the original cookie and output and close hooks. */
345 d->cookie = stream->__cookie;
346 d->output = stream->__room_funcs.__output;
347 d->close = stream->__io_funcs.__close;
348 d->fileno = stream->__io_funcs.__fileno;
349 d->seek = stream->__io_funcs.__seek;
351 /* Take over the stream. */
352 wrap_stream (stream, d);
354 /* Line-wrapping streams are normally line-buffered. This is not
355 required, just assumed desired. The wrapping feature should continue
356 to work if the stream is switched to full or no buffering. */
357 stream->__linebuf = 1;
359 d->lmargin = lmargin;
360 d->rmargin = rmargin;
361 d->wmargin = wmargin;
363 return stream;
366 /* Remove the hooks placed in STREAM by `line_wrap_stream'. */
367 void
368 line_unwrap_stream (FILE *stream)
370 struct line_wrap_data *d = stream->__cookie;
371 unwrap_stream (stream, d);
372 free (d);
375 /* Functions on wrapped streams. */
377 /* Returns true if STREAM is line wrapped. */
378 inline int
379 line_wrapped (FILE *stream)
381 return (stream->__room_funcs.__output == &__line_wrap_output);
384 /* If STREAM is not line-wrapped, return 0. Otherwise all pending text
385 buffered text in STREAM so that the POINT_OFFS field refers to the last
386 position in the stdio buffer, and return the line wrap state object for
387 STREAM. Since all text has been processed, this means that (1) the
388 POINT_COL field refers to the column at which any new text would be added,
389 and (2) any changes to the margin parameters will only affect new text. */
390 struct line_wrap_data *
391 __line_wrap_update (FILE *stream)
393 if (line_wrapped (stream))
395 struct line_wrap_data *d = stream->__cookie, *wc = 0;
397 if (stream->__linebuf_active)
398 /* This is an active line-buffered stream, so its put-limit is set to
399 the beginning of the buffer in order to force a __flshfp call on
400 each putc (see below). We undo this hack here (by setting the
401 limit to the end of the buffer) to simplify the interface with the
402 output-room function. */
403 stream->__put_limit = stream->__buffer + stream->__bufsize;
405 lwupdate (stream, EOF, &wc);
407 if (stream->__linebuf)
409 /* This is a line-buffered stream, and it is now ready to do some
410 output. We call this an "active line-buffered stream". We set
411 the put_limit to the beginning of the buffer, so the next `putc'
412 call will force a call to flshfp. Setting the linebuf_active
413 flag tells the code above (on the next call) to undo this
414 hackery. */
415 stream->__put_limit = stream->__buffer;
416 stream->__linebuf_active = 1;
419 ensure_wrapped (stream, &wc);
421 return d;
423 else
424 return 0;
427 /* If STREAM is not line-wrapped return -1, else return its left margin. */
428 inline size_t
429 line_wrap_lmargin (FILE *stream)
431 if (! line_wrapped (stream))
432 return -1;
433 return ((struct line_wrap_data *)stream->__cookie)->lmargin;
436 /* If STREAM is not line-wrapped return -1, else set its left margin to
437 LMARGIN and return the old value. */
438 inline size_t
439 line_wrap_set_lmargin (FILE *stream, size_t lmargin)
441 struct line_wrap_data *d = __line_wrap_update (stream);
442 if (d)
444 size_t old = d->lmargin;
445 d->lmargin = lmargin;
446 return old;
448 else
449 return -1;
452 /* If STREAM is not line-wrapped return -1, else return its left margin. */
453 inline size_t
454 line_wrap_rmargin (FILE *stream)
456 if (! line_wrapped (stream))
457 return -1;
458 return ((struct line_wrap_data *)stream->__cookie)->rmargin;
461 /* If STREAM is not line-wrapped return -1, else set its right margin to
462 RMARGIN and return the old value. */
463 inline size_t
464 line_wrap_set_rmargin (FILE *stream, size_t rmargin)
466 struct line_wrap_data *d = __line_wrap_update (stream);
467 if (d)
469 size_t old = d->rmargin;
470 d->rmargin = rmargin;
471 return old;
473 else
474 return -1;
477 /* If STREAM is not line-wrapped return -1, else return its wrap margin. */
478 inline size_t
479 line_wrap_wmargin (FILE *stream)
481 if (! line_wrapped (stream))
482 return -1;
483 return ((struct line_wrap_data *)stream->__cookie)->wmargin;
486 /* If STREAM is not line-wrapped return -1, else set its left margin to
487 WMARGIN and return the old value. */
488 inline size_t
489 line_wrap_set_wmargin (FILE *stream, size_t wmargin)
491 struct line_wrap_data *d = __line_wrap_update (stream);
492 if (d)
494 size_t old = d->wmargin;
495 d->wmargin = wmargin;
496 return old;
498 else
499 return -1;
502 /* If STREAM is not line-wrapped return -1, else return the column number of
503 the current output point. */
504 inline size_t
505 line_wrap_point (FILE *stream)
507 struct line_wrap_data *d = __line_wrap_update (stream);
508 return d ? (d->point_col >= 0 ? d->point_col : 0) : -1;
511 #ifdef TEST
513 main (int argc, char **argv)
515 int c;
516 puts ("stopme");
517 line_wrap_stream (stdout, atoi (argv[1]), atoi (argv[2] ?: "-1"));
518 while ((c = getchar()) != EOF) putchar (c);
519 return 0;
521 #endif