in src/vasprintf.c [294:550]
static int dispatch(xprintf_struct *s)
{
const char *initial_ptr;
char format_string[24]; /* max length may be something like "% +-#032768.32768Ld" */
char *format_ptr;
int flag_plus, flag_minus, flag_space, flag_sharp, flag_zero;
int width, prec, modifier, approx_width;
char type;
/* most of those variables are here to rewrite the format string */
#define SRCTXT (s->src_string)
#define DESTTXT (s->dest_string)
/* incoherent format string. Characters after the '%' will be printed with the next call */
#define INCOHERENT() do {SRCTXT=initial_ptr; return 0;} while (0) /* do/while to avoid */
#define INCOHERENT_TEST() do {if(*SRCTXT==0) INCOHERENT();} while (0) /* a null statement */
/* 'normal' text */
if (*SRCTXT != '%')
return usual_char(s);
/* we then have a '%' */
SRCTXT++;
/* don't check for end-of-string ; this is done later */
/* '%%' escape sequence */
if (*SRCTXT == '%') {
if (realloc_buff(s, (size_t)1) == EOF) /* because we can have "%%%%%%%%..." */
return EOF;
*DESTTXT = '%';
DESTTXT++;
SRCTXT++;
(s->real_len)++;
(s->pseudo_len)++;
return 0;
}
/* '%' managing */
initial_ptr = SRCTXT; /* save current pointer in case of incorrect */
/* 'decoding'. Points just after the '%' so the '%' */
/* won't be printed in any case, as required. */
/* flag */
flag_plus = flag_minus = flag_space = flag_sharp = flag_zero = 0;
for (;; SRCTXT++) {
if (*SRCTXT == ' ')
flag_space = 1;
else if (*SRCTXT == '+')
flag_plus = 1;
else if (*SRCTXT == '-')
flag_minus = 1;
else if (*SRCTXT == '#')
flag_sharp = 1;
else if (*SRCTXT == '0')
flag_zero = 1;
else
break;
}
INCOHERENT_TEST(); /* here is the first test for end of string */
/* width */
if (*SRCTXT == '*') { /* width given by next argument */
SRCTXT++;
width = va_arg(s->vargs, int);
if ((size_t)width > 0x3fffU) /* 'size_t' to check against negative values too */
width = 0x3fff;
} else if (isdigit((unsigned char)*SRCTXT)) /* width given as ASCII number */
width = getint(&SRCTXT);
else
width = -1; /* no width specified */
INCOHERENT_TEST();
/* .prec */
if (*SRCTXT == '.') {
SRCTXT++;
if (*SRCTXT == '*') { /* .prec given by next argument */
SRCTXT++;
prec = va_arg(s->vargs, int);
if ((size_t)prec >= 0x3fffU) /* 'size_t' to check against negative values too */
prec = 0x3fff;
} else { /* .prec given as ASCII number */
if (isdigit((unsigned char)*SRCTXT) == 0)
INCOHERENT();
prec = getint(&SRCTXT);
}
INCOHERENT_TEST();
} else
prec = -1; /* no .prec specified */
/* modifier */
switch (*SRCTXT) {
case 'L':
case 'h':
case 'l':
case 'z':
case 't':
modifier = *SRCTXT;
SRCTXT++;
if (modifier=='l' && *SRCTXT=='l') {
SRCTXT++;
modifier = 'L'; /* 'll' == 'L' long long == long double */
} /* only for compatibility ; not portable */
INCOHERENT_TEST();
break;
default:
modifier = -1; /* no modifier specified */
break;
}
/* type */
type = *SRCTXT;
if (strchr("diouxXfegEGcspn",type) == NULL)
INCOHERENT(); /* unknown type */
SRCTXT++;
/* rewrite format-string */
format_string[0] = '%';
format_ptr = &(format_string[1]);
if (flag_plus) {
*format_ptr = '+';
format_ptr++;
}
if (flag_minus) {
*format_ptr = '-';
format_ptr++;
}
if (flag_space) {
*format_ptr = ' ';
format_ptr++;
}
if (flag_sharp) {
*format_ptr = '#';
format_ptr++;
}
if (flag_zero) {
*format_ptr = '0';
format_ptr++;
} /* '0' *must* be the last one */
if (width != -1) {
sprintf(format_ptr, "%i", width);
format_ptr += strlen(format_ptr);
}
if (prec != -1) {
*format_ptr = '.';
format_ptr++;
sprintf(format_ptr, "%i", prec);
format_ptr += strlen(format_ptr);
}
if (modifier != -1) {
if (modifier == 'L' && strchr("diouxX",type) != NULL) {
*format_ptr = 'l';
format_ptr++;
*format_ptr = 'l';
format_ptr++;
} else {
*format_ptr = modifier;
format_ptr++;
}
}
*format_ptr = type;
format_ptr++;
*format_ptr = 0;
/* vague approximation of minimal length if width or prec are specified */
approx_width = width + prec;
if (approx_width < 0) /* because width == -1 and/or prec == -1 */
approx_width = 0;
switch (type) {
/* int */
case 'd':
case 'i':
case 'o':
case 'u':
case 'x':
case 'X':
switch (modifier) {
case -1 :
return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, int));
case 'L':
return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, long long int));
case 'l':
return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, long int));
case 'h':
return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, int));
case 'z':
return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, size_t));
case 't':
return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, ptrdiff_t));
/* 'int' instead of 'short int' because default promotion is 'int' */
default:
INCOHERENT();
}
/* char */
case 'c':
if (modifier != -1)
INCOHERENT();
return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, int));
/* 'int' instead of 'char' because default promotion is 'int' */
/* math */
case 'e':
case 'f':
case 'g':
case 'E':
case 'G':
switch (modifier) {
case -1 : /* because of default promotion, no modifier means 'l' */
case 'l':
return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, double));
case 'L':
return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, long double));
default:
INCOHERENT();
}
/* string */
case 's':
return type_s(s, width, prec, format_string, va_arg(s->vargs, const char*));
/* pointer */
case 'p':
if (modifier == -1)
return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, void *));
INCOHERENT();
/* store */
case 'n':
if (modifier == -1) {
int * p;
p = va_arg(s->vargs, int *);
if (p != NULL) {
*p = s->pseudo_len;
return 0;
}
return EOF;
}
INCOHERENT();
} /* switch */
INCOHERENT(); /* unknown type */
#undef INCOHERENT
#undef INCOHERENT_TEST
#undef SRCTXT
#undef DESTTXT
}