Rewrite format parser

This commit is contained in:
Mario Hüttel 2023-05-31 21:51:46 +02:00
parent af27e79678
commit 3f698fb5f1

View File

@ -8,7 +8,7 @@ enum format_specifier_type {
FMT_UNSIGNED, FMT_UNSIGNED,
FMT_STRING, FMT_STRING,
FMT_HEXUINT_CAPITALIZED, FMT_HEXUINT_CAPITALIZED,
FMT_HEXUNIT, FMT_HEXUINT,
FMT_CHAR, FMT_CHAR,
FMT_SIZE, FMT_SIZE,
FMT_PERCENT, FMT_PERCENT,
@ -21,13 +21,83 @@ struct format_specifier {
size_t length_of_provided_specifier; size_t length_of_provided_specifier;
}; };
enum format_parser_state {
PARSER_PERCENT = 0,
PARSER_SECOND,
PARSER_NUM,
PARSER_SPECIFIER,
};
static int parse_number(const char *str, size_t len)
{
int ret = 0;
int base;
size_t idx;
char c;
if (!len)
return 0;
for (idx = 0, base = 1; idx < len; idx++) {
c = str[len - idx - 1];
if (c > '9' || c < '0')
return -1;
ret += ((int)(c-'0'))* base;
base *= 10;
}
return ret;
}
static enum format_specifier_type resolve_specifier(char c)
{
enum format_specifier_type ret;
switch (c) {
case 's':
ret = FMT_STRING;
break;
case 'z':
ret = FMT_SIZE;
break;
case 'u':
ret = FMT_UNSIGNED;
break;
case 'i': /* Fallthru */
case 'd':
ret = FMT_INTEGER;
break;
case 'x':
ret = FMT_HEXUINT;
break;
case 'X':
ret = FMT_HEXUINT_CAPITALIZED;
break;
case 'c':
ret = FMT_CHAR;
break;
case '%':
ret = FMT_PERCENT;
break;
default:
ret = FMT_INVALID;
break;
}
return ret;
}
static void parse_format_specifier(const char *start, struct format_specifier *result) static void parse_format_specifier(const char *start, struct format_specifier *result)
{ {
size_t fmt_len = 1ull; size_t fmt_len = 1ull;
bool keep_parsing = true; bool keep_parsing = true;
bool is_in_number = false; int num;
const char *number; const char *current_token;
size_t num_len = 0; size_t token_length = 0;
enum format_parser_state state = PARSER_PERCENT;
enum format_parser_state next_state;
const char *ptr;
bool advance_ptr;
result->specifier = FMT_INVALID; result->specifier = FMT_INVALID;
result->zero_pad = 0; result->zero_pad = 0;
@ -36,99 +106,99 @@ static void parse_format_specifier(const char *start, struct format_specifier *r
if (!start) if (!start)
return; return;
/* Check if specifier starts with % */ ptr = start;
if (*start != '%')
return;
start++;
while (keep_parsing) { while (keep_parsing) {
/* Parse the format specifier*/
switch (*start) { advance_ptr = false;
case '0': next_state = state;
if (fmt_len == 1) {
result->zero_pad = 1; switch (state) {
break; case PARSER_PERCENT:
if (*ptr == '%') {
advance_ptr++;
next_state = PARSER_SECOND;
} }
/* Fallthru */ break;
case '1': case PARSER_SECOND:
case '2': if (*ptr == '0') {
case '3': result->zero_pad = true;
case '4': advance_ptr++;
case '5': next_state = PARSER_NUM;
case '6': } else if (*ptr > '9' || *ptr < '0') {
case '7': /* Specifier */
case '8': next_state = PARSER_SPECIFIER;
case '9':
/* Number */
if (!is_in_number) {
/* Start of number */
is_in_number = true;
number = start;
num_len = 0;
} else { } else {
/* Continue parsing number */ next_state = PARSER_NUM;
num_len++; current_token = ptr;
token_length = 0;
} }
break; break;
case 'c': case PARSER_NUM:
result->specifier = FMT_CHAR; if (*ptr >= '0' && *ptr <= '9') {
keep_parsing = false; /* Number */
break; advance_ptr = true;
case 'z': next_state = PARSER_NUM;
result->specifier = FMT_SIZE; token_length++;
keep_parsing = false; } else {
break; num = parse_number(current_token, token_length);
case 'x': if (num < 0) {
result->specifier = FMT_HEXUNIT; keep_parsing = false;
keep_parsing = false; }
break; next_state = PARSER_SPECIFIER;
case 'X': result->length = (size_t)num;
result->specifier = FMT_HEXUINT_CAPITALIZED;
keep_parsing = false;
break;
case 's':
result->specifier = FMT_STRING;
keep_parsing = false;
break;
case 'u':
result->specifier = FMT_UNSIGNED;
keep_parsing = false;
break;
case 'i': /* Falltrhu */
case 'd':
result->specifier = FMT_INTEGER;
keep_parsing = false;
break;
case '%':
if (fmt_len == 1) {
result->specifier = FMT_PERCENT;
} }
keep_parsing = false; break;
default: case PARSER_SPECIFIER:
/* Unrecognized stuff. Abort */ result->specifier = resolve_specifier(*ptr);
if (result->specifier == FMT_PERCENT) {
if (result->length != 0 || result->zero_pad != 0) {
result->specifier = FMT_INVALID;
}
}
result->length_of_provided_specifier = ptr - start + 1;
keep_parsing = false; keep_parsing = false;
break; break;
} }
if (keep_parsing == false) { if (advance_ptr) {
/* End reached. Check the number */ ptr++;
} }
state = next_state;
start++;
fmt_len++;
} }
} }
int simple_snprintf(char *dest, size_t dest_size, const char *fmt, ...) int simple_snprintf(char *dest, size_t dest_size, const char *fmt, ...)
{ {
int ret = -1; int ret = -1;
int written = 0;
size_t dest_idx;
const char *fmt_iter;
struct format_specifier specifier;
if (!dest || !dest_size || !fmt) { if (!dest || !dest_size || !fmt) {
/* Parameter error */ /* Parameter error */
return -1000; return -1000;
} }
dest_idx = 0;
fmt_iter = fmt;
while (*fmt_iter) {
parse_format_specifier(fmt_iter, &specifier);
if (specifier.specifier == FMT_INVALID) {
if (dest_idx < dest_size - 1) {
written++;
dest[dest_idx] = *fmt_iter;
} else {
/* Full. set terminator */
dest[dest_size-1] = 0;
break;
}
} else {
/* Do handling of stuff */
}
}
return ret; return ret;
} }