source: trunk/third/sendmail/libsm/vfprintf.c @ 19204

Revision 19204, 23.8 KB checked in by zacheiss, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19203, which included commits to RCS files with non-trunk default branches.
Line 
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>
16SM_IDSTR(id, "@(#)$Id: vfprintf.c,v 1.1.1.1 2003-04-08 15:06:05 zacheiss 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
36static void     sm_find_arguments __P((const char *, va_list , va_list **));
37static void     sm_grow_type_table_x __P((unsigned char **, int *));
38static 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
56static int
57sm_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
96static int
97sm_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
171int
172sm_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
361rflag:          ch = *fmt++;
362reswitch:       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";
632hex:                    _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 */
639nosign:                 sign = '\0';
640
641                        /*
642                        **  ``... diouXx conversions ... if a precision is
643                        **  specified, the 0 flag will be ignored.''
644                        **      -- ANSI X3J11
645                        */
646
647number:                 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        }
771done:
772        FLUSH();
773error:
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
815static void
816sm_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
886rflag:          ch = *fmt++;
887reswitch:       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        }
1005done:
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
1084static void
1085sm_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}
Note: See TracBrowser for help on using the repository browser.