Rewrite format parser
This commit is contained in:
parent
af27e79678
commit
3f698fb5f1
@ -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,14 +21,84 @@ 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;
|
||||
result->length_of_provided_specifier = 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;
|
||||
}
|
||||
/* 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;
|
||||
|
||||
advance_ptr = false;
|
||||
next_state = state;
|
||||
|
||||
switch (state) {
|
||||
case PARSER_PERCENT:
|
||||
if (*ptr == '%') {
|
||||
advance_ptr++;
|
||||
next_state = PARSER_SECOND;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user