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