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_STRING,
FMT_HEXUINT_CAPITALIZED,
FMT_HEXUNIT,
FMT_HEXUINT,
FMT_CHAR,
FMT_SIZE,
FMT_PERCENT,
@ -21,13 +21,83 @@ struct format_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)
{
size_t fmt_len = 1ull;
bool keep_parsing = true;
bool is_in_number = false;
const char *number;
size_t num_len = 0;
int num;
const char *current_token;
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->zero_pad = 0;
@ -36,99 +106,99 @@ static void parse_format_specifier(const char *start, struct format_specifier *r
if (!start)
return;
/* Check if specifier starts with % */
if (*start != '%')
return;
start++;
ptr = start;
while (keep_parsing) {
/* Parse the format specifier*/
switch (*start) {
case '0':
if (fmt_len == 1) {
result->zero_pad = 1;
break;
advance_ptr = false;
next_state = state;
switch (state) {
case PARSER_PERCENT:
if (*ptr == '%') {
advance_ptr++;
next_state = PARSER_SECOND;
}
/* Fallthru */
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
/* Number */
if (!is_in_number) {
/* Start of number */
is_in_number = true;
number = start;
num_len = 0;
break;
case PARSER_SECOND:
if (*ptr == '0') {
result->zero_pad = true;
advance_ptr++;
next_state = PARSER_NUM;
} else if (*ptr > '9' || *ptr < '0') {
/* Specifier */
next_state = PARSER_SPECIFIER;
} else {
/* Continue parsing number */
num_len++;
next_state = PARSER_NUM;
current_token = ptr;
token_length = 0;
}
break;
case 'c':
result->specifier = FMT_CHAR;
keep_parsing = false;
break;
case 'z':
result->specifier = FMT_SIZE;
keep_parsing = false;
break;
case 'x':
result->specifier = FMT_HEXUNIT;
keep_parsing = false;
break;
case 'X':
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;
case PARSER_NUM:
if (*ptr >= '0' && *ptr <= '9') {
/* Number */
advance_ptr = true;
next_state = PARSER_NUM;
token_length++;
} else {
num = parse_number(current_token, token_length);
if (num < 0) {
keep_parsing = false;
}
next_state = PARSER_SPECIFIER;
result->length = (size_t)num;
}
keep_parsing = false;
default:
/* Unrecognized stuff. Abort */
break;
case PARSER_SPECIFIER:
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;
break;
}
if (keep_parsing == false) {
/* End reached. Check the number */
if (advance_ptr) {
ptr++;
}
start++;
fmt_len++;
state = next_state;
}
}
int simple_snprintf(char *dest, size_t dest_size, const char *fmt, ...)
{
int ret = -1;
int written = 0;
size_t dest_idx;
const char *fmt_iter;
struct format_specifier specifier;
if (!dest || !dest_size || !fmt) {
/* Parameter error */
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;
}