headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / libroot / posix / stdio / vfprintf.c
blobe1d5024192e67dadfa3d9903ddcb9035894d9f1c
1 /*-
2 * Copyright (c) 1990 The Regents of the University of California.
3 * All rights reserved.
5 * This code is derived from software contributed to Berkeley by
6 * Chris Torek.
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. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
38 * Actual printf innards.
40 * This code is large and complicated...
43 #include <sys/types.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <errno.h>
50 #ifdef __STDC__
51 # include <stdarg.h>
52 #else
53 # include <varargs.h>
54 #endif
56 #include <errno_private.h>
58 #include "local.h"
59 #include "fvwrite.h"
61 static void __find_arguments (const char *fmt0, va_list ap,
62 va_list **argtable);
63 static int __grow_type_table (unsigned char **typetable,
64 int *tablesize);
67 * Flush out all the vectors defined by the given uio,
68 * then reset it so that it can be reused.
71 static int
72 __sprint(FILE *fp, register struct __suio *uio)
74 register int err;
76 if (uio->uio_resid == 0) {
77 uio->uio_iovcnt = 0;
78 return 0;
80 err = __sfvwrite(fp, uio);
81 uio->uio_resid = 0;
82 uio->uio_iovcnt = 0;
83 return err;
88 * Helper function for `fprintf to unbuffered unix file': creates a
89 * temporary buffer. We only work on write-only files; this avoids
90 * worries about ungetc buffers and so forth.
93 static int
94 __sbprintf(register FILE *fp, const char *fmt, va_list ap)
96 int ret;
97 FILE fake;
98 unsigned char buf[BUFSIZ];
100 /* copy the important variables */
101 fake._flags = fp->_flags & ~__SNBF;
102 fake._file = fp->_file;
103 fake._cookie = fp->_cookie;
104 fake._write = fp->_write;
106 /* set up the buffer */
107 fake._bf._base = fake._p = buf;
108 fake._bf._size = fake._w = sizeof(buf);
109 fake._lbfsize = 0; /* not actually used, but Just In Case */
111 /* do the work, then copy any error status */
112 ret = vfprintf(&fake, fmt, ap);
113 if (ret >= 0 && fflush(&fake))
114 ret = EOF;
115 if (fake._flags & __SERR)
116 fp->_flags |= __SERR;
117 return (ret);
121 #ifdef FLOATING_POINT
122 #include <locale.h>
123 #include <math.h>
124 #include "floatio.h"
126 #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */
127 #define DEFPREC 6
129 static char *cvt (double, int, int, char *, int *, int, int *);
130 static int exponent (char *, int, int);
132 #else /* no FLOATING_POINT */
133 #define BUF 40
134 #endif /* FLOATING_POINT */
136 #define STATIC_ARG_TBL_SIZE 8 /* Size of static argument table. */
140 * Macros for converting digits to letters and vice versa
142 #define to_digit(c) ((c) - '0')
143 #define is_digit(c) ((unsigned)to_digit(c) <= 9)
144 #define to_char(n) ((n) + '0')
147 * Flags used during conversion.
149 #define ALT 0x001 /* alternate form */
150 #define HEXPREFIX 0x002 /* add 0x or 0X prefix */
151 #define LADJUST 0x004 /* left adjustment */
152 #define LONGDBL 0x008 /* long double; unimplemented */
153 #define LONGINT 0x010 /* long integer */
154 #define QUADINT 0x020 /* quad integer */
155 #define SHORTINT 0x040 /* short integer */
156 #define ZEROPAD 0x080 /* zero (as opposed to blank) pad */
157 #define FPT 0x100 /* Floating point number */
160 vfprintf(FILE *fp, const char *fmt0, va_list ap)
162 register char *fmt; /* format string */
163 register int ch; /* character from fmt */
164 register int n, m, n2; /* handy integers (short term usage) */
165 register char *cp; /* handy char pointer (short term usage) */
166 register struct __siov *iovp;/* for PRINT macro */
167 register int flags; /* flags as above */
168 int ret; /* return value accumulator */
169 int width; /* width from format (%8d), or 0 */
170 int prec; /* precision from format (%.3d), or -1 */
171 char sign; /* sign prefix (' ', '+', '-', or \0) */
172 wchar_t wc;
173 #ifdef FLOATING_POINT
174 char *decimal_point = localeconv()->decimal_point;
175 char softsign; /* temporary negative sign for floats */
176 double _double; /* double precision arguments %[eEfgG] */
177 int expt; /* integer value of exponent */
178 int expsize; /* character count for expstr */
179 int ndig; /* actual number of digits returned by cvt */
180 char expstr[7]; /* buffer for exponent string */
181 #endif
183 #ifdef __GNUC__ /* gcc has builtin quad type (long long) SOS */
184 #define quad_t long long
185 #define u_quad_t unsigned long long
186 #endif
188 u_quad_t _uquad; /* integer arguments %[diouxX] */
189 enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */
190 int dprec; /* a copy of prec if [diouxX], 0 otherwise */
191 int realsz; /* field size expanded by dprec */
192 int size; /* size of converted field or string */
193 char *xdigs=NULL; /* digits for [xX] conversion */
194 #define NIOV 8
195 struct __suio uio; /* output information: summary */
196 struct __siov iov[NIOV];/* ... and individual io vectors */
197 char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */
198 char ox[2]; /* space for 0x hex-prefix */
199 va_list *argtable; /* args, built due to positional arg */
200 va_list statargtable[STATIC_ARG_TBL_SIZE];
201 int nextarg; /* 1-based argument index */
202 va_list orgap; /* original argument pointer */
205 * Choose PADSIZE to trade efficiency vs. size. If larger printf
206 * fields occur frequently, increase PADSIZE and make the initialisers
207 * below longer.
209 #define PADSIZE 16 /* pad chunk size */
210 static char blanks[PADSIZE] =
211 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
212 static char zeroes[PADSIZE] =
213 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
216 * BEWARE, these `goto error' on error, and PAD uses `n'.
218 #define PRINT(ptr, len) do { \
219 iovp->iov_base = (ptr); \
220 iovp->iov_len = (len); \
221 uio.uio_resid += (len); \
222 iovp++; \
223 if (++uio.uio_iovcnt >= NIOV) { \
224 if (__sprint(fp, &uio)) \
225 goto error; \
226 iovp = iov; \
228 } while (0)
229 #define PAD(howmany, with) do { \
230 if ((n = (howmany)) > 0) { \
231 while (n > PADSIZE) { \
232 PRINT(with, PADSIZE); \
233 n -= PADSIZE; \
235 PRINT(with, n); \
237 } while (0)
238 #define FLUSH() do { \
239 if (uio.uio_resid && __sprint(fp, &uio)) \
240 goto error; \
241 uio.uio_iovcnt = 0; \
242 iovp = iov; \
243 } while (0)
246 * To extend shorts properly, we need both signed and unsigned
247 * argument extraction methods.
249 #define SARG() \
250 (flags&QUADINT ? va_arg(ap, quad_t) : \
251 flags&LONGINT ? GETARG(long) : \
252 flags&SHORTINT ? (long)(short)GETARG(int) : \
253 (long)GETARG(int))
254 #define UARG() \
255 (flags&QUADINT ? va_arg(ap, u_quad_t) : \
256 flags&LONGINT ? GETARG(u_long) : \
257 flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \
258 (u_long)GETARG(u_int))
261 * Get * arguments, including the form *nn$. Preserve the nextarg
262 * that the argument can be gotten once the type is determined.
264 #define GETASTER(val) \
265 n2 = 0; \
266 cp = fmt; \
267 while (is_digit(*cp)) { \
268 n2 = 10 * n2 + to_digit(*cp); \
269 cp++; \
271 if (*cp == '$') { \
272 int hold = nextarg; \
273 if (argtable == NULL) { \
274 argtable = statargtable; \
275 __find_arguments(fmt0, orgap, &argtable); \
277 nextarg = n2; \
278 val = GETARG(int); \
279 nextarg = hold; \
280 fmt = ++cp; \
282 else { \
283 val = GETARG(int); \
287 * Get the argument indexed by nextarg. If the argument table is
288 * built, use it to get the argument. If its not, get the next
289 * argument (and arguments must be gotten sequentially).
291 #define GETARG(type) \
292 (((argtable != NULL) ? (void)(ap = argtable[nextarg]) : (void)0), \
293 nextarg++, va_arg(ap, type))
295 /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */
296 if (cantwrite(fp)) {
297 __set_errno(EBADF);
298 return (EOF);
301 /* optimise fprintf(stderr) (and other unbuffered Unix files) */
302 if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
303 fp->_file >= 0)
304 return (__sbprintf(fp, fmt0, ap));
306 fmt = (char *)fmt0;
307 argtable = NULL;
308 nextarg = 1;
309 orgap = ap;
310 uio.uio_iov = iovp = iov;
311 uio.uio_resid = 0;
312 uio.uio_iovcnt = 0;
313 ret = 0;
316 * Scan the format for conversions (`%' character).
318 for (;;) {
319 cp = fmt;
320 while ((n = mbtowc(&wc, fmt, MB_CUR_MAX)) > 0) {
321 fmt += n;
322 if (wc == '%') {
323 fmt--;
324 break;
327 if ((m = fmt - cp) != 0) {
328 PRINT(cp, m);
329 ret += m;
331 if (n <= 0)
332 goto done;
333 fmt++; /* skip over '%' */
335 flags = 0;
336 dprec = 0;
337 width = 0;
338 prec = -1;
339 sign = '\0';
341 rflag: ch = *fmt++;
342 reswitch: switch (ch) {
343 case ' ':
345 * ``If the space and + flags both appear, the space
346 * flag will be ignored.''
347 * -- ANSI X3J11
349 if (!sign)
350 sign = ' ';
351 goto rflag;
352 case '#':
353 flags |= ALT;
354 goto rflag;
355 case '*':
357 * ``A negative field width argument is taken as a
358 * - flag followed by a positive field width.''
359 * -- ANSI X3J11
360 * They don't exclude field widths read from args.
362 GETASTER(width);
363 if (width >= 0)
364 goto rflag;
365 width = -width;
366 /* FALLTHROUGH */
367 case '-':
368 flags |= LADJUST;
369 goto rflag;
370 case '+':
371 sign = '+';
372 goto rflag;
373 case '.':
374 if ((ch = *fmt++) == '*') {
375 GETASTER(n);
376 prec = n < 0 ? -1 : n;
377 goto rflag;
379 n = 0;
380 while (is_digit(ch)) {
381 n = 10 * n + to_digit(ch);
382 ch = *fmt++;
384 if (ch == '$') {
385 nextarg = n;
386 if (argtable == NULL) {
387 argtable = statargtable;
388 __find_arguments(fmt0, orgap,
389 &argtable);
391 goto rflag;
393 prec = n < 0 ? -1 : n;
394 goto reswitch;
395 case '0':
397 * ``Note that 0 is taken as a flag, not as the
398 * beginning of a field width.''
399 * -- ANSI X3J11
401 flags |= ZEROPAD;
402 goto rflag;
403 case '1': case '2': case '3': case '4':
404 case '5': case '6': case '7': case '8': case '9':
405 n = 0;
406 do {
407 n = 10 * n + to_digit(ch);
408 ch = *fmt++;
409 } while (is_digit(ch));
410 if (ch == '$') {
411 nextarg = n;
412 if (argtable == NULL) {
413 argtable = statargtable;
414 __find_arguments(fmt0, orgap,
415 &argtable);
417 goto rflag;
419 width = n;
420 goto reswitch;
421 case 'L':
422 flags |= QUADINT
423 #ifdef FLOATING_POINT
424 | LONGDBL
425 #endif
427 goto rflag;
428 case 'h':
429 flags |= SHORTINT;
430 goto rflag;
431 case 'l':
432 if (*fmt == 'l') {
433 fmt++;
434 flags |= QUADINT;
435 } else {
436 flags |= LONGINT;
438 goto rflag;
439 case 'q':
440 flags |= QUADINT;
441 goto rflag;
442 case 'c':
443 *(cp = buf) = GETARG(int);
444 size = 1;
445 sign = '\0';
446 break;
447 case 'D':
448 flags |= LONGINT;
449 /*FALLTHROUGH*/
450 case 'd':
451 case 'i':
452 _uquad = SARG();
453 if ((quad_t)_uquad < 0) {
454 _uquad = -_uquad;
455 sign = '-';
457 base = DEC;
458 goto number;
459 #ifdef FLOATING_POINT
460 case 'e':
461 case 'E':
462 case 'f':
463 case 'g':
464 case 'G':
465 if (prec == -1) {
466 prec = DEFPREC;
467 } else if ((ch == 'g' || ch == 'G') && prec == 0) {
468 prec = 1;
471 if (flags & LONGDBL) {
472 _double = (double) GETARG(long double);
473 } else {
474 _double = GETARG(double);
477 /* do this before tricky precision changes */
478 if (isinf(_double)) {
479 if (_double < 0)
480 sign = '-';
481 cp = "Inf";
482 size = 3;
483 break;
485 if (isnan(_double)) {
486 cp = "NaN";
487 size = 3;
488 break;
491 flags |= FPT;
492 cp = cvt(_double, prec, flags, &softsign,
493 &expt, ch, &ndig);
494 if (ch == 'g' || ch == 'G') {
495 if (expt <= -4 || expt > prec)
496 ch = (ch == 'g') ? 'e' : 'E';
497 else
498 ch = 'g';
500 if (ch <= 'e') { /* 'e' or 'E' fmt */
501 --expt;
502 expsize = exponent(expstr, expt, ch);
503 size = expsize + ndig;
504 if (ndig > 1 || flags & ALT)
505 ++size;
506 } else if (ch == 'f') { /* f fmt */
507 if (expt > 0) {
508 size = expt;
509 if (prec || flags & ALT)
510 size += prec + 1;
511 } else /* "0.X" */
512 size = prec + 2;
513 } else if (expt >= ndig) { /* fixed g fmt */
514 size = expt;
515 if (flags & ALT)
516 ++size;
517 } else
518 size = ndig + (expt > 0 ?
519 1 : 2 - expt);
521 if (softsign)
522 sign = '-';
523 break;
524 #endif /* FLOATING_POINT */
525 case 'n':
526 if (flags & QUADINT)
527 *GETARG(quad_t *) = ret;
528 else if (flags & LONGINT)
529 *GETARG(long *) = ret;
530 else if (flags & SHORTINT)
531 *GETARG(short *) = ret;
532 else
533 *GETARG(int *) = ret;
534 continue; /* no output */
535 case 'O':
536 flags |= LONGINT;
537 /*FALLTHROUGH*/
538 case 'o':
539 _uquad = UARG();
540 base = OCT;
541 goto nosign;
542 case 'p':
544 * ``The argument shall be a pointer to void. The
545 * value of the pointer is converted to a sequence
546 * of printable characters, in an implementation-
547 * defined manner.''
548 * -- ANSI X3J11
550 /* NOSTRICT */
551 _uquad = (u_long)GETARG(void *);
552 base = HEX;
553 xdigs = "0123456789abcdef";
554 flags |= HEXPREFIX;
555 ch = 'x';
556 goto nosign;
557 case 's':
558 if ((cp = GETARG(char *)) == NULL)
559 cp = "(null)";
560 if (prec >= 0) {
562 * can't use strlen; can only look for the
563 * NUL in the first `prec' characters, and
564 * strlen() will go further.
566 char *p = memchr(cp, 0, prec);
568 if (p != NULL) {
569 size = p - cp;
570 if (size > prec)
571 size = prec;
572 } else
573 size = prec;
574 } else
575 size = strlen(cp);
576 sign = '\0';
577 break;
578 case 'U':
579 flags |= LONGINT;
580 /*FALLTHROUGH*/
581 case 'u':
582 _uquad = UARG();
583 base = DEC;
584 goto nosign;
585 case 'X':
586 xdigs = "0123456789ABCDEF";
587 goto hex;
588 case 'x':
589 xdigs = "0123456789abcdef";
590 hex: _uquad = UARG();
591 base = HEX;
592 /* leading 0x/X only if non-zero */
593 if (flags & ALT && _uquad != 0)
594 flags |= HEXPREFIX;
596 /* unsigned conversions */
597 nosign: sign = '\0';
599 * ``... diouXx conversions ... if a precision is
600 * specified, the 0 flag will be ignored.''
601 * -- ANSI X3J11
603 number: if ((dprec = prec) >= 0)
604 flags &= ~ZEROPAD;
607 * ``The result of converting a zero value with an
608 * explicit precision of zero is no characters.''
609 * -- ANSI X3J11
611 cp = buf + BUF;
612 if (_uquad != 0 || prec != 0) {
614 * Unsigned mod is hard, and unsigned mod
615 * by a constant is easier than that by
616 * a variable; hence this switch.
618 switch (base) {
619 case OCT:
620 do {
621 *--cp = to_char(_uquad & 7);
622 _uquad >>= 3;
623 } while (_uquad);
624 /* handle octal leading 0 */
625 if (flags & ALT && *cp != '0')
626 *--cp = '0';
627 break;
629 case DEC:
630 /* many numbers are 1 digit */
631 while (_uquad >= 10) {
632 *--cp = to_char(_uquad % 10);
633 _uquad /= 10;
635 *--cp = to_char(_uquad);
636 break;
638 case HEX:
639 do {
640 *--cp = xdigs[_uquad & 15];
641 _uquad >>= 4;
642 } while (_uquad);
643 break;
645 default:
646 cp = "bug in vfprintf: bad base";
647 size = strlen(cp);
648 goto skipsize;
651 size = buf + BUF - cp;
652 skipsize:
653 break;
654 default: /* "%?" prints ?, unless ? is NUL */
655 if (ch == '\0')
656 goto done;
657 /* pretend it was %c with argument ch */
658 cp = buf;
659 *cp = ch;
660 size = 1;
661 sign = '\0';
662 break;
666 * All reasonable formats wind up here. At this point, `cp'
667 * points to a string which (if not flags&LADJUST) should be
668 * padded out to `width' places. If flags&ZEROPAD, it should
669 * first be prefixed by any sign or other prefix; otherwise,
670 * it should be blank padded before the prefix is emitted.
671 * After any left-hand padding and prefixing, emit zeroes
672 * required by a decimal [diouxX] precision, then print the
673 * string proper, then emit zeroes required by any leftover
674 * floating precision; finally, if LADJUST, pad with blanks.
676 * Compute actual size, so we know how much to pad.
677 * size excludes decimal prec; realsz includes it.
679 realsz = dprec > size ? dprec : size;
680 if (sign)
681 realsz++;
682 else if (flags & HEXPREFIX)
683 realsz+= 2;
685 /* right-adjusting blank padding */
686 if ((flags & (LADJUST|ZEROPAD)) == 0)
687 PAD(width - realsz, blanks);
689 /* prefix */
690 if (sign) {
691 PRINT(&sign, 1);
692 } else if (flags & HEXPREFIX) {
693 ox[0] = '0';
694 ox[1] = ch;
695 PRINT(ox, 2);
698 /* right-adjusting zero padding */
699 if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
700 PAD(width - realsz, zeroes);
702 /* leading zeroes from decimal precision */
703 PAD(dprec - size, zeroes);
705 /* the string or number proper */
706 #ifdef FLOATING_POINT
707 if ((flags & FPT) == 0) {
708 PRINT(cp, size);
709 } else { /* glue together f_p fragments */
710 if (ch >= 'f') { /* 'f' or 'g' */
711 if (_double == 0) {
712 /* kludge for __dtoa irregularity */
713 PRINT("0", 1);
714 if (expt < ndig || (flags & ALT) != 0) {
715 PRINT(decimal_point, 1);
716 PAD(ndig - 1, zeroes);
718 } else if (expt <= 0) {
719 PRINT("0", 1);
720 PRINT(decimal_point, 1);
721 PAD(-expt, zeroes);
722 PRINT(cp, ndig);
723 } else if (expt >= ndig) {
724 PRINT(cp, ndig);
725 PAD(expt - ndig, zeroes);
726 if (flags & ALT)
727 PRINT(".", 1);
728 } else {
729 PRINT(cp, expt);
730 cp += expt;
731 PRINT(".", 1);
732 PRINT(cp, ndig-expt);
734 } else { /* 'e' or 'E' */
735 if (ndig > 1 || flags & ALT) {
736 ox[0] = *cp++;
737 ox[1] = '.';
738 PRINT(ox, 2);
739 if (_double) {
740 PRINT(cp, ndig-1);
741 } else /* 0.[0..] */
742 /* __dtoa irregularity */
743 PAD(ndig - 1, zeroes);
744 } else /* XeYYY */
745 PRINT(cp, 1);
746 PRINT(expstr, expsize);
749 #else
750 PRINT(cp, size);
751 #endif
752 /* left-adjusting padding (always blank) */
753 if (flags & LADJUST)
754 PAD(width - realsz, blanks);
756 /* finally, adjust ret */
757 ret += width > realsz ? width : realsz;
758 FLUSH(); /* copy out the I/O vectors */
760 done:
762 FLUSH();
763 error:
764 if (argtable != NULL && argtable != statargtable)
765 free(argtable);
766 return (__sferror(fp) ? EOF : ret);
767 /* NOTREACHED */
771 * Type ids for argument type table.
773 #define T_UNUSED 0
774 #define T_SHORT 1
775 #define T_U_SHORT 2
776 #define TP_SHORT 3
777 #define T_INT 4
778 #define T_U_INT 5
779 #define TP_INT 6
780 #define T_LONG 7
781 #define T_U_LONG 8
782 #define TP_LONG 9
783 #define T_QUAD 10
784 #define T_U_QUAD 11
785 #define TP_QUAD 12
786 #define T_DOUBLE 13
787 #define T_LONG_DOUBLE 14
788 #define TP_CHAR 15
789 #define TP_VOID 16
793 * Find all arguments when a positional parameter is encountered. Returns a
794 * table, indexed by argument number, of pointers to each arguments. The
795 * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
796 * It will be replaces with a malloc-ed on if it overflows.
799 static void
800 __find_arguments(const char *fmt0, va_list ap, va_list **argtable)
802 register char *fmt; /* format string */
803 register int ch; /* character from fmt */
804 register int n, n2; /* handy integer (short term usage) */
805 register char *cp; /* handy char pointer (short term usage) */
806 register int flags; /* flags as above */
807 unsigned char *typetable; /* table of types */
808 unsigned char stattypetable[STATIC_ARG_TBL_SIZE];
809 int tablesize; /* current size of type table */
810 int tablemax; /* largest used index in table */
811 int nextarg; /* 1-based argument index */
814 * Add an argument type to the table, expanding if necessary.
816 #define ADDTYPE(type) \
817 ((nextarg >= tablesize) ? \
818 __grow_type_table(&typetable, &tablesize) : 0, \
819 typetable[nextarg++] = type, \
820 (nextarg > tablemax) ? tablemax = nextarg : 0)
822 #define ADDSARG() \
823 ((flags&LONGINT) ? ADDTYPE(T_LONG) : \
824 ((flags&SHORTINT) ? ADDTYPE(T_SHORT) : ADDTYPE(T_INT)))
826 #define ADDUARG() \
827 ((flags&LONGINT) ? ADDTYPE(T_U_LONG) : \
828 ((flags&SHORTINT) ? ADDTYPE(T_U_SHORT) : ADDTYPE(T_U_INT)))
831 * Add * arguments to the type array.
833 #define ADDASTER() \
834 n2 = 0; \
835 cp = fmt; \
836 while (is_digit(*cp)) { \
837 n2 = 10 * n2 + to_digit(*cp); \
838 cp++; \
840 if (*cp == '$') { \
841 int hold = nextarg; \
842 nextarg = n2; \
843 ADDTYPE(T_INT); \
844 nextarg = hold; \
845 fmt = ++cp; \
846 } else { \
847 ADDTYPE(T_INT); \
849 fmt = (char *)fmt0;
850 typetable = stattypetable;
851 tablesize = STATIC_ARG_TBL_SIZE;
852 tablemax = 0;
853 nextarg = 1;
854 memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE);
857 * Scan the format for conversions (`%' character).
859 for (;;) {
860 for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
861 /* void */;
862 if (ch == '\0')
863 goto done;
864 fmt++; /* skip over '%' */
866 flags = 0;
868 rflag: ch = *fmt++;
869 reswitch: switch (ch) {
870 case ' ':
871 case '#':
872 goto rflag;
873 case '*':
874 ADDASTER();
875 goto rflag;
876 case '-':
877 case '+':
878 goto rflag;
879 case '.':
880 if ((ch = *fmt++) == '*') {
881 ADDASTER();
882 goto rflag;
884 while (is_digit(ch)) {
885 ch = *fmt++;
887 goto reswitch;
888 case '0':
889 goto rflag;
890 case '1': case '2': case '3': case '4':
891 case '5': case '6': case '7': case '8': case '9':
892 n = 0;
893 do {
894 n = 10 * n + to_digit(ch);
895 ch = *fmt++;
896 } while (is_digit(ch));
897 if (ch == '$') {
898 nextarg = n;
899 goto rflag;
901 goto reswitch;
902 case 'L':
903 flags |= QUADINT
904 #ifdef FLOATING_POINT
905 | LONGDBL
906 #endif
908 goto rflag;
909 case 'h':
910 flags |= SHORTINT;
911 goto rflag;
912 case 'l':
913 if (*fmt == 'l') {
914 fmt++;
915 flags |= QUADINT;
916 } else
917 flags |= LONGINT;
919 goto rflag;
920 case 'q':
921 flags |= QUADINT;
922 goto rflag;
923 case 'c':
924 ADDTYPE(T_INT);
925 break;
926 case 'D':
927 flags |= LONGINT;
928 /*FALLTHROUGH*/
929 case 'd':
930 case 'i':
931 if (flags & QUADINT) {
932 ADDTYPE(T_QUAD);
933 } else {
934 ADDSARG();
936 break;
937 #ifdef FLOATING_POINT
938 case 'e':
939 case 'E':
940 case 'f':
941 case 'g':
942 case 'G':
943 if (flags & LONGDBL)
944 ADDTYPE(T_LONG_DOUBLE);
945 else
946 ADDTYPE(T_DOUBLE);
947 break;
948 #endif /* FLOATING_POINT */
949 case 'n':
950 if (flags & QUADINT)
951 ADDTYPE(TP_QUAD);
952 else if (flags & LONGINT)
953 ADDTYPE(TP_LONG);
954 else if (flags & SHORTINT)
955 ADDTYPE(TP_SHORT);
956 else
957 ADDTYPE(TP_INT);
958 continue; /* no output */
959 case 'O':
960 flags |= LONGINT;
961 /*FALLTHROUGH*/
962 case 'o':
963 if (flags & QUADINT)
964 ADDTYPE(T_U_QUAD);
965 else
966 ADDUARG();
967 break;
968 case 'p':
969 ADDTYPE(TP_VOID);
970 break;
971 case 's':
972 ADDTYPE(TP_CHAR);
973 break;
974 case 'U':
975 flags |= LONGINT;
976 /*FALLTHROUGH*/
977 case 'u':
978 if (flags & QUADINT)
979 ADDTYPE(T_U_QUAD);
980 else
981 ADDUARG();
982 break;
983 case 'X':
984 case 'x':
985 if (flags & QUADINT)
986 ADDTYPE(T_U_QUAD);
987 else
988 ADDUARG();
989 break;
990 default: /* "%?" prints ?, unless ? is NUL */
991 if (ch == '\0')
992 goto done;
993 break;
996 done:
998 * Build the argument table.
1000 if (tablemax >= STATIC_ARG_TBL_SIZE) {
1001 *argtable = (va_list *)
1002 malloc(sizeof (va_list) * (tablemax + 1));
1005 #if 0
1006 /* XXX is this required? */
1007 (*argtable) [0] = NULL;
1008 #endif
1009 for (n = 1; n <= tablemax; n++) {
1010 (*argtable)[n] = ap;
1011 switch (typetable[n]) {
1012 case T_UNUSED:
1013 (void) va_arg(ap, int);
1014 break;
1015 case T_SHORT:
1016 (void) va_arg(ap, int);
1017 break;
1018 case T_U_SHORT:
1019 (void) va_arg(ap, int);
1020 break;
1021 case TP_SHORT:
1022 (void) va_arg(ap, short *);
1023 break;
1024 case T_INT:
1025 (void) va_arg(ap, int);
1026 break;
1027 case T_U_INT:
1028 (void) va_arg(ap, unsigned int);
1029 break;
1030 case TP_INT:
1031 (void) va_arg(ap, int *);
1032 break;
1033 case T_LONG:
1034 (void) va_arg(ap, long);
1035 break;
1036 case T_U_LONG:
1037 (void) va_arg(ap, unsigned long);
1038 break;
1039 case TP_LONG:
1040 (void) va_arg(ap, long *);
1041 break;
1042 case T_QUAD:
1043 (void) va_arg(ap, quad_t);
1044 break;
1045 case T_U_QUAD:
1046 (void) va_arg(ap, u_quad_t);
1047 break;
1048 case TP_QUAD:
1049 (void) va_arg(ap, quad_t *);
1050 break;
1051 case T_DOUBLE:
1052 (void) va_arg(ap, double);
1053 break;
1054 case T_LONG_DOUBLE:
1055 (void) va_arg(ap, long double);
1056 break;
1057 case TP_CHAR:
1058 (void) va_arg(ap, char *);
1059 break;
1060 case TP_VOID:
1061 (void) va_arg(ap, void *);
1062 break;
1066 if (typetable != NULL && typetable != stattypetable)
1067 free(typetable);
1072 * Increase the size of the type table.
1075 static int
1076 __grow_type_table(unsigned char **typetable, int *tablesize)
1078 unsigned char *oldtable = *typetable;
1079 int newsize = *tablesize * 2;
1081 if (*tablesize == STATIC_ARG_TBL_SIZE) {
1082 *typetable = (unsigned char *)
1083 malloc(sizeof (unsigned char) * newsize);
1084 /* XXX unchecked */
1085 bcopy(oldtable, *typetable, *tablesize);
1086 } else {
1087 *typetable = (unsigned char *)
1088 realloc(*typetable, sizeof (unsigned char) * newsize);
1089 /* XXX unchecked */
1091 memset(*typetable + *tablesize, T_UNUSED, (newsize - *tablesize));
1093 *tablesize = newsize;
1094 return(0);
1098 #ifdef FLOATING_POINT
1100 extern char *__dtoa __P((double, int, int, int *, int *, char **));
1102 static char *
1103 cvt(double value, int ndigits, int flags, char *sign, int *decpt, int ch, int *length)
1105 int mode, dsgn;
1106 char *digits, *bp, *rve;
1108 if (ch == 'f') {
1109 mode = 3; /* ndigits after the decimal point */
1110 } else {
1111 /* To obtain ndigits after the decimal point for the 'e'
1112 * and 'E' formats, round to ndigits + 1 significant
1113 * figures.
1115 if (ch == 'e' || ch == 'E') {
1116 ndigits++;
1118 mode = 2; /* ndigits significant digits */
1121 if (value < 0) {
1122 value = -value;
1123 *sign = '-';
1124 } else
1125 *sign = '\000';
1126 digits = __dtoa(value, mode, ndigits, decpt, &dsgn, &rve);
1127 if ((ch != 'g' && ch != 'G') || flags & ALT) { /* Print trailing zeros */
1128 bp = digits + ndigits;
1129 if (ch == 'f') {
1130 if (*digits == '0' && value)
1131 *decpt = -ndigits + 1;
1132 bp += *decpt;
1134 if (value == 0) /* kludge for __dtoa irregularity */
1135 rve = bp;
1136 while (rve < bp)
1137 *rve++ = '0';
1139 *length = rve - digits;
1140 return (digits);
1144 static int
1145 exponent(char *p0, int exp, int fmtch)
1147 register char *p, *t;
1148 char expbuf[MAXEXP];
1150 p = p0;
1151 *p++ = fmtch;
1152 if (exp < 0) {
1153 exp = -exp;
1154 *p++ = '-';
1156 else
1157 *p++ = '+';
1158 t = expbuf + MAXEXP;
1159 if (exp > 9) {
1160 do {
1161 *--t = to_char(exp % 10);
1162 } while ((exp /= 10) > 9);
1163 *--t = to_char(exp);
1164 for (; t < expbuf + MAXEXP; *p++ = *t++);
1166 else {
1167 *p++ = '0';
1168 *p++ = to_char(exp);
1170 return (p - p0);
1172 #endif /* FLOATING_POINT */