diff options
Diffstat (limited to 'pom.c')
-rw-r--r-- | pom.c | 167 |
1 files changed, 124 insertions, 43 deletions
@@ -190,6 +190,36 @@ struct parser { char buf[4096]; }; +#include "errors.c" + +void +pom_set_error_language(const char *lang) { + if (lang) + snprintf(error_language, sizeof error_language-1, "%s", lang); + else + strcpy(error_language, "en-US"); +} + + +static const char * +get_error_message(enum error_id id) { + assert(id < ERROR_COUNT); + const char *const *messages = error_messages_en; + for (size_t i = 0; i < sizeof error_messages / sizeof error_messages[0]; i++) { + const char *lang = error_messages[i].lang; + size_t lang_len = strlen(lang); + if (strncmp(error_language, lang, lang_len) == 0 + && (error_language[lang_len] == 0 || error_language[lang_len] == '-')) { + // language match + messages = error_messages[i].messages; + break; + } + } + const char *message = messages[id] ? messages[id] : error_messages_en[id]; + assert(message); + return message; +} + #ifdef POM_NO_STDIO #define fatal_error(...) abort() #else @@ -208,11 +238,11 @@ fatal_error(const char *fmt, ...) { #endif // Make an error with no next-error. -static pom_error *make_error(PRINTF_FORMAT_STRING const char *file, uint64_t line, const char *fmt, ...) ATTRIBUTE_PRINTF(3, 4); static pom_error * -make_error(const char *file, uint64_t line, const char *fmt, ...) { +make_error(const char *file, uint64_t line, enum error_id id, ...) { + const char *fmt = get_error_message(id); va_list args, args_copy; - va_start(args, fmt); + va_start(args, id); va_copy(args_copy, args); bool bad_fmt = false; int len = vsnprintf(NULL, 0, fmt, args); @@ -222,7 +252,7 @@ make_error(const char *file, uint64_t line, const char *fmt, ...) { bad_fmt = true; len = strlen(fmt); } - pom_error *err = malloc(sizeof(pom_error) + len * 2 + strlen(file) + 48); + pom_error *err = malloc(sizeof(pom_error) + len * 2 + strlen(file) + 64); if (err) { char *message = (char *)(err + 1); if (bad_fmt) { @@ -240,7 +270,9 @@ make_error(const char *file, uint64_t line, const char *fmt, ...) { #pragma GCC diagnostic ignored "-Wunknown-warning-option" #pragma GCC diagnostic ignored "-Wrestrict" #endif - sprintf(string, "Error:\n%s:%" PRIu64 ": %s\n", file, line, message); + sprintf(string, "%s\n%s:%" PRIu64 ": %s\n", + get_error_message(ERROR_HEADER), + file, line, message); #if __GNUC__ >= 4 #pragma GCC diagnostic pop #endif @@ -369,13 +401,13 @@ parser_append_char(struct parser *parser, char c) { if (pc) *pc = c; } -static void parser_error(struct parser *parser, PRINTF_FORMAT_STRING const char *fmt, ...) ATTRIBUTE_PRINTF(2, 3); static void -parser_error(struct parser *parser, const char *fmt, ...) { +parser_error(struct parser *parser, enum error_id id, ...) { if (parser->out_of_memory) return; if (parser->errors.count >= 1000) return; // don't bother at this point. + const char *fmt = get_error_message(id); va_list args, args_copy; - va_start(args, fmt); + va_start(args, id); va_copy(args_copy, args); bool bad_fmt = false; int error_len = vsnprintf(NULL, 0, fmt, args); @@ -415,7 +447,7 @@ parser_read_to_buf(struct parser *parser, bool skip_bom) { // EOF reached. eof: if (utf8_state) { - parser_error(parser, "Invalid UTF-8 (want continuation byte, got EOF)."); + parser_error(parser, ERROR_INVALID_UTF8); } parser->eof = true; return false; @@ -428,7 +460,7 @@ parser_read_to_buf(struct parser *parser, bool skip_bom) { if (read_count < sizeof parser->buf - 1) parser->short_read = true; if (parser->leftover_cr && buf[0] != '\n') - parser_error(parser, "Carriage return with no newline after it."); + parser_error(parser, ERROR_ASCII_CONTROL, '\r'); size_t in = 0, out = 0; uint64_t original_line_number = parser->line_number; if (skip_bom && read_count >= 3 @@ -447,18 +479,18 @@ parser_read_to_buf(struct parser *parser, bool skip_bom) { if (in == read_count - 1) { parser->leftover_cr = true; } else if (buf[in + 1] != '\n') { - parser_error(parser, "Carriage return with no newline after it."); + parser_error(parser, ERROR_ASCII_CONTROL, '\r'); } continue; } else if (byte == '\n') { parser->line_number++; } else if (byte >= 0 && byte < 32 && byte != '\t') { - parser_error(parser, "Illegal control character (ASCII code %d)", byte); + parser_error(parser, ERROR_ASCII_CONTROL, byte); continue; } } else if (byte < 0xC2) { utf8_invalid_start_byte: - parser_error(parser, "Invalid UTF-8 (invalid start byte 0x%02X)", byte); + parser_error(parser, ERROR_INVALID_UTF8); continue; } else if (byte < 0xE0) { // 2-byte sequence @@ -487,31 +519,31 @@ parser_read_to_buf(struct parser *parser, bool skip_bom) { } else if (utf8_state == UTF8_STATE_1CONT || utf8_state == UTF8_STATE_2CONT || utf8_state == UTF8_STATE_3CONT) { utf8_state -= 1; if ((byte & 0xC0) != 0x80) { - parser_error(parser, "Invalid UTF-8 (want continuation byte, got 0x%02X)", byte); + parser_error(parser, ERROR_INVALID_UTF8); continue; } } else if (utf8_state == UTF8_STATE_2CONT_GTEQ_A0) { utf8_state = UTF8_STATE_1CONT; if (byte < 0xA0 || (byte & 0xC0) != 0x80) { - parser_error(parser, "Invalid UTF-8 (want continuation byte >= 0xA0, got 0x%02X)", byte); + parser_error(parser, ERROR_INVALID_UTF8); continue; } } else if (utf8_state == UTF8_STATE_2CONT_LT_A0) { utf8_state = UTF8_STATE_1CONT; if (byte >= 0xA0 || (byte & 0xC0) != 0x80) { - parser_error(parser, "Invalid UTF-8 (want continuation byte < 0xA0, got 0x%02X)", byte); + parser_error(parser, ERROR_INVALID_UTF8); continue; } } else if (utf8_state == UTF8_STATE_3CONT_GTEQ_90) { utf8_state = UTF8_STATE_2CONT; if (byte < 0x90 || (byte & 0xC0) != 0x80) { - parser_error(parser, "Invalid UTF-8 (want continuation byte >= 0x90, got 0x%02X)", byte); + parser_error(parser, ERROR_INVALID_UTF8); continue; } } else if (utf8_state == UTF8_STATE_3CONT_LT_90) { utf8_state = UTF8_STATE_2CONT; if (byte >= 0x90 || (byte & 0xC0) != 0x80) { - parser_error(parser, "Invalid UTF-8 (want continuation byte < 0x90, got 0x%02X)", byte); + parser_error(parser, ERROR_INVALID_UTF8); continue; } } else { @@ -573,18 +605,20 @@ strip_trailing_accepted_spaces(char *s) { } static void -check_valid_key(struct parser *parser, const char *key) { +check_if_key_is_valid(struct parser *parser, const char *key) { uint8_t c; + if (key[0] == 0) + parser_error(parser, ERROR_EMPTY_KEY); if (key[0] == '.') - parser_error(parser, "Key shouldn't begin with .: %s", key); + parser_error(parser, ERROR_KEY_STARTS_WITH_DOT, key); for (size_t i = 0; (c = key[i]); i++) { bool bad = false; if (c < 64) { if (c == '.') { if (key[i+1] == 0) { - parser_error(parser, "Key shouldn't end with .: %s", key); + parser_error(parser, ERROR_KEY_ENDS_WITH_DOT, key); } else if (key[i+1] == '.') { - parser_error(parser, "Key shouldn't contain ..: %s", key); + parser_error(parser, ERROR_KEY_DOT_DOT, key); } } // bitmask of disallowed ASCII characters 0-63 @@ -594,7 +628,7 @@ check_valid_key(struct parser *parser, const char *key) { bad = (0xfc0000017c000001U >> c) & 1; } if (bad) { - parser_error(parser, "Invalid character in key: '%c' (ASCII %d)", c, c); + parser_error(parser, ERROR_KEY_INVALID_CHAR, c, c); } } } @@ -616,7 +650,7 @@ parse_escape_sequence(struct parser *parser, const char **p_str) { switch (*str++) { invalid_sequence: { int len = (int)(str - *p_str); - parser_error(parser, "Invalid escape sequence: \\%.*s", len, *p_str); + parser_error(parser, ERROR_INVALID_ESCAPE, len, *p_str); return; } break; case 'n': @@ -704,7 +738,7 @@ parse_quoted_value(struct parser *parser, const char *first_line) { while ((c = *line++)) { if (c != ' ' && c != '\t') { parser_error(parser, - "Stray characters after closing %c", + ERROR_STRAY_CHARS_AFTER_QUOTED, delimiter); } } @@ -738,7 +772,7 @@ parse_line(struct parser *parser) { strip_trailing_accepted_spaces(line); size_t len = strlen(line); if (line[len-1] != ']') { - parser_error(parser, "Line starting with [ must end with ]"); + parser_error(parser, ERROR_MISMATCHED_SQUARE_BRACKETS); return; } line += 1; @@ -750,7 +784,7 @@ parse_line(struct parser *parser) { current_section[len] = 0; parser->current_section.len = len; if (len) - check_valid_key(parser, current_section); + check_if_key_is_valid(parser, current_section); return; } size_t equals_idx; @@ -760,12 +794,12 @@ parse_line(struct parser *parser) { break; } if (line[i] == 0) { - parser_error(parser, "Line should start with [ or contain an ="); + parser_error(parser, ERROR_INVALID_LINE); return; } } if (equals_idx == 0) { - parser_error(parser, "Expected key name before ="); + parser_error(parser, ERROR_EMPTY_KEY); return; } size_t key_idx = parser->string_data.count; @@ -781,7 +815,7 @@ parse_line(struct parser *parser) { memcpy(p, line, equals_idx); p[equals_idx] = 0; strip_trailing_accepted_spaces(p); - check_valid_key(parser, key); + check_if_key_is_valid(parser, key); } size_t value_start_idx = equals_idx + 1; while (line[value_start_idx] == ' ' || line[value_start_idx] == '\t') @@ -1025,8 +1059,7 @@ parser_finish(struct parser *parser) { max_line = item1->line; } parser->line_number = max_line; - parser_error(parser, "Re-definition of %s (previously defined on line %" PRIu64 ")", - item1->key, min_line); + parser_error(parser, ERROR_REDEFINITION, item1->key, min_line); } if (parser->errors.count) { conf_free(conf); @@ -1055,7 +1088,7 @@ pom_load(const char *filename, if (error) *error = NULL; // Start by allocating out-of-memory error, so we can just return // it if we run out of memory. - pom_error *out_of_memory = make_error(filename, 1, "Out of memory."); + pom_error *out_of_memory = make_error(filename, 1, ERROR_OUT_OF_MEMORY); if (!out_of_memory) return NULL; char *current_section = calloc(1, 1); if (!current_section) { @@ -1087,7 +1120,7 @@ pom_load(const char *filename, if (error) { // shouldn't realistically overflow given that we cut off at 1000 errors size_t len = (parser->errors.count + 1) * (sizeof(pom_error) + strlen(filename) + 32) - + parser->error_messages.count * 2 + 16; + + parser->error_messages.count * 2 + 64; // convert parser_errors to pom_error. pom_error *errors = malloc(len); if (errors) { @@ -1107,7 +1140,7 @@ pom_load(const char *filename, } // create string containing all error messages char *string = strchr(filename, '\0') + 1, *s = string; - strcpy(s, "Error:\n"); + sprintf(s, "%s\n", get_error_message(ERROR_HEADER)); s = strchr(s, 0); for (size_t i = 0; i < parser->errors.count; i++) { const pom_error *e = &errors[i]; @@ -1164,7 +1197,16 @@ pom_load_file(const char *filename, FILE *file, pom_error **error) { fatal_error("%s called with NULL file name", __func__); if (!file) fatal_error("%s called with NULL file", __func__); - return pom_load(filename, read_file, file, error); + pom_conf *conf = pom_load(filename, read_file, file, error); + if (ferror(file)) { + if (error) { + free(*error); + *error = make_error(filename, 1, ERROR_FILE_READ); + } + pom_conf_free(conf); + conf = NULL; + } + return conf; } pom_conf * @@ -1175,7 +1217,7 @@ pom_load_path(const char *path, pom_error **error) { if (!fp) { if (error) { const char *message = strerror(errno); - *error = make_error(path, 1, "Couldn't open file: %s", message); + *error = make_error(path, 1, ERROR_CANT_OPEN_FILE, message); } return NULL; } @@ -1547,10 +1589,15 @@ parse_uint(const char *s, uint64_t *val) { if (*s == '0' && (s[1] == 'x' || s[1] == 'X')) { // hexadecimal for (size_t i = 2; s[i]; i++) { - if (parse_hex_digit(s[i]) == -1) + int digit = parse_hex_digit(s[i]); + if (digit == -1) return false; + value <<= 4; + value |= digit; + if (value >> 53) { + // too big return false; + } } - value = (uint64_t) strtoull(s, NULL, 16); } else if (*s == '0') { if (s[1] != 0) { // leading zero @@ -1558,10 +1605,16 @@ parse_uint(const char *s, uint64_t *val) { } } else { for (size_t i = 0; s[i]; i++) { - if (s[i] < '0' || s[i] > '9') + int digit = s[i] - '0'; + if (digit < 0 || digit > 9) + return false; + value *= 10; + value += digit; + if (value >> 53) { + // too big return false; + } } - value = (uint64_t) strtoull(s, NULL, 10); } if (value >> 53) { // too big! @@ -1571,6 +1624,21 @@ parse_uint(const char *s, uint64_t *val) { return true; } +static bool +parse_int(const char *s, int64_t *val) { + int sign = 1; + if (*s == '-') { + sign = -1; + s++; + if (*s == '+') + return false; + } + uint64_t absval; + if (!parse_uint(s, &absval)) return false; + *val = (int64_t)absval * sign; + return true; +} + const char * pom_conf_get_uint(const pom_conf *conf, const char *key, uint64_t *value_uint) { check_conf(conf); @@ -1578,10 +1646,23 @@ pom_conf_get_uint(const pom_conf *conf, const char *key, uint64_t *value_uint) { if (!value_uint) fatal_error("NULL value passed to %s", __func__); *value_uint = 0; const char *value_str = pom_conf_get(conf, key); - if (!value_str) { + if (!value_str) return ""; - } if (parse_uint(value_str, value_uint)) return NULL; return value_str; } + +const char * +pom_conf_get_int(const pom_conf *conf, const char *key, int64_t *value_int) { + check_conf(conf); + if (!key) fatal_error("NULL key passed to %s", __func__); + if (!value_int) fatal_error("NULL value passed to %s", __func__); + *value_int = 0; + const char *value_str = pom_conf_get(conf, key); + if (!value_str) + return ""; + if (parse_int(value_str, value_int)) + return NULL; + return value_str; +} |