1 /* -*- buffer-read-only: t -*- vi: set ro: */
2 /* DO NOT EDIT! GENERATED AUTOMATICALLY! */
3 /* Word-wrapping and line-truncating streams
4 Copyright (C) 1997-1999, 2001-2003, 2005, 2009-2010 Free Software
6 This file is part of the GNU C Library.
7 Written by Miles Bader <miles@gnu.ai.mit.edu>.
9 This program is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
22 /* This package emulates glibc `line_wrap_stream' semantics for systems that
35 #include "argp-fmtstream.h"
36 #include "argp-namefrob.h"
38 #ifndef ARGP_FMTSTREAM_USE_LINEWRAP
41 #define isblank(ch) ((ch)==' ' || (ch)=='\t')
44 #if defined _LIBC && defined USE_IN_LIBIO
46 # include <libio/libioP.h>
47 # define __vsnprintf(s, l, f, a) _IO_vsnprintf (s, l, f, a)
50 #define INIT_BUF_SIZE 200
51 #define PRINTF_SIZE_GUESS 150
53 /* Return an argp_fmtstream that outputs to STREAM, and which prefixes lines
54 written on it with LMARGIN spaces and limits them to RMARGIN columns
55 total. If WMARGIN >= 0, words that extend past RMARGIN are wrapped by
56 replacing the whitespace before them with a newline and WMARGIN spaces.
57 Otherwise, chars beyond RMARGIN are simply dropped until a newline.
58 Returns NULL if there was an error. */
60 __argp_make_fmtstream (FILE *stream,
61 size_t lmargin, size_t rmargin, ssize_t wmargin)
65 fs = (struct argp_fmtstream *) malloc (sizeof (struct argp_fmtstream));
70 fs->lmargin = lmargin;
71 fs->rmargin = rmargin;
72 fs->wmargin = wmargin;
76 fs->buf = (char *) malloc (INIT_BUF_SIZE);
85 fs->end = fs->buf + INIT_BUF_SIZE;
94 weak_alias (__argp_make_fmtstream, argp_make_fmtstream)
98 /* Flush FS to its stream, and free it (but don't close the stream). */
100 __argp_fmtstream_free (argp_fmtstream_t fs)
102 __argp_fmtstream_update (fs);
106 __fxprintf (fs->stream, "%.*s", (int) (fs->p - fs->buf), fs->buf);
108 fwrite_unlocked (fs->buf, 1, fs->p - fs->buf, fs->stream);
117 weak_alias (__argp_fmtstream_free, argp_fmtstream_free)
121 /* Process FS's buffer so that line wrapping is done from POINT_OFFS to the
122 end of its buffer. This code is mostly from glibc stdio/linewrap.c. */
124 __argp_fmtstream_update (argp_fmtstream_t fs)
129 /* Scan the buffer for newlines. */
130 buf = fs->buf + fs->point_offs;
135 if (fs->point_col == 0 && fs->lmargin != 0)
137 /* We are starting a new line. Print spaces to the left margin. */
138 const size_t pad = fs->lmargin;
139 if (fs->p + pad < fs->end)
141 /* We can fit in them in the buffer by moving the
142 buffer text up and filling in the beginning. */
143 memmove (buf + pad, buf, fs->p - buf);
144 fs->p += pad; /* Compensate for bigger buffer. */
145 memset (buf, ' ', pad); /* Fill in the spaces. */
146 buf += pad; /* Don't bother searching them. */
150 /* No buffer space for spaces. Must flush. */
152 for (i = 0; i < pad; i++)
155 if (_IO_fwide (fs->stream, 0) > 0)
156 putwc_unlocked (L' ', fs->stream);
159 putc_unlocked (' ', fs->stream);
166 nl = memchr (buf, '\n', len);
168 if (fs->point_col < 0)
173 /* The buffer ends in a partial line. */
175 if (fs->point_col + len < fs->rmargin)
177 /* The remaining buffer text is a partial line and fits
178 within the maximum line width. Advance point for the
179 characters to be written and stop scanning. */
180 fs->point_col += len;
184 /* Set the end-of-line pointer for the code below to
185 the end of the buffer. */
188 else if (fs->point_col + (nl - buf) < (ssize_t) fs->rmargin)
190 /* The buffer contains a full line that fits within the maximum
191 line width. Reset point and scan the next line. */
197 /* This line is too long. */
202 /* Truncate the line by overwriting the excess with the
203 newline and anything after it in the buffer. */
206 memmove (buf + (r - fs->point_col), nl, fs->p - nl);
207 fs->p -= buf + (r - fs->point_col) - nl;
208 /* Reset point for the next line and start scanning it. */
210 buf += r + 1; /* Skip full line plus \n. */
214 /* The buffer ends with a partial line that is beyond the
215 maximum line width. Advance point for the characters
216 written, and discard those past the max from the buffer. */
217 fs->point_col += len;
218 fs->p -= fs->point_col - r;
224 /* Do word wrap. Go to the column just past the maximum line
225 width and scan back for the beginning of the word there.
226 Then insert a line break. */
231 p = buf + (r + 1 - fs->point_col);
232 while (p >= buf && !isblank ((unsigned char) *p))
234 nextline = p + 1; /* This will begin the next line. */
238 /* Swallow separating blanks. */
242 while (p >= buf && isblank ((unsigned char) *p));
243 nl = p + 1; /* The newline will replace the first blank. */
247 /* A single word that is greater than the maximum line width.
248 Oh well. Put it on an overlong line by itself. */
249 p = buf + (r + 1 - fs->point_col);
250 /* Find the end of the long word. */
254 while (p < nl && !isblank ((unsigned char) *p));
257 /* It already ends a line. No fussing required. */
262 /* We will move the newline to replace the first blank. */
264 /* Swallow separating blanks. */
267 while (isblank ((unsigned char) *p));
268 /* The next line will start here. */
272 /* Note: There are a bunch of tests below for
273 NEXTLINE == BUF + LEN + 1; this case is where NL happens to fall
274 at the end of the buffer, and NEXTLINE is in fact empty (and so
275 we need not be careful to maintain its contents). */
277 if ((nextline == buf + len + 1
278 ? fs->end - nl < fs->wmargin + 1
279 : nextline - (nl + 1) < fs->wmargin)
282 /* The margin needs more blanks than we removed. */
283 if (fs->end - fs->p > fs->wmargin + 1)
284 /* Make some space for them. */
286 size_t mv = fs->p - nextline;
287 memmove (nl + 1 + fs->wmargin, nextline, mv);
288 nextline = nl + 1 + fs->wmargin;
289 len = nextline + mv - buf;
293 /* Output the first line so we can use the space. */
296 __fxprintf (fs->stream, "%.*s\n",
297 (int) (nl - fs->buf), fs->buf);
300 fwrite_unlocked (fs->buf, 1, nl - fs->buf, fs->stream);
301 putc_unlocked ('\n', fs->stream);
304 len += buf - fs->buf;
309 /* We can fit the newline and blanks in before
313 if (nextline - nl >= fs->wmargin
314 || (nextline == buf + len + 1 && fs->end - nextline >= fs->wmargin))
315 /* Add blanks up to the wrap margin column. */
316 for (i = 0; i < fs->wmargin; ++i)
319 for (i = 0; i < fs->wmargin; ++i)
321 if (_IO_fwide (fs->stream, 0) > 0)
322 putwc_unlocked (L' ', fs->stream);
325 putc_unlocked (' ', fs->stream);
327 /* Copy the tail of the original buffer into the current buffer
330 memmove (nl, nextline, buf + len - nextline);
331 len -= nextline - buf;
333 /* Continue the scan on the remaining lines in the buffer. */
336 /* Restore bufp to include all the remaining text. */
339 /* Reset the counter of what has been output this line. If wmargin
340 is 0, we want to avoid the lmargin getting added, so we set
341 point_col to a magic value of -1 in that case. */
342 fs->point_col = fs->wmargin ? fs->wmargin : -1;
346 /* Remember that we've scanned as far as the end of the buffer. */
347 fs->point_offs = fs->p - fs->buf;
350 /* Ensure that FS has space for AMOUNT more bytes in its buffer, either by
351 growing the buffer, or by flushing it. True is returned iff we succeed. */
353 __argp_fmtstream_ensure (struct argp_fmtstream *fs, size_t amount)
355 if ((size_t) (fs->end - fs->p) < amount)
359 /* Flush FS's buffer. */
360 __argp_fmtstream_update (fs);
363 __fxprintf (fs->stream, "%.*s", (int) (fs->p - fs->buf), fs->buf);
364 wrote = fs->p - fs->buf;
366 wrote = fwrite_unlocked (fs->buf, 1, fs->p - fs->buf, fs->stream);
368 if (wrote == fs->p - fs->buf)
376 fs->point_offs -= wrote;
377 memmove (fs->buf, fs->buf + wrote, fs->p - fs->buf);
381 if ((size_t) (fs->end - fs->buf) < amount)
382 /* Gotta grow the buffer. */
384 size_t old_size = fs->end - fs->buf;
385 size_t new_size = old_size + amount;
388 if (new_size < old_size || ! (new_buf = realloc (fs->buf, new_size)))
390 __set_errno (ENOMEM);
395 fs->end = new_buf + new_size;
404 __argp_fmtstream_printf (struct argp_fmtstream *fs, const char *fmt, ...)
408 size_t size_guess = PRINTF_SIZE_GUESS; /* How much space to reserve. */
414 if (! __argp_fmtstream_ensure (fs, size_guess))
417 va_start (args, fmt);
418 avail = fs->end - fs->p;
419 out = __vsnprintf (fs->p, avail, fmt, args);
421 if ((size_t) out >= avail)
422 size_guess = out + 1;
424 while ((size_t) out >= avail);
433 weak_alias (__argp_fmtstream_printf, argp_fmtstream_printf)
437 #endif /* !ARGP_FMTSTREAM_USE_LINEWRAP */