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