Refactor config.c to change c-macros to functions
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 1m56s

Resolves #9
This commit is contained in:
Stephen Seo 2024-11-08 14:00:36 +09:00
parent 80b1d49e74
commit 95aef64618

View file

@ -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,
&current_separating_key_value,
&current_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,
&current_separating_key_value,
&current_separating_key_value_size)) {
return config;
}
}
if (!current_separating_key_value) {