Date: Sun, 8 Sep 1996 17:56:40 -0700 (PDT) From: Mark Diekhans <markd@Grizzly.COM> To: freebsd-hackers@freebsd.org Subject: First pass at XPG/3 style positional arguments for *printf functions Message-ID: <199609090056.RAA00220@osprey.grizzly.com>
next in thread | raw e-mail | index | archive | help
Enclosed is a patch to 2.2-960801-SNAP lib/libc/stdio/vfprintf.c and lib/libc/stdio/printf.3 to implement the XPG/3 style positional arguments (e.g. %2$s) to the *printf family of functions. Its been tested against a program derived from the Tcl tests, so I believe its fairly solid. This functionality is essential for building localized software. It is part most current libc implementations (including GNU). Comments welcome and if any one wants to work with me on getting this into current, that would be great. Mark *** printf.3.ORG Sun Sep 8 13:56:14 1996 --- printf.3 Sun Sep 8 15:15:18 1996 *************** *** 176,181 **** --- 176,191 ---- the following appear in sequence: .Bl -bullet .It + An optional field, consisting of a decimal digit string followed by a + .Cm $ , + specifying the next argument to access . + If this field is not provided, the argument following the last + argument accessed will be used. + Arguments are numbered starting at + .Cm 1 . + If unaccessed arguments in the format string are interspersed with ones that + are accessed the results will be indeterminate. + .It Zero or more of the following flags: .Bl -hyphen .It *************** *** 394,399 **** --- 404,411 ---- A field width or precision, or both, may be indicated by an asterisk .Ql * + or an asterisk followed by one or more decimal digits and a + .Ql $ instead of a digit string. In this case, an *************** *** 402,407 **** --- 414,421 ---- A negative field width is treated as a left adjustment flag followed by a positive field width; a negative precision is treated as though it were missing. + If a single format directive mixes positional (nn$) + and non-positional arguments, the results are undefined. .Pp The conversion specifiers and their meanings are: .Bl -tag -width "diouxX" *** vfprintf.c.ORG Sat Sep 7 19:47:59 1996 --- vfprintf.c Sun Sep 8 16:16:35 1996 *************** *** 75,80 **** --- 75,82 ---- static int __sbprintf __P((FILE *, const char *, va_list)); static char * __ultoa __P((u_long, char *, int, int, char *)); static char * __uqtoa __P((u_quad_t, char *, int, int, char *)); + static void __find_arguments __P((const char *, va_list, void ***)); + static void __grow_type_table __P((int, unsigned char **, int *)); /* * Flush out all the vectors defined by the given uio, *************** *** 274,279 **** --- 276,282 ---- #endif /* FLOATING_POINT */ + #define STATIC_ARG_TBL_SIZE 8 /* Size of static argument table. */ /* * Flags used during conversion. *************** *** 295,301 **** { register char *fmt; /* format string */ register int ch; /* character from fmt */ ! register int n; /* handy integer (short term usage) */ register char *cp; /* handy char pointer (short term usage) */ register struct __siov *iovp;/* for PRINT macro */ register int flags; /* flags as above */ --- 298,304 ---- { register char *fmt; /* format string */ register int ch; /* character from fmt */ ! register int n, n2; /* handy integer (short term usage) */ register char *cp; /* handy char pointer (short term usage) */ register struct __siov *iovp;/* for PRINT macro */ register int flags; /* flags as above */ *************** *** 323,328 **** --- 326,335 ---- struct __siov iov[NIOV];/* ... and individual io vectors */ char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ char ox[2]; /* space for 0x hex-prefix */ + void **argtable; /* args, built due to positional arg */ + void *statargtable [STATIC_ARG_TBL_SIZE]; + int nextarg; /* 1-based argument index */ + va_list orgap; /* original argument pointer */ /* * Choose PADSIZE to trade efficiency vs. size. If larger printf *************** *** 365,382 **** iovp = iov; \ } /* * To extend shorts properly, we need both signed and unsigned * argument extraction methods. */ #define SARG() \ ! (flags&LONGINT ? va_arg(ap, long) : \ ! flags&SHORTINT ? (long)(short)va_arg(ap, int) : \ ! (long)va_arg(ap, int)) #define UARG() \ ! (flags&LONGINT ? va_arg(ap, u_long) : \ ! flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \ ! (u_long)va_arg(ap, u_int)) #ifdef _THREAD_SAFE _thread_flockfile(fp,__FILE__,__LINE__); --- 372,424 ---- iovp = iov; \ } + /* + * Get the argument indexed by nextarg. If the argument table is + * built, use it to get the argument. If its not, get the next + * argument (and arguments must be gotten sequentially). + */ + #define GETARG(type) \ + ((argtable != NULL) ? *((type*)(argtable[nextarg++])) : \ + (nextarg++, va_arg(ap, type))) + /* * To extend shorts properly, we need both signed and unsigned * argument extraction methods. */ #define SARG() \ ! (flags&LONGINT ? GETARG(long) : \ ! flags&SHORTINT ? (long)(short)GETARG(int) : \ ! (long)GETARG(int)) #define UARG() \ ! (flags&LONGINT ? GETARG(u_long) : \ ! flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \ ! (u_long)GETARG(u_int)) ! ! /* ! * Get * arguments, including the form *nn$. Preserve the nextarg ! * that the argument can be gotten once the type is determined. ! */ ! #define GETASTER(val) \ ! n2 = 0; \ ! cp = fmt; \ ! while (is_digit(*cp)) { \ ! n2 = 10 * n2 + to_digit(*cp); \ ! cp++; \ ! } \ ! if (*cp == '$') { \ ! int hold = nextarg; \ ! if (argtable == NULL) { \ ! argtable = statargtable; \ ! __find_arguments (fmt0, orgap, &argtable); \ ! } \ ! nextarg = n2; \ ! val = GETARG (int); \ ! nextarg = hold; \ ! fmt = ++cp; \ ! } else { \ ! val = GETARG (int); \ ! } ! #ifdef _THREAD_SAFE _thread_flockfile(fp,__FILE__,__LINE__); *************** *** 399,404 **** --- 441,449 ---- } fmt = (char *)fmt0; + argtable = NULL; + nextarg = 1; + orgap = ap; uio.uio_iov = iovp = iov; uio.uio_resid = 0; uio.uio_iovcnt = 0; *************** *** 445,451 **** * -- ANSI X3J11 * They don't exclude field widths read from args. */ ! if ((width = va_arg(ap, int)) >= 0) goto rflag; width = -width; /* FALLTHROUGH */ --- 490,497 ---- * -- ANSI X3J11 * They don't exclude field widths read from args. */ ! GETASTER (width); ! if (width >= 0) goto rflag; width = -width; /* FALLTHROUGH */ *************** *** 457,463 **** goto rflag; case '.': if ((ch = *fmt++) == '*') { ! n = va_arg(ap, int); prec = n < 0 ? -1 : n; goto rflag; } --- 503,509 ---- goto rflag; case '.': if ((ch = *fmt++) == '*') { ! GETASTER (n); prec = n < 0 ? -1 : n; goto rflag; } *************** *** 483,488 **** --- 529,543 ---- n = 10 * n + to_digit(ch); ch = *fmt++; } while (is_digit(ch)); + if (ch == '$') { + nextarg = n; + if (argtable == NULL) { + argtable = statargtable; + __find_arguments (fmt0, orgap, + &argtable); + } + goto rflag; + } width = n; goto reswitch; #ifdef FLOATING_POINT *************** *** 500,506 **** flags |= QUADINT; goto rflag; case 'c': ! *(cp = buf) = va_arg(ap, int); size = 1; sign = '\0'; break; --- 555,561 ---- flags |= QUADINT; goto rflag; case 'c': ! *(cp = buf) = GETARG(int); size = 1; sign = '\0'; break; *************** *** 510,516 **** case 'd': case 'i': if (flags & QUADINT) { ! uqval = va_arg(ap, quad_t); if ((quad_t)uqval < 0) { uqval = -uqval; sign = '-'; --- 565,571 ---- case 'd': case 'i': if (flags & QUADINT) { ! uqval = GETARG(quad_t); if ((quad_t)uqval < 0) { uqval = -uqval; sign = '-'; *************** *** 536,544 **** fp_begin: if (prec == -1) prec = DEFPREC; if (flags & LONGDBL) ! _double = (double)va_arg(ap, long double); else ! _double = va_arg(ap, double); /* do this before tricky precision changes */ if (isinf(_double)) { if (_double < 0) --- 591,599 ---- fp_begin: if (prec == -1) prec = DEFPREC; if (flags & LONGDBL) ! _double = (double)GETARG(long double); else ! _double = GETARG(double); /* do this before tricky precision changes */ if (isinf(_double)) { if (_double < 0) *************** *** 588,607 **** #endif /* FLOATING_POINT */ case 'n': if (flags & QUADINT) ! *va_arg(ap, quad_t *) = ret; else if (flags & LONGINT) ! *va_arg(ap, long *) = ret; else if (flags & SHORTINT) ! *va_arg(ap, short *) = ret; else ! *va_arg(ap, int *) = ret; continue; /* no output */ case 'O': flags |= LONGINT; /*FALLTHROUGH*/ case 'o': if (flags & QUADINT) ! uqval = va_arg(ap, u_quad_t); else ulval = UARG(); base = 8; --- 643,662 ---- #endif /* FLOATING_POINT */ case 'n': if (flags & QUADINT) ! *GETARG(quad_t *) = ret; else if (flags & LONGINT) ! *GETARG(long *) = ret; else if (flags & SHORTINT) ! *GETARG(short *) = ret; else ! *GETARG(int *) = ret; continue; /* no output */ case 'O': flags |= LONGINT; /*FALLTHROUGH*/ case 'o': if (flags & QUADINT) ! uqval = GETARG(u_quad_t); else ulval = UARG(); base = 8; *************** *** 614,627 **** * defined manner.'' * -- ANSI X3J11 */ ! ulval = (u_long)va_arg(ap, void *); base = 16; xdigs = "0123456789abcdef"; flags = (flags & ~QUADINT) | HEXPREFIX; ch = 'x'; goto nosign; case 's': ! if ((cp = va_arg(ap, char *)) == NULL) cp = "(null)"; if (prec >= 0) { /* --- 669,682 ---- * defined manner.'' * -- ANSI X3J11 */ ! ulval = (u_long)GETARG(void *); base = 16; xdigs = "0123456789abcdef"; flags = (flags & ~QUADINT) | HEXPREFIX; ch = 'x'; goto nosign; case 's': ! if ((cp = GETARG(char *)) == NULL) cp = "(null)"; if (prec >= 0) { /* *************** *** 646,652 **** /*FALLTHROUGH*/ case 'u': if (flags & QUADINT) ! uqval = va_arg(ap, u_quad_t); else ulval = UARG(); base = 10; --- 701,707 ---- /*FALLTHROUGH*/ case 'u': if (flags & QUADINT) ! uqval = GETARG(u_quad_t); else ulval = UARG(); base = 10; *************** *** 657,663 **** case 'x': xdigs = "0123456789abcdef"; hex: if (flags & QUADINT) ! uqval = va_arg(ap, u_quad_t); else ulval = UARG(); base = 16; --- 712,718 ---- case 'x': xdigs = "0123456789abcdef"; hex: if (flags & QUADINT) ! uqval = GETARG(u_quad_t); else ulval = UARG(); base = 16; *************** *** 809,817 **** --- 864,1195 ---- #ifdef _THREAD_SAFE _thread_funlockfile(fp); #endif + if ((argtable != NULL) && (argtable != statargtable)) + free (argtable); return (ret); /* NOTREACHED */ } + + /* + * Type ids for argument type table. + */ + #define T_UNUSED 0 + #define T_SHORT 1 + #define T_U_SHORT 2 + #define TP_SHORT 3 + #define T_INT 4 + #define T_U_INT 5 + #define TP_INT 6 + #define T_LONG 7 + #define T_U_LONG 8 + #define TP_LONG 9 + #define T_QUAD 10 + #define T_U_QUAD 11 + #define TP_QUAD 12 + #define T_DOUBLE 13 + #define T_LONG_DOUBLE 14 + #define TP_CHAR 15 + #define TP_VOID 16 + + /* + * Find all arguments when a positional parameter is encountered. Returns a + * table, indexed by argument number, of pointers to each arguments. The + * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries. + * It will be replaces with a malloc-ed on if it overflows. + */ + static void + __find_arguments (fmt0, ap, argtable) + const char *fmt0; + va_list ap; + void ***argtable; + { + register char *fmt; /* format string */ + register int ch; /* character from fmt */ + register int n, n2; /* handy integer (short term usage) */ + register char *cp; /* handy char pointer (short term usage) */ + register int flags; /* flags as above */ + int width; /* width from format (%8d), or 0 */ + unsigned char *typetable; /* table of types */ + unsigned char stattypetable [STATIC_ARG_TBL_SIZE]; + int tablesize; /* current size of type table */ + int tablemax; /* largest used index in table */ + int nextarg; /* 1-based argument index */ + + /* + * Add an argument type to the table, expanding if necessary. + */ + #define ADDTYPE(type) \ + ((nextarg >= tablesize) ? \ + __grow_type_table(nextarg, &typetable, &tablesize) : 0, \ + typetable[nextarg++] = type, \ + (nextarg > tablemax) ? tablemax = nextarg : 0) + + #define ADDSARG() \ + ((flags&LONGINT) ? ADDTYPE(T_LONG) : \ + ((flags&SHORTINT) ? ADDTYPE(T_SHORT) : ADDTYPE(T_INT))) + + #define ADDUARG() \ + ((flags&LONGINT) ? ADDTYPE(T_U_LONG) : \ + ((flags&SHORTINT) ? ADDTYPE(T_U_SHORT) : ADDTYPE(T_U_INT))) + + /* + * Add * arguments to the type array. + */ + #define ADDASTER() \ + n2 = 0; \ + cp = fmt; \ + while (is_digit(*cp)) { \ + n2 = 10 * n2 + to_digit(*cp); \ + cp++; \ + } \ + if (*cp == '$') { \ + int hold = nextarg; \ + nextarg = n2; \ + ADDTYPE (T_INT); \ + nextarg = hold; \ + fmt = ++cp; \ + } else { \ + ADDTYPE (T_INT); \ + } + fmt = (char *)fmt0; + typetable = stattypetable; + tablesize = STATIC_ARG_TBL_SIZE; + tablemax = 0; + nextarg = 1; + memset (typetable, T_UNUSED, STATIC_ARG_TBL_SIZE); + + /* + * Scan the format for conversions (`%' character). + */ + for (;;) { + for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) + /* void */; + if (ch == '\0') + goto done; + fmt++; /* skip over '%' */ + + flags = 0; + width = 0; + + rflag: ch = *fmt++; + reswitch: switch (ch) { + case ' ': + case '#': + goto rflag; + case '*': + ADDASTER (); + goto rflag; + case '-': + case '+': + goto rflag; + case '.': + if ((ch = *fmt++) == '*') { + ADDASTER (); + goto rflag; + } + while (is_digit(ch)) { + ch = *fmt++; + } + goto reswitch; + case '0': + goto rflag; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = 0; + do { + n = 10 * n + to_digit(ch); + ch = *fmt++; + } while (is_digit(ch)); + if (ch == '$') { + nextarg = n; + goto rflag; + } + width = n; + goto reswitch; + #ifdef FLOATING_POINT + case 'L': + flags |= LONGDBL; + goto rflag; + #endif + case 'h': + flags |= SHORTINT; + goto rflag; + case 'l': + flags |= LONGINT; + goto rflag; + case 'q': + flags |= QUADINT; + goto rflag; + case 'c': + ADDTYPE(T_INT); + break; + case 'D': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'd': + case 'i': + if (flags & QUADINT) { + ADDTYPE(T_QUAD); + } else { + ADDSARG(); + } + break; + #ifdef FLOATING_POINT + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + if (flags & LONGDBL) + ADDTYPE(T_LONG_DOUBLE); + else + ADDTYPE(T_DOUBLE); + break; + #endif /* FLOATING_POINT */ + case 'n': + if (flags & QUADINT) + ADDTYPE(TP_QUAD); + else if (flags & LONGINT) + ADDTYPE(TP_LONG); + else if (flags & SHORTINT) + ADDTYPE(TP_SHORT); + else + ADDTYPE(TP_INT); + continue; /* no output */ + case 'O': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'o': + if (flags & QUADINT) + ADDTYPE(T_U_QUAD); + else + ADDUARG(); + break; + case 'p': + ADDTYPE(TP_VOID); + break; + case 's': + ADDTYPE(TP_CHAR); + break; + case 'U': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'u': + if (flags & QUADINT) + ADDTYPE(T_U_QUAD); + else + ADDUARG(); + break; + case 'X': + case 'x': + if (flags & QUADINT) + ADDTYPE(T_U_QUAD); + else + ADDUARG(); + break; + default: /* "%?" prints ?, unless ? is NUL */ + if (ch == '\0') + goto done; + break; + } + } + done: + /* + * Build the argument table. + */ + if (tablemax >= STATIC_ARG_TBL_SIZE) { + *argtable = (void **) + malloc (sizeof (void *) * (tablemax + 1)); + } + + (*argtable) [0] = NULL; + for (n = 1; n <= tablemax; n++) { + (*argtable) [n] = ap; + switch (typetable [n]) { + case T_UNUSED: + (void) va_arg (ap, int); + break; + case T_SHORT: + (void) va_arg (ap, int); + break; + case T_U_SHORT: + (void) va_arg (ap, int); + break; + case TP_SHORT: + (void) va_arg (ap, short *); + break; + case T_INT: + (void) va_arg (ap, int); + break; + case T_U_INT: + (void) va_arg (ap, unsigned int); + break; + case TP_INT: + (void) va_arg (ap, int *); + break; + case T_LONG: + (void) va_arg (ap, long); + break; + case T_U_LONG: + (void) va_arg (ap, unsigned long); + break; + case TP_LONG: + (void) va_arg (ap, long *); + break; + case T_QUAD: + (void) va_arg (ap, quad_t); + break; + case T_U_QUAD: + (void) va_arg (ap, u_quad_t); + break; + case TP_QUAD: + (void) va_arg (ap, quad_t *); + break; + case T_DOUBLE: + (void) va_arg (ap, double); + break; + case T_LONG_DOUBLE: + (void) va_arg (ap, long double); + break; + case TP_CHAR: + (void) va_arg (ap, char *); + break; + case TP_VOID: + (void) va_arg (ap, void *); + break; + } + } + + if ((typetable != NULL) && (typetable != stattypetable)) + free (typetable); + } + + /* + * Increase the size of the type table. + */ + static void + __grow_type_table (nextarg, typetable, tablesize) + int nextarg; + unsigned char **typetable; + int *tablesize; + { + unsigned char *oldtable = *typetable; + int newsize = *tablesize * 2; + + if (*tablesize == STATIC_ARG_TBL_SIZE) { + *typetable = (unsigned char *) + malloc (sizeof (unsigned char) * newsize); + bcopy (oldtable, *typetable, *tablesize); + } else { + *typetable = (unsigned char *) + realloc (typetable, sizeof (unsigned char) * newsize); + + } + memset (&typetable [*tablesize], T_UNUSED, (newsize - *tablesize)); + + *tablesize = newsize; + } + #ifdef FLOATING_POINT
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199609090056.RAA00220>