]> git.cworth.org Git - tar/blob - gnu/argp-fmtstream.c
upstream: Fix extraction of device nodes.
[tar] / gnu / argp-fmtstream.c
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
5    Foundation, Inc.
6    This file is part of the GNU C Library.
7    Written by Miles Bader <miles@gnu.ai.mit.edu>.
8
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.
13
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.
18
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/>.  */
21
22 /* This package emulates glibc `line_wrap_stream' semantics for systems that
23    don't have that.  */
24
25 #ifdef HAVE_CONFIG_H
26 # include <config.h>
27 #endif
28
29 #include <stdlib.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <stdarg.h>
33 #include <ctype.h>
34
35 #include "argp-fmtstream.h"
36 #include "argp-namefrob.h"
37
38 #ifndef ARGP_FMTSTREAM_USE_LINEWRAP
39
40 #ifndef isblank
41 #define isblank(ch) ((ch)==' ' || (ch)=='\t')
42 #endif
43
44 #if defined _LIBC && defined USE_IN_LIBIO
45 # include <wchar.h>
46 # include <libio/libioP.h>
47 # define __vsnprintf(s, l, f, a) _IO_vsnprintf (s, l, f, a)
48 #endif
49
50 #define INIT_BUF_SIZE 200
51 #define PRINTF_SIZE_GUESS 150
52 \f
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.  */
59 argp_fmtstream_t
60 __argp_make_fmtstream (FILE *stream,
61                        size_t lmargin, size_t rmargin, ssize_t wmargin)
62 {
63   argp_fmtstream_t fs;
64
65   fs = (struct argp_fmtstream *) malloc (sizeof (struct argp_fmtstream));
66   if (fs != NULL)
67     {
68       fs->stream = stream;
69
70       fs->lmargin = lmargin;
71       fs->rmargin = rmargin;
72       fs->wmargin = wmargin;
73       fs->point_col = 0;
74       fs->point_offs = 0;
75
76       fs->buf = (char *) malloc (INIT_BUF_SIZE);
77       if (! fs->buf)
78         {
79           free (fs);
80           fs = 0;
81         }
82       else
83         {
84           fs->p = fs->buf;
85           fs->end = fs->buf + INIT_BUF_SIZE;
86         }
87     }
88
89   return fs;
90 }
91 #if 0
92 /* Not exported.  */
93 #ifdef weak_alias
94 weak_alias (__argp_make_fmtstream, argp_make_fmtstream)
95 #endif
96 #endif
97
98 /* Flush FS to its stream, and free it (but don't close the stream).  */
99 void
100 __argp_fmtstream_free (argp_fmtstream_t fs)
101 {
102   __argp_fmtstream_update (fs);
103   if (fs->p > fs->buf)
104     {
105 #ifdef USE_IN_LIBIO
106       __fxprintf (fs->stream, "%.*s", (int) (fs->p - fs->buf), fs->buf);
107 #else
108       fwrite_unlocked (fs->buf, 1, fs->p - fs->buf, fs->stream);
109 #endif
110     }
111   free (fs->buf);
112   free (fs);
113 }
114 #if 0
115 /* Not exported.  */
116 #ifdef weak_alias
117 weak_alias (__argp_fmtstream_free, argp_fmtstream_free)
118 #endif
119 #endif
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.  */
123 void
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++)
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                 }
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         }
188       else if (fs->point_col + (nl - buf) < (ssize_t) fs->rmargin)
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 ((unsigned char) *p))
233             --p;
234           nextline = p + 1;     /* This will begin the next line.  */
235
236           if (nextline > buf)
237             {
238               /* Swallow separating blanks.  */
239               if (p >= buf)
240                 do
241                   --p;
242                 while (p >= buf && isblank ((unsigned char) *p));
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               if (p < nl)
252                 do
253                   ++p;
254                 while (p < nl && !isblank ((unsigned char) *p));
255               if (p == nl)
256                 {
257                   /* It already ends a line.  No fussing required.  */
258                   fs->point_col = 0;
259                   buf = nl + 1;
260                   continue;
261                 }
262               /* We will move the newline to replace the first blank.  */
263               nl = p;
264               /* Swallow separating blanks.  */
265               do
266                 ++p;
267               while (isblank ((unsigned char) *p));
268               /* The next line will start here.  */
269               nextline = p;
270             }
271
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).  */
276
277           if ((nextline == buf + len + 1
278                ? fs->end - nl < fs->wmargin + 1
279                : nextline - (nl + 1) < fs->wmargin)
280               && fs->p > nextline)
281             {
282               /* The margin needs more blanks than we removed.  */
283               if (fs->end - fs->p > fs->wmargin + 1)
284                 /* Make some space for them.  */
285                 {
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;
290                   *nl++ = '\n';
291                 }
292               else
293                 /* Output the first line so we can use the space.  */
294                 {
295 #ifdef _LIBC
296                   __fxprintf (fs->stream, "%.*s\n",
297                               (int) (nl - fs->buf), fs->buf);
298 #else
299                   if (nl > fs->buf)
300                     fwrite_unlocked (fs->buf, 1, nl - fs->buf, fs->stream);
301                   putc_unlocked ('\n', fs->stream);
302 #endif
303
304                   len += buf - fs->buf;
305                   nl = buf = fs->buf;
306                 }
307             }
308           else
309             /* We can fit the newline and blanks in before
310                the next word.  */
311             *nl++ = '\n';
312
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)
317               *nl++ = ' ';
318           else
319             for (i = 0; i < fs->wmargin; ++i)
320 #ifdef USE_IN_LIBIO
321               if (_IO_fwide (fs->stream, 0) > 0)
322                 putwc_unlocked (L' ', fs->stream);
323               else
324 #endif
325                 putc_unlocked (' ', fs->stream);
326
327           /* Copy the tail of the original buffer into the current buffer
328              position.  */
329           if (nl < nextline)
330             memmove (nl, nextline, buf + len - nextline);
331           len -= nextline - buf;
332
333           /* Continue the scan on the remaining lines in the buffer.  */
334           buf = nl;
335
336           /* Restore bufp to include all the remaining text.  */
337           fs->p = nl + len;
338
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;
343         }
344     }
345
346   /* Remember that we've scanned as far as the end of the buffer.  */
347   fs->point_offs = fs->p - fs->buf;
348 }
349 \f
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. */
352 int
353 __argp_fmtstream_ensure (struct argp_fmtstream *fs, size_t amount)
354 {
355   if ((size_t) (fs->end - fs->p) < amount)
356     {
357       ssize_t wrote;
358
359       /* Flush FS's buffer.  */
360       __argp_fmtstream_update (fs);
361
362 #ifdef _LIBC
363       __fxprintf (fs->stream, "%.*s", (int) (fs->p - fs->buf), fs->buf);
364       wrote = fs->p - fs->buf;
365 #else
366       wrote = fwrite_unlocked (fs->buf, 1, fs->p - fs->buf, fs->stream);
367 #endif
368       if (wrote == fs->p - fs->buf)
369         {
370           fs->p = fs->buf;
371           fs->point_offs = 0;
372         }
373       else
374         {
375           fs->p -= wrote;
376           fs->point_offs -= wrote;
377           memmove (fs->buf, fs->buf + wrote, fs->p - fs->buf);
378           return 0;
379         }
380
381       if ((size_t) (fs->end - fs->buf) < amount)
382         /* Gotta grow the buffer.  */
383         {
384           size_t old_size = fs->end - fs->buf;
385           size_t new_size = old_size + amount;
386           char *new_buf;
387
388           if (new_size < old_size || ! (new_buf = realloc (fs->buf, new_size)))
389             {
390               __set_errno (ENOMEM);
391               return 0;
392             }
393
394           fs->buf = new_buf;
395           fs->end = new_buf + new_size;
396           fs->p = fs->buf;
397         }
398     }
399
400   return 1;
401 }
402 \f
403 ssize_t
404 __argp_fmtstream_printf (struct argp_fmtstream *fs, const char *fmt, ...)
405 {
406   int out;
407   size_t avail;
408   size_t size_guess = PRINTF_SIZE_GUESS; /* How much space to reserve. */
409
410   do
411     {
412       va_list args;
413
414       if (! __argp_fmtstream_ensure (fs, size_guess))
415         return -1;
416
417       va_start (args, fmt);
418       avail = fs->end - fs->p;
419       out = __vsnprintf (fs->p, avail, fmt, args);
420       va_end (args);
421       if ((size_t) out >= avail)
422         size_guess = out + 1;
423     }
424   while ((size_t) out >= avail);
425
426   fs->p += out;
427
428   return out;
429 }
430 #if 0
431 /* Not exported.  */
432 #ifdef weak_alias
433 weak_alias (__argp_fmtstream_printf, argp_fmtstream_printf)
434 #endif
435 #endif
436
437 #endif /* !ARGP_FMTSTREAM_USE_LINEWRAP */