static int parsefmt()

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;
}