Rewrite format parser
This commit is contained in:
		@@ -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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user