cql_noexport sem_t printf_iterator_next()

in sources/printf.c [366:484]


cql_noexport sem_t printf_iterator_next(printf_iterator *iterator) {
  Contract(iterator);
  Contract(iterator->sem_type == SEM_TYPE_PENDING);
  // We should either be at the start of a substituion or resuming a
  // substitution with a '*' width specifier.
  Contract(iterator->state == PRINTF_STATE_START || iterator->width == PRINTF_WIDTH_STAR);

  for (;;) {
    // If we encountered an error or hit the end of the string, stop.
    if (iterator->sem_type != SEM_TYPE_PENDING) {
      return iterator->sem_type;
    }

    // Read the current character in the format string.
    char c = *iterator->format_string;

    // Check if we're at the end of the string. If so, stop.
    if (c == '\0') {
      if (iterator->state == PRINTF_STATE_START) {
        // We hit the end while not within a substitution, so we're simply done.
        iterator->sem_type = SEM_TYPE_OK;
      } else {
        // We hit the end in the middle of a substitution, so the substitution
        // is incomplete and the format string is invalid.
        printf_iterator_error(iterator, "CQL0420: incomplete substitution in format string", NULL);
      }
      return iterator->sem_type;
    }

    // Here, we dispatch appropriately based on the current state. If the
    // current character should be consumed, we `break` to jump out of the
    // switch and advance the string to the next character at the end of the for
    // loop. If we want to go onto another step without consuming the current
    // character, we `continue` instead to jump back to the top of the for loop
    // without advancing the string.
    switch (iterator->state) {
      case PRINTF_STATE_START:
        if (c == '%') {
          iterator->state = PRINTF_STATE_PERCENT;
        }
        break;
      case PRINTF_STATE_PERCENT:
        if (c == '%') {
          iterator->state = PRINTF_STATE_START;
          break;
        }
        iterator->state = PRINTF_STATE_FLAG;
        continue;
      case PRINTF_STATE_FLAG:
        if (printf_is_flag_char(c)) {
          printf_iterator_add_flag_char(iterator, c);
          break;
        }
        iterator->state = PRINTF_STATE_WIDTH;
        continue;
      case PRINTF_STATE_WIDTH:
        if (c >= '0' && c <= '9') {
          printf_set_width(iterator, PRINTF_WIDTH_NUMERIC);
          iterator->state = PRINTF_STATE_WIDTH_NUMERIC;
          break;
        }
        if (c == '*') {
          // Return the fact that we need an integer and prepare to resume
          // parsing the rest of the substitution later.
          printf_iterator_suspend_for_star(iterator);
          return SEM_TYPE_INTEGER;
        }
        printf_set_width(iterator, PRINTF_WIDTH_NONE);
        iterator->state = PRINTF_STATE_DOT;
        continue;
      case PRINTF_STATE_WIDTH_NUMERIC:
        if (c >= '0' && c <= '9') {
          break;
        }
        iterator->state = PRINTF_STATE_DOT;
        continue;
      case PRINTF_STATE_DOT:
        if (c == '.') {
          iterator->state = PRINTF_STATE_PRECISION;
          break;
        }
        iterator->state = PRINTF_STATE_LENGTH_LONG;
        continue;
      case PRINTF_STATE_PRECISION:
        if (c >= '0' && c <= '9') {
          break;
        }
        iterator->state = PRINTF_STATE_LENGTH_LONG;
        continue;
      case PRINTF_STATE_LENGTH_LONG:
        if (c == 'l') {
          iterator->state = PRINTF_STATE_LENGTH_LONG_LONG;
          break;
        }
        printf_set_length(iterator, PRINTF_LENGTH_DEFAULT);
        iterator->state = PRINTF_STATE_TYPE;
        continue;
      case PRINTF_STATE_LENGTH_LONG_LONG:
        if (c == 'l') {
          printf_set_length(iterator, PRINTF_LENGTH_LONG_LONG);
          iterator->state = PRINTF_STATE_TYPE;
          break;
        }
        printf_set_length(iterator, PRINTF_LENGTH_LONG);
        iterator->state = PRINTF_STATE_TYPE;
        continue;
      case PRINTF_STATE_TYPE:
        printf_iterator_set_type_char(iterator, c);
        sem_t sem_type = iterator->sem_type;
        if (sem_type != SEM_TYPE_ERROR) {
          printf_iterator_reset(iterator);
        }
        return sem_type;
    }

    // Consume the current character and continue.
    iterator->format_string++;
  }
}