* Make-dist (subdir): Make empty value really empty.
[glibc.git] / stdio / linewrap.c
blob7f6576f29b3314b424d0010abaaa4a7a1b585739
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. */
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 /* Cookie io functions that might get called on a wrapped stream.
55 Must pass the original cookie to the original functions. */
57 static int
58 lwclose (void *cookie)
60 struct line_wrap_data *d = cookie;
61 return (*d->close) (d->cookie);
64 static int
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. */
73 void
74 __line_wrap_output (FILE *stream, int c)
76 char *buf, *nl;
77 size_t len;
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)
88 size_t r;
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. */
103 else
105 /* No buffer space for spaces. Must flush. */
106 size_t i;
107 char *olimit;
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;
118 d->point = pad;
121 len = stream->__bufp - buf;
122 nl = memchr (buf, '\n', len);
124 if (!nl)
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. */
133 d->point += len;
134 break;
136 else
137 /* Set the end-of-line pointer for the code below to
138 the end of the buffer. */
139 nl = stream->__bufp;
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. */
145 d->point = 0;
146 buf = nl + 1;
147 continue;
150 /* This line is too long. */
151 r = d->rmargin;
153 if (d->wmargin < 0)
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. */
162 d->point = 0;
163 buf += r + 1; /* Skip full line plus \n. */
165 else
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. */
170 d->point += len;
171 stream->__bufp -= d->point - r;
172 if (c != '\n')
173 /* Swallow the extra character too. */
174 c = EOF;
175 break;
178 else
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. */
184 char *p, *nextline;
185 int i;
187 p = buf + (r + 1 - d->point);
188 while (p >= buf && !isblank (*p))
189 --p;
190 nextline = p + 1; /* This will begin the next line. */
192 if (nextline > buf)
194 /* Swallow separating blanks. */
196 --p;
197 while (isblank (*p));
198 nl = p + 1; /* The newline will replace the first blank. */
200 else
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. */
207 ++p;
208 while (p < nl && !isblank (*p));
209 if (p == nl)
211 /* It already ends a line. No fussing required. */
212 d->point = 0;
213 buf = nl + 1;
214 continue;
216 /* We will move the newline to replace the first blank. */
217 nl = p;
218 /* Swallow separating blanks. */
220 ++p;
221 while (isblank (*p));
222 /* The next line will start here. */
223 nextline = p;
226 /* Temporarily reset bufp to include just the first line. */
227 stream->__bufp = nl;
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');
232 else
233 /* We can fit the newline and blanks in before
234 the next word. */
235 *stream->__bufp++ = '\n';
237 /* Reset the counter of what has been output this line. */
238 d->point = 0;
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
245 position. */
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);
261 if (c == '\n')
262 d->point = 0;
263 else if (c != EOF)
264 ++d->point;
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. */
276 FILE *
277 line_wrap_stream (FILE *stream, size_t lmargin, size_t rmargin, size_t wmargin)
279 struct line_wrap_data *d = malloc (sizeof *d);
281 if (!d)
282 return NULL;
284 /* Ensure full setup before we start tweaking. */
285 fflush (stream);
287 /* Initialize our wrapping state. */
288 d->point = 0;
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;
308 return stream;
311 /* Remove the hooks placed in STREAM by `line_wrap_stream'. */
312 void
313 line_unwrap_stream (FILE *stream)
315 struct line_wrap_data *d = stream->__cookie;
316 unwrap_stream (stream, d);
317 free (d);
320 /* Functions on wrapped streams. */
322 /* Returns true if STREAM is line wrapped. */
323 inline int
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. */
330 size_t
331 line_wrap_lmargin (FILE *stream)
333 if (! line_wrapped (stream))
334 return -1;
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. */
340 size_t
341 line_wrap_set_lmargin (FILE *stream, size_t lmargin)
343 if (! line_wrapped (stream))
344 return -1;
345 else
347 struct line_wrap_data *d = stream->__cookie;
348 size_t old = d->lmargin;
349 d->lmargin = lmargin;
350 return old;
354 /* If STREAM is not line-wrapped return -1, else return its left margin. */
355 size_t
356 line_wrap_rmargin (FILE *stream)
358 if (! line_wrapped (stream))
359 return -1;
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. */
365 size_t
366 line_wrap_set_rmargin (FILE *stream, size_t rmargin)
368 if (! line_wrapped (stream))
369 return -1;
370 else
372 struct line_wrap_data *d = stream->__cookie;
373 size_t old = d->rmargin;
374 d->rmargin = rmargin;
375 return old;
379 /* If STREAM is not line-wrapped return -1, else return its wrap margin. */
380 size_t
381 line_wrap_wmargin (FILE *stream)
383 if (! line_wrapped (stream))
384 return -1;
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. */
390 size_t
391 line_wrap_set_wmargin (FILE *stream, size_t wmargin)
393 if (! line_wrapped (stream))
394 return -1;
395 else
397 struct line_wrap_data *d = stream->__cookie;
398 size_t old = d->wmargin;
399 d->wmargin = wmargin;
400 return old;
404 /* If STREAM is not line-wrapped return -1, else return the column number of
405 the current output point. */
406 size_t
407 line_wrap_point (FILE *stream)
409 if (! line_wrapped (stream))
410 return -1;
411 return ((struct line_wrap_data *)stream->__cookie)->point;
414 #ifdef TEST
416 main (int argc, char **argv)
418 int c;
419 puts ("stopme");
420 line_wrap_stream (stdout, atoi (argv[1]), atoi (argv[2] ?: "-1"));
421 while ((c = getchar()) != EOF) putchar (c);
422 return 0;
424 #endif