1 | /* Word-wrapping and line-truncating streams |
---|
2 | Copyright (C) 1997, 1998, 1999, 2001 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 | The GNU C Library is free software; you can redistribute it and/or |
---|
7 | modify it under the terms of the GNU Library General Public License as |
---|
8 | published by the Free Software Foundation; either version 2 of the |
---|
9 | License, or (at your option) any later version. |
---|
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 |
---|
14 | Library General Public License for more details. |
---|
15 | |
---|
16 | You should have received a copy of the GNU Library General Public |
---|
17 | License along with the GNU C Library; see the file COPYING.LIB. If not, |
---|
18 | write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
---|
19 | Boston, MA 02111-1307, USA. */ |
---|
20 | |
---|
21 | /* This package emulates glibc `line_wrap_stream' semantics for systems that |
---|
22 | don't have that. */ |
---|
23 | |
---|
24 | #ifdef HAVE_CONFIG_H |
---|
25 | #include <config.h> |
---|
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 "src/lib/argp/argp-fmtstream.h" |
---|
35 | #include "src/lib/argp/argp-namefrob.h" |
---|
36 | |
---|
37 | #ifndef ARGP_FMTSTREAM_USE_LINEWRAP |
---|
38 | |
---|
39 | #ifndef isblank |
---|
40 | #define isblank(ch) ((ch)==' ' || (ch)=='\t') |
---|
41 | #endif |
---|
42 | |
---|
43 | #if defined _LIBC && defined USE_IN_LIBIO |
---|
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 | /* Return an argp_fmtstream that outputs to STREAM, and which prefixes lines |
---|
52 | written on it with LMARGIN spaces and limits them to RMARGIN columns |
---|
53 | total. If WMARGIN >= 0, words that extend past RMARGIN are wrapped by |
---|
54 | replacing the whitespace before them with a newline and WMARGIN spaces. |
---|
55 | Otherwise, chars beyond RMARGIN are simply dropped until a newline. |
---|
56 | Returns NULL if there was an error. */ |
---|
57 | argp_fmtstream_t |
---|
58 | __argp_make_fmtstream (FILE *stream, |
---|
59 | size_t lmargin, size_t rmargin, ssize_t wmargin) |
---|
60 | { |
---|
61 | argp_fmtstream_t fs = malloc (sizeof (struct argp_fmtstream)); |
---|
62 | if (fs) |
---|
63 | { |
---|
64 | fs->stream = stream; |
---|
65 | |
---|
66 | fs->lmargin = lmargin; |
---|
67 | fs->rmargin = rmargin; |
---|
68 | fs->wmargin = wmargin; |
---|
69 | fs->point_col = 0; |
---|
70 | fs->point_offs = 0; |
---|
71 | |
---|
72 | fs->buf = malloc (INIT_BUF_SIZE); |
---|
73 | if (! fs->buf) |
---|
74 | { |
---|
75 | free (fs); |
---|
76 | fs = 0; |
---|
77 | } |
---|
78 | else |
---|
79 | { |
---|
80 | fs->p = fs->buf; |
---|
81 | fs->end = fs->buf + INIT_BUF_SIZE; |
---|
82 | } |
---|
83 | } |
---|
84 | |
---|
85 | return fs; |
---|
86 | } |
---|
87 | #ifdef weak_alias |
---|
88 | weak_alias (__argp_make_fmtstream, argp_make_fmtstream) |
---|
89 | #endif |
---|
90 | |
---|
91 | /* Flush FS to its stream, and free it (but don't close the stream). */ |
---|
92 | void |
---|
93 | __argp_fmtstream_free (argp_fmtstream_t fs) |
---|
94 | { |
---|
95 | __argp_fmtstream_update (fs); |
---|
96 | if (fs->p > fs->buf) |
---|
97 | FWRITE_UNLOCKED (fs->buf, 1, fs->p - fs->buf, fs->stream); |
---|
98 | free (fs->buf); |
---|
99 | free (fs); |
---|
100 | } |
---|
101 | #ifdef weak_alias |
---|
102 | weak_alias (__argp_fmtstream_free, argp_fmtstream_free) |
---|
103 | #endif |
---|
104 | |
---|
105 | /* Process FS's buffer so that line wrapping is done from POINT_OFFS to the |
---|
106 | end of its buffer. This code is mostly from glibc stdio/linewrap.c. */ |
---|
107 | void |
---|
108 | __argp_fmtstream_update (argp_fmtstream_t fs) |
---|
109 | { |
---|
110 | char *buf, *nl; |
---|
111 | size_t len; |
---|
112 | |
---|
113 | /* Scan the buffer for newlines. */ |
---|
114 | buf = fs->buf + fs->point_offs; |
---|
115 | while (buf < fs->p) |
---|
116 | { |
---|
117 | size_t r; |
---|
118 | |
---|
119 | if (fs->point_col == 0 && fs->lmargin != 0) |
---|
120 | { |
---|
121 | /* We are starting a new line. Print spaces to the left margin. */ |
---|
122 | const size_t pad = fs->lmargin; |
---|
123 | if (fs->p + pad < fs->end) |
---|
124 | { |
---|
125 | /* We can fit in them in the buffer by moving the |
---|
126 | buffer text up and filling in the beginning. */ |
---|
127 | memmove (buf + pad, buf, fs->p - buf); |
---|
128 | fs->p += pad; /* Compensate for bigger buffer. */ |
---|
129 | memset (buf, ' ', pad); /* Fill in the spaces. */ |
---|
130 | buf += pad; /* Don't bother searching them. */ |
---|
131 | } |
---|
132 | else |
---|
133 | { |
---|
134 | /* No buffer space for spaces. Must flush. */ |
---|
135 | size_t i; |
---|
136 | for (i = 0; i < pad; i++) |
---|
137 | PUTC_UNLOCKED (' ', fs->stream); |
---|
138 | } |
---|
139 | fs->point_col = pad; |
---|
140 | } |
---|
141 | |
---|
142 | len = fs->p - buf; |
---|
143 | nl = memchr (buf, '\n', len); |
---|
144 | |
---|
145 | if (fs->point_col < 0) |
---|
146 | fs->point_col = 0; |
---|
147 | |
---|
148 | if (!nl) |
---|
149 | { |
---|
150 | /* The buffer ends in a partial line. */ |
---|
151 | |
---|
152 | if (fs->point_col + len < fs->rmargin) |
---|
153 | { |
---|
154 | /* The remaining buffer text is a partial line and fits |
---|
155 | within the maximum line width. Advance point for the |
---|
156 | characters to be written and stop scanning. */ |
---|
157 | fs->point_col += len; |
---|
158 | break; |
---|
159 | } |
---|
160 | else |
---|
161 | /* Set the end-of-line pointer for the code below to |
---|
162 | the end of the buffer. */ |
---|
163 | nl = fs->p; |
---|
164 | } |
---|
165 | else if (fs->point_col + (nl - buf) < (ssize_t) fs->rmargin) |
---|
166 | { |
---|
167 | /* The buffer contains a full line that fits within the maximum |
---|
168 | line width. Reset point and scan the next line. */ |
---|
169 | fs->point_col = 0; |
---|
170 | buf = nl + 1; |
---|
171 | continue; |
---|
172 | } |
---|
173 | |
---|
174 | /* This line is too long. */ |
---|
175 | r = fs->rmargin - 1; |
---|
176 | |
---|
177 | if (fs->wmargin < 0) |
---|
178 | { |
---|
179 | /* Truncate the line by overwriting the excess with the |
---|
180 | newline and anything after it in the buffer. */ |
---|
181 | if (nl < fs->p) |
---|
182 | { |
---|
183 | memmove (buf + (r - fs->point_col), nl, fs->p - nl); |
---|
184 | fs->p -= buf + (r - fs->point_col) - nl; |
---|
185 | /* Reset point for the next line and start scanning it. */ |
---|
186 | fs->point_col = 0; |
---|
187 | buf += r + 1; /* Skip full line plus \n. */ |
---|
188 | } |
---|
189 | else |
---|
190 | { |
---|
191 | /* The buffer ends with a partial line that is beyond the |
---|
192 | maximum line width. Advance point for the characters |
---|
193 | written, and discard those past the max from the buffer. */ |
---|
194 | fs->point_col += len; |
---|
195 | fs->p -= fs->point_col - r; |
---|
196 | break; |
---|
197 | } |
---|
198 | } |
---|
199 | else |
---|
200 | { |
---|
201 | /* Do word wrap. Go to the column just past the maximum line |
---|
202 | width and scan back for the beginning of the word there. |
---|
203 | Then insert a line break. */ |
---|
204 | |
---|
205 | char *p, *nextline; |
---|
206 | int i; |
---|
207 | |
---|
208 | p = buf + (r + 1 - fs->point_col); |
---|
209 | while (p >= buf && !isblank (*p)) |
---|
210 | --p; |
---|
211 | nextline = p + 1; /* This will begin the next line. */ |
---|
212 | |
---|
213 | if (nextline > buf) |
---|
214 | { |
---|
215 | /* Swallow separating blanks. */ |
---|
216 | if (p >= buf) |
---|
217 | do |
---|
218 | --p; |
---|
219 | while (p >= buf && isblank (*p)); |
---|
220 | nl = p + 1; /* The newline will replace the first blank. */ |
---|
221 | } |
---|
222 | else |
---|
223 | { |
---|
224 | /* A single word that is greater than the maximum line width. |
---|
225 | Oh well. Put it on an overlong line by itself. */ |
---|
226 | p = buf + (r + 1 - fs->point_col); |
---|
227 | /* Find the end of the long word. */ |
---|
228 | do |
---|
229 | ++p; |
---|
230 | while (p < nl && !isblank (*p)); |
---|
231 | if (p == nl) |
---|
232 | { |
---|
233 | /* It already ends a line. No fussing required. */ |
---|
234 | fs->point_col = 0; |
---|
235 | buf = nl + 1; |
---|
236 | continue; |
---|
237 | } |
---|
238 | /* We will move the newline to replace the first blank. */ |
---|
239 | nl = p; |
---|
240 | /* Swallow separating blanks. */ |
---|
241 | do |
---|
242 | ++p; |
---|
243 | while (isblank (*p)); |
---|
244 | /* The next line will start here. */ |
---|
245 | nextline = p; |
---|
246 | } |
---|
247 | |
---|
248 | /* Note: There are a bunch of tests below for |
---|
249 | NEXTLINE == BUF + LEN + 1; this case is where NL happens to fall |
---|
250 | at the end of the buffer, and NEXTLINE is in fact empty (and so |
---|
251 | we need not be careful to maintain its contents). */ |
---|
252 | |
---|
253 | if (nextline == buf + len + 1 |
---|
254 | ? fs->end - nl < fs->wmargin + 1 |
---|
255 | : nextline - (nl + 1) < fs->wmargin) |
---|
256 | { |
---|
257 | /* The margin needs more blanks than we removed. */ |
---|
258 | if (fs->end - fs->p > fs->wmargin + 1) |
---|
259 | /* Make some space for them. */ |
---|
260 | { |
---|
261 | size_t mv = fs->p - nextline; |
---|
262 | memmove (nl + 1 + fs->wmargin, nextline, mv); |
---|
263 | nextline = nl + 1 + fs->wmargin; |
---|
264 | len = nextline + mv - buf; |
---|
265 | *nl++ = '\n'; |
---|
266 | } |
---|
267 | else |
---|
268 | /* Output the first line so we can use the space. */ |
---|
269 | { |
---|
270 | if (nl > fs->buf) |
---|
271 | FWRITE_UNLOCKED (fs->buf, 1, nl - fs->buf, fs->stream); |
---|
272 | PUTC_UNLOCKED ('\n', fs->stream); |
---|
273 | len += buf - fs->buf; |
---|
274 | nl = buf = fs->buf; |
---|
275 | } |
---|
276 | } |
---|
277 | else |
---|
278 | /* We can fit the newline and blanks in before |
---|
279 | the next word. */ |
---|
280 | *nl++ = '\n'; |
---|
281 | |
---|
282 | if (nextline - nl >= fs->wmargin |
---|
283 | || (nextline == buf + len + 1 && fs->end - nextline >= fs->wmargin)) |
---|
284 | /* Add blanks up to the wrap margin column. */ |
---|
285 | for (i = 0; i < fs->wmargin; ++i) |
---|
286 | *nl++ = ' '; |
---|
287 | else |
---|
288 | for (i = 0; i < fs->wmargin; ++i) |
---|
289 | PUTC_UNLOCKED (' ', fs->stream); |
---|
290 | |
---|
291 | /* Copy the tail of the original buffer into the current buffer |
---|
292 | position. */ |
---|
293 | if (nl < nextline) |
---|
294 | memmove (nl, nextline, buf + len - nextline); |
---|
295 | len -= nextline - buf; |
---|
296 | |
---|
297 | /* Continue the scan on the remaining lines in the buffer. */ |
---|
298 | buf = nl; |
---|
299 | |
---|
300 | /* Restore bufp to include all the remaining text. */ |
---|
301 | fs->p = nl + len; |
---|
302 | |
---|
303 | /* Reset the counter of what has been output this line. If wmargin |
---|
304 | is 0, we want to avoid the lmargin getting added, so we set |
---|
305 | point_col to a magic value of -1 in that case. */ |
---|
306 | fs->point_col = fs->wmargin ? fs->wmargin : -1; |
---|
307 | } |
---|
308 | } |
---|
309 | |
---|
310 | /* Remember that we've scanned as far as the end of the buffer. */ |
---|
311 | fs->point_offs = fs->p - fs->buf; |
---|
312 | } |
---|
313 | |
---|
314 | /* Ensure that FS has space for AMOUNT more bytes in its buffer, either by |
---|
315 | growing the buffer, or by flushing it. True is returned iff we succeed. */ |
---|
316 | int |
---|
317 | __argp_fmtstream_ensure (struct argp_fmtstream *fs, size_t amount) |
---|
318 | { |
---|
319 | if ((size_t) (fs->end - fs->p) < amount) |
---|
320 | { |
---|
321 | ssize_t wrote; |
---|
322 | |
---|
323 | /* Flush FS's buffer. */ |
---|
324 | __argp_fmtstream_update (fs); |
---|
325 | |
---|
326 | wrote = FWRITE_UNLOCKED (fs->buf, 1, fs->p - fs->buf, fs->stream); |
---|
327 | if (wrote == fs->p - fs->buf) |
---|
328 | { |
---|
329 | fs->p = fs->buf; |
---|
330 | fs->point_offs = 0; |
---|
331 | } |
---|
332 | else |
---|
333 | { |
---|
334 | fs->p -= wrote; |
---|
335 | fs->point_offs -= wrote; |
---|
336 | memmove (fs->buf, fs->buf + wrote, fs->p - fs->buf); |
---|
337 | return 0; |
---|
338 | } |
---|
339 | |
---|
340 | if ((size_t) (fs->end - fs->buf) < amount) |
---|
341 | /* Gotta grow the buffer. */ |
---|
342 | { |
---|
343 | size_t new_size = fs->end - fs->buf + amount; |
---|
344 | char *new_buf = realloc (fs->buf, new_size); |
---|
345 | |
---|
346 | if (! new_buf) |
---|
347 | { |
---|
348 | __set_errno (ENOMEM); |
---|
349 | return 0; |
---|
350 | } |
---|
351 | |
---|
352 | fs->buf = new_buf; |
---|
353 | fs->end = new_buf + new_size; |
---|
354 | fs->p = fs->buf; |
---|
355 | } |
---|
356 | } |
---|
357 | |
---|
358 | return 1; |
---|
359 | } |
---|
360 | |
---|
361 | ssize_t |
---|
362 | __argp_fmtstream_printf (struct argp_fmtstream *fs, const char *fmt, ...) |
---|
363 | { |
---|
364 | size_t out; |
---|
365 | size_t avail; |
---|
366 | size_t size_guess = PRINTF_SIZE_GUESS; /* How much space to reserve. */ |
---|
367 | |
---|
368 | do |
---|
369 | { |
---|
370 | va_list args; |
---|
371 | |
---|
372 | if (! __argp_fmtstream_ensure (fs, size_guess)) |
---|
373 | return -1; |
---|
374 | |
---|
375 | va_start (args, fmt); |
---|
376 | avail = fs->end - fs->p; |
---|
377 | out = __vsnprintf (fs->p, avail, fmt, args); |
---|
378 | va_end (args); |
---|
379 | if (out >= avail) |
---|
380 | size_guess = out + 1; |
---|
381 | } |
---|
382 | while (out >= avail); |
---|
383 | |
---|
384 | fs->p += out; |
---|
385 | |
---|
386 | return out; |
---|
387 | } |
---|
388 | #ifdef weak_alias |
---|
389 | weak_alias (__argp_fmtstream_printf, argp_fmtstream_printf) |
---|
390 | #endif |
---|
391 | |
---|
392 | /* Duplicate the inline definitions in argp-fmtstream.h, for compilers |
---|
393 | * that don't do inlining. */ |
---|
394 | size_t |
---|
395 | __argp_fmtstream_write (argp_fmtstream_t __fs, |
---|
396 | __const char *__str, size_t __len) |
---|
397 | { |
---|
398 | if (__fs->p + __len <= __fs->end || __argp_fmtstream_ensure (__fs, __len)) |
---|
399 | { |
---|
400 | memcpy (__fs->p, __str, __len); |
---|
401 | __fs->p += __len; |
---|
402 | return __len; |
---|
403 | } |
---|
404 | else |
---|
405 | return 0; |
---|
406 | } |
---|
407 | |
---|
408 | int |
---|
409 | __argp_fmtstream_puts (argp_fmtstream_t __fs, __const char *__str) |
---|
410 | { |
---|
411 | size_t __len = strlen (__str); |
---|
412 | if (__len) |
---|
413 | { |
---|
414 | size_t __wrote = __argp_fmtstream_write (__fs, __str, __len); |
---|
415 | return __wrote == __len ? 0 : -1; |
---|
416 | } |
---|
417 | else |
---|
418 | return 0; |
---|
419 | } |
---|
420 | |
---|
421 | int |
---|
422 | __argp_fmtstream_putc (argp_fmtstream_t __fs, int __ch) |
---|
423 | { |
---|
424 | if (__fs->p < __fs->end || __argp_fmtstream_ensure (__fs, 1)) |
---|
425 | return *__fs->p++ = __ch; |
---|
426 | else |
---|
427 | return EOF; |
---|
428 | } |
---|
429 | |
---|
430 | /* Set __FS's left margin to __LMARGIN and return the old value. */ |
---|
431 | size_t |
---|
432 | __argp_fmtstream_set_lmargin (argp_fmtstream_t __fs, size_t __lmargin) |
---|
433 | { |
---|
434 | size_t __old; |
---|
435 | if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) |
---|
436 | __argp_fmtstream_update (__fs); |
---|
437 | __old = __fs->lmargin; |
---|
438 | __fs->lmargin = __lmargin; |
---|
439 | return __old; |
---|
440 | } |
---|
441 | |
---|
442 | /* Set __FS's right margin to __RMARGIN and return the old value. */ |
---|
443 | size_t |
---|
444 | __argp_fmtstream_set_rmargin (argp_fmtstream_t __fs, size_t __rmargin) |
---|
445 | { |
---|
446 | size_t __old; |
---|
447 | if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) |
---|
448 | __argp_fmtstream_update (__fs); |
---|
449 | __old = __fs->rmargin; |
---|
450 | __fs->rmargin = __rmargin; |
---|
451 | return __old; |
---|
452 | } |
---|
453 | |
---|
454 | /* Set FS's wrap margin to __WMARGIN and return the old value. */ |
---|
455 | size_t |
---|
456 | __argp_fmtstream_set_wmargin (argp_fmtstream_t __fs, size_t __wmargin) |
---|
457 | { |
---|
458 | size_t __old; |
---|
459 | if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) |
---|
460 | __argp_fmtstream_update (__fs); |
---|
461 | __old = __fs->wmargin; |
---|
462 | __fs->wmargin = __wmargin; |
---|
463 | return __old; |
---|
464 | } |
---|
465 | |
---|
466 | /* Return the column number of the current output point in __FS. */ |
---|
467 | size_t |
---|
468 | __argp_fmtstream_point (argp_fmtstream_t __fs) |
---|
469 | { |
---|
470 | if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) |
---|
471 | __argp_fmtstream_update (__fs); |
---|
472 | return __fs->point_col >= 0 ? __fs->point_col : 0; |
---|
473 | } |
---|
474 | |
---|
475 | #endif /* !ARGP_FMTSTREAM_USE_LINEWRAP */ |
---|