diff --git a/src/config.c b/src/config.c index 5af1ffe..1f76371 100644 --- a/src/config.c +++ b/src/config.c @@ -26,215 +26,16 @@ // Local includes #include "constants.h" -#define SINGLE_QUOTE_DECREMENT() \ - for(; single_quote_count > 0; --single_quote_count) { \ - if ((state & 1) == 0) { \ - key_buf[key_idx++] = '\''; \ - if (key_idx >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) { \ - fprintf(stderr, \ - "ERROR: config file \"key\" is larger than %u bytes!\n", \ - C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1); \ - c_simple_http_clean_up_parsed_config(&config); \ - config.hash_map = NULL; \ - return config; \ - } \ - } else { \ - value_buf[value_idx++] = '\''; \ - if (value_idx >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) { \ - fprintf(stderr, \ - "ERROR: config file \"value\" is larger than %u bytes!\n", \ - C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1); \ - c_simple_http_clean_up_parsed_config(&config); \ - config.hash_map = NULL; \ - return config; \ - } \ - } \ - } - -#define DOUBLE_QUOTE_DECREMENT() \ - for(; double_quote_count > 0; --double_quote_count) { \ - if ((state & 1) == 0) { \ - key_buf[key_idx++] = '"'; \ - if (key_idx >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) { \ - fprintf(stderr, \ - "ERROR: config file \"key\" is larger than %u bytes!\n", \ - C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1); \ - c_simple_http_clean_up_parsed_config(&config); \ - config.hash_map = NULL; \ - return config; \ - } \ - } else { \ - value_buf[value_idx++] = '"'; \ - if (value_idx >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) { \ - fprintf(stderr, \ - "ERROR: config file \"value\" is larger than %u bytes!\n", \ - C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1); \ - c_simple_http_clean_up_parsed_config(&config); \ - config.hash_map = NULL; \ - return config; \ - } \ - } \ - } - -#define CHECK_ADD_VALUE() \ - if (value_idx < C_SIMPLE_HTTP_CONFIG_BUF_SIZE) { \ - value_buf[value_idx++] = 0; \ - } else { \ - fprintf(stderr, \ - "ERROR: config file \"value\" is larger than %u bytes!\n", \ - C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1); \ - c_simple_http_clean_up_parsed_config(&config); \ - config.hash_map = NULL; \ - return config; \ - } \ - state &= 0xFFFFFFFE; \ - \ - /* Check if key is separating_key. */ \ - if (strcmp((char*)key_buf, separating_key) == 0) { \ - if (current_separating_key_value) { \ - if (required_names) { \ - C_SIMPLE_HTTP_HashMapWrapper *hash_map_wrapper = \ - simple_archiver_hash_map_get( \ - config.hash_map, \ - current_separating_key_value, \ - current_separating_key_value_size); \ - C_SIMPLE_HTTP_INTERNAL_RequiredIter req_iter_struct; \ - req_iter_struct.hash_map = hash_map_wrapper->hash_map; \ - if (paths->count != 0) { \ - req_iter_struct.path = paths->tail->prev->data; \ - } else { \ - req_iter_struct.path = NULL; \ - } \ - const char *missing_key = simple_archiver_list_get( \ - required_names, \ - c_simple_http_required_iter_fn, \ - &req_iter_struct); \ - if (missing_key) { \ - fprintf(stderr, \ - "WARNING: config file did not have required key \"%s\"!" \ - " Returning NULL map!\n", \ - missing_key); \ - c_simple_http_clean_up_parsed_config(&config); \ - config.hash_map = NULL; \ - return config; \ - } \ - } \ - \ - state &= 0xFFFFFFFD; \ - free(current_separating_key_value); \ - } \ - current_separating_key_value = malloc(value_idx); \ - memcpy(current_separating_key_value, value_buf, value_idx); \ - current_separating_key_value_size = value_idx; \ - /* At this point, key is separating_key. */ \ - SDArchiverHashMap *hash_map = simple_archiver_hash_map_init(); \ - unsigned char *key = malloc(separating_key_size); \ - strncpy((char*)key, separating_key, separating_key_size); \ - unsigned char *value = malloc(value_idx); \ - memcpy(value, value_buf, value_idx); \ - if (simple_archiver_hash_map_insert(hash_map, \ - value, \ - key, \ - separating_key_size, \ - NULL, \ - NULL) != 0) { \ - fprintf(stderr, \ - "ERROR: Failed to create hash map for new separating_key " \ - "block!\n"); \ - c_simple_http_clean_up_parsed_config(&config); \ - config.hash_map = NULL; \ - free(key); \ - free(value); \ - return config; \ - } \ - \ - C_SIMPLE_HTTP_HashMapWrapper *wrapper = malloc(sizeof(C_SIMPLE_HTTP_HashMapWrapper)); \ - wrapper->paths = hash_map; \ - \ - if (simple_archiver_hash_map_insert( \ - config.hash_map, \ - wrapper, \ - value, \ - value_idx, \ - c_simple_http_hash_map_wrapper_cleanup_hashmap_fn, \ - simple_archiver_helper_datastructure_cleanup_nop) != 0) { \ - fprintf(stderr, \ - "ERROR: Failed to insert new hash map for new PATH block!\n"); \ - c_simple_http_clean_up_parsed_config(&config); \ - config.hash_map = NULL; \ - c_simple_http_hash_map_wrapper_cleanup(wrapper); \ - return config; \ - } \ - simple_archiver_list_add(paths, value, \ - simple_archiver_helper_datastructure_cleanup_nop); \ - } else if (!current_separating_key_value) { \ - fprintf( \ - stderr, \ - "ERROR: config file has invalid key: No preceding \"%s\" " \ - "key!\n", separating_key); \ - c_simple_http_clean_up_parsed_config(&config); \ - config.hash_map = NULL; \ - return config; \ - } else { \ - /* Non-separating_key key. */ \ - C_SIMPLE_HTTP_HashMapWrapper *hash_map_wrapper = \ - simple_archiver_hash_map_get( \ - config.hash_map, \ - current_separating_key_value, \ - current_separating_key_value_size); \ - if (!hash_map_wrapper) { \ - fprintf(stderr, \ - "ERROR: Internal error failed to get existing hash map with path " \ - "\"%s\"!", current_separating_key_value); \ - c_simple_http_clean_up_parsed_config(&config); \ - config.hash_map = NULL; \ - return config; \ - } \ - \ - unsigned char *key = malloc(key_idx); \ - memcpy(key, key_buf, key_idx); \ - unsigned char *value = malloc(value_idx); \ - memcpy(value, value_buf, value_idx); \ - \ - if (simple_archiver_hash_map_insert(hash_map_wrapper->paths, \ - value, \ - key, \ - key_idx, \ - NULL, \ - NULL) != 0) { \ - fprintf(stderr, \ - "ERROR: Internal error failed to insert into hash map with path " \ - "\"%s\"!", current_separating_key_value); \ - c_simple_http_clean_up_parsed_config(&config); \ - config.hash_map = NULL; \ - free(key); \ - free(value); \ - return config; \ - } \ - } \ - key_idx = 0; \ - value_idx = 0; - -void c_simple_http_hash_map_wrapper_cleanup_hashmap_fn(void *data) { - C_SIMPLE_HTTP_HashMapWrapper *wrapper = data; - simple_archiver_hash_map_free(&wrapper->paths); - free(wrapper); -} - -void c_simple_http_hash_map_wrapper_cleanup(C_SIMPLE_HTTP_HashMapWrapper *wrapper) { - simple_archiver_hash_map_free(&wrapper->paths); - free(wrapper); -} - -void c_simple_http_hash_map_cleanup_helper(SDArchiverHashMap *hash_map) { - simple_archiver_hash_map_free(&hash_map); -} - typedef struct C_SIMPLE_HTTP_INTERNAL_RequiredIter { SDArchiverHashMap *hash_map; const char *path; } C_SIMPLE_HTTP_INTERNAL_RequiredIter; +typedef struct C_SIMPLE_HTTP_INTERNAL_RequiredCheck { + const SDArchiverHashMap *map_of_paths_and_their_vars; + const SDArchiverLinkedList *required; +} C_SIMPLE_HTTP_INTERNAL_RequiredCheck; + int c_simple_http_required_iter_fn(void *data, void *ud) { C_SIMPLE_HTTP_INTERNAL_RequiredIter *req_iter_struct = ud; uint32_t data_str_length = (uint32_t)strlen(data) + 1; @@ -260,10 +61,243 @@ int c_simple_http_required_iter_fn(void *data, void *ud) { return 0; } -typedef struct C_SIMPLE_HTTP_INTERNAL_RequiredCheck { - const SDArchiverHashMap *map_of_paths_and_their_vars; - const SDArchiverLinkedList *required; -} C_SIMPLE_HTTP_INTERNAL_RequiredCheck; +void c_simple_http_hash_map_wrapper_cleanup_hashmap_fn(void *data) { + C_SIMPLE_HTTP_HashMapWrapper *wrapper = data; + simple_archiver_hash_map_free(&wrapper->paths); + free(wrapper); +} + +void c_simple_http_hash_map_wrapper_cleanup( + C_SIMPLE_HTTP_HashMapWrapper *wrapper) { + simple_archiver_hash_map_free(&wrapper->paths); + free(wrapper); +} + +/// Returns non-zero if config should be returned. +int internal_single_quote_decrement(uint_fast8_t *single_quote_count, + uint32_t *state, + unsigned char *key_buf, + uint32_t *key_idx, + unsigned char *value_buf, + uint32_t *value_idx, + C_SIMPLE_HTTP_ParsedConfig *config) { + for(; (*single_quote_count) > 0; --(*single_quote_count)) { + if (((*state) & 1) == 0) { + key_buf[(*key_idx)++] = '\''; + if ((*key_idx) >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) { + fprintf(stderr, + "ERROR: config file \"key\" is larger than %u bytes!\n", + C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1); + c_simple_http_clean_up_parsed_config(config); + config->hash_map = NULL; + return 1; + } + } else { + value_buf[(*value_idx)++] = '\''; + if ((*value_idx) >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) { + fprintf(stderr, + "ERROR: config file \"value\" is larger than %u bytes!\n", + C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1); + c_simple_http_clean_up_parsed_config(config); + config->hash_map = NULL; + return 1; + } + } + } + return 0; +} + +/// Returns non-zero if config should be returned. +int internal_double_quote_decrement(uint_fast8_t *double_quote_count, + uint32_t *state, + unsigned char *key_buf, + uint32_t *key_idx, + unsigned char *value_buf, + uint32_t *value_idx, + C_SIMPLE_HTTP_ParsedConfig *config) { + for(; (*double_quote_count) > 0; --(*double_quote_count)) { + if (((*state) & 1) == 0) { + key_buf[(*key_idx)++] = '"'; + if ((*key_idx) >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) { + fprintf(stderr, + "ERROR: config file \"key\" is larger than %u bytes!\n", + C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1); + c_simple_http_clean_up_parsed_config(config); + config->hash_map = NULL; + return 1; + } + } else { + value_buf[(*value_idx)++] = '"'; + if ((*value_idx) >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) { + fprintf(stderr, + "ERROR: config file \"value\" is larger than %u bytes!\n", + C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1); + c_simple_http_clean_up_parsed_config(config); + config->hash_map = NULL; + return 1; + } + } + } + return 0; +} + +/// Returns non-zero if config should be returned. +int internal_check_add_value(uint32_t *state, + unsigned char *key_buf, + uint32_t *key_idx, + unsigned char *value_buf, + uint32_t *value_idx, + C_SIMPLE_HTTP_ParsedConfig *config, + const char *separating_key, + const SDArchiverLinkedList *required_names, + const uint32_t separating_key_size, + SDArchiverLinkedList *paths, + char **current_separating_key_value, + uint32_t *current_separating_key_value_size) { + if ((*value_idx) < C_SIMPLE_HTTP_CONFIG_BUF_SIZE) { + value_buf[(*value_idx)++] = 0; + } else { + fprintf(stderr, + "ERROR: config file \"value\" is larger than %u bytes!\n", + C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1); + c_simple_http_clean_up_parsed_config(config); + config->hash_map = NULL; + return 1; + } + (*state) &= 0xFFFFFFFE; + + /* Check if key is separating_key. */ + if (strcmp((char*)key_buf, separating_key) == 0) { + if (*current_separating_key_value) { + if (required_names) { + C_SIMPLE_HTTP_HashMapWrapper *hash_map_wrapper = + simple_archiver_hash_map_get( + config->hash_map, + (*current_separating_key_value), + (*current_separating_key_value_size)); + C_SIMPLE_HTTP_INTERNAL_RequiredIter req_iter_struct; + req_iter_struct.hash_map = hash_map_wrapper->hash_map; + if (paths->count != 0) { + req_iter_struct.path = paths->tail->prev->data; + } else { + req_iter_struct.path = NULL; + } + const char *missing_key = simple_archiver_list_get( + required_names, + c_simple_http_required_iter_fn, + &req_iter_struct); + if (missing_key) { + fprintf(stderr, + "WARNING: config file did not have required key \"%s\"!" + " Returning NULL map!\n", + missing_key); + c_simple_http_clean_up_parsed_config(config); + config->hash_map = NULL; + return 1; + } + } + + (*state) &= 0xFFFFFFFD; + free((*current_separating_key_value)); + } + (*current_separating_key_value) = malloc((*value_idx)); + memcpy((*current_separating_key_value), value_buf, (*value_idx)); + (*current_separating_key_value_size) = (*value_idx); + /* At this point, key is separating_key. */ + SDArchiverHashMap *hash_map = simple_archiver_hash_map_init(); + unsigned char *key = malloc(separating_key_size); + strncpy((char*)key, separating_key, separating_key_size); + unsigned char *value = malloc(*value_idx); + memcpy(value, value_buf, (*value_idx)); + if (simple_archiver_hash_map_insert(hash_map, + value, + key, + separating_key_size, + NULL, + NULL) != 0) { + fprintf(stderr, + "ERROR: Failed to create hash map for new separating_key " + "block!\n"); + c_simple_http_clean_up_parsed_config(config); + config->hash_map = NULL; + free(key); + free(value); + return 1; + } + + C_SIMPLE_HTTP_HashMapWrapper *wrapper = + malloc(sizeof(C_SIMPLE_HTTP_HashMapWrapper)); + wrapper->paths = hash_map; + + if (simple_archiver_hash_map_insert( + config->hash_map, + wrapper, + value, + (*value_idx), + c_simple_http_hash_map_wrapper_cleanup_hashmap_fn, + simple_archiver_helper_datastructure_cleanup_nop) != 0) { + fprintf(stderr, + "ERROR: Failed to insert new hash map for new PATH block!\n"); + c_simple_http_clean_up_parsed_config(config); + config->hash_map = NULL; + c_simple_http_hash_map_wrapper_cleanup(wrapper); + return 1; + } + simple_archiver_list_add(paths, value, + simple_archiver_helper_datastructure_cleanup_nop); + } else if (!(*current_separating_key_value)) { + fprintf( + stderr, + "ERROR: config file has invalid key: No preceding \"%s\" " + "key!\n", separating_key); + c_simple_http_clean_up_parsed_config(config); + config->hash_map = NULL; + return 1; + } else { + /* Non-separating_key key. */ + C_SIMPLE_HTTP_HashMapWrapper *hash_map_wrapper = + simple_archiver_hash_map_get( + config->hash_map, + (*current_separating_key_value), + (*current_separating_key_value_size)); + if (!hash_map_wrapper) { + fprintf(stderr, + "ERROR: Internal error failed to get existing hash map with path " + "\"%s\"!", (*current_separating_key_value)); + c_simple_http_clean_up_parsed_config(config); + config->hash_map = NULL; + return 1; + } + + unsigned char *key = malloc(*key_idx); + memcpy(key, key_buf, *key_idx); + unsigned char *value = malloc(*value_idx); + memcpy(value, value_buf, (*value_idx)); + + if (simple_archiver_hash_map_insert(hash_map_wrapper->paths, + value, + key, + *key_idx, + NULL, + NULL) != 0) { + fprintf(stderr, + "ERROR: Internal error failed to insert into hash map with path " + "\"%s\"!", (*current_separating_key_value)); + c_simple_http_clean_up_parsed_config(config); + config->hash_map = NULL; + free(key); + free(value); + return 1; + } + } + (*key_idx) = 0; + (*value_idx) = 0; + return 0; +} + +void c_simple_http_hash_map_cleanup_helper(SDArchiverHashMap *hash_map) { + simple_archiver_hash_map_free(&hash_map); +} int c_simple_http_check_required_iter_fn(void *path_void_str, void *ud) { C_SIMPLE_HTTP_INTERNAL_RequiredCheck *req = ud; @@ -332,8 +366,8 @@ C_SIMPLE_HTTP_ParsedConfig c_simple_http_parse_config( // 01xx - reading value is single quoted // 10xx - reading value is double quoted uint32_t state = 0; - unsigned char single_quote_count = 0; - unsigned char double_quote_count = 0; + uint_fast8_t single_quote_count = 0; + uint_fast8_t double_quote_count = 0; int32_t c; while (feof(f) == 0) { @@ -342,20 +376,60 @@ C_SIMPLE_HTTP_ParsedConfig c_simple_http_parse_config( break; } else if ((state & 0xC) == 0 && (c == ' ' || c == '\t')) { // Ignore whitespace when not quoted. - SINGLE_QUOTE_DECREMENT(); - DOUBLE_QUOTE_DECREMENT(); + if (internal_single_quote_decrement(&single_quote_count, + &state, + key_buf, + &key_idx, + value_buf, + &value_idx, + &config)) { + return config; + } + if (internal_double_quote_decrement(&double_quote_count, + &state, + key_buf, + &key_idx, + value_buf, + &value_idx, + &config)) { + return config; + } continue; } else if ((state & 1) == 0 && (state & 0xC) == 0 && (c == '\r' || c == '\n')) { // Ignore newlines when parsing for key and when not quoted. - SINGLE_QUOTE_DECREMENT(); - DOUBLE_QUOTE_DECREMENT(); + if (internal_single_quote_decrement(&single_quote_count, + &state, + key_buf, + &key_idx, + value_buf, + &value_idx, + &config)) { + return config; + } + if (internal_double_quote_decrement(&double_quote_count, + &state, + key_buf, + &key_idx, + value_buf, + &value_idx, + &config)) { + return config; + } continue; } else if ((state & 1) == 1) { if (c == '\'') { ++single_quote_count; - DOUBLE_QUOTE_DECREMENT(); + if (internal_double_quote_decrement(&double_quote_count, + &state, + key_buf, + &key_idx, + value_buf, + &value_idx, + &config)) { + return config; + } if (((state & 0xC) == 0x4 || (state & 0xC) == 0) && single_quote_count >= C_SIMPLE_HTTP_QUOTE_COUNT_MAX) { @@ -371,7 +445,15 @@ C_SIMPLE_HTTP_ParsedConfig c_simple_http_parse_config( continue; } else if (c == '"') { ++double_quote_count; - SINGLE_QUOTE_DECREMENT(); + if (internal_single_quote_decrement(&single_quote_count, + &state, + key_buf, + &key_idx, + value_buf, + &value_idx, + &config)) { + return config; + } if (((state & 0xC) == 0x8 || (state & 0xC) == 0) && double_quote_count >= C_SIMPLE_HTTP_QUOTE_COUNT_MAX) { @@ -386,8 +468,24 @@ C_SIMPLE_HTTP_ParsedConfig c_simple_http_parse_config( } continue; } else { - SINGLE_QUOTE_DECREMENT(); - DOUBLE_QUOTE_DECREMENT(); + if (internal_single_quote_decrement(&single_quote_count, + &state, + key_buf, + &key_idx, + value_buf, + &value_idx, + &config)) { + return config; + } + if (internal_double_quote_decrement(&double_quote_count, + &state, + key_buf, + &key_idx, + value_buf, + &value_idx, + &config)) { + return config; + } } } if ((state & 1) == 0) { @@ -426,7 +524,20 @@ C_SIMPLE_HTTP_ParsedConfig c_simple_http_parse_config( return config; } } else { - CHECK_ADD_VALUE() + if (internal_check_add_value(&state, + key_buf, + &key_idx, + value_buf, + &value_idx, + &config, + separating_key, + required_names, + separating_key_size, + paths, + ¤t_separating_key_value, + ¤t_separating_key_value_size)) { + return config; + } } } } @@ -435,7 +546,20 @@ C_SIMPLE_HTTP_ParsedConfig c_simple_http_parse_config( if ((state & 0x1) == 1 && (state & 0xC) == 0 && value_idx != 0) { // Leftover "value" not added yet. - CHECK_ADD_VALUE() + if (internal_check_add_value(&state, + key_buf, + &key_idx, + value_buf, + &value_idx, + &config, + separating_key, + required_names, + separating_key_size, + paths, + ¤t_separating_key_value, + ¤t_separating_key_value_size)) { + return config; + } } if (!current_separating_key_value) {