Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'newlib/libc/stdio/vfprintf.c')
-rw-r--r--newlib/libc/stdio/vfprintf.c614
1 files changed, 577 insertions, 37 deletions
diff --git a/newlib/libc/stdio/vfprintf.c b/newlib/libc/stdio/vfprintf.c
index 889a7a083..8e1efd7bb 100644
--- a/newlib/libc/stdio/vfprintf.c
+++ b/newlib/libc/stdio/vfprintf.c
@@ -111,7 +111,7 @@ Supporting OS subroutines required: <<close>>, <<fstat>>, <<isatty>>,
<<lseek>>, <<read>>, <<sbrk>>, <<write>>.
*/
-/*-
+/*
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
@@ -174,6 +174,11 @@ static char *rcsid = "$Id$";
# undef _NO_LONGLONG
#endif
+#define _NO_POS_ARGS
+#if defined WANT_IO_POS_ARGS
+# undef _NO_POS_ARGS
+#endif
+
#include <_ansi.h>
#include <stdio.h>
#include <stdlib.h>
@@ -279,6 +284,46 @@ static int exponent _PARAMS((char *, int, int));
#endif /* FLOATING_POINT */
+#ifndef _NO_LONG_LONG
+#define quad_t long long
+#define u_quad_t unsigned long long
+#else
+#define quad_t long
+#define u_quad_t unsigned long
+#endif
+
+typedef quad_t * quad_ptr_t;
+typedef void * void_ptr_t;
+typedef char * char_ptr_t;
+typedef long * long_ptr_t;
+typedef int * int_ptr_t;
+typedef short * short_ptr_t;
+
+#ifndef _NO_POS_ARGS
+#define MAX_POS_ARGS 32
+
+union arg_val
+{
+ int val_int;
+ u_int val_u_int;
+ long val_long;
+ u_long val_u_long;
+ float val_float;
+ double val_double;
+ _LONG_DOUBLE val__LONG_DOUBLE;
+ int_ptr_t val_int_ptr_t;
+ short_ptr_t val_short_ptr_t;
+ long_ptr_t val_long_ptr_t;
+ char_ptr_t val_char_ptr_t;
+ quad_ptr_t val_quad_ptr_t;
+ void_ptr_t val_void_ptr_t;
+ quad_t val_quad_t;
+ u_quad_t val_u_quad_t;
+};
+
+static union arg_val *get_arg (int n, char *fmt, va_list *ap, int *numargs, union arg_val *args,
+ int *arg_type, char **last_fmt);
+#endif /* !_NO_POS_ARGS */
/*
* Macros for converting digits to letters and vice versa
@@ -306,6 +351,8 @@ static int exponent _PARAMS((char *, int, int));
#define ZEROPAD 0x080 /* zero (as opposed to blank) pad */
#define FPT 0x100 /* Floating point number */
+int _EXFUN (_VFPRINTF_R, (struct _reent *, FILE *, _CONST char *, va_list));
+
int
_DEFUN (VFPRINTF, (fp, fmt0, ap),
FILE * fp _AND
@@ -333,6 +380,17 @@ _DEFUN (_VFPRINTF_R, (data, fp, fmt0, ap),
register char *cp; /* handy char pointer (short term usage) */
register struct __siov *iovp;/* for PRINT macro */
register int flags; /* flags as above */
+ char *fmt_anchor; /* current format spec being processed */
+ int N; /* arg number */
+ int arg_index; /* index into args processed directly */
+#ifndef _NO_POS_ARGS
+ int numargs; /* number of varargs read */
+ char *saved_fmt; /* saved fmt pointer */
+ union arg_val args[MAX_POS_ARGS];
+ int arg_type[MAX_POS_ARGS];
+ int is_pos_arg; /* is current format positional? */
+ int old_is_pos_arg; /* is current format positional? */
+#endif
int ret; /* return value accumulator */
int width; /* width from format (%8d), or 0 */
int prec; /* precision from format (%.3d), or -1 */
@@ -355,16 +413,9 @@ _DEFUN (_VFPRINTF_R, (data, fp, fmt0, ap),
char expstr[7]; /* buffer for exponent string */
#endif
-#ifndef _NO_LONGLONG
-#define quad_t long long
-#define u_quad_t unsigned long long
-#endif
-#ifndef _NO_LONGLONG
u_quad_t _uquad; /* integer arguments %[diouxX] */
-#else
- u_long _uquad;
-#endif
+
enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */
int dprec; /* a copy of prec if [diouxX], 0 otherwise */
int realsz; /* field size expanded by dprec */
@@ -418,30 +469,47 @@ _DEFUN (_VFPRINTF_R, (data, fp, fmt0, ap),
iovp = iov; \
}
+ /* Macros to support positional arguments */
+#ifndef _NO_POS_ARGS
+#define GET_ARG(n, ap, type) \
+ ( is_pos_arg \
+ ? n < numargs \
+ ? args[n].val_##type \
+ : get_arg (n, fmt_anchor, &ap, &numargs, args, arg_type, &saved_fmt)->val_##type \
+ : arg_index++ < numargs \
+ ? args[n].val_##type \
+ : numargs < MAX_POS_ARGS \
+ ? args[numargs++].val_##type = va_arg(ap, type) \
+ : va_arg(ap, type) \
+ )
+#else
+#define GET_ARG(n, ap, type) (va_arg(ap, type))
+#endif
+
/*
* To extend shorts properly, we need both signed and unsigned
* argument extraction methods.
*/
#ifndef _NO_LONGLONG
#define SARG() \
- (flags&QUADINT ? va_arg(ap, quad_t) : \
- flags&LONGINT ? va_arg(ap, long) : \
- flags&SHORTINT ? (long)(short)va_arg(ap, int) : \
- (long)va_arg(ap, int))
+ (flags&QUADINT ? GET_ARG(N, ap, quad_t) : \
+ flags&LONGINT ? GET_ARG(N, ap, long) : \
+ flags&SHORTINT ? (long)(short)GET_ARG(N, ap, int) : \
+ (long)GET_ARG(N, ap, int))
#define UARG() \
- (flags&QUADINT ? va_arg(ap, u_quad_t) : \
- flags&LONGINT ? va_arg(ap, u_long) : \
- flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \
- (u_long)va_arg(ap, u_int))
+ (flags&QUADINT ? GET_ARG(N, ap, u_quad_t) : \
+ flags&LONGINT ? GET_ARG(N, ap, u_long) : \
+ flags&SHORTINT ? (u_long)(u_short)GET_ARG(N, ap, int) : \
+ (u_long)GET_ARG(N, ap, u_int))
#else
#define SARG() \
- (flags&LONGINT ? va_arg(ap, long) : \
- flags&SHORTINT ? (long)(short)va_arg(ap, int) : \
- (long)va_arg(ap, int))
+ (flags&LONGINT ? GET_ARG(N, ap, long) : \
+ flags&SHORTINT ? (long)(short)GET_ARG(N, ap, int) : \
+ (long)GET_ARG(N, 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))
+ (flags&LONGINT ? GET_ARG(N, ap, u_long) : \
+ flags&SHORTINT ? (u_long)(u_short)GET_ARG(N, ap, int) : \
+ (u_long)GET_ARG(N, ap, u_int))
#endif
/* sorry, fprintf(read_only_file, "") returns EOF, not 0 */
@@ -458,6 +526,13 @@ _DEFUN (_VFPRINTF_R, (data, fp, fmt0, ap),
uio.uio_resid = 0;
uio.uio_iovcnt = 0;
ret = 0;
+ arg_index = 0;
+#ifndef _NO_POS_ARGS
+ saved_fmt = NULL;
+ arg_type[0] = -1;
+ numargs = 0;
+ is_pos_arg = 0;
+#endif
/*
* Scan the format for conversions (`%' character).
@@ -477,6 +552,8 @@ _DEFUN (_VFPRINTF_R, (data, fp, fmt0, ap),
}
if (n <= 0)
goto done;
+
+ fmt_anchor = fmt;
fmt++; /* skip over '%' */
flags = 0;
@@ -484,6 +561,10 @@ _DEFUN (_VFPRINTF_R, (data, fp, fmt0, ap),
width = 0;
prec = -1;
sign = '\0';
+ N = arg_index;
+#ifndef _NO_POS_ARGS
+ is_pos_arg = 0;
+#endif
rflag: ch = *fmt++;
reswitch: switch (ch) {
@@ -500,13 +581,47 @@ reswitch: switch (ch) {
flags |= ALT;
goto rflag;
case '*':
+ n = N;
+#ifndef _NO_POS_ARGS
+ /* we must check for positional arg used for dynamic width */
+ old_is_pos_arg = is_pos_arg;
+ is_pos_arg = 0;
+ if (is_digit(*fmt)) {
+ char *old_fmt = fmt;
+
+ n = 0;
+ ch = *fmt++;
+ do {
+ n = 10 * n + to_digit(ch);
+ ch = *fmt++;
+ } while (is_digit(ch));
+
+ if (ch == '$') {
+ if (n <= MAX_POS_ARGS) {
+ n -= 1;
+ is_pos_arg = 1;
+ }
+ else
+ goto error;
+ }
+ else {
+ fmt = old_fmt;
+ goto rflag;
+ }
+ }
+#endif /* !_NO_POS_ARGS */
+
/*
* ``A negative field width argument is taken as a
* - flag followed by a positive field width.''
* -- ANSI X3J11
* They don't exclude field widths read from args.
*/
- if ((width = va_arg(ap, int)) >= 0)
+ width = GET_ARG(n, ap, int);
+#ifndef _NO_POS_ARGS
+ is_pos_arg = old_is_pos_arg;
+#endif
+ if (width >= 0)
goto rflag;
width = -width;
/* FALLTHROUGH */
@@ -518,8 +633,41 @@ reswitch: switch (ch) {
goto rflag;
case '.':
if ((ch = *fmt++) == '*') {
- n = va_arg(ap, int);
- prec = n < 0 ? -1 : n;
+ n = N;
+#ifndef _NO_POS_ARGS
+ /* we must check for positional arg used for dynamic width */
+ old_is_pos_arg = is_pos_arg;
+ is_pos_arg = 0;
+ if (is_digit(*fmt)) {
+ char *old_fmt = fmt;
+
+ n = 0;
+ ch = *fmt++;
+ do {
+ n = 10 * n + to_digit(ch);
+ ch = *fmt++;
+ } while (is_digit(ch));
+
+ if (ch == '$') {
+ if (n <= MAX_POS_ARGS) {
+ n -= 1;
+ is_pos_arg = 1;
+ }
+ else
+ goto error;
+ }
+ else {
+ fmt = old_fmt;
+ goto rflag;
+ }
+ }
+#endif /* !_NO_POS_ARGS */
+ prec = GET_ARG(n, ap, int);
+#ifndef _NO_POS_ARGS
+ is_pos_arg = old_is_pos_arg;
+#endif
+ if (prec < 0)
+ prec = -1;
goto rflag;
}
n = 0;
@@ -544,6 +692,17 @@ reswitch: switch (ch) {
n = 10 * n + to_digit(ch);
ch = *fmt++;
} while (is_digit(ch));
+#ifndef _NO_POS_ARGS
+ if (ch == '$') {
+ if (n <= MAX_POS_ARGS) {
+ N = n - 1;
+ is_pos_arg = 1;
+ goto rflag;
+ }
+ else
+ goto error;
+ }
+#endif /* !_NO_POS_ARGS */
width = n;
goto reswitch;
#ifdef FLOATING_POINT
@@ -566,7 +725,7 @@ reswitch: switch (ch) {
flags |= QUADINT;
goto rflag;
case 'c':
- *(cp = buf) = va_arg(ap, int);
+ *(cp = buf) = GET_ARG(N, ap, int);
size = 1;
sign = '\0';
break;
@@ -602,9 +761,9 @@ reswitch: switch (ch) {
#ifdef _NO_LONGDBL
if (flags & LONGDBL) {
- _fpvalue = (double) va_arg(ap, _LONG_DOUBLE);
+ _fpvalue = (double) GET_ARG(N, ap, _LONG_DOUBLE);
} else {
- _fpvalue = va_arg(ap, double);
+ _fpvalue = GET_ARG(N, ap, double);
}
/* do this before tricky precision changes */
@@ -624,9 +783,9 @@ reswitch: switch (ch) {
#else /* !_NO_LONGDBL */
if (flags & LONGDBL) {
- _fpvalue = va_arg(ap, _LONG_DOUBLE);
+ _fpvalue = GET_ARG(N, ap, _LONG_DOUBLE);
} else {
- _fpvalue = (_LONG_DOUBLE)va_arg(ap, double);
+ _fpvalue = (_LONG_DOUBLE)GET_ARG(N, ap, double);
}
/* do this before tricky precision changes */
@@ -684,15 +843,15 @@ reswitch: switch (ch) {
case 'n':
#ifndef _NO_LONGLONG
if (flags & QUADINT)
- *va_arg(ap, quad_t *) = ret;
+ *GET_ARG(N, ap, quad_ptr_t *) = ret;
else
#endif
if (flags & LONGINT)
- *va_arg(ap, long *) = ret;
+ *GET_ARG(N, ap, long_ptr_t) = ret;
else if (flags & SHORTINT)
- *va_arg(ap, short *) = ret;
+ *GET_ARG(N, ap, short_ptr_t) = ret;
else
- *va_arg(ap, int *) = ret;
+ *GET_ARG(N, ap, int_ptr_t) = ret;
continue; /* no output */
case 'O':
flags |= LONGINT;
@@ -710,14 +869,14 @@ reswitch: switch (ch) {
* -- ANSI X3J11
*/
/* NOSTRICT */
- _uquad = (u_long)(unsigned _POINTER_INT)va_arg(ap, void *);
+ _uquad = (u_long)(unsigned _POINTER_INT)GET_ARG(N, ap, void_ptr_t);
base = HEX;
xdigs = "0123456789abcdef";
flags |= HEXPREFIX;
ch = 'x';
goto nosign;
case 's':
- if ((cp = va_arg(ap, char *)) == NULL)
+ if ((cp = GET_ARG(N, ap, char_ptr_t)) == NULL)
cp = "(null)";
if (prec >= 0) {
/*
@@ -1053,3 +1212,384 @@ exponent(p0, exp, fmtch)
return (p - p0);
}
#endif /* FLOATING_POINT */
+
+
+#ifndef _NO_POS_ARGS
+
+/* Positional argument support.
+ Written by Jeff Johnston
+
+ Copyright (c) 2002 Red Hat Incorporated.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ The name of Red Hat Incorporated may not be used to endorse
+ or promote products derived from this software without specific
+ prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL RED HAT INCORPORATED BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+typedef enum {
+ ZERO, /* '0' */
+ DIGIT, /* '1-9' */
+ DOLLAR, /* '$' */
+ MODFR, /* spec modifier */
+ SPEC, /* format specifier */
+ DOT, /* '.' */
+ STAR, /* '*' */
+ FLAG, /* format flag */
+ OTHER, /* all other chars */
+ MAX_CH_CLASS /* place-holder */
+} CH_CLASS;
+
+typedef enum {
+ START, /* start */
+ SFLAG, /* seen a flag */
+ WDIG, /* seen digits in width area */
+ WIDTH, /* processed width */
+ SMOD, /* seen spec modifier */
+ SDOT, /* seen dot */
+ VARW, /* have variable width specifier */
+ VARP, /* have variable precision specifier */
+ PREC, /* processed precision */
+ VWDIG, /* have digits in variable width specification */
+ VPDIG, /* have digits in variable precision specification */
+ DONE, /* done */
+ MAX_STATE, /* place-holder */
+} STATE;
+
+typedef enum {
+ NOOP, /* do nothing */
+ NUMBER, /* build a number from digits */
+ SKIPNUM, /* skip over digits */
+ GETMOD, /* get and process format modifier */
+ GETARG, /* get and process argument */
+ GETPW, /* get variable precision or width */
+ GETPWB, /* get variable precision or width and pushback fmt char */
+ GETPOS, /* get positional parameter value */
+ PWPOS, /* get positional parameter value for variable width or precision */
+} ACTION;
+
+const static CH_CLASS chclass[256] = {
+ /* 00-07 */ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
+ /* 08-0f */ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
+ /* 10-17 */ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
+ /* 18-1f */ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
+ /* 20-27 */ FLAG, OTHER, OTHER, FLAG, DOLLAR, OTHER, OTHER, OTHER,
+ /* 28-2f */ OTHER, OTHER, STAR, FLAG, OTHER, FLAG, DOT, OTHER,
+ /* 30-37 */ ZERO, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT,
+ /* 38-3f */ DIGIT, DIGIT, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
+ /* 40-47 */ OTHER, OTHER, OTHER, OTHER, SPEC, SPEC, OTHER, SPEC,
+ /* 48-4f */ OTHER, OTHER, OTHER, OTHER, MODFR, OTHER, OTHER, SPEC,
+ /* 50-57 */ OTHER, OTHER, OTHER, OTHER, OTHER, SPEC, OTHER, SPEC,
+ /* 58-5f */ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
+ /* 60-67 */ OTHER, OTHER, OTHER, SPEC, SPEC, SPEC, SPEC, SPEC,
+ /* 68-6f */ MODFR, SPEC, OTHER, OTHER, MODFR, OTHER, OTHER, SPEC,
+ /* 70-77 */ SPEC, MODFR, OTHER, SPEC, OTHER, SPEC, OTHER, OTHER,
+ /* 78-7f */ SPEC, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
+ /* 80-87 */ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
+ /* 88-8f */ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
+ /* 90-97 */ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
+ /* 98-9f */ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
+ /* a0-a7 */ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
+ /* a8-af */ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
+ /* b0-b7 */ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
+ /* b8-bf */ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
+ /* c0-c7 */ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
+ /* c8-cf */ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
+ /* d0-d7 */ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
+ /* d8-df */ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
+ /* e0-e7 */ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
+ /* e8-ef */ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
+ /* f0-f7 */ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
+ /* f8-ff */ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
+};
+
+const static STATE state_table[MAX_STATE][MAX_CH_CLASS] = {
+ /* '0' '1-9' '$' MODFR SPEC '.' '*' FLAG OTHER */
+ /* START */ { SFLAG, WDIG, DONE, SMOD, DONE, SDOT, VARW, SFLAG, DONE },
+ /* SFLAG */ { SFLAG, WDIG, DONE, SMOD, DONE, SDOT, VARW, SFLAG, DONE },
+ /* WDIG */ { DONE, DONE, WIDTH, SMOD, DONE, SDOT, DONE, DONE, DONE },
+ /* WIDTH */ { DONE, DONE, DONE, SMOD, DONE, SDOT, DONE, DONE, DONE },
+ /* SMOD */ { DONE, DONE, DONE, DONE, DONE, DONE, DONE, DONE, DONE },
+ /* SDOT */ { SDOT, PREC, DONE, SMOD, DONE, DONE, VARP, DONE, DONE },
+ /* VARW */ { DONE, VWDIG, DONE, SMOD, DONE, SDOT, DONE, DONE, DONE },
+ /* VARP */ { DONE, VPDIG, DONE, SMOD, DONE, DONE, DONE, DONE, DONE },
+ /* PREC */ { DONE, DONE, DONE, SMOD, DONE, DONE, DONE, DONE, DONE },
+ /* VWDIG */ { DONE, DONE, WIDTH, DONE, DONE, DONE, DONE, DONE, DONE },
+ /* VPDIG */ { DONE, DONE, PREC, DONE, DONE, DONE, DONE, DONE, DONE },
+};
+
+const static ACTION action_table[MAX_STATE][MAX_CH_CLASS] = {
+ /* '0' '1-9' '$' MODFR SPEC '.' '*' FLAG OTHER */
+ /* START */ { NOOP, NUMBER, NOOP, GETMOD, GETARG, NOOP, NOOP, NOOP, NOOP },
+ /* SFLAG */ { NOOP, NUMBER, NOOP, GETMOD, GETARG, NOOP, NOOP, NOOP, NOOP },
+ /* WDIG */ { NOOP, NOOP, GETPOS, GETMOD, GETARG, NOOP, NOOP, NOOP, NOOP },
+ /* WIDTH */ { NOOP, NOOP, NOOP, GETMOD, GETARG, NOOP, NOOP, NOOP, NOOP },
+ /* SMOD */ { NOOP, NOOP, NOOP, NOOP, GETARG, NOOP, NOOP, NOOP, NOOP },
+ /* SDOT */ { NOOP, SKIPNUM, NOOP, GETMOD, GETARG, NOOP, NOOP, NOOP, NOOP },
+ /* VARW */ { NOOP, NUMBER, NOOP, GETPW, GETPWB, GETPW, NOOP, NOOP, NOOP },
+ /* VARP */ { NOOP, NUMBER, NOOP, GETPW, GETPWB, NOOP, NOOP, NOOP, NOOP },
+ /* PREC */ { NOOP, NOOP, NOOP, GETMOD, GETARG, NOOP, NOOP, NOOP, NOOP },
+ /* VWDIG */ { NOOP, NOOP, PWPOS, NOOP, NOOP, NOOP, NOOP, NOOP, NOOP },
+ /* VPDIG */ { NOOP, NOOP, PWPOS, NOOP, NOOP, NOOP, NOOP, NOOP, NOOP },
+};
+
+/* function to get positional parameter N where n = N - 1 */
+static union arg_val *
+get_arg (int n, char *fmt, va_list *ap, int *numargs_p, union arg_val *args,
+ int *arg_type, char **last_fmt)
+{
+ int ch;
+ wchar_t wc;
+ int nbytes, number, flags;
+ int spec_type;
+ int numargs = *numargs_p;
+ CH_CLASS chtype;
+ STATE state, next_state;
+ ACTION action;
+ int pos, last_arg;
+ int wc_state = 0;
+ int max_pos_arg = n;
+ enum types { INT, LONG_INT, SHORT_INT, QUAD_INT, CHAR, CHAR_PTR, DOUBLE, LONG_DOUBLE };
+
+ /* if this isn't the first call, pick up where we left off last time */
+ if (*last_fmt != NULL)
+ fmt = *last_fmt;
+
+ /* we need to process either to end of fmt string or until we have actually
+ read the desired parameter from the vararg list. */
+ while (*fmt && n >= numargs)
+ {
+ while ((nbytes = _mbtowc_r(_REENT, &wc, fmt, MB_CUR_MAX, &wc_state)) > 0)
+ {
+ fmt += nbytes;
+ if (wc == '%')
+ break;
+ }
+
+ if (nbytes <= 0)
+ break;
+
+ state = START;
+ flags = 0;
+ pos = -1;
+ number = 0;
+ spec_type = INT;
+
+ /* Use state/action table to process format specifiers. We ignore invalid
+ formats and we are only interested in information that tells us how to
+ read the vararg list. */
+ while (state != DONE)
+ {
+ ch = *fmt++;
+ chtype = chclass[ch];
+ next_state = state_table[state][chtype];
+ action = action_table[state][chtype];
+ state = next_state;
+
+ switch (action)
+ {
+ case GETMOD: /* we have format modifier */
+ switch (ch)
+ {
+ case 'h':
+ flags |= SHORTINT;
+ break;
+ case 'L':
+ flags |= LONGDBL;
+ break;
+ case 'q':
+ flags |= QUADINT;
+ break;
+ case 'l':
+ default:
+ if (*fmt == 'l')
+ {
+ flags |= QUADINT;
+ ++fmt;
+ }
+ else
+ flags |= LONGINT;
+ break;
+ }
+ break;
+ case GETARG: /* we have format specifier */
+ {
+ numargs &= (MAX_POS_ARGS - 1);
+ /* process the specifier and translate it to a type to fetch from varargs */
+ switch (ch)
+ {
+ case 'd':
+ case 'i':
+ case 'o':
+ case 'x':
+ case 'X':
+ case 'u':
+ if (flags & LONGINT)
+ spec_type = LONG_INT;
+ else if (flags & SHORTINT)
+ spec_type = SHORT_INT;
+#ifndef _NO_LONG_LONG
+ else if (flags & QUADINT)
+ spec_type = QUAD_INT;
+#endif
+ else
+ spec_type = INT;
+ break;
+ case 'D':
+ case 'U':
+ case 'O':
+ spec_type = LONG_INT;
+ break;
+ case 'f':
+ case 'g':
+ case 'G':
+ case 'E':
+ case 'e':
+#ifndef _NO_LONGDBL
+ if (flags & LONGDBL)
+ spec_type = LONG_DOUBLE;
+ else
+#endif
+ spec_type = DOUBLE;
+ break;
+ case 's':
+ case 'p':
+ spec_type = CHAR_PTR;
+ break;
+ case 'c':
+ spec_type = CHAR;
+ break;
+ }
+
+ /* if we have a positional parameter, just store the type, otherwise
+ fetch the parameter from the vararg list */
+ if (pos != -1)
+ arg_type[pos] = spec_type;
+ else
+ {
+ switch (spec_type)
+ {
+ case LONG_INT:
+ args[numargs++].val_long = va_arg(*ap, long);
+ break;
+ case QUAD_INT:
+ args[numargs++].val_quad_t = va_arg(*ap, quad_t);
+ break;
+ case CHAR:
+ case SHORT_INT:
+ case INT:
+ args[numargs++].val_int = va_arg(*ap, int);
+ break;
+ case CHAR_PTR:
+ args[numargs++].val_char_ptr_t = va_arg(*ap, char *);
+ break;
+ case DOUBLE:
+ args[numargs++].val_double = va_arg(*ap, double);
+ break;
+ case LONG_DOUBLE:
+ args[numargs++].val__LONG_DOUBLE = va_arg(*ap, _LONG_DOUBLE);
+ break;
+ }
+ }
+ }
+ break;
+ case GETPOS: /* we have positional specifier */
+ if (arg_type[0] == -1)
+ memset (arg_type, 0, sizeof(int) * MAX_POS_ARGS);
+ pos = number - 1;
+ max_pos_arg = (max_pos_arg > pos ? max_pos_arg : pos);
+ break;
+ case PWPOS: /* we have positional specifier for width or precision */
+ if (arg_type[0] == -1)
+ memset (arg_type, 0, sizeof(int) * MAX_POS_ARGS);
+ number -= 1;
+ arg_type[number] = INT;
+ max_pos_arg = (max_pos_arg > number ? max_pos_arg : number);
+ break;
+ case GETPWB: /* we require format pushback */
+ --fmt;
+ /* fallthrough */
+ case GETPW: /* we have a variable precision or width to acquire */
+ args[numargs++].val_int = va_arg(*ap, int);
+ break;
+ case NUMBER: /* we have a number to process */
+ number = (ch - '0');
+ while ((ch = *fmt) != '\0' && is_digit(ch))
+ {
+ number = number * 10 + (ch - '0');
+ ++fmt;
+ }
+ break;
+ case SKIPNUM: /* we have a number to skip */
+ while ((ch = *fmt) != '\0' && is_digit(ch))
+ ++fmt;
+ break;
+ case NOOP:
+ default:
+ break; /* do nothing */
+ }
+ }
+ }
+
+ /* process all arguments up to at least the one we are looking for and if we
+ have seen the end of the string, then process up to the max argument needed */
+ if (*fmt == '\0')
+ last_arg = max_pos_arg;
+ else
+ last_arg = n;
+
+ while (numargs <= last_arg)
+ {
+ switch (arg_type[numargs])
+ {
+ case LONG_INT:
+ args[numargs++].val_long = va_arg(*ap, long);
+ break;
+ case QUAD_INT:
+ args[numargs++].val_quad_t = va_arg(*ap, quad_t);
+ break;
+ case CHAR_PTR:
+ args[numargs++].val_char_ptr_t = va_arg(*ap, char *);
+ break;
+ case DOUBLE:
+ args[numargs++].val_double = va_arg(*ap, double);
+ break;
+ case LONG_DOUBLE:
+ args[numargs++].val__LONG_DOUBLE = va_arg(*ap, _LONG_DOUBLE);
+ break;
+ case INT:
+ case SHORT_INT:
+ case CHAR:
+ default:
+ args[numargs++].val_int = va_arg(*ap, int);
+ break;
+ }
+ }
+
+ /* alter the global numargs value and keep a reference to the last bit of the fmt
+ string we processed here because the caller will continue processing where we started */
+ *numargs_p = numargs;
+ *last_fmt = fmt;
+ return &args[n];
+}
+#endif /* !_NO_POS_ARGS */