Merge from vendor branch GDB:
[dragonfly.git] / contrib / sendmail-8.13.4 / libsm / vfprintf.c
1 /*
2  * Copyright (c) 2000-2001, 2004 Sendmail, Inc. and its suppliers.
3  *      All rights reserved.
4  * Copyright (c) 1990
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Chris Torek.
9  *
10  * By using this file, you agree to the terms and conditions set
11  * forth in the LICENSE file which can be found at the top level of
12  * the sendmail distribution.
13  */
14
15 #include <sm/gen.h>
16 SM_IDSTR(id, "@(#)$Id: vfprintf.c,v 1.53 2004/08/03 20:54:49 ca Exp $")
17
18 /*
19 **  Overall:
20 **  Actual printing innards.
21 **  This code is large and complicated...
22 */
23
24 #include <sys/types.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <sm/config.h>
29 #include <sm/varargs.h>
30 #include <sm/io.h>
31 #include <sm/heap.h>
32 #include <sm/conf.h>
33 #include "local.h"
34 #include "fvwrite.h"
35
36 static int      sm_bprintf __P((SM_FILE_T *, const char *, va_list));
37 static void     sm_find_arguments __P((const char *, va_list , va_list **));
38 static void     sm_grow_type_table_x __P((unsigned char **, int *));
39 static int      sm_print __P((SM_FILE_T *, int, struct sm_uio *));
40
41 /*
42 **  SM_PRINT -- print/flush to the file
43 **
44 **  Flush out all the vectors defined by the given uio,
45 **  then reset it so that it can be reused.
46 **
47 **      Parameters:
48 **              fp -- file pointer
49 **              timeout -- time to complete operation (milliseconds)
50 **              uio -- vector list of memory locations of data for printing
51 **
52 **      Results:
53 **              Success: 0 (zero)
54 **              Failure:
55 */
56
57 static int
58 sm_print(fp, timeout, uio)
59         SM_FILE_T *fp;
60         int timeout;
61         register struct sm_uio *uio;
62 {
63         register int err;
64
65         if (uio->uio_resid == 0)
66         {
67                 uio->uio_iovcnt = 0;
68                 return 0;
69         }
70         err = sm_fvwrite(fp, timeout, uio);
71         uio->uio_resid = 0;
72         uio->uio_iovcnt = 0;
73         return err;
74 }
75
76 /*
77 **  SM_BPRINTF -- allow formating to an unbuffered file.
78 **
79 **  Helper function for `fprintf to unbuffered unix file': creates a
80 **  temporary buffer (via a "fake" file pointer).
81 **  We only work on write-only files; this avoids
82 **  worries about ungetc buffers and so forth.
83 **
84 **      Parameters:
85 **              fp -- the file to send the o/p to
86 **              fmt -- format instructions for the o/p
87 **              ap -- vectors of data units used for formating
88 **
89 **      Results:
90 **              Failure: SM_IO_EOF and errno set
91 **              Success: number of data units used in the formating
92 **
93 **      Side effects:
94 **              formatted o/p can be SM_IO_BUFSIZ length maximum
95 */
96
97 static int
98 sm_bprintf(fp, fmt, ap)
99         SM_FILE_T *fp;
100         const char *fmt;
101         SM_VA_LOCAL_DECL
102 {
103         int ret;
104         SM_FILE_T fake;
105         unsigned char buf[SM_IO_BUFSIZ];
106         extern const char SmFileMagic[];
107
108         /* copy the important variables */
109         fake.sm_magic = SmFileMagic;
110         fake.f_timeout = SM_TIME_FOREVER;
111         fake.f_timeoutstate = SM_TIME_BLOCK;
112         fake.f_flags = fp->f_flags & ~SMNBF;
113         fake.f_file = fp->f_file;
114         fake.f_cookie = fp->f_cookie;
115         fake.f_write = fp->f_write;
116         fake.f_close = NULL;
117         fake.f_open = NULL;
118         fake.f_read = NULL;
119         fake.f_seek = NULL;
120         fake.f_setinfo = fake.f_getinfo = NULL;
121         fake.f_type = "sm_bprintf:fake";
122
123         /* set up the buffer */
124         fake.f_bf.smb_base = fake.f_p = buf;
125         fake.f_bf.smb_size = fake.f_w = sizeof(buf);
126         fake.f_lbfsize = 0;     /* not actually used, but Just In Case */
127
128         /* do the work, then copy any error status */
129         ret = sm_io_vfprintf(&fake, SM_TIME_FOREVER, fmt, ap);
130         if (ret >= 0 && sm_io_flush(&fake, SM_TIME_FOREVER))
131                 ret = SM_IO_EOF;        /* errno set by sm_io_flush */
132         if (fake.f_flags & SMERR)
133                 fp->f_flags |= SMERR;
134         return ret;
135 }
136
137
138 #define BUF             40
139
140 #define STATIC_ARG_TBL_SIZE 8   /* Size of static argument table. */
141
142
143 /* Macros for converting digits to letters and vice versa */
144 #define to_digit(c)     ((c) - '0')
145 #define is_digit(c)     ((unsigned) to_digit(c) <= 9)
146 #define to_char(n)      ((char) (n) + '0')
147
148 /* 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 LONGINT         0x010           /* long integer */
153 #define QUADINT         0x020           /* quad integer */
154 #define SHORTINT        0x040           /* short integer */
155 #define ZEROPAD         0x080           /* zero (as opposed to blank) pad */
156 #define FPT             0x100           /* Floating point number */
157
158 /*
159 **  SM_IO_VPRINTF -- performs actual formating for o/p
160 **
161 **      Parameters:
162 **              fp -- file pointer for o/p
163 **              timeout -- time to complete the print
164 **              fmt0 -- formating directives
165 **              ap -- vectors with data units for formating
166 **
167 **      Results:
168 **              Success: number of data units used for formatting
169 **              Failure: SM_IO_EOF and sets errno
170 */
171
172 int
173 sm_io_vfprintf(fp, timeout, fmt0, ap)
174         SM_FILE_T *fp;
175         int timeout;
176         const char *fmt0;
177         SM_VA_LOCAL_DECL
178 {
179         register char *fmt;     /* format string */
180         register int ch;        /* character from fmt */
181         register int n, m, n2;  /* handy integers (short term usage) */
182         register char *cp;      /* handy char pointer (short term usage) */
183         register struct sm_iov *iovp;/* for PRINT macro */
184         register int flags;     /* flags as above */
185         int ret;                /* return value accumulator */
186         int width;              /* width from format (%8d), or 0 */
187         int prec;               /* precision from format (%.3d), or -1 */
188         char sign;              /* sign prefix (' ', '+', '-', or \0) */
189         wchar_t wc;
190         ULONGLONG_T _uquad;     /* integer arguments %[diouxX] */
191         enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */
192         int dprec;              /* a copy of prec if [diouxX], 0 otherwise */
193         int realsz;             /* field size expanded by dprec */
194         int size;               /* size of converted field or string */
195         char *xdigs="0123456789abcdef"; /* digits for [xX] conversion */
196 #define NIOV 8
197         struct sm_uio uio;      /* output information: summary */
198         struct sm_iov iov[NIOV];/* ... and individual io vectors */
199         char buf[BUF];          /* space for %c, %[diouxX], %[eEfgG] */
200         char ox[2];             /* space for 0x hex-prefix */
201         va_list *argtable;      /* args, built due to positional arg */
202         va_list statargtable[STATIC_ARG_TBL_SIZE];
203         int nextarg;            /* 1-based argument index */
204         va_list orgap;          /* original argument pointer */
205
206         /*
207         **  Choose PADSIZE to trade efficiency vs. size.  If larger printf
208         **  fields occur frequently, increase PADSIZE and make the initialisers
209         **  below longer.
210         */
211 #define PADSIZE 16              /* pad chunk size */
212         static char blanks[PADSIZE] =
213          {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
214         static char zeroes[PADSIZE] =
215          {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
216
217         /*
218         **  BEWARE, these `goto error' on error, and PAD uses `n'.
219         */
220 #define PRINT(ptr, len) do { \
221         iovp->iov_base = (ptr); \
222         iovp->iov_len = (len); \
223         uio.uio_resid += (len); \
224         iovp++; \
225         if (++uio.uio_iovcnt >= NIOV) \
226         { \
227                 if (sm_print(fp, timeout, &uio)) \
228                         goto error; \
229                 iovp = iov; \
230         } \
231 } while (0)
232 #define PAD(howmany, with) do \
233 { \
234         if ((n = (howmany)) > 0) \
235         { \
236                 while (n > PADSIZE) { \
237                         PRINT(with, PADSIZE); \
238                         n -= PADSIZE; \
239                 } \
240                 PRINT(with, n); \
241         } \
242 } while (0)
243 #define FLUSH() do \
244 { \
245         if (uio.uio_resid && sm_print(fp, timeout, &uio)) \
246                 goto error; \
247         uio.uio_iovcnt = 0; \
248         iovp = iov; \
249 } while (0)
250
251         /*
252         **  To extend shorts properly, we need both signed and unsigned
253         **  argument extraction methods.
254         */
255 #define SARG() \
256         (flags&QUADINT ? SM_VA_ARG(ap, LONGLONG_T) : \
257             flags&LONGINT ? GETARG(long) : \
258             flags&SHORTINT ? (long) (short) GETARG(int) : \
259             (long) GETARG(int))
260 #define UARG() \
261         (flags&QUADINT ? SM_VA_ARG(ap, ULONGLONG_T) : \
262             flags&LONGINT ? GETARG(unsigned long) : \
263             flags&SHORTINT ? (unsigned long) (unsigned short) GETARG(int) : \
264             (unsigned long) GETARG(unsigned int))
265
266         /*
267         **  Get * arguments, including the form *nn$.  Preserve the nextarg
268         **  that the argument can be gotten once the type is determined.
269         */
270 #define GETASTER(val) \
271         n2 = 0; \
272         cp = fmt; \
273         while (is_digit(*cp)) \
274         { \
275                 n2 = 10 * n2 + to_digit(*cp); \
276                 cp++; \
277         } \
278         if (*cp == '$') \
279         { \
280                 int hold = nextarg; \
281                 if (argtable == NULL) \
282                 { \
283                         argtable = statargtable; \
284                         sm_find_arguments(fmt0, orgap, &argtable); \
285                 } \
286                 nextarg = n2; \
287                 val = GETARG(int); \
288                 nextarg = hold; \
289                 fmt = ++cp; \
290         } \
291         else \
292         { \
293                 val = GETARG(int); \
294         }
295
296 /*
297 **  Get the argument indexed by nextarg.   If the argument table is
298 **  built, use it to get the argument.  If its not, get the next
299 **  argument (and arguments must be gotten sequentially).
300 */
301
302 #if SM_VA_STD
303 # define GETARG(type) \
304         (((argtable != NULL) ? (void) (ap = argtable[nextarg]) : (void) 0), \
305          nextarg++, SM_VA_ARG(ap, type))
306 #else /* SM_VA_STD */
307 # define GETARG(type) \
308         ((argtable != NULL) ? (*((type*)(argtable[nextarg++]))) : \
309                               (nextarg++, SM_VA_ARG(ap, type)))
310 #endif /* SM_VA_STD */
311
312         /* sorry, fprintf(read_only_file, "") returns SM_IO_EOF, not 0 */
313         if (cantwrite(fp))
314         {
315                 errno = EBADF;
316                 return SM_IO_EOF;
317         }
318
319         /* optimise fprintf(stderr) (and other unbuffered Unix files) */
320         if ((fp->f_flags & (SMNBF|SMWR|SMRW)) == (SMNBF|SMWR) &&
321             fp->f_file >= 0)
322                 return sm_bprintf(fp, fmt0, ap);
323
324         fmt = (char *) fmt0;
325         argtable = NULL;
326         nextarg = 1;
327         SM_VA_COPY(orgap, ap);
328         uio.uio_iov = iovp = iov;
329         uio.uio_resid = 0;
330         uio.uio_iovcnt = 0;
331         ret = 0;
332
333         /* Scan the format for conversions (`%' character). */
334         for (;;)
335         {
336                 cp = fmt;
337                 n = 0;
338                 while ((wc = *fmt) != '\0')
339                 {
340                         if (wc == '%')
341                         {
342                                 n = 1;
343                                 break;
344                         }
345                         fmt++;
346                 }
347                 if ((m = fmt - cp) != 0)
348                 {
349                         PRINT(cp, m);
350                         ret += m;
351                 }
352                 if (n <= 0)
353                         goto done;
354                 fmt++;          /* skip over '%' */
355
356                 flags = 0;
357                 dprec = 0;
358                 width = 0;
359                 prec = -1;
360                 sign = '\0';
361
362 rflag:          ch = *fmt++;
363 reswitch:       switch (ch)
364                 {
365                   case ' ':
366
367                         /*
368                         **  ``If the space and + flags both appear, the space
369                         **  flag will be ignored.''
370                         **      -- ANSI X3J11
371                         */
372
373                         if (!sign)
374                                 sign = ' ';
375                         goto rflag;
376                   case '#':
377                         flags |= ALT;
378                         goto rflag;
379                   case '*':
380
381                         /*
382                         **  ``A negative field width argument is taken as a
383                         **  - flag followed by a positive field width.''
384                         **      -- ANSI X3J11
385                         **  They don't exclude field widths read from args.
386                         */
387
388                         GETASTER(width);
389                         if (width >= 0)
390                                 goto rflag;
391                         width = -width;
392                         /* FALLTHROUGH */
393                   case '-':
394                         flags |= LADJUST;
395                         goto rflag;
396                   case '+':
397                         sign = '+';
398                         goto rflag;
399                   case '.':
400                         if ((ch = *fmt++) == '*')
401                         {
402                                 GETASTER(n);
403                                 prec = n < 0 ? -1 : n;
404                                 goto rflag;
405                         }
406                         n = 0;
407                         while (is_digit(ch))
408                         {
409                                 n = 10 * n + to_digit(ch);
410                                 ch = *fmt++;
411                         }
412                         if (ch == '$')
413                         {
414                                 nextarg = n;
415                                 if (argtable == NULL)
416                                 {
417                                         argtable = statargtable;
418                                         sm_find_arguments(fmt0, orgap,
419                                             &argtable);
420                                 }
421                                 goto rflag;
422                         }
423                         prec = n < 0 ? -1 : n;
424                         goto reswitch;
425                   case '0':
426
427                         /*
428                         **  ``Note that 0 is taken as a flag, not as the
429                         **  beginning of a field width.''
430                         **      -- ANSI X3J11
431                         */
432
433                         flags |= ZEROPAD;
434                         goto rflag;
435                   case '1': case '2': case '3': case '4':
436                   case '5': case '6': case '7': case '8': case '9':
437                         n = 0;
438                         do
439                         {
440                                 n = 10 * n + to_digit(ch);
441                                 ch = *fmt++;
442                         } while (is_digit(ch));
443                         if (ch == '$')
444                         {
445                                 nextarg = n;
446                                 if (argtable == NULL)
447                                 {
448                                         argtable = statargtable;
449                                         sm_find_arguments(fmt0, orgap,
450                                             &argtable);
451                                 }
452                                 goto rflag;
453                         }
454                         width = n;
455                         goto reswitch;
456                   case 'h':
457                         flags |= SHORTINT;
458                         goto rflag;
459                   case 'l':
460                         if (*fmt == 'l')
461                         {
462                                 fmt++;
463                                 flags |= QUADINT;
464                         }
465                         else
466                         {
467                                 flags |= LONGINT;
468                         }
469                         goto rflag;
470                   case 'q':
471                         flags |= QUADINT;
472                         goto rflag;
473                   case 'c':
474                         *(cp = buf) = GETARG(int);
475                         size = 1;
476                         sign = '\0';
477                         break;
478                   case 'D':
479                         flags |= LONGINT;
480                         /*FALLTHROUGH*/
481                   case 'd':
482                   case 'i':
483                         _uquad = SARG();
484                         if ((LONGLONG_T) _uquad < 0)
485                         {
486                                 _uquad = -(LONGLONG_T) _uquad;
487                                 sign = '-';
488                         }
489                         base = DEC;
490                         goto number;
491                   case 'e':
492                   case 'E':
493                   case 'f':
494                   case 'g':
495                   case 'G':
496                         {
497                                 double val;
498                                 char *p;
499                                 char fmt[16];
500                                 char out[150];
501                                 size_t len;
502
503                                 /*
504                                 **  This code implements floating point output
505                                 **  in the most portable manner possible,
506                                 **  relying only on 'sprintf' as defined by
507                                 **  the 1989 ANSI C standard.
508                                 **  We silently cap width and precision
509                                 **  at 120, to avoid buffer overflow.
510                                 */
511
512                                 val = GETARG(double);
513
514                                 p = fmt;
515                                 *p++ = '%';
516                                 if (sign)
517                                         *p++ = sign;
518                                 if (flags & ALT)
519                                         *p++ = '#';
520                                 if (flags & LADJUST)
521                                         *p++ = '-';
522                                 if (flags & ZEROPAD)
523                                         *p++ = '0';
524                                 *p++ = '*';
525                                 if (prec >= 0)
526                                 {
527                                         *p++ = '.';
528                                         *p++ = '*';
529                                 }
530                                 *p++ = ch;
531                                 *p = '\0';
532
533                                 if (width > 120)
534                                         width = 120;
535                                 if (prec > 120)
536                                         prec = 120;
537                                 if (prec >= 0)
538                                         sprintf(out, fmt, width, prec, val);
539                                 else
540                                         sprintf(out, fmt, width, val);
541                                 len = strlen(out);
542                                 PRINT(out, len);
543                                 FLUSH();
544                                 continue;
545                         }
546                 case 'n':
547                         if (flags & QUADINT)
548                                 *GETARG(LONGLONG_T *) = ret;
549                         else if (flags & LONGINT)
550                                 *GETARG(long *) = ret;
551                         else if (flags & SHORTINT)
552                                 *GETARG(short *) = ret;
553                         else
554                                 *GETARG(int *) = ret;
555                         continue;       /* no output */
556                   case 'O':
557                         flags |= LONGINT;
558                         /*FALLTHROUGH*/
559                   case 'o':
560                         _uquad = UARG();
561                         base = OCT;
562                         goto nosign;
563                   case 'p':
564
565                         /*
566                         **  ``The argument shall be a pointer to void.  The
567                         **  value of the pointer is converted to a sequence
568                         **  of printable characters, in an implementation-
569                         **  defined manner.''
570                         **      -- ANSI X3J11
571                         */
572
573                         /* NOSTRICT */
574                         {
575                                 union
576                                 {
577                                         void *p;
578                                         ULONGLONG_T ll;
579                                         unsigned long l;
580                                         unsigned i;
581                                 } u;
582                                 u.p = GETARG(void *);
583                                 if (sizeof(void *) == sizeof(ULONGLONG_T))
584                                         _uquad = u.ll;
585                                 else if (sizeof(void *) == sizeof(long))
586                                         _uquad = u.l;
587                                 else
588                                         _uquad = u.i;
589                         }
590                         base = HEX;
591                         xdigs = "0123456789abcdef";
592                         flags |= HEXPREFIX;
593                         ch = 'x';
594                         goto nosign;
595                   case 's':
596                         if ((cp = GETARG(char *)) == NULL)
597                                 cp = "(null)";
598                         if (prec >= 0)
599                         {
600                                 /*
601                                 **  can't use strlen; can only look for the
602                                 **  NUL in the first `prec' characters, and
603                                 **  strlen() will go further.
604                                 */
605
606                                 char *p = memchr(cp, 0, prec);
607
608                                 if (p != NULL)
609                                 {
610                                         size = p - cp;
611                                         if (size > prec)
612                                                 size = prec;
613                                 }
614                                 else
615                                         size = prec;
616                         }
617                         else
618                                 size = strlen(cp);
619                         sign = '\0';
620                         break;
621                   case 'U':
622                         flags |= LONGINT;
623                         /*FALLTHROUGH*/
624                   case 'u':
625                         _uquad = UARG();
626                         base = DEC;
627                         goto nosign;
628                   case 'X':
629                         xdigs = "0123456789ABCDEF";
630                         goto hex;
631                   case 'x':
632                         xdigs = "0123456789abcdef";
633 hex:                    _uquad = UARG();
634                         base = HEX;
635                         /* leading 0x/X only if non-zero */
636                         if (flags & ALT && _uquad != 0)
637                                 flags |= HEXPREFIX;
638
639                         /* unsigned conversions */
640 nosign:                 sign = '\0';
641
642                         /*
643                         **  ``... diouXx conversions ... if a precision is
644                         **  specified, the 0 flag will be ignored.''
645                         **      -- ANSI X3J11
646                         */
647
648 number:                 if ((dprec = prec) >= 0)
649                                 flags &= ~ZEROPAD;
650
651                         /*
652                         **  ``The result of converting a zero value with an
653                         **  explicit precision of zero is no characters.''
654                         **      -- ANSI X3J11
655                         */
656
657                         cp = buf + BUF;
658                         if (_uquad != 0 || prec != 0)
659                         {
660                                 /*
661                                 **  Unsigned mod is hard, and unsigned mod
662                                 **  by a constant is easier than that by
663                                 **  a variable; hence this switch.
664                                 */
665
666                                 switch (base)
667                                 {
668                                   case OCT:
669                                         do
670                                         {
671                                                 *--cp = to_char(_uquad & 7);
672                                                 _uquad >>= 3;
673                                         } while (_uquad);
674                                         /* handle octal leading 0 */
675                                         if (flags & ALT && *cp != '0')
676                                                 *--cp = '0';
677                                         break;
678
679                                   case DEC:
680                                         /* many numbers are 1 digit */
681                                         while (_uquad >= 10)
682                                         {
683                                                 *--cp = to_char(_uquad % 10);
684                                                 _uquad /= 10;
685                                         }
686                                         *--cp = to_char(_uquad);
687                                         break;
688
689                                   case HEX:
690                                         do
691                                         {
692                                                 *--cp = xdigs[_uquad & 15];
693                                                 _uquad >>= 4;
694                                         } while (_uquad);
695                                         break;
696
697                                   default:
698                                         cp = "bug in sm_io_vfprintf: bad base";
699                                         size = strlen(cp);
700                                         goto skipsize;
701                                 }
702                         }
703                         size = buf + BUF - cp;
704                   skipsize:
705                         break;
706                   default:      /* "%?" prints ?, unless ? is NUL */
707                         if (ch == '\0')
708                                 goto done;
709                         /* pretend it was %c with argument ch */
710                         cp = buf;
711                         *cp = ch;
712                         size = 1;
713                         sign = '\0';
714                         break;
715                 }
716
717                 /*
718                 **  All reasonable formats wind up here.  At this point, `cp'
719                 **  points to a string which (if not flags&LADJUST) should be
720                 **  padded out to `width' places.  If flags&ZEROPAD, it should
721                 **  first be prefixed by any sign or other prefix; otherwise,
722                 **  it should be blank padded before the prefix is emitted.
723                 **  After any left-hand padding and prefixing, emit zeroes
724                 **  required by a decimal [diouxX] precision, then print the
725                 **  string proper, then emit zeroes required by any leftover
726                 **  floating precision; finally, if LADJUST, pad with blanks.
727                 **
728                 **  Compute actual size, so we know how much to pad.
729                 **  size excludes decimal prec; realsz includes it.
730                 */
731
732                 realsz = dprec > size ? dprec : size;
733                 if (sign)
734                         realsz++;
735                 else if (flags & HEXPREFIX)
736                         realsz+= 2;
737
738                 /* right-adjusting blank padding */
739                 if ((flags & (LADJUST|ZEROPAD)) == 0)
740                         PAD(width - realsz, blanks);
741
742                 /* prefix */
743                 if (sign)
744                 {
745                         PRINT(&sign, 1);
746                 }
747                 else if (flags & HEXPREFIX)
748                 {
749                         ox[0] = '0';
750                         ox[1] = ch;
751                         PRINT(ox, 2);
752                 }
753
754                 /* right-adjusting zero padding */
755                 if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
756                         PAD(width - realsz, zeroes);
757
758                 /* leading zeroes from decimal precision */
759                 PAD(dprec - size, zeroes);
760
761                 /* the string or number proper */
762                 PRINT(cp, size);
763                 /* left-adjusting padding (always blank) */
764                 if (flags & LADJUST)
765                         PAD(width - realsz, blanks);
766
767                 /* finally, adjust ret */
768                 ret += width > realsz ? width : realsz;
769
770                 FLUSH();        /* copy out the I/O vectors */
771         }
772 done:
773         FLUSH();
774 error:
775         if ((argtable != NULL) && (argtable != statargtable))
776                 sm_free(argtable);
777         return sm_error(fp) ? SM_IO_EOF : ret;
778         /* NOTREACHED */
779 }
780
781 /* Type ids for argument type table. */
782 #define T_UNUSED        0
783 #define T_SHORT         1
784 #define T_U_SHORT       2
785 #define TP_SHORT        3
786 #define T_INT           4
787 #define T_U_INT         5
788 #define TP_INT          6
789 #define T_LONG          7
790 #define T_U_LONG        8
791 #define TP_LONG         9
792 #define T_QUAD          10
793 #define T_U_QUAD        11
794 #define TP_QUAD         12
795 #define T_DOUBLE        13
796 #define TP_CHAR         15
797 #define TP_VOID         16
798
799 /*
800 **  SM_FIND_ARGUMENTS -- find all args when a positional parameter is found.
801 **
802 **  Find all arguments when a positional parameter is encountered.  Returns a
803 **  table, indexed by argument number, of pointers to each arguments.  The
804 **  initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
805 **  It will be replaced with a malloc-ed one if it overflows.
806 **
807 **      Parameters:
808 **              fmt0 -- formating directives
809 **              ap -- vector list of data unit for formating consumption
810 **              argtable -- an indexable table (returned) of 'ap'
811 **
812 **      Results:
813 **              none.
814 */
815
816 static void
817 sm_find_arguments(fmt0, ap, argtable)
818         const char *fmt0;
819         SM_VA_LOCAL_DECL
820         va_list **argtable;
821 {
822         register char *fmt;     /* format string */
823         register int ch;        /* character from fmt */
824         register int n, n2;     /* handy integer (short term usage) */
825         register char *cp;      /* handy char pointer (short term usage) */
826         register int flags;     /* flags as above */
827         unsigned char *typetable; /* table of types */
828         unsigned char stattypetable[STATIC_ARG_TBL_SIZE];
829         int tablesize;          /* current size of type table */
830         int tablemax;           /* largest used index in table */
831         int nextarg;            /* 1-based argument index */
832
833         /* Add an argument type to the table, expanding if necessary. */
834 #define ADDTYPE(type) \
835         ((nextarg >= tablesize) ? \
836                 (sm_grow_type_table_x(&typetable, &tablesize), 0) : 0, \
837         typetable[nextarg++] = type, \
838         (nextarg > tablemax) ? tablemax = nextarg : 0)
839
840 #define ADDSARG() \
841         ((flags & LONGINT) ? ADDTYPE(T_LONG) : \
842                 ((flags & SHORTINT) ? ADDTYPE(T_SHORT) : ADDTYPE(T_INT)))
843
844 #define ADDUARG() \
845         ((flags & LONGINT) ? ADDTYPE(T_U_LONG) : \
846                 ((flags & SHORTINT) ? ADDTYPE(T_U_SHORT) : ADDTYPE(T_U_INT)))
847
848         /* Add * arguments to the type array. */
849 #define ADDASTER() \
850         n2 = 0; \
851         cp = fmt; \
852         while (is_digit(*cp)) \
853         { \
854                 n2 = 10 * n2 + to_digit(*cp); \
855                 cp++; \
856         } \
857         if (*cp == '$') \
858         { \
859                 int hold = nextarg; \
860                 nextarg = n2; \
861                 ADDTYPE (T_INT); \
862                 nextarg = hold; \
863                 fmt = ++cp; \
864         } \
865         else \
866         { \
867                 ADDTYPE (T_INT); \
868         }
869         fmt = (char *) fmt0;
870         typetable = stattypetable;
871         tablesize = STATIC_ARG_TBL_SIZE;
872         tablemax = 0;
873         nextarg = 1;
874         (void) memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE);
875
876         /* Scan the format for conversions (`%' character). */
877         for (;;)
878         {
879                 for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
880                         /* void */;
881                 if (ch == '\0')
882                         goto done;
883                 fmt++;          /* skip over '%' */
884
885                 flags = 0;
886
887 rflag:          ch = *fmt++;
888 reswitch:       switch (ch)
889                 {
890                   case ' ':
891                   case '#':
892                         goto rflag;
893                   case '*':
894                         ADDASTER();
895                         goto rflag;
896                   case '-':
897                   case '+':
898                         goto rflag;
899                   case '.':
900                         if ((ch = *fmt++) == '*')
901                         {
902                                 ADDASTER();
903                                 goto rflag;
904                         }
905                         while (is_digit(ch))
906                         {
907                                 ch = *fmt++;
908                         }
909                         goto reswitch;
910                   case '0':
911                         goto rflag;
912                   case '1': case '2': case '3': case '4':
913                   case '5': case '6': case '7': case '8': case '9':
914                         n = 0;
915                         do
916                         {
917                                 n = 10 * n + to_digit(ch);
918                                 ch = *fmt++;
919                         } while (is_digit(ch));
920                         if (ch == '$')
921                         {
922                                 nextarg = n;
923                                 goto rflag;
924                         }
925                         goto reswitch;
926                   case 'h':
927                         flags |= SHORTINT;
928                         goto rflag;
929                   case 'l':
930                         flags |= LONGINT;
931                         goto rflag;
932                   case 'q':
933                         flags |= QUADINT;
934                         goto rflag;
935                   case 'c':
936                         ADDTYPE(T_INT);
937                         break;
938                   case 'D':
939                         flags |= LONGINT;
940                         /*FALLTHROUGH*/
941                   case 'd':
942                   case 'i':
943                         if (flags & QUADINT)
944                         {
945                                 ADDTYPE(T_QUAD);
946                         }
947                         else
948                         {
949                                 ADDSARG();
950                         }
951                         break;
952                   case 'e':
953                   case 'E':
954                   case 'f':
955                   case 'g':
956                   case 'G':
957                         ADDTYPE(T_DOUBLE);
958                         break;
959                   case 'n':
960                         if (flags & QUADINT)
961                                 ADDTYPE(TP_QUAD);
962                         else if (flags & LONGINT)
963                                 ADDTYPE(TP_LONG);
964                         else if (flags & SHORTINT)
965                                 ADDTYPE(TP_SHORT);
966                         else
967                                 ADDTYPE(TP_INT);
968                         continue;       /* no output */
969                   case 'O':
970                         flags |= LONGINT;
971                         /*FALLTHROUGH*/
972                   case 'o':
973                         if (flags & QUADINT)
974                                 ADDTYPE(T_U_QUAD);
975                         else
976                                 ADDUARG();
977                         break;
978                   case 'p':
979                         ADDTYPE(TP_VOID);
980                         break;
981                   case 's':
982                         ADDTYPE(TP_CHAR);
983                         break;
984                   case 'U':
985                         flags |= LONGINT;
986                         /*FALLTHROUGH*/
987                   case 'u':
988                         if (flags & QUADINT)
989                                 ADDTYPE(T_U_QUAD);
990                         else
991                                 ADDUARG();
992                         break;
993                   case 'X':
994                   case 'x':
995                         if (flags & QUADINT)
996                                 ADDTYPE(T_U_QUAD);
997                         else
998                                 ADDUARG();
999                         break;
1000                   default:      /* "%?" prints ?, unless ? is NUL */
1001                         if (ch == '\0')
1002                                 goto done;
1003                         break;
1004                 }
1005         }
1006 done:
1007         /* Build the argument table. */
1008         if (tablemax >= STATIC_ARG_TBL_SIZE)
1009         {
1010                 *argtable = (va_list *)
1011                     sm_malloc(sizeof(va_list) * (tablemax + 1));
1012         }
1013
1014         for (n = 1; n <= tablemax; n++)
1015         {
1016                 SM_VA_COPY((*argtable)[n], ap);
1017                 switch (typetable [n])
1018                 {
1019                   case T_UNUSED:
1020                         (void) SM_VA_ARG(ap, int);
1021                         break;
1022                   case T_SHORT:
1023                         (void) SM_VA_ARG(ap, int);
1024                         break;
1025                   case T_U_SHORT:
1026                         (void) SM_VA_ARG(ap, int);
1027                         break;
1028                   case TP_SHORT:
1029                         (void) SM_VA_ARG(ap, short *);
1030                         break;
1031                   case T_INT:
1032                         (void) SM_VA_ARG(ap, int);
1033                         break;
1034                   case T_U_INT:
1035                         (void) SM_VA_ARG(ap, unsigned int);
1036                         break;
1037                   case TP_INT:
1038                         (void) SM_VA_ARG(ap, int *);
1039                         break;
1040                   case T_LONG:
1041                         (void) SM_VA_ARG(ap, long);
1042                         break;
1043                   case T_U_LONG:
1044                         (void) SM_VA_ARG(ap, unsigned long);
1045                         break;
1046                   case TP_LONG:
1047                         (void) SM_VA_ARG(ap, long *);
1048                         break;
1049                   case T_QUAD:
1050                         (void) SM_VA_ARG(ap, LONGLONG_T);
1051                         break;
1052                   case T_U_QUAD:
1053                         (void) SM_VA_ARG(ap, ULONGLONG_T);
1054                         break;
1055                   case TP_QUAD:
1056                         (void) SM_VA_ARG(ap, LONGLONG_T *);
1057                         break;
1058                   case T_DOUBLE:
1059                         (void) SM_VA_ARG(ap, double);
1060                         break;
1061                   case TP_CHAR:
1062                         (void) SM_VA_ARG(ap, char *);
1063                         break;
1064                   case TP_VOID:
1065                         (void) SM_VA_ARG(ap, void *);
1066                         break;
1067                 }
1068         }
1069
1070         if ((typetable != NULL) && (typetable != stattypetable))
1071                 sm_free(typetable);
1072 }
1073
1074 /*
1075 **  SM_GROW_TYPE_TABLE -- Increase the size of the type table.
1076 **
1077 **      Parameters:
1078 **              tabletype -- type of table to grow
1079 **              tablesize -- requested new table size
1080 **
1081 **      Results:
1082 **              Raises an exception if can't allocate memory.
1083 */
1084
1085 static void
1086 sm_grow_type_table_x(typetable, tablesize)
1087         unsigned char **typetable;
1088         int *tablesize;
1089 {
1090         unsigned char *oldtable = *typetable;
1091         int newsize = *tablesize * 2;
1092
1093         if (*tablesize == STATIC_ARG_TBL_SIZE)
1094         {
1095                 *typetable = (unsigned char *) sm_malloc_x(sizeof(unsigned char)
1096                                                            * newsize);
1097                 (void) memmove(*typetable, oldtable, *tablesize);
1098         }
1099         else
1100         {
1101                 *typetable = (unsigned char *) sm_realloc_x(typetable,
1102                                         sizeof(unsigned char) * newsize);
1103         }
1104         (void) memset(&typetable [*tablesize], T_UNUSED,
1105                        (newsize - *tablesize));
1106
1107         *tablesize = newsize;
1108 }