scoutos / prex-0.9.0 / usr / lib / libc / stdio / vfprintf.c @ 03e9c04a
History | View | Annotate | Download (13.2 KB)
1 | 03e9c04a | Brad Neuman | /*-
|
---|---|---|---|
2 | * Copyright (c) 1990, 1993
|
||
3 | * The Regents of the University of California. All rights reserved.
|
||
4 | *
|
||
5 | * This code is derived from software contributed to Berkeley by
|
||
6 | * Chris Torek.
|
||
7 | *
|
||
8 | * Redistribution and use in source and binary forms, with or without
|
||
9 | * modification, are permitted provided that the following conditions
|
||
10 | * are met:
|
||
11 | * 1. Redistributions of source code must retain the above copyright
|
||
12 | * notice, this list of conditions and the following disclaimer.
|
||
13 | * 2. Redistributions in binary form must reproduce the above copyright
|
||
14 | * notice, this list of conditions and the following disclaimer in the
|
||
15 | * documentation and/or other materials provided with the distribution.
|
||
16 | * 3. Neither the name of the University nor the names of its contributors
|
||
17 | * may be used to endorse or promote products derived from this software
|
||
18 | * without specific prior written permission.
|
||
19 | *
|
||
20 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||
21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||
23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||
24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||
25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||
26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||
28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||
29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||
30 | * SUCH DAMAGE.
|
||
31 | */
|
||
32 | |||
33 | /*
|
||
34 | * Actual printf innards.
|
||
35 | *
|
||
36 | * This code is large and complicated...
|
||
37 | */
|
||
38 | |||
39 | #include <sys/types.h> |
||
40 | |||
41 | #include <limits.h> |
||
42 | #include <stdio.h> |
||
43 | #include <stdlib.h> |
||
44 | #include <string.h> |
||
45 | #include <stdarg.h> |
||
46 | |||
47 | #include "local.h" |
||
48 | #include "fvwrite.h" |
||
49 | |||
50 | /*
|
||
51 | * Flush out all the vectors defined by the given uio,
|
||
52 | * then reset it so that it can be reused.
|
||
53 | */
|
||
54 | static int |
||
55 | __sprint(FILE *fp, struct __suio *uio)
|
||
56 | { |
||
57 | int error;
|
||
58 | |||
59 | if (uio->uio_resid == 0) { |
||
60 | uio->uio_iovcnt = 0;
|
||
61 | return (0); |
||
62 | } |
||
63 | error = __sfvwrite(fp, uio); |
||
64 | uio->uio_resid = 0;
|
||
65 | uio->uio_iovcnt = 0;
|
||
66 | return (error);
|
||
67 | } |
||
68 | |||
69 | /*
|
||
70 | * Helper function for `fprintf to unbuffered unix file': creates a
|
||
71 | * temporary buffer. We only work on write-only files; this avoids
|
||
72 | * worries about ungetc buffers and so forth.
|
||
73 | */
|
||
74 | static int |
||
75 | __sbprintf(FILE *fp, const char *fmt, va_list ap) |
||
76 | { |
||
77 | int ret;
|
||
78 | FILE fake; |
||
79 | unsigned char buf[BUFSIZ]; |
||
80 | |||
81 | /* copy the important variables */
|
||
82 | fake._flags = fp->_flags & ~__SNBF; |
||
83 | fake._file = fp->_file; |
||
84 | |||
85 | /* set up the buffer */
|
||
86 | fake._bf._base = fake._p = buf; |
||
87 | fake._bf._size = fake._w = sizeof(buf);
|
||
88 | |||
89 | /* do the work, then copy any error status */
|
||
90 | ret = vfprintf(&fake, fmt, ap); |
||
91 | if (ret >= 0 && fflush(&fake)) |
||
92 | ret = EOF;
|
||
93 | if (fake._flags & __SERR)
|
||
94 | fp->_flags |= __SERR; |
||
95 | return (ret);
|
||
96 | } |
||
97 | |||
98 | /*
|
||
99 | * Macros for converting digits to letters and vice versa
|
||
100 | */
|
||
101 | #define to_digit(c) ((c) - '0') |
||
102 | #define is_digit(c) ((unsigned)to_digit(c) <= 9) |
||
103 | #define to_char(n) ((n) + '0') |
||
104 | |||
105 | /*
|
||
106 | * Convert an unsigned long to ASCII for printf purposes, returning
|
||
107 | * a pointer to the first character of the string representation.
|
||
108 | * Octal numbers can be forced to have a leading zero; hex numbers
|
||
109 | * use the given digits.
|
||
110 | */
|
||
111 | static char * |
||
112 | __ultoa(u_long val, char *endp, int base, int octzero, char *xdigs) |
||
113 | { |
||
114 | char *cp = endp;
|
||
115 | long sval;
|
||
116 | |||
117 | /*
|
||
118 | * Handle the three cases separately, in the hope of getting
|
||
119 | * better/faster code.
|
||
120 | */
|
||
121 | switch (base) {
|
||
122 | case 10: |
||
123 | if (val < 10) { /* many numbers are 1 digit */ |
||
124 | *--cp = to_char(val); |
||
125 | return (cp);
|
||
126 | } |
||
127 | /*
|
||
128 | * On many machines, unsigned arithmetic is harder than
|
||
129 | * signed arithmetic, so we do at most one unsigned mod and
|
||
130 | * divide; this is sufficient to reduce the range of
|
||
131 | * the incoming value to where signed arithmetic works.
|
||
132 | */
|
||
133 | if (val > LONG_MAX) {
|
||
134 | *--cp = to_char(val % 10);
|
||
135 | sval = val / 10;
|
||
136 | } else
|
||
137 | sval = val; |
||
138 | do {
|
||
139 | *--cp = to_char(sval % 10);
|
||
140 | sval /= 10;
|
||
141 | } while (sval != 0); |
||
142 | break;
|
||
143 | |||
144 | case 8: |
||
145 | do {
|
||
146 | *--cp = to_char(val & 7);
|
||
147 | val >>= 3;
|
||
148 | } while (val);
|
||
149 | if (octzero && *cp != '0') |
||
150 | *--cp = '0';
|
||
151 | break;
|
||
152 | |||
153 | case 16: |
||
154 | do {
|
||
155 | *--cp = xdigs[val & 15];
|
||
156 | val >>= 4;
|
||
157 | } while (val);
|
||
158 | break;
|
||
159 | |||
160 | default: /* oops */ |
||
161 | exit(1);
|
||
162 | } |
||
163 | return (cp);
|
||
164 | } |
||
165 | |||
166 | |||
167 | #define BUF 32 |
||
168 | |||
169 | |||
170 | /*
|
||
171 | * Flags used during conversion.
|
||
172 | */
|
||
173 | #define ALT 0x001 /* alternate form */ |
||
174 | #define HEXPREFIX 0x002 /* add 0x or 0X prefix */ |
||
175 | #define LADJUST 0x004 /* left adjustment */ |
||
176 | #define LONGDBL 0x008 /* long double; unimplemented */ |
||
177 | #define LONGINT 0x010 /* long integer */ |
||
178 | #define SHORTINT 0x040 /* short integer */ |
||
179 | #define ZEROPAD 0x080 /* zero (as opposed to blank) pad */ |
||
180 | int
|
||
181 | vfprintf(fp, fmt0, ap) |
||
182 | FILE *fp; |
||
183 | const char *fmt0; |
||
184 | va_list ap; |
||
185 | { |
||
186 | char *fmt; /* format string */ |
||
187 | int ch; /* character from fmt */ |
||
188 | int n; /* handy integer (short term usage) */ |
||
189 | char *cp; /* handy char pointer (short term usage) */ |
||
190 | struct __siov *iovp; /* for PRINT macro */ |
||
191 | int flags; /* flags as above */ |
||
192 | int ret; /* return value accumulator */ |
||
193 | int width; /* width from format (%8d), or 0 */ |
||
194 | int prec; /* precision from format (%.3d), or -1 */ |
||
195 | char sign; /* sign prefix (' ', '+', '-', or \0) */ |
||
196 | u_long ulval; /* integer arguments %[diouxX] */
|
||
197 | int base; /* base for [diouxX] conversion */ |
||
198 | int dprec; /* a copy of prec if [diouxX], 0 otherwise */ |
||
199 | int fieldsz; /* field size expanded by sign, etc */ |
||
200 | int realsz; /* field size expanded by dprec */ |
||
201 | int size; /* size of converted field or string */ |
||
202 | char *xdigs = NULL; /* digits for [xX] conversion */ |
||
203 | #define NIOV 8 |
||
204 | struct __suio uio; /* output information: summary */ |
||
205 | struct __siov iov[NIOV];/* ... and individual io vectors */ |
||
206 | char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ |
||
207 | char ox[2]; /* space for 0x hex-prefix */ |
||
208 | |||
209 | /*
|
||
210 | * Choose PADSIZE to trade efficiency vs. size. If larger printf
|
||
211 | * fields occur frequently, increase PADSIZE and make the initialisers
|
||
212 | * below longer.
|
||
213 | */
|
||
214 | #define PADSIZE 16 /* pad chunk size */ |
||
215 | static char blanks[PADSIZE] = |
||
216 | {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; |
||
217 | static char zeroes[PADSIZE] = |
||
218 | {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; |
||
219 | |||
220 | /*
|
||
221 | * BEWARE, these `goto error' on error, and PAD uses `n'.
|
||
222 | */
|
||
223 | #define PRINT(ptr, len) { \
|
||
224 | iovp->iov_base = (ptr); \ |
||
225 | iovp->iov_len = (len); \ |
||
226 | uio.uio_resid += (len); \ |
||
227 | iovp++; \ |
||
228 | if (++uio.uio_iovcnt >= NIOV) { \
|
||
229 | if (__sprint(fp, &uio)) \
|
||
230 | goto error; \
|
||
231 | iovp = iov; \ |
||
232 | } \ |
||
233 | } |
||
234 | #define PAD(howmany, with) { \
|
||
235 | if ((n = (howmany)) > 0) { \ |
||
236 | while (n > PADSIZE) { \
|
||
237 | PRINT(with, PADSIZE); \ |
||
238 | n -= PADSIZE; \ |
||
239 | } \ |
||
240 | PRINT(with, n); \ |
||
241 | } \ |
||
242 | } |
||
243 | #define FLUSH() { \
|
||
244 | if (uio.uio_resid && __sprint(fp, &uio)) \
|
||
245 | goto error; \
|
||
246 | uio.uio_iovcnt = 0; \
|
||
247 | iovp = iov; \ |
||
248 | } |
||
249 | |||
250 | /*
|
||
251 | * To extend shorts properly, we need both signed and unsigned
|
||
252 | * argument extraction methods.
|
||
253 | */
|
||
254 | #define SARG() \
|
||
255 | (flags&LONGINT ? va_arg(ap, long) : \
|
||
256 | flags&SHORTINT ? (long)(short)va_arg(ap, int) : \ |
||
257 | (long)va_arg(ap, int)) |
||
258 | #define UARG() \
|
||
259 | (flags&LONGINT ? va_arg(ap, u_long) : \ |
||
260 | flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \
|
||
261 | (u_long)va_arg(ap, u_int)) |
||
262 | |||
263 | /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */
|
||
264 | if (cantwrite(fp))
|
||
265 | return (EOF); |
||
266 | |||
267 | /* optimise fprintf(stderr) (and other unbuffered Unix files) */
|
||
268 | if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
|
||
269 | fp->_file >= 0)
|
||
270 | return (__sbprintf(fp, fmt0, ap));
|
||
271 | |||
272 | fmt = (char *)fmt0;
|
||
273 | uio.uio_iov = iovp = iov; |
||
274 | uio.uio_resid = 0;
|
||
275 | uio.uio_iovcnt = 0;
|
||
276 | ret = 0;
|
||
277 | |||
278 | /*
|
||
279 | * Scan the format for conversions (`%' character).
|
||
280 | */
|
||
281 | for (;;) {
|
||
282 | for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) |
||
283 | /* void */;
|
||
284 | if ((n = fmt - cp) != 0) { |
||
285 | PRINT(cp, n); |
||
286 | ret += n; |
||
287 | } |
||
288 | if (ch == '\0') |
||
289 | goto done;
|
||
290 | fmt++; /* skip over '%' */
|
||
291 | |||
292 | flags = 0;
|
||
293 | dprec = 0;
|
||
294 | width = 0;
|
||
295 | prec = -1;
|
||
296 | sign = '\0';
|
||
297 | |||
298 | rflag: ch = *fmt++;
|
||
299 | reswitch: switch (ch) { |
||
300 | case ' ': |
||
301 | /*
|
||
302 | * ``If the space and + flags both appear, the space
|
||
303 | * flag will be ignored.''
|
||
304 | * -- ANSI X3J11
|
||
305 | */
|
||
306 | if (!sign)
|
||
307 | sign = ' ';
|
||
308 | goto rflag;
|
||
309 | case '#': |
||
310 | flags |= ALT; |
||
311 | goto rflag;
|
||
312 | case '*': |
||
313 | /*
|
||
314 | * ``A negative field width argument is taken as a
|
||
315 | * - flag followed by a positive field width.''
|
||
316 | * -- ANSI X3J11
|
||
317 | * They don't exclude field widths read from args.
|
||
318 | */
|
||
319 | if ((width = va_arg(ap, int)) >= 0) |
||
320 | goto rflag;
|
||
321 | width = -width; |
||
322 | /* FALLTHROUGH */
|
||
323 | case '-': |
||
324 | flags |= LADJUST; |
||
325 | goto rflag;
|
||
326 | case '+': |
||
327 | sign = '+';
|
||
328 | goto rflag;
|
||
329 | case '.': |
||
330 | if ((ch = *fmt++) == '*') { |
||
331 | n = va_arg(ap, int);
|
||
332 | prec = n < 0 ? -1 : n; |
||
333 | goto rflag;
|
||
334 | } |
||
335 | n = 0;
|
||
336 | while (is_digit(ch)) {
|
||
337 | n = 10 * n + to_digit(ch);
|
||
338 | ch = *fmt++; |
||
339 | } |
||
340 | prec = n < 0 ? -1 : n; |
||
341 | goto reswitch;
|
||
342 | case '0': |
||
343 | /*
|
||
344 | * ``Note that 0 is taken as a flag, not as the
|
||
345 | * beginning of a field width.''
|
||
346 | * -- ANSI X3J11
|
||
347 | */
|
||
348 | flags |= ZEROPAD; |
||
349 | goto rflag;
|
||
350 | case '1': case '2': case '3': case '4': |
||
351 | case '5': case '6': case '7': case '8': case '9': |
||
352 | n = 0;
|
||
353 | do {
|
||
354 | n = 10 * n + to_digit(ch);
|
||
355 | ch = *fmt++; |
||
356 | } while (is_digit(ch));
|
||
357 | width = n; |
||
358 | goto reswitch;
|
||
359 | case 'h': |
||
360 | flags |= SHORTINT; |
||
361 | goto rflag;
|
||
362 | case 'l': |
||
363 | flags |= LONGINT; |
||
364 | goto rflag;
|
||
365 | case 'c': |
||
366 | *(cp = buf) = va_arg(ap, int);
|
||
367 | size = 1;
|
||
368 | sign = '\0';
|
||
369 | break;
|
||
370 | case 'D': |
||
371 | flags |= LONGINT; |
||
372 | /*FALLTHROUGH*/
|
||
373 | case 'd': |
||
374 | case 'i': |
||
375 | ulval = SARG(); |
||
376 | if ((long)ulval < 0) { |
||
377 | ulval = -ulval; |
||
378 | sign = '-';
|
||
379 | } |
||
380 | base = 10;
|
||
381 | goto number;
|
||
382 | case 'n': |
||
383 | if (flags & LONGINT)
|
||
384 | *va_arg(ap, long *) = ret;
|
||
385 | else if (flags & SHORTINT) |
||
386 | *va_arg(ap, short *) = ret;
|
||
387 | else
|
||
388 | *va_arg(ap, int *) = ret;
|
||
389 | continue; /* no output */ |
||
390 | case 'O': |
||
391 | flags |= LONGINT; |
||
392 | /*FALLTHROUGH*/
|
||
393 | case 'o': |
||
394 | ulval = UARG(); |
||
395 | base = 8;
|
||
396 | goto nosign;
|
||
397 | case 'p': |
||
398 | /*
|
||
399 | * ``The argument shall be a pointer to void. The
|
||
400 | * value of the pointer is converted to a sequence
|
||
401 | * of printable characters, in an implementation-
|
||
402 | * defined manner.''
|
||
403 | * -- ANSI X3J11
|
||
404 | */
|
||
405 | ulval = (u_long)va_arg(ap, void *);
|
||
406 | base = 16;
|
||
407 | xdigs = "0123456789abcdef";
|
||
408 | flags |= HEXPREFIX; |
||
409 | ch = 'x';
|
||
410 | goto nosign;
|
||
411 | case 's': |
||
412 | if ((cp = va_arg(ap, char *)) == NULL) |
||
413 | cp = "(null)";
|
||
414 | if (prec >= 0) { |
||
415 | /*
|
||
416 | * can't use strlen; can only look for the
|
||
417 | * NUL in the first `prec' characters, and
|
||
418 | * strlen() will go further.
|
||
419 | */
|
||
420 | char *p = memchr(cp, 0, prec); |
||
421 | |||
422 | if (p != NULL) { |
||
423 | size = p - cp; |
||
424 | if (size > prec)
|
||
425 | size = prec; |
||
426 | } else
|
||
427 | size = prec; |
||
428 | } else
|
||
429 | size = strlen(cp); |
||
430 | sign = '\0';
|
||
431 | break;
|
||
432 | case 'U': |
||
433 | flags |= LONGINT; |
||
434 | /*FALLTHROUGH*/
|
||
435 | case 'u': |
||
436 | ulval = UARG(); |
||
437 | base = 10;
|
||
438 | goto nosign;
|
||
439 | case 'X': |
||
440 | xdigs = "0123456789ABCDEF";
|
||
441 | goto hex;
|
||
442 | case 'x': |
||
443 | xdigs = "0123456789abcdef";
|
||
444 | hex:
|
||
445 | ulval = UARG(); |
||
446 | base = 16;
|
||
447 | /* leading 0x/X only if non-zero */
|
||
448 | if (flags & ALT && ulval != 0) |
||
449 | flags |= HEXPREFIX; |
||
450 | |||
451 | /* unsigned conversions */
|
||
452 | nosign: sign = '\0'; |
||
453 | /*
|
||
454 | * ``... diouXx conversions ... if a precision is
|
||
455 | * specified, the 0 flag will be ignored.''
|
||
456 | * -- ANSI X3J11
|
||
457 | */
|
||
458 | number: if ((dprec = prec) >= 0) |
||
459 | flags &= ~ZEROPAD; |
||
460 | |||
461 | /*
|
||
462 | * ``The result of converting a zero value with an
|
||
463 | * explicit precision of zero is no characters.''
|
||
464 | * -- ANSI X3J11
|
||
465 | */
|
||
466 | cp = buf + BUF; |
||
467 | if (ulval != 0 || prec != 0) |
||
468 | cp = __ultoa(ulval, cp, base, |
||
469 | flags & ALT, xdigs); |
||
470 | size = buf + BUF - cp; |
||
471 | break;
|
||
472 | default: /* "%?" prints ?, unless ? is NUL */ |
||
473 | if (ch == '\0') |
||
474 | goto done;
|
||
475 | /* pretend it was %c with argument ch */
|
||
476 | cp = buf; |
||
477 | *cp = ch; |
||
478 | size = 1;
|
||
479 | sign = '\0';
|
||
480 | break;
|
||
481 | } |
||
482 | |||
483 | /*
|
||
484 | * All reasonable formats wind up here. At this point, `cp'
|
||
485 | * points to a string which (if not flags&LADJUST) should be
|
||
486 | * padded out to `width' places. If flags&ZEROPAD, it should
|
||
487 | * first be prefixed by any sign or other prefix; otherwise,
|
||
488 | * it should be blank padded before the prefix is emitted.
|
||
489 | * After any left-hand padding and prefixing, emit zeroes
|
||
490 | * required by a decimal [diouxX] precision, then print the
|
||
491 | * string proper, then emit zeroes required by any leftover
|
||
492 | * floating precision; finally, if LADJUST, pad with blanks.
|
||
493 | *
|
||
494 | * Compute actual size, so we know how much to pad.
|
||
495 | * fieldsz excludes decimal prec; realsz includes it.
|
||
496 | */
|
||
497 | fieldsz = size; |
||
498 | if (sign)
|
||
499 | fieldsz++; |
||
500 | else if (flags & HEXPREFIX) |
||
501 | fieldsz += 2;
|
||
502 | realsz = dprec > fieldsz ? dprec : fieldsz; |
||
503 | |||
504 | /* right-adjusting blank padding */
|
||
505 | if ((flags & (LADJUST|ZEROPAD)) == 0) |
||
506 | PAD(width - realsz, blanks); |
||
507 | |||
508 | /* prefix */
|
||
509 | if (sign) {
|
||
510 | PRINT(&sign, 1);
|
||
511 | } else if (flags & HEXPREFIX) { |
||
512 | ox[0] = '0'; |
||
513 | ox[1] = ch;
|
||
514 | PRINT(ox, 2);
|
||
515 | } |
||
516 | |||
517 | /* right-adjusting zero padding */
|
||
518 | if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
|
||
519 | PAD(width - realsz, zeroes); |
||
520 | |||
521 | /* leading zeroes from decimal precision */
|
||
522 | PAD(dprec - fieldsz, zeroes); |
||
523 | |||
524 | /* the string or number proper */
|
||
525 | PRINT(cp, size); |
||
526 | |||
527 | /* left-adjusting padding (always blank) */
|
||
528 | if (flags & LADJUST)
|
||
529 | PAD(width - realsz, blanks); |
||
530 | |||
531 | /* finally, adjust ret */
|
||
532 | ret += width > realsz ? width : realsz; |
||
533 | |||
534 | FLUSH(); /* copy out the I/O vectors */
|
||
535 | } |
||
536 | done:
|
||
537 | FLUSH(); |
||
538 | error:
|
||
539 | return (__sferror(fp) ? EOF : ret); |
||
540 | /* NOTREACHED */
|
||
541 | } |