]> git.ipfire.org Git - thirdparty/glibc.git/blame - argp/argp-fmtstream.c
(CFLAGS-tst-align.c): Add -mpreferred-stack-boundary=4.
[thirdparty/glibc.git] / argp / argp-fmtstream.c
CommitLineData
c84142e8 1/* Word-wrapping and line-truncating streams
a334319f 2 Copyright (C) 1997,1998,1999,2001,2002,2003 Free Software Foundation, Inc.
c84142e8
UD
3 This file is part of the GNU C Library.
4 Written by Miles Bader <miles@gnu.ai.mit.edu>.
5
6 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
c84142e8
UD
10
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
41bdb6e2 14 Lesser General Public License for more details.
c84142e8 15
41bdb6e2
AJ
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA. */
c84142e8
UD
20
21/* This package emulates glibc `line_wrap_stream' semantics for systems that
22 don't have that. */
23
24#ifdef HAVE_CONFIG_H
a334319f 25#include <config.h>
c84142e8
UD
26#endif
27
28#include <stdlib.h>
29#include <string.h>
30#include <errno.h>
31#include <stdarg.h>
32#include <ctype.h>
33
34#include "argp-fmtstream.h"
35#include "argp-namefrob.h"
36
37#ifndef ARGP_FMTSTREAM_USE_LINEWRAP
38
39#ifndef isblank
40#define isblank(ch) ((ch)==' ' || (ch)=='\t')
41#endif
42
50304ef0 43#if defined _LIBC && defined USE_IN_LIBIO
51028f34 44# include <wchar.h>
08a0d60a 45# include <libio/libioP.h>
50304ef0
UD
46# define __vsnprintf(s, l, f, a) _IO_vsnprintf (s, l, f, a)
47#endif
48
c84142e8
UD
49#define INIT_BUF_SIZE 200
50#define PRINTF_SIZE_GUESS 150
51\f
52/* Return an argp_fmtstream that outputs to STREAM, and which prefixes lines
53 written on it with LMARGIN spaces and limits them to RMARGIN columns
54 total. If WMARGIN >= 0, words that extend past RMARGIN are wrapped by
55 replacing the whitespace before them with a newline and WMARGIN spaces.
56 Otherwise, chars beyond RMARGIN are simply dropped until a newline.
57 Returns NULL if there was an error. */
58argp_fmtstream_t
59__argp_make_fmtstream (FILE *stream,
60 size_t lmargin, size_t rmargin, ssize_t wmargin)
61{
51028f34
UD
62 argp_fmtstream_t fs;
63
64 fs = (struct argp_fmtstream *) malloc (sizeof (struct argp_fmtstream));
65 if (fs != NULL)
c84142e8
UD
66 {
67 fs->stream = stream;
68
69 fs->lmargin = lmargin;
70 fs->rmargin = rmargin;
71 fs->wmargin = wmargin;
72 fs->point_col = 0;
73 fs->point_offs = 0;
74
51028f34 75 fs->buf = (char *) malloc (INIT_BUF_SIZE);
c84142e8
UD
76 if (! fs->buf)
77 {
78 free (fs);
79 fs = 0;
80 }
81 else
82 {
83 fs->p = fs->buf;
84 fs->end = fs->buf + INIT_BUF_SIZE;
85 }
86 }
87
88 return fs;
89}
f5bf21a7
UD
90#if 0
91/* Not exported. */
c84142e8
UD
92#ifdef weak_alias
93weak_alias (__argp_make_fmtstream, argp_make_fmtstream)
94#endif
f5bf21a7 95#endif
c84142e8
UD
96
97/* Flush FS to its stream, and free it (but don't close the stream). */
98void
99__argp_fmtstream_free (argp_fmtstream_t fs)
100{
101 __argp_fmtstream_update (fs);
102 if (fs->p > fs->buf)
51028f34
UD
103 {
104#ifdef USE_IN_LIBIO
a334319f
UD
105 if (_IO_fwide (fs->stream, 0) > 0)
106 __fwprintf (fs->stream, L"%.*s", (int) (fs->p - fs->buf), fs->buf);
107 else
51028f34 108#endif
a334319f 109 fwrite_unlocked (fs->buf, 1, fs->p - fs->buf, fs->stream);
51028f34 110 }
c84142e8
UD
111 free (fs->buf);
112 free (fs);
113}
f5bf21a7
UD
114#if 0
115/* Not exported. */
c84142e8
UD
116#ifdef weak_alias
117weak_alias (__argp_fmtstream_free, argp_fmtstream_free)
118#endif
f5bf21a7 119#endif
c84142e8
UD
120\f
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. */
123void
124__argp_fmtstream_update (argp_fmtstream_t fs)
125{
126 char *buf, *nl;
127 size_t len;
128
129 /* Scan the buffer for newlines. */
130 buf = fs->buf + fs->point_offs;
131 while (buf < fs->p)
132 {
133 size_t r;
134
135 if (fs->point_col == 0 && fs->lmargin != 0)
136 {
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)
140 {
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. */
147 }
148 else
149 {
150 /* No buffer space for spaces. Must flush. */
151 size_t i;
152 for (i = 0; i < pad; i++)
51028f34
UD
153 {
154#ifdef USE_IN_LIBIO
155 if (_IO_fwide (fs->stream, 0) > 0)
156 putwc_unlocked (L' ', fs->stream);
157 else
158#endif
159 putc_unlocked (' ', fs->stream);
160 }
c84142e8
UD
161 }
162 fs->point_col = pad;
163 }
164
165 len = fs->p - buf;
166 nl = memchr (buf, '\n', len);
167
168 if (fs->point_col < 0)
169 fs->point_col = 0;
170
171 if (!nl)
172 {
173 /* The buffer ends in a partial line. */
174
175 if (fs->point_col + len < fs->rmargin)
176 {
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;
181 break;
182 }
183 else
184 /* Set the end-of-line pointer for the code below to
185 the end of the buffer. */
186 nl = fs->p;
187 }
c131718c 188 else if (fs->point_col + (nl - buf) < (ssize_t) fs->rmargin)
c84142e8
UD
189 {
190 /* The buffer contains a full line that fits within the maximum
191 line width. Reset point and scan the next line. */
192 fs->point_col = 0;
193 buf = nl + 1;
194 continue;
195 }
196
197 /* This line is too long. */
198 r = fs->rmargin - 1;
199
200 if (fs->wmargin < 0)
201 {
202 /* Truncate the line by overwriting the excess with the
203 newline and anything after it in the buffer. */
204 if (nl < fs->p)
205 {
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. */
209 fs->point_col = 0;
210 buf += r + 1; /* Skip full line plus \n. */
211 }
212 else
213 {
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;
219 break;
220 }
221 }
222 else
223 {
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. */
227
228 char *p, *nextline;
229 int i;
230
231 p = buf + (r + 1 - fs->point_col);
232 while (p >= buf && !isblank (*p))
233 --p;
234 nextline = p + 1; /* This will begin the next line. */
235
236 if (nextline > buf)
237 {
238 /* Swallow separating blanks. */
2698e32c 239 if (p >= buf)
c84142e8
UD
240 do
241 --p;
2698e32c 242 while (p >= buf && isblank (*p));
c84142e8
UD
243 nl = p + 1; /* The newline will replace the first blank. */
244 }
245 else
246 {
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. */
251 do
252 ++p;
253 while (p < nl && !isblank (*p));
254 if (p == nl)
255 {
256 /* It already ends a line. No fussing required. */
257 fs->point_col = 0;
258 buf = nl + 1;
259 continue;
260 }
261 /* We will move the newline to replace the first blank. */
262 nl = p;
263 /* Swallow separating blanks. */
264 do
265 ++p;
266 while (isblank (*p));
267 /* The next line will start here. */
268 nextline = p;
269 }
270
5a97622d
UD
271 /* Note: There are a bunch of tests below for
272 NEXTLINE == BUF + LEN + 1; this case is where NL happens to fall
273 at the end of the buffer, and NEXTLINE is in fact empty (and so
274 we need not be careful to maintain its contents). */
275
cf155743
UD
276 if ((nextline == buf + len + 1
277 ? fs->end - nl < fs->wmargin + 1
278 : nextline - (nl + 1) < fs->wmargin)
279 && fs->p > nextline)
6e4c40ba
UD
280 {
281 /* The margin needs more blanks than we removed. */
282 if (fs->end - fs->p > fs->wmargin + 1)
283 /* Make some space for them. */
284 {
285 size_t mv = fs->p - nextline;
286 memmove (nl + 1 + fs->wmargin, nextline, mv);
287 nextline = nl + 1 + fs->wmargin;
288 len = nextline + mv - buf;
289 *nl++ = '\n';
290 }
291 else
292 /* Output the first line so we can use the space. */
293 {
a334319f
UD
294#ifdef USE_IN_LIBIO
295 if (_IO_fwide (fs->stream, 0) > 0)
296 __fwprintf (fs->stream, L"%.*s\n",
297 (int) (nl - fs->buf), fs->buf);
298 else
51028f34 299#endif
a334319f
UD
300 {
301 if (nl > fs->buf)
302 fwrite_unlocked (fs->buf, 1, nl - fs->buf, fs->stream);
303 putc_unlocked ('\n', fs->stream);
304 }
6e4c40ba
UD
305 len += buf - fs->buf;
306 nl = buf = fs->buf;
307 }
308 }
c84142e8
UD
309 else
310 /* We can fit the newline and blanks in before
311 the next word. */
312 *nl++ = '\n';
313
5a97622d
UD
314 if (nextline - nl >= fs->wmargin
315 || (nextline == buf + len + 1 && fs->end - nextline >= fs->wmargin))
c84142e8
UD
316 /* Add blanks up to the wrap margin column. */
317 for (i = 0; i < fs->wmargin; ++i)
318 *nl++ = ' ';
319 else
320 for (i = 0; i < fs->wmargin; ++i)
51028f34
UD
321#ifdef USE_IN_LIBIO
322 if (_IO_fwide (fs->stream, 0) > 0)
323 putwc_unlocked (L' ', fs->stream);
324 else
325#endif
326 putc_unlocked (' ', fs->stream);
c84142e8
UD
327
328 /* Copy the tail of the original buffer into the current buffer
329 position. */
5a97622d 330 if (nl < nextline)
c84142e8
UD
331 memmove (nl, nextline, buf + len - nextline);
332 len -= nextline - buf;
333
334 /* Continue the scan on the remaining lines in the buffer. */
335 buf = nl;
336
337 /* Restore bufp to include all the remaining text. */
338 fs->p = nl + len;
339
340 /* Reset the counter of what has been output this line. If wmargin
341 is 0, we want to avoid the lmargin getting added, so we set
342 point_col to a magic value of -1 in that case. */
343 fs->point_col = fs->wmargin ? fs->wmargin : -1;
344 }
345 }
346
347 /* Remember that we've scanned as far as the end of the buffer. */
348 fs->point_offs = fs->p - fs->buf;
349}
350\f
351/* Ensure that FS has space for AMOUNT more bytes in its buffer, either by
352 growing the buffer, or by flushing it. True is returned iff we succeed. */
353int
354__argp_fmtstream_ensure (struct argp_fmtstream *fs, size_t amount)
355{
c131718c 356 if ((size_t) (fs->end - fs->p) < amount)
c84142e8
UD
357 {
358 ssize_t wrote;
359
360 /* Flush FS's buffer. */
361 __argp_fmtstream_update (fs);
362
a334319f
UD
363#ifdef USE_IN_LIBIO
364 if (_IO_fwide (fs->stream, 0) > 0)
365 {
366 __fwprintf (fs->stream, L"%.*s", (int) (fs->p - fs->buf), fs->buf);
367 wrote = fs->p - fs->buf;
368 }
369 else
51028f34 370#endif
a334319f 371 wrote = fwrite_unlocked (fs->buf, 1, fs->p - fs->buf, fs->stream);
c84142e8
UD
372 if (wrote == fs->p - fs->buf)
373 {
374 fs->p = fs->buf;
375 fs->point_offs = 0;
376 }
377 else
378 {
379 fs->p -= wrote;
380 fs->point_offs -= wrote;
381 memmove (fs->buf, fs->buf + wrote, fs->p - fs->buf);
382 return 0;
383 }
384
c131718c 385 if ((size_t) (fs->end - fs->buf) < amount)
c84142e8
UD
386 /* Gotta grow the buffer. */
387 {
c706f2a3
UD
388 size_t old_size = fs->end - fs->buf;
389 size_t new_size = old_size + amount;
390 char *new_buf;
c84142e8 391
c706f2a3 392 if (new_size < old_size || ! (new_buf = realloc (fs->buf, new_size)))
c84142e8
UD
393 {
394 __set_errno (ENOMEM);
395 return 0;
396 }
397
398 fs->buf = new_buf;
399 fs->end = new_buf + new_size;
400 fs->p = fs->buf;
401 }
402 }
403
404 return 1;
405}
406\f
407ssize_t
408__argp_fmtstream_printf (struct argp_fmtstream *fs, const char *fmt, ...)
409{
c131718c 410 int out;
b9d3d9f7 411 size_t avail;
c84142e8
UD
412 size_t size_guess = PRINTF_SIZE_GUESS; /* How much space to reserve. */
413
414 do
415 {
416 va_list args;
417
418 if (! __argp_fmtstream_ensure (fs, size_guess))
419 return -1;
c84142e8
UD
420
421 va_start (args, fmt);
b9d3d9f7
UD
422 avail = fs->end - fs->p;
423 out = __vsnprintf (fs->p, avail, fmt, args);
c84142e8 424 va_end (args);
57b36a0a 425 if ((size_t) out >= avail)
b9d3d9f7 426 size_guess = out + 1;
c84142e8 427 }
57b36a0a 428 while ((size_t) out >= avail);
c84142e8
UD
429
430 fs->p += out;
431
432 return out;
433}
f5bf21a7
UD
434#if 0
435/* Not exported. */
c84142e8
UD
436#ifdef weak_alias
437weak_alias (__argp_fmtstream_printf, argp_fmtstream_printf)
438#endif
f5bf21a7 439#endif
c84142e8
UD
440
441#endif /* !ARGP_FMTSTREAM_USE_LINEWRAP */