static int dispatch()

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
}