1 /* Word-wrapping and line-truncating streams.
2 Copyright (C) 1996 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
17 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
18 Cambridge, MA 02139, USA. */
27 void __line_wrap_output (FILE *, int);
29 /* Install our hooks into a stream. */
31 wrap_stream (FILE *stream
, struct line_wrap_data
*d
)
33 static __io_close_fn lwclose
;
34 static __io_fileno_fn lwfileno
;
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. */
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 /* Cookie io functions that might get called on a wrapped stream.
55 Must pass the original cookie to the original functions. */
58 lwclose (void *cookie
)
60 struct line_wrap_data
*d
= cookie
;
61 return (*d
->close
) (d
->cookie
);
65 lwfileno (void *cookie
)
67 struct line_wrap_data
*d
= cookie
;
68 return (*d
->fileno
) (d
->cookie
);
71 /* This function is called when STREAM must be flushed.
72 C is EOF or a character to be appended to the buffer contents. */
74 __line_wrap_output (FILE *stream
, int c
)
79 /* Extract our data and restore the stream's original cookie
80 and output function so writes we do really go out. */
81 struct line_wrap_data
*d
= stream
->__cookie
;
82 unwrap_stream (stream
, d
);
84 /* Scan the buffer for newlines. */
85 buf
= stream
->__buffer
;
86 while ((buf
< stream
->__bufp
|| (c
!= EOF
&& c
!= '\n')) && !stream
->__error
)
90 if (d
->point
== 0 && d
->lmargin
!= 0)
92 /* We are starting a new line. Print spaces to the left margin. */
93 const size_t pad
= d
->lmargin
;
94 if (stream
->__bufp
+ pad
< stream
->__put_limit
)
96 /* We can fit in them in the buffer by moving the
97 buffer text up and filling in the beginning. */
98 memmove (buf
+ pad
, buf
, stream
->__bufp
- buf
);
99 stream
->__bufp
+= pad
; /* Compensate for bigger buffer. */
100 memset (buf
, ' ', pad
); /* Fill in the spaces. */
101 buf
+= pad
; /* Don't bother searching them. */
105 /* No buffer space for spaces. Must flush. */
109 len
= stream
->__bufp
- buf
;
110 olimit
= stream
->__put_limit
;
111 stream
->__bufp
= stream
->__put_limit
= buf
;
112 for (i
= 0; i
< pad
; ++i
)
113 (*d
->output
) (stream
, ' ');
114 stream
->__put_limit
= olimit
;
115 memcpy (stream
->__bufp
, buf
, len
);
116 stream
->__bufp
+= len
;
121 len
= stream
->__bufp
- buf
;
122 nl
= memchr (buf
, '\n', len
);
126 /* The buffer ends in a partial line. */
128 if (d
->point
+ len
+ (c
!= EOF
&& c
!= '\n') <= d
->rmargin
)
130 /* The remaining buffer text is a partial line and fits
131 within the maximum line width. Advance point for the
132 characters to be written and stop scanning. */
137 /* Set the end-of-line pointer for the code below to
138 the end of the buffer. */
141 else if (d
->point
+ (nl
- buf
) <= d
->rmargin
)
143 /* The buffer contains a full line that fits within the maximum
144 line width. Reset point and scan the next line. */
150 /* This line is too long. */
155 /* Truncate the line by overwriting the excess with the
156 newline and anything after it in the buffer. */
157 if (nl
< stream
->__bufp
)
159 memcpy (buf
+ (r
- d
->point
), nl
, stream
->__bufp
- nl
);
160 stream
->__bufp
-= buf
+ (r
- d
->point
) - nl
;
161 /* Reset point for the next line and start scanning it. */
163 buf
+= r
+ 1; /* Skip full line plus \n. */
167 /* The buffer ends with a partial line that is beyond the
168 maximum line width. Advance point for the characters
169 written, and discard those past the max from the buffer. */
171 stream
->__bufp
-= d
->point
- r
;
173 /* Swallow the extra character too. */
180 /* Do word wrap. Go to the column just past the maximum line
181 width and scan back for the beginning of the word there.
182 Then insert a line break. */
187 p
= buf
+ (r
+ 1 - d
->point
);
188 while (p
>= buf
&& !isblank (*p
))
190 nextline
= p
+ 1; /* This will begin the next line. */
194 /* Swallow separating blanks. */
197 while (isblank (*p
));
198 nl
= p
+ 1; /* The newline will replace the first blank. */
202 /* A single word that is greater than the maximum line width.
203 Oh well. Put it on an overlong line by itself. */
204 p
= buf
+ (r
+ 1 - d
->point
);
205 /* Find the end of the long word. */
208 while (p
< nl
&& !isblank (*p
));
211 /* It already ends a line. No fussing required. */
216 /* We will move the newline to replace the first blank. */
218 /* Swallow separating blanks. */
221 while (isblank (*p
));
222 /* The next line will start here. */
226 /* Temporarily reset bufp to include just the first line. */
228 if (nextline
- (nl
+ 1) < d
->wmargin
)
229 /* The margin needs more blanks than we removed.
230 Output the first line so we can use the space. */
231 (*d
->output
) (stream
, '\n');
233 /* We can fit the newline and blanks in before
235 *stream
->__bufp
++ = '\n';
237 /* Reset the counter of what has been output this line. */
240 /* Add blanks up to the wrap margin column. */
241 for (i
= 0; i
< d
->wmargin
; ++i
)
242 *stream
->__bufp
++ = ' ';
244 /* Copy the tail of the original buffer into the current buffer
246 if (stream
->__bufp
!= nextline
)
247 memcpy (stream
->__bufp
, nextline
, buf
+ len
- nextline
);
248 len
-= nextline
- buf
;
250 /* Continue the scan on the remaining lines in the buffer. */
251 buf
= stream
->__bufp
;
253 /* Restore bufp to include all the remaining text. */
254 stream
->__bufp
+= len
;
258 if (!stream
->__error
)
260 (*d
->output
) (stream
, c
);
267 wrap_stream (stream
, d
);
270 /* Modify STREAM so that it prefixes lines written on it with LMARGIN spaces
271 and limits them to RMARGIN columns total. If WMARGIN >= 0, words that
272 extend past RMARGIN are wrapped by replacing the whitespace before them
273 with a newline and WMARGIN spaces. Otherwise, chars beyond RMARGIN are
274 simply dropped until a newline. Returns STREAM after modifying it, or
275 NULL if there was an error. */
277 line_wrap_stream (FILE *stream
, size_t lmargin
, size_t rmargin
, size_t wmargin
)
279 struct line_wrap_data
*d
= malloc (sizeof *d
);
284 /* Ensure full setup before we start tweaking. */
287 /* Initialize our wrapping state. */
290 /* Save the original cookie and output and close hooks. */
291 d
->cookie
= stream
->__cookie
;
292 d
->output
= stream
->__room_funcs
.__output
;
293 d
->close
= stream
->__io_funcs
.__close
;
294 d
->fileno
= stream
->__io_funcs
.__fileno
;
296 /* Take over the stream. */
297 wrap_stream (stream
, d
);
299 /* Line-wrapping streams are normally line-buffered. This is not
300 required, just assumed desired. The wrapping feature should continue
301 to work if the stream is switched to full or no buffering. */
302 stream
->__linebuf
= 1;
304 d
->lmargin
= lmargin
;
305 d
->rmargin
= rmargin
;
306 d
->wmargin
= wmargin
;
311 /* Remove the hooks placed in STREAM by `line_wrap_stream'. */
313 line_unwrap_stream (FILE *stream
)
315 struct line_wrap_data
*d
= stream
->__cookie
;
316 unwrap_stream (stream
, d
);
320 /* Functions on wrapped streams. */
322 /* Returns true if STREAM is line wrapped. */
324 line_wrapped (FILE *stream
)
326 return (stream
->__room_funcs
.__output
== &__line_wrap_output
);
329 /* If STREAM is not line-wrapped return -1, else return its left margin. */
331 line_wrap_lmargin (FILE *stream
)
333 if (! line_wrapped (stream
))
335 return ((struct line_wrap_data
*)stream
->__cookie
)->lmargin
;
338 /* If STREAM is not line-wrapped return -1, else set its left margin to
339 LMARGIN and return the old value. */
341 line_wrap_set_lmargin (FILE *stream
, size_t lmargin
)
343 if (! line_wrapped (stream
))
347 struct line_wrap_data
*d
= stream
->__cookie
;
348 size_t old
= d
->lmargin
;
349 d
->lmargin
= lmargin
;
354 /* If STREAM is not line-wrapped return -1, else return its left margin. */
356 line_wrap_rmargin (FILE *stream
)
358 if (! line_wrapped (stream
))
360 return ((struct line_wrap_data
*)stream
->__cookie
)->rmargin
;
363 /* If STREAM is not line-wrapped return -1, else set its right margin to
364 RMARGIN and return the old value. */
366 line_wrap_set_rmargin (FILE *stream
, size_t rmargin
)
368 if (! line_wrapped (stream
))
372 struct line_wrap_data
*d
= stream
->__cookie
;
373 size_t old
= d
->rmargin
;
374 d
->rmargin
= rmargin
;
379 /* If STREAM is not line-wrapped return -1, else return its wrap margin. */
381 line_wrap_wmargin (FILE *stream
)
383 if (! line_wrapped (stream
))
385 return ((struct line_wrap_data
*)stream
->__cookie
)->wmargin
;
388 /* If STREAM is not line-wrapped return -1, else set its left margin to
389 WMARGIN and return the old value. */
391 line_wrap_set_wmargin (FILE *stream
, size_t wmargin
)
393 if (! line_wrapped (stream
))
397 struct line_wrap_data
*d
= stream
->__cookie
;
398 size_t old
= d
->wmargin
;
399 d
->wmargin
= wmargin
;
404 /* If STREAM is not line-wrapped return -1, else return the column number of
405 the current output point. */
407 line_wrap_point (FILE *stream
)
409 if (! line_wrapped (stream
))
411 return ((struct line_wrap_data
*)stream
->__cookie
)->point
;
416 main (int argc
, char **argv
)
420 line_wrap_stream (stdout
, atoi (argv
[1]), atoi (argv
[2] ?: "-1"));
421 while ((c
= getchar()) != EOF
) putchar (c
);