in libs/curl/lib/mprintf.c [216:634]
static int parsefmt(const char *format,
struct outsegment *out,
struct va_input *in,
int *opieces,
int *ipieces, va_list arglist)
{
char *fmt = (char *)format;
int param_num = 0;
int param;
int width;
int precision;
unsigned int flags;
FormatType type;
int max_param = -1;
int i;
int ocount = 0;
unsigned char usedinput[MAX_PARAMETERS/8];
size_t outlen = 0;
struct outsegment *optr;
int use_dollar = DOLLAR_UNKNOWN;
char *start = fmt;
/* clear, set a bit for each used input */
memset(usedinput, 0, sizeof(usedinput));
while(*fmt) {
if(*fmt == '%') {
struct va_input *iptr;
bool loopit = TRUE;
fmt++;
outlen = (size_t)(fmt - start - 1);
if(*fmt == '%') {
/* this means a %% that should be output only as %. Create an output
segment. */
if(outlen) {
optr = &out[ocount++];
if(ocount > MAX_SEGMENTS)
return PFMT_MANYSEGS;
optr->input = 0;
optr->flags = FLAGS_SUBSTR;
optr->start = start;
optr->outlen = outlen;
}
start = fmt;
fmt++;
continue; /* while */
}
flags = 0;
width = precision = 0;
if(use_dollar != DOLLAR_NOPE) {
param = dollarstring(fmt, &fmt);
if(param < 0) {
if(use_dollar == DOLLAR_USE)
/* illegal combo */
return PFMT_DOLLAR;
/* we got no positional, just get the next arg */
param = -1;
use_dollar = DOLLAR_NOPE;
}
else
use_dollar = DOLLAR_USE;
}
else
param = -1;
/* Handle the flags */
while(loopit) {
switch(*fmt++) {
case ' ':
flags |= FLAGS_SPACE;
break;
case '+':
flags |= FLAGS_SHOWSIGN;
break;
case '-':
flags |= FLAGS_LEFT;
flags &= ~(unsigned int)FLAGS_PAD_NIL;
break;
case '#':
flags |= FLAGS_ALT;
break;
case '.':
if('*' == *fmt) {
/* The precision is picked from a specified parameter */
flags |= FLAGS_PRECPARAM;
fmt++;
if(use_dollar == DOLLAR_USE) {
precision = dollarstring(fmt, &fmt);
if(precision < 0)
/* illegal combo */
return PFMT_DOLLARPREC;
}
else
/* get it from the next argument */
precision = -1;
}
else {
bool is_neg = FALSE;
flags |= FLAGS_PREC;
precision = 0;
if('-' == *fmt) {
is_neg = TRUE;
fmt++;
}
while(ISDIGIT(*fmt)) {
if(precision > INT_MAX/10)
return PFMT_PREC;
precision *= 10;
precision += *fmt - '0';
fmt++;
}
if(is_neg)
precision = -precision;
}
if((flags & (FLAGS_PREC | FLAGS_PRECPARAM)) ==
(FLAGS_PREC | FLAGS_PRECPARAM))
/* it is not permitted to use both kinds of precision for the same
argument */
return PFMT_PRECMIX;
break;
case 'h':
flags |= FLAGS_SHORT;
break;
#if defined(_WIN32) || defined(_WIN32_WCE)
case 'I':
/* Non-ANSI integer extensions I32 I64 */
if((fmt[0] == '3') && (fmt[1] == '2')) {
flags |= FLAGS_LONG;
fmt += 2;
}
else if((fmt[0] == '6') && (fmt[1] == '4')) {
flags |= FLAGS_LONGLONG;
fmt += 2;
}
else {
#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG)
flags |= FLAGS_LONGLONG;
#else
flags |= FLAGS_LONG;
#endif
}
break;
#endif /* _WIN32 || _WIN32_WCE */
case 'l':
if(flags & FLAGS_LONG)
flags |= FLAGS_LONGLONG;
else
flags |= FLAGS_LONG;
break;
case 'L':
flags |= FLAGS_LONGDOUBLE;
break;
case 'q':
flags |= FLAGS_LONGLONG;
break;
case 'z':
/* the code below generates a warning if -Wunreachable-code is
used */
#if (SIZEOF_SIZE_T > SIZEOF_LONG)
flags |= FLAGS_LONGLONG;
#else
flags |= FLAGS_LONG;
#endif
break;
case 'O':
#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG)
flags |= FLAGS_LONGLONG;
#else
flags |= FLAGS_LONG;
#endif
break;
case '0':
if(!(flags & FLAGS_LEFT))
flags |= FLAGS_PAD_NIL;
FALLTHROUGH();
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
flags |= FLAGS_WIDTH;
width = 0;
fmt--;
do {
if(width > INT_MAX/10)
return PFMT_WIDTH;
width *= 10;
width += *fmt - '0';
fmt++;
} while(ISDIGIT(*fmt));
break;
case '*': /* read width from argument list */
flags |= FLAGS_WIDTHPARAM;
if(use_dollar == DOLLAR_USE) {
width = dollarstring(fmt, &fmt);
if(width < 0)
/* illegal combo */
return PFMT_DOLLARWIDTH;
}
else
/* pick from the next argument */
width = -1;
break;
default:
loopit = FALSE;
fmt--;
break;
} /* switch */
} /* while */
switch(*fmt) {
case 'S':
flags |= FLAGS_ALT;
FALLTHROUGH();
case 's':
type = FORMAT_STRING;
break;
case 'n':
type = FORMAT_INTPTR;
break;
case 'p':
type = FORMAT_PTR;
break;
case 'd':
case 'i':
if(flags & FLAGS_LONGLONG)
type = FORMAT_LONGLONG;
else if(flags & FLAGS_LONG)
type = FORMAT_LONG;
else
type = FORMAT_INT;
break;
case 'u':
if(flags & FLAGS_LONGLONG)
type = FORMAT_LONGLONGU;
else if(flags & FLAGS_LONG)
type = FORMAT_LONGU;
else
type = FORMAT_INTU;
flags |= FLAGS_UNSIGNED;
break;
case 'o':
type = FORMAT_INT;
flags |= FLAGS_OCTAL;
break;
case 'x':
type = FORMAT_INTU;
flags |= FLAGS_HEX|FLAGS_UNSIGNED;
break;
case 'X':
type = FORMAT_INTU;
flags |= FLAGS_HEX|FLAGS_UPPER|FLAGS_UNSIGNED;
break;
case 'c':
type = FORMAT_INT;
flags |= FLAGS_CHAR;
break;
case 'f':
type = FORMAT_DOUBLE;
break;
case 'e':
type = FORMAT_DOUBLE;
flags |= FLAGS_FLOATE;
break;
case 'E':
type = FORMAT_DOUBLE;
flags |= FLAGS_FLOATE|FLAGS_UPPER;
break;
case 'g':
type = FORMAT_DOUBLE;
flags |= FLAGS_FLOATG;
break;
case 'G':
type = FORMAT_DOUBLE;
flags |= FLAGS_FLOATG|FLAGS_UPPER;
break;
default:
/* invalid instruction, disregard and continue */
continue;
} /* switch */
if(flags & FLAGS_WIDTHPARAM) {
if(width < 0)
width = param_num++;
else {
/* if this identifies a parameter already used, this
is illegal */
if(usedinput[width/8] & (1 << (width&7)))
return PFMT_WIDTHARG;
}
if(width >= MAX_PARAMETERS)
return PFMT_MANYARGS;
if(width >= max_param)
max_param = width;
in[width].type = FORMAT_WIDTH;
/* mark as used */
usedinput[width/8] |= (unsigned char)(1 << (width&7));
}
if(flags & FLAGS_PRECPARAM) {
if(precision < 0)
precision = param_num++;
else {
/* if this identifies a parameter already used, this
is illegal */
if(usedinput[precision/8] & (1 << (precision&7)))
return PFMT_PRECARG;
}
if(precision >= MAX_PARAMETERS)
return PFMT_MANYARGS;
if(precision >= max_param)
max_param = precision;
in[precision].type = FORMAT_PRECISION;
usedinput[precision/8] |= (unsigned char)(1 << (precision&7));
}
/* Handle the specifier */
if(param < 0)
param = param_num++;
if(param >= MAX_PARAMETERS)
return PFMT_MANYARGS;
if(param >= max_param)
max_param = param;
iptr = &in[param];
iptr->type = type;
/* mark this input as used */
usedinput[param/8] |= (unsigned char)(1 << (param&7));
fmt++;
optr = &out[ocount++];
if(ocount > MAX_SEGMENTS)
return PFMT_MANYSEGS;
optr->input = (unsigned int)param;
optr->flags = flags;
optr->width = width;
optr->precision = precision;
optr->start = start;
optr->outlen = outlen;
start = fmt;
}
else
fmt++;
}
/* is there a trailing piece */
outlen = (size_t)(fmt - start);
if(outlen) {
optr = &out[ocount++];
if(ocount > MAX_SEGMENTS)
return PFMT_MANYSEGS;
optr->input = 0;
optr->flags = FLAGS_SUBSTR;
optr->start = start;
optr->outlen = outlen;
}
/* Read the arg list parameters into our data list */
for(i = 0; i < max_param + 1; i++) {
struct va_input *iptr = &in[i];
if(!(usedinput[i/8] & (1 << (i&7))))
/* bad input */
return PFMT_INPUTGAP;
/* based on the type, read the correct argument */
switch(iptr->type) {
case FORMAT_STRING:
iptr->val.str = va_arg(arglist, char *);
break;
case FORMAT_INTPTR:
case FORMAT_PTR:
iptr->val.ptr = va_arg(arglist, void *);
break;
case FORMAT_LONGLONGU:
iptr->val.numu = (mp_uintmax_t)va_arg(arglist, mp_uintmax_t);
break;
case FORMAT_LONGLONG:
iptr->val.nums = (mp_intmax_t)va_arg(arglist, mp_intmax_t);
break;
case FORMAT_LONGU:
iptr->val.numu = (mp_uintmax_t)va_arg(arglist, unsigned long);
break;
case FORMAT_LONG:
iptr->val.nums = (mp_intmax_t)va_arg(arglist, long);
break;
case FORMAT_INTU:
iptr->val.numu = (mp_uintmax_t)va_arg(arglist, unsigned int);
break;
case FORMAT_INT:
case FORMAT_WIDTH:
case FORMAT_PRECISION:
iptr->val.nums = (mp_intmax_t)va_arg(arglist, int);
break;
case FORMAT_DOUBLE:
iptr->val.dnum = va_arg(arglist, double);
break;
default:
DEBUGASSERT(NULL); /* unexpected */
break;
}
}
*ipieces = max_param + 1;
*opieces = ocount;
return PFMT_OK;
}