Correct conditional to correctly treat segments which end on a boundary.
[dragonfly.git] / contrib / sendmail-8.13.8 / 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.54 2005/05/16 03:52:00 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 #if HASSNPRINTF
539                                         snprintf(out, sizeof(out), fmt, width,
540                                                 prec, val);
541 #else /* HASSNPRINTF */
542                                         sprintf(out, fmt, width, prec, val);
543 #endif /* HASSNPRINTF */
544                                 else
545 #if HASSNPRINTF
546                                         snprintf(out, sizeof(out), fmt, width,
547                                                 val);
548 #else /* HASSNPRINTF */
549                                         sprintf(out, fmt, width, val);
550 #endif /* HASSNPRINTF */
551                                 len = strlen(out);
552                                 PRINT(out, len);
553                                 FLUSH();
554                                 continue;
555                         }
556                 case 'n':
557                         if (flags & QUADINT)
558                                 *GETARG(LONGLONG_T *) = ret;
559                         else if (flags & LONGINT)
560                                 *GETARG(long *) = ret;
561                         else if (flags & SHORTINT)
562                                 *GETARG(short *) = ret;
563                         else
564                                 *GETARG(int *) = ret;
565                         continue;       /* no output */
566                   case 'O':
567                         flags |= LONGINT;
568                         /*FALLTHROUGH*/
569                   case 'o':
570                         _uquad = UARG();
571                         base = OCT;
572                         goto nosign;
573                   case 'p':
574
575                         /*
576                         **  ``The argument shall be a pointer to void.  The
577                         **  value of the pointer is converted to a sequence
578                         **  of printable characters, in an implementation-
579                         **  defined manner.''
580                         **      -- ANSI X3J11
581                         */
582
583                         /* NOSTRICT */
584                         {
585                                 union
586                                 {
587                                         void *p;
588                                         ULONGLONG_T ll;
589                                         unsigned long l;
590                                         unsigned i;
591                                 } u;
592                                 u.p = GETARG(void *);
593                                 if (sizeof(void *) == sizeof(ULONGLONG_T))
594                                         _uquad = u.ll;
595                                 else if (sizeof(void *) == sizeof(long))
596                                         _uquad = u.l;
597                                 else
598                                         _uquad = u.i;
599                         }
600                         base = HEX;
601                         xdigs = "0123456789abcdef";
602                         flags |= HEXPREFIX;
603                         ch = 'x';
604                         goto nosign;
605                   case 's':
606                         if ((cp = GETARG(char *)) == NULL)
607                                 cp = "(null)";
608                         if (prec >= 0)
609                         {
610                                 /*
611                                 **  can't use strlen; can only look for the
612                                 **  NUL in the first `prec' characters, and
613                                 **  strlen() will go further.
614                                 */
615
616                                 char *p = memchr(cp, 0, prec);
617
618                                 if (p != NULL)
619                                 {
620                                         size = p - cp;
621                                         if (size > prec)
622                                                 size = prec;
623                                 }
624                                 else
625                                         size = prec;
626                         }
627                         else
628                                 size = strlen(cp);
629                         sign = '\0';
630                         break;
631                   case 'U':
632                         flags |= LONGINT;
633                         /*FALLTHROUGH*/
634                   case 'u':
635                         _uquad = UARG();
636                         base = DEC;
637                         goto nosign;
638                   case 'X':
639                         xdigs = "0123456789ABCDEF";
640                         goto hex;
641                   case 'x':
642                         xdigs = "0123456789abcdef";
643 hex:                    _uquad = UARG();
644                         base = HEX;
645                         /* leading 0x/X only if non-zero */
646                         if (flags & ALT && _uquad != 0)
647                                 flags |= HEXPREFIX;
648
649                         /* unsigned conversions */
650 nosign:                 sign = '\0';
651
652                         /*
653                         **  ``... diouXx conversions ... if a precision is
654                         **  specified, the 0 flag will be ignored.''
655                         **      -- ANSI X3J11
656                         */
657
658 number:                 if ((dprec = prec) >= 0)
659                                 flags &= ~ZEROPAD;
660
661                         /*
662                         **  ``The result of converting a zero value with an
663                         **  explicit precision of zero is no characters.''
664                         **      -- ANSI X3J11
665                         */
666
667                         cp = buf + BUF;
668                         if (_uquad != 0 || prec != 0)
669                         {
670                                 /*
671                                 **  Unsigned mod is hard, and unsigned mod
672                                 **  by a constant is easier than that by
673                                 **  a variable; hence this switch.
674                                 */
675
676                                 switch (base)
677                                 {
678                                   case OCT:
679                                         do
680                                         {
681                                                 *--cp = to_char(_uquad & 7);
682                                                 _uquad >>= 3;
683                                         } while (_uquad);
684                                         /* handle octal leading 0 */
685                                         if (flags & ALT && *cp != '0')
686                                                 *--cp = '0';
687                                         break;
688
689                                   case DEC:
690                                         /* many numbers are 1 digit */
691                                         while (_uquad >= 10)
692                                         {
693                                                 *--cp = to_char(_uquad % 10);
694                                                 _uquad /= 10;
695                                         }
696                                         *--cp = to_char(_uquad);
697                                         break;
698
699                                   case HEX:
700                                         do
701                                         {
702                                                 *--cp = xdigs[_uquad & 15];
703                                                 _uquad >>= 4;
704                                         } while (_uquad);
705                                         break;
706
707                                   default:
708                                         cp = "bug in sm_io_vfprintf: bad base";
709                                         size = strlen(cp);
710                                         goto skipsize;
711                                 }
712                         }
713                         size = buf + BUF - cp;
714                   skipsize:
715                         break;
716                   default:      /* "%?" prints ?, unless ? is NUL */
717                         if (ch == '\0')
718                                 goto done;
719                         /* pretend it was %c with argument ch */
720                         cp = buf;
721                         *cp = ch;
722                         size = 1;
723                         sign = '\0';
724                         break;
725                 }
726
727                 /*
728                 **  All reasonable formats wind up here.  At this point, `cp'
729                 **  points to a string which (if not flags&LADJUST) should be
730                 **  padded out to `width' places.  If flags&ZEROPAD, it should
731                 **  first be prefixed by any sign or other prefix; otherwise,
732                 **  it should be blank padded before the prefix is emitted.
733                 **  After any left-hand padding and prefixing, emit zeroes
734                 **  required by a decimal [diouxX] precision, then print the
735                 **  string proper, then emit zeroes required by any leftover
736                 **  floating precision; finally, if LADJUST, pad with blanks.
737                 **
738                 **  Compute actual size, so we know how much to pad.
739                 **  size excludes decimal prec; realsz includes it.
740                 */
741
742                 realsz = dprec > size ? dprec : size;
743                 if (sign)
744                         realsz++;
745                 else if (flags & HEXPREFIX)
746                         realsz+= 2;
747
748                 /* right-adjusting blank padding */
749                 if ((flags & (LADJUST|ZEROPAD)) == 0)
750                         PAD(width - realsz, blanks);
751
752                 /* prefix */
753                 if (sign)
754                 {
755                         PRINT(&sign, 1);
756                 }
757                 else if (flags & HEXPREFIX)
758                 {
759                         ox[0] = '0';
760                         ox[1] = ch;
761                         PRINT(ox, 2);
762                 }
763
764                 /* right-adjusting zero padding */
765                 if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
766                         PAD(width - realsz, zeroes);
767
768                 /* leading zeroes from decimal precision */
769                 PAD(dprec - size, zeroes);
770
771                 /* the string or number proper */
772                 PRINT(cp, size);
773                 /* left-adjusting padding (always blank) */
774                 if (flags & LADJUST)
775                         PAD(width - realsz, blanks);
776
777                 /* finally, adjust ret */
778                 ret += width > realsz ? width : realsz;
779
780                 FLUSH();        /* copy out the I/O vectors */
781         }
782 done:
783         FLUSH();
784 error:
785         if ((argtable != NULL) && (argtable != statargtable))
786                 sm_free(argtable);
787         return sm_error(fp) ? SM_IO_EOF : ret;
788         /* NOTREACHED */
789 }
790
791 /* Type ids for argument type table. */
792 #define T_UNUSED        0
793 #define T_SHORT         1
794 #define T_U_SHORT       2
795 #define TP_SHORT        3
796 #define T_INT           4
797 #define T_U_INT         5
798 #define TP_INT          6
799 #define T_LONG          7
800 #define T_U_LONG        8
801 #define TP_LONG         9
802 #define T_QUAD          10
803 #define T_U_QUAD        11
804 #define TP_QUAD         12
805 #define T_DOUBLE        13
806 #define TP_CHAR         15
807 #define TP_VOID         16
808
809 /*
810 **  SM_FIND_ARGUMENTS -- find all args when a positional parameter is found.
811 **
812 **  Find all arguments when a positional parameter is encountered.  Returns a
813 **  table, indexed by argument number, of pointers to each arguments.  The
814 **  initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
815 **  It will be replaced with a malloc-ed one if it overflows.
816 **
817 **      Parameters:
818 **              fmt0 -- formating directives
819 **              ap -- vector list of data unit for formating consumption
820 **              argtable -- an indexable table (returned) of 'ap'
821 **
822 **      Results:
823 **              none.
824 */
825
826 static void
827 sm_find_arguments(fmt0, ap, argtable)
828         const char *fmt0;
829         SM_VA_LOCAL_DECL
830         va_list **argtable;
831 {
832         register char *fmt;     /* format string */
833         register int ch;        /* character from fmt */
834         register int n, n2;     /* handy integer (short term usage) */
835         register char *cp;      /* handy char pointer (short term usage) */
836         register int flags;     /* flags as above */
837         unsigned char *typetable; /* table of types */
838         unsigned char stattypetable[STATIC_ARG_TBL_SIZE];
839         int tablesize;          /* current size of type table */
840         int tablemax;           /* largest used index in table */
841         int nextarg;            /* 1-based argument index */
842
843         /* Add an argument type to the table, expanding if necessary. */
844 #define ADDTYPE(type) \
845         ((nextarg >= tablesize) ? \
846                 (sm_grow_type_table_x(&typetable, &tablesize), 0) : 0, \
847         typetable[nextarg++] = type, \
848         (nextarg > tablemax) ? tablemax = nextarg : 0)
849
850 #define ADDSARG() \
851         ((flags & LONGINT) ? ADDTYPE(T_LONG) : \
852                 ((flags & SHORTINT) ? ADDTYPE(T_SHORT) : ADDTYPE(T_INT)))
853
854 #define ADDUARG() \
855         ((flags & LONGINT) ? ADDTYPE(T_U_LONG) : \
856                 ((flags & SHORTINT) ? ADDTYPE(T_U_SHORT) : ADDTYPE(T_U_INT)))
857
858         /* Add * arguments to the type array. */
859 #define ADDASTER() \
860         n2 = 0; \
861         cp = fmt; \
862         while (is_digit(*cp)) \
863         { \
864                 n2 = 10 * n2 + to_digit(*cp); \
865                 cp++; \
866         } \
867         if (*cp == '$') \
868         { \
869                 int hold = nextarg; \
870                 nextarg = n2; \
871                 ADDTYPE (T_INT); \
872                 nextarg = hold; \
873                 fmt = ++cp; \
874         } \
875         else \
876         { \
877                 ADDTYPE (T_INT); \
878         }
879         fmt = (char *) fmt0;
880         typetable = stattypetable;
881         tablesize = STATIC_ARG_TBL_SIZE;
882         tablemax = 0;
883         nextarg = 1;
884         (void) memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE);
885
886         /* Scan the format for conversions (`%' character). */
887         for (;;)
888         {
889                 for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
890                         /* void */;
891                 if (ch == '\0')
892                         goto done;
893                 fmt++;          /* skip over '%' */
894
895                 flags = 0;
896
897 rflag:          ch = *fmt++;
898 reswitch:       switch (ch)
899                 {
900                   case ' ':
901                   case '#':
902                         goto rflag;
903                   case '*':
904                         ADDASTER();
905                         goto rflag;
906                   case '-':
907                   case '+':
908                         goto rflag;
909                   case '.':
910                         if ((ch = *fmt++) == '*')
911                         {
912                                 ADDASTER();
913                                 goto rflag;
914                         }
915                         while (is_digit(ch))
916                         {
917                                 ch = *fmt++;
918                         }
919                         goto reswitch;
920                   case '0':
921                         goto rflag;
922                   case '1': case '2': case '3': case '4':
923                   case '5': case '6': case '7': case '8': case '9':
924                         n = 0;
925                         do
926                         {
927                                 n = 10 * n + to_digit(ch);
928                                 ch = *fmt++;
929                         } while (is_digit(ch));
930                         if (ch == '$')
931                         {
932                                 nextarg = n;
933                                 goto rflag;
934                         }
935                         goto reswitch;
936                   case 'h':
937                         flags |= SHORTINT;
938                         goto rflag;
939                   case 'l':
940                         flags |= LONGINT;
941                         goto rflag;
942                   case 'q':
943                         flags |= QUADINT;
944                         goto rflag;
945                   case 'c':
946                         ADDTYPE(T_INT);
947                         break;
948                   case 'D':
949                         flags |= LONGINT;
950                         /*FALLTHROUGH*/
951                   case 'd':
952                   case 'i':
953                         if (flags & QUADINT)
954                         {
955                                 ADDTYPE(T_QUAD);
956                         }
957                         else
958                         {
959                                 ADDSARG();
960                         }
961                         break;
962                   case 'e':
963                   case 'E':
964                   case 'f':
965                   case 'g':
966                   case 'G':
967                         ADDTYPE(T_DOUBLE);
968                         break;
969                   case 'n':
970                         if (flags & QUADINT)
971                                 ADDTYPE(TP_QUAD);
972                         else if (flags & LONGINT)
973                                 ADDTYPE(TP_LONG);
974                         else if (flags & SHORTINT)
975                                 ADDTYPE(TP_SHORT);
976                         else
977                                 ADDTYPE(TP_INT);
978                         continue;       /* no output */
979                   case 'O':
980                         flags |= LONGINT;
981                         /*FALLTHROUGH*/
982                   case 'o':
983                         if (flags & QUADINT)
984                                 ADDTYPE(T_U_QUAD);
985                         else
986                                 ADDUARG();
987                         break;
988                   case 'p':
989                         ADDTYPE(TP_VOID);
990                         break;
991                   case 's':
992                         ADDTYPE(TP_CHAR);
993                         break;
994                   case 'U':
995                         flags |= LONGINT;
996                         /*FALLTHROUGH*/
997                   case 'u':
998                         if (flags & QUADINT)
999                                 ADDTYPE(T_U_QUAD);
1000                         else
1001                                 ADDUARG();
1002                         break;
1003                   case 'X':
1004                   case 'x':
1005                         if (flags & QUADINT)
1006                                 ADDTYPE(T_U_QUAD);
1007                         else
1008                                 ADDUARG();
1009                         break;
1010                   default:      /* "%?" prints ?, unless ? is NUL */
1011                         if (ch == '\0')
1012                                 goto done;
1013                         break;
1014                 }
1015         }
1016 done:
1017         /* Build the argument table. */
1018         if (tablemax >= STATIC_ARG_TBL_SIZE)
1019         {
1020                 *argtable = (va_list *)
1021                     sm_malloc(sizeof(va_list) * (tablemax + 1));
1022         }
1023
1024         for (n = 1; n <= tablemax; n++)
1025         {
1026                 SM_VA_COPY((*argtable)[n], ap);
1027                 switch (typetable [n])
1028                 {
1029                   case T_UNUSED:
1030                         (void) SM_VA_ARG(ap, int);
1031                         break;
1032                   case T_SHORT:
1033                         (void) SM_VA_ARG(ap, int);
1034                         break;
1035                   case T_U_SHORT:
1036                         (void) SM_VA_ARG(ap, int);
1037                         break;
1038                   case TP_SHORT:
1039                         (void) SM_VA_ARG(ap, short *);
1040                         break;
1041                   case T_INT:
1042                         (void) SM_VA_ARG(ap, int);
1043                         break;
1044                   case T_U_INT:
1045                         (void) SM_VA_ARG(ap, unsigned int);
1046                         break;
1047                   case TP_INT:
1048                         (void) SM_VA_ARG(ap, int *);
1049                         break;
1050                   case T_LONG:
1051                         (void) SM_VA_ARG(ap, long);
1052                         break;
1053                   case T_U_LONG:
1054                         (void) SM_VA_ARG(ap, unsigned long);
1055                         break;
1056                   case TP_LONG:
1057                         (void) SM_VA_ARG(ap, long *);
1058                         break;
1059                   case T_QUAD:
1060                         (void) SM_VA_ARG(ap, LONGLONG_T);
1061                         break;
1062                   case T_U_QUAD:
1063                         (void) SM_VA_ARG(ap, ULONGLONG_T);
1064                         break;
1065                   case TP_QUAD:
1066                         (void) SM_VA_ARG(ap, LONGLONG_T *);
1067                         break;
1068                   case T_DOUBLE:
1069                         (void) SM_VA_ARG(ap, double);
1070                         break;
1071                   case TP_CHAR:
1072                         (void) SM_VA_ARG(ap, char *);
1073                         break;
1074                   case TP_VOID:
1075                         (void) SM_VA_ARG(ap, void *);
1076                         break;
1077                 }
1078         }
1079
1080         if ((typetable != NULL) && (typetable != stattypetable))
1081                 sm_free(typetable);
1082 }
1083
1084 /*
1085 **  SM_GROW_TYPE_TABLE -- Increase the size of the type table.
1086 **
1087 **      Parameters:
1088 **              tabletype -- type of table to grow
1089 **              tablesize -- requested new table size
1090 **
1091 **      Results:
1092 **              Raises an exception if can't allocate memory.
1093 */
1094
1095 static void
1096 sm_grow_type_table_x(typetable, tablesize)
1097         unsigned char **typetable;
1098         int *tablesize;
1099 {
1100         unsigned char *oldtable = *typetable;
1101         int newsize = *tablesize * 2;
1102
1103         if (*tablesize == STATIC_ARG_TBL_SIZE)
1104         {
1105                 *typetable = (unsigned char *) sm_malloc_x(sizeof(unsigned char)
1106                                                            * newsize);
1107                 (void) memmove(*typetable, oldtable, *tablesize);
1108         }
1109         else
1110         {
1111                 *typetable = (unsigned char *) sm_realloc_x(typetable,
1112                                         sizeof(unsigned char) * newsize);
1113         }
1114         (void) memset(&typetable [*tablesize], T_UNUSED,
1115                        (newsize - *tablesize));
1116
1117         *tablesize = newsize;
1118 }