Rewrite format parser
This commit is contained in:
parent
af27e79678
commit
3f698fb5f1
@ -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') {
|
||||||
|
/* Number */
|
||||||
|
advance_ptr = true;
|
||||||
|
next_state = PARSER_NUM;
|
||||||
|
token_length++;
|
||||||
|
} else {
|
||||||
|
num = parse_number(current_token, token_length);
|
||||||
|
if (num < 0) {
|
||||||
keep_parsing = false;
|
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;
|
|
||||||
}
|
}
|
||||||
keep_parsing = false;
|
next_state = PARSER_SPECIFIER;
|
||||||
default:
|
result->length = (size_t)num;
|
||||||
/* 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;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user