Add casts to reduce gcc warning noise.
[kopensolaris-gnu/glibc.git] / stdio / linewrap.c
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.
4
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.
9
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.
14
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.  */
19
20 #include <stdio.h>
21 #include <ctype.h>
22 #include <string.h>
23 #include <stdlib.h>
24
25 #include <linewrap.h>
26 \f
27 void __line_wrap_output (FILE *, int);
28
29 /* Install our hooks into a stream.  */
30 static inline void
31 wrap_stream (FILE *stream, struct line_wrap_data *d)
32 {
33   static __io_close_fn lwclose;
34   static __io_fileno_fn lwfileno;
35
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.  */
41 }
42
43 /* Restore a stream to its original state.  */
44 static inline void
45 unwrap_stream (FILE *stream, struct line_wrap_data *d)
46 {
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;
52 }
53
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)
59 {
60   if (*wrapper_cookie == 0)
61     {
62       *wrapper_cookie = stream->__cookie;
63       unwrap_stream (stream, *wrapper_cookie);
64     }
65 }
66
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)
72 {
73   if (*wrapper_cookie)
74     {
75       wrap_stream (stream, *wrapper_cookie);
76       *wrapper_cookie = 0;
77     }
78 }
79 \f
80 /* Cookie io functions that might get called on a wrapped stream.
81    Must pass the original cookie to the original functions.  */
82
83 static int
84 lwclose (void *cookie)
85 {
86   struct line_wrap_data *d = cookie;
87   return (*d->close) (d->cookie);
88 }
89
90 static int
91 lwfileno (void *cookie)
92 {
93   struct line_wrap_data *d = cookie;
94   return (*d->fileno) (d->cookie);
95 }
96 \f
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)
105 {
106   char *buf, *nl;
107   size_t len;
108   struct line_wrap_data *d = *wrapper_cookie ?: stream->__cookie;
109
110   /* Scan the buffer for newlines.  */
111   buf = stream->__buffer + d->point_offs;
112   while ((buf < stream->__bufp || (c != EOF && c != '\n')) && !stream->__error)
113     {
114       size_t r;
115
116       if (d->point_col == 0 && d->lmargin != 0)
117         {
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)
121             {
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.  */
128             }
129           else
130             {
131               /* No buffer space for spaces.  Must flush.  */
132               size_t i;
133               char *olimit;
134
135               ensure_unwrapped (stream, wrapper_cookie);
136
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;
145             }
146           d->point_col = pad;
147         }
148
149       len = stream->__bufp - buf;
150       nl = memchr (buf, '\n', len);
151
152       if (d->point_col < 0)
153         d->point_col = 0;
154
155       if (!nl)
156         {
157           /* The buffer ends in a partial line.  */
158
159           if (d->point_col + len + (c != EOF && c != '\n') < d->rmargin)
160             {
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;
166             }
167           else
168             /* Set the end-of-line pointer for the code below to
169                the end of the buffer.  */
170             nl = stream->__bufp;
171         }
172       else if ((size_t) d->point_col + (nl - buf) < d->rmargin)
173         {
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;
179         }
180
181       /* This line is too long.  */
182       r = d->rmargin - 1;
183
184       if (d->wmargin < 0)
185         {
186           /* Truncate the line by overwriting the excess with the
187              newline and anything after it in the buffer.  */
188           if (nl < stream->__bufp)
189             {
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. */
195             }
196           else
197             {
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;
207             }
208         }
209       else
210         {
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.  */
214
215           char *p, *nextline;
216           int i;
217
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.  */
222
223           if (nextline > buf)
224             {
225               /* Swallow separating blanks.  */
226               do
227                 --p;
228               while (isblank (*p));
229               nl = p + 1;       /* The newline will replace the first blank. */
230             }
231           else
232             {
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.  */
237               do
238                 ++p;
239               while (p < nl && !isblank (*p));
240               if (p == nl)
241                 {
242                   /* It already ends a line.  No fussing required.  */
243                   d->point_col = 0;
244                   buf = nl + 1;
245                   continue;
246                 }
247               /* We will move the newline to replace the first blank.  */
248               nl = p;
249               /* Swallow separating blanks.  */
250               do
251                 ++p;
252               while (isblank (*p));
253               /* The next line will start here.  */
254               nextline = p;
255             }
256
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.  */
262             {
263               ensure_unwrapped (stream, wrapper_cookie);
264               (*d->output) (stream, '\n');
265             }
266           else
267             /* We can fit the newline and blanks in before
268                the next word.  */
269             *stream->__bufp++ = '\n';
270
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;
275
276           /* Add blanks up to the wrap margin column.  */
277           for (i = 0; i < d->wmargin; ++i)
278             *stream->__bufp++ = ' ';
279
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;
285
286           /* Continue the scan on the remaining lines in the buffer.  */
287           buf = stream->__bufp;
288
289           /* Restore bufp to include all the remaining text.  */
290           stream->__bufp += len;
291         }
292     }
293
294   /* Remember that we've scanned as far as the end of the buffer.  */
295   d->point_offs = stream->__bufp - stream->__buffer;
296
297   return c;
298 }
299 \f
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)
304 {
305   struct line_wrap_data *d = 0;
306
307   c = lwupdate (stream, c, &d);
308
309   if (!stream->__error)
310     {
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;
318     }
319
320   ensure_wrapped (stream, &d);
321 }
322 \f
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)
331 {
332   struct line_wrap_data *d = malloc (sizeof *d);
333
334   if (!d)
335     return NULL;
336
337   /* Ensure full setup before we start tweaking.  */
338   fflush (stream);
339
340   /* Initialize our wrapping state.  */
341   d->point_col = 0;
342   d->point_offs = 0;
343
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;
350
351   /* Take over the stream.  */
352   wrap_stream (stream, d);
353
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;
358
359   d->lmargin = lmargin;
360   d->rmargin = rmargin;
361   d->wmargin = wmargin;
362
363   return stream;
364 }
365
366 /* Remove the hooks placed in STREAM by `line_wrap_stream'.  */
367 void
368 line_unwrap_stream (FILE *stream)
369 {
370   struct line_wrap_data *d = stream->__cookie;
371   unwrap_stream (stream, d);
372   free (d);
373 }
374 \f
375 /* Functions on wrapped streams.  */
376
377 /* Returns true if STREAM is line wrapped.  */
378 inline int
379 line_wrapped (FILE *stream)
380 {
381   return (stream->__room_funcs.__output == &__line_wrap_output);
382 }
383
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)
392 {
393   if (line_wrapped (stream))
394     {
395       struct line_wrap_data *d = stream->__cookie, *wc = 0;
396
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;
404
405       lwupdate (stream, EOF, &wc);
406
407       if (stream->__linebuf)
408         {
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;
417         }
418
419       ensure_wrapped (stream, &wc);
420
421       return d;
422     }
423   else
424     return 0;
425 }
426 \f
427 /* If STREAM is not line-wrapped return -1, else return its left margin.  */
428 inline size_t
429 line_wrap_lmargin (FILE *stream)
430 {
431   if (! line_wrapped (stream))
432     return -1;
433   return ((struct line_wrap_data *)stream->__cookie)->lmargin;
434 }
435
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)
440 {
441   struct line_wrap_data *d = __line_wrap_update (stream);
442   if (d)
443     {
444       size_t old = d->lmargin;
445       d->lmargin = lmargin;
446       return old;
447     }
448   else
449     return -1;
450 }
451
452 /* If STREAM is not line-wrapped return -1, else return its left margin.  */
453 inline size_t
454 line_wrap_rmargin (FILE *stream)
455 {
456   if (! line_wrapped (stream))
457     return -1;
458   return ((struct line_wrap_data *)stream->__cookie)->rmargin;
459 }
460
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)
465 {
466   struct line_wrap_data *d = __line_wrap_update (stream);
467   if (d)
468     {
469       size_t old = d->rmargin;
470       d->rmargin = rmargin;
471       return old;
472     }
473   else
474     return -1;
475 }
476
477 /* If STREAM is not line-wrapped return -1, else return its wrap margin.  */
478 inline size_t
479 line_wrap_wmargin (FILE *stream)
480 {
481   if (! line_wrapped (stream))
482     return -1;
483   return ((struct line_wrap_data *)stream->__cookie)->wmargin;
484 }
485
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)
490 {
491   struct line_wrap_data *d = __line_wrap_update (stream);
492   if (d)
493     {
494       size_t old = d->wmargin;
495       d->wmargin = wmargin;
496       return old;
497     }
498   else
499     return -1;
500 }
501
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)
506 {
507   struct line_wrap_data *d = __line_wrap_update (stream);
508   return d ? (d->point_col >= 0 ? d->point_col : 0) : -1;
509 }
510 \f
511 #ifdef TEST
512 int
513 main (int argc, char **argv)
514 {
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;
520 }
521 #endif