source: trunk/essentials/app-arch/tar/lib/argp-fmtstream.c

Last change on this file was 3342, checked in by bird, 18 years ago

tar 1.16.1

File size: 11.2 KB
Line 
1/* Word-wrapping and line-truncating streams
2 Copyright (C) 1997-1999,2001,2002,2003,2005 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Written by Miles Bader <miles@gnu.ai.mit.edu>.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 This program 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
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation,
18 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
19
20/* This package emulates glibc `line_wrap_stream' semantics for systems that
21 don't have that. */
22
23#ifdef HAVE_CONFIG_H
24# include <config.h>
25#endif
26
27#include <stdlib.h>
28#include <string.h>
29#include <errno.h>
30#include <stdarg.h>
31#include <ctype.h>
32
33#include "argp-fmtstream.h"
34#include "argp-namefrob.h"
35
36#ifndef ARGP_FMTSTREAM_USE_LINEWRAP
37
38#ifndef isblank
39#define isblank(ch) ((ch)==' ' || (ch)=='\t')
40#endif
41
42#if defined _LIBC && defined USE_IN_LIBIO
43# include <wchar.h>
44# include <libio/libioP.h>
45# define __vsnprintf(s, l, f, a) _IO_vsnprintf (s, l, f, a)
46#endif
47
48#define INIT_BUF_SIZE 200
49#define PRINTF_SIZE_GUESS 150
50
51
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{
62 argp_fmtstream_t fs;
63
64 fs = (struct argp_fmtstream *) malloc (sizeof (struct argp_fmtstream));
65 if (fs != NULL)
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
75 fs->buf = (char *) malloc (INIT_BUF_SIZE);
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}
90#if 0
91/* Not exported. */
92#ifdef weak_alias
93weak_alias (__argp_make_fmtstream, argp_make_fmtstream)
94#endif
95#endif
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)
103 {
104#ifdef USE_IN_LIBIO
105 __fxprintf (fs->stream, "%.*s", (int) (fs->p - fs->buf), fs->buf);
106#else
107 fwrite_unlocked (fs->buf, 1, fs->p - fs->buf, fs->stream);
108#endif
109 }
110 free (fs->buf);
111 free (fs);
112}
113#if 0
114/* Not exported. */
115#ifdef weak_alias
116weak_alias (__argp_fmtstream_free, argp_fmtstream_free)
117#endif
118#endif
119
120
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++)
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 (*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 (*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 (*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 (*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
350
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{
356 if ((size_t) (fs->end - fs->p) < amount)
357 {
358 ssize_t wrote;
359
360 /* Flush FS's buffer. */
361 __argp_fmtstream_update (fs);
362
363#ifdef _LIBC
364 __fxprintf (fs->stream, "%.*s", (int) (fs->p - fs->buf), fs->buf);
365 wrote = fs->p - fs->buf;
366#else
367 wrote = fwrite_unlocked (fs->buf, 1, fs->p - fs->buf, fs->stream);
368#endif
369 if (wrote == fs->p - fs->buf)
370 {
371 fs->p = fs->buf;
372 fs->point_offs = 0;
373 }
374 else
375 {
376 fs->p -= wrote;
377 fs->point_offs -= wrote;
378 memmove (fs->buf, fs->buf + wrote, fs->p - fs->buf);
379 return 0;
380 }
381
382 if ((size_t) (fs->end - fs->buf) < amount)
383 /* Gotta grow the buffer. */
384 {
385 size_t old_size = fs->end - fs->buf;
386 size_t new_size = old_size + amount;
387 char *new_buf;
388
389 if (new_size < old_size || ! (new_buf = realloc (fs->buf, new_size)))
390 {
391 __set_errno (ENOMEM);
392 return 0;
393 }
394
395 fs->buf = new_buf;
396 fs->end = new_buf + new_size;
397 fs->p = fs->buf;
398 }
399 }
400
401 return 1;
402}
403
404
405ssize_t
406__argp_fmtstream_printf (struct argp_fmtstream *fs, const char *fmt, ...)
407{
408 int out;
409 size_t avail;
410 size_t size_guess = PRINTF_SIZE_GUESS; /* How much space to reserve. */
411
412 do
413 {
414 va_list args;
415
416 if (! __argp_fmtstream_ensure (fs, size_guess))
417 return -1;
418
419 va_start (args, fmt);
420 avail = fs->end - fs->p;
421 out = __vsnprintf (fs->p, avail, fmt, args);
422 va_end (args);
423 if ((size_t) out >= avail)
424 size_guess = out + 1;
425 }
426 while ((size_t) out >= avail);
427
428 fs->p += out;
429
430 return out;
431}
432#if 0
433/* Not exported. */
434#ifdef weak_alias
435weak_alias (__argp_fmtstream_printf, argp_fmtstream_printf)
436#endif
437#endif
438
439#endif /* !ARGP_FMTSTREAM_USE_LINEWRAP */
Note: See TracBrowser for help on using the repository browser.