diff options
-rw-r--r-- | lib/config.c | 13 | ||||
-rw-r--r-- | lib/strlist.c | 36 | ||||
-rw-r--r-- | tests/test_config_read.c | 80 |
3 files changed, 120 insertions, 9 deletions
diff --git a/lib/config.c b/lib/config.c index 292c633..0fb17e8 100644 --- a/lib/config.c +++ b/lib/config.c @@ -74,8 +74,9 @@ ConfigItem **config_read(const char *filename) { // Get a pointer to the key pair separator char *sep_pos = strchr(lptr, sep_ch); if (!sep_pos) { - printf("invalid entry on line %zu: missing '%s': '%s'\n", record, sep_str, lptr); - continue; + fprintf(stderr, "invalid entry on line %zu: missing '%s': '%s'\n", record, sep_str, lptr); + config_free(config); + return NULL; } // These values are approximations. The real length(s) are recorded using strlen below. @@ -126,8 +127,9 @@ ConfigItem **config_read(const char *filename) { // Populate the value, and ignore any inline comments while (*lptr) { if (*lptr == '#' || *lptr == ';') { - // strip trailing whitespace where the comment is and stop processing - value = strip(value); + // `value` was populated up to the comment. + // now strip whitespace from the string `value` actually points to + value_orig = strip(value_orig); break; } *value++ = *lptr++; @@ -168,6 +170,9 @@ ConfigItem **config_read(const char *filename) { * @param item `ConfigItem` array */ void config_free(ConfigItem **item) { + if (item == NULL) { + return; + } for (size_t i = 0; item[i] != NULL; i++) { free(item[i]->key); free(item[i]->value); diff --git a/lib/strlist.c b/lib/strlist.c index 1cab324..5c1f0e2 100644 --- a/lib/strlist.c +++ b/lib/strlist.c @@ -10,6 +10,9 @@ * @param pStrList `StrList` */ void strlist_free(StrList *pStrList) { + if (pStrList == NULL) { + return; + } for (size_t i = 0; i < pStrList->num_inuse; i++) { free(pStrList->data[i]); } @@ -23,7 +26,13 @@ void strlist_free(StrList *pStrList) { * @param str */ void strlist_append(StrList *pStrList, char *str) { - char **tmp = realloc(pStrList->data, (pStrList->num_alloc + 1) * sizeof(char *)); + char **tmp = NULL; + + if (pStrList == NULL) { + return; + } + + tmp = realloc(pStrList->data, (pStrList->num_alloc + 1) * sizeof(char *)); if (tmp == NULL) { strlist_free(pStrList); perror("failed to append to array"); @@ -43,7 +52,13 @@ void strlist_append(StrList *pStrList, char *str) { * @param pStrList2 `StrList` */ void strlist_append_strlist(StrList *pStrList1, StrList *pStrList2) { - size_t count = strlist_count(pStrList2); + size_t count = 0; + + if (pStrList1 == NULL || pStrList2 == NULL) { + return; + } + + count = strlist_count(pStrList2); for (size_t i = 0; i < count; i++) { char *item = strlist_item(pStrList2, i); strlist_append(pStrList1, item); @@ -97,7 +112,13 @@ static int _strlist_dsc_cmpfn(const void *a, const void *b) { * @param mode Available modes: `STRLIST_DEFAULT` (alphabetic), `STRLIST_ASC` (ascending), `STRLIST_DSC` (descending) */ void strlist_sort(StrList *pStrList, unsigned int mode) { + // TODO: use strsort_array() instead instead of duplicating effort void *fn = NULL; + + if (pStrList == NULL) { + return; + } + switch (mode) { case STRLIST_ASC: fn = _strlist_asc_cmpfn; @@ -121,8 +142,13 @@ void strlist_sort(StrList *pStrList, unsigned int mode) { void strlist_reverse(StrList *pStrList) { char *tmp = NULL; size_t i = 0; - size_t j = pStrList->num_inuse - 1; + size_t j = 0; + + if (pStrList == NULL) { + return; + } + j = pStrList->num_inuse - 1; for (i = 0; i < j; i++) { tmp = pStrList->data[i]; pStrList->data[i] = pStrList->data[j]; @@ -149,7 +175,7 @@ size_t strlist_count(StrList *pStrList) { void strlist_set(StrList *pStrList, size_t index, char *value) { char *tmp = NULL; char *item = NULL; - if (index > strlist_count(pStrList)) { + if (pStrList == NULL || index > strlist_count(pStrList)) { return; } if ((item = strlist_item(pStrList, index)) == NULL) { @@ -176,7 +202,7 @@ void strlist_set(StrList *pStrList, size_t index, char *value) { * @return string */ char *strlist_item(StrList *pStrList, size_t index) { - if (index > strlist_count(pStrList)) { + if (pStrList == NULL || index > strlist_count(pStrList)) { return NULL; } return pStrList->data[index]; diff --git a/tests/test_config_read.c b/tests/test_config_read.c new file mode 100644 index 0000000..f932b5d --- /dev/null +++ b/tests/test_config_read.c @@ -0,0 +1,80 @@ +#include "spm.h" +#include "framework.h" + +const char *mockConfig[] = { + (const char *){"\n" // empty line + "; comment_semicolon\n" + "# comment_hash\n" + "string_unquoted = unquoted string\n" + "string_quoted = \"quoted string\"\n" + "inline_comment_semicolon = inline semicolon comment ; inline_comment_semicolon\n" + "inline_comment_hash = inline hash comment # inline_comment_hash\n" + "whitespace = \t ignored\t \n" + "integer = 1\n" + "decimal = 1.0\n" + ""}, + (const char *){ + "oh\n" + "no\n" + "=\n" + "extreme failure\n", + }, + NULL, // end +}; + +#define SETARG_UINT(ARGINDEX, VALUE) .arg[ARGINDEX].unsigned_integer = VALUE +#define GETARG_UINT(CASE, ARGINDEX) CASE.arg[ARGINDEX].unsigned_integer + +const char *testFmt = "case '%s': returned '%s', expected '%s'\n"; +const char *testSizesFmt = "case '%s': returned '%zu', expected '%zu'\n"; +struct TestCase testCase[] = { + {.caseValue.sptr = "string_unquoted", .truthValue.sptr = "unquoted string", SETARG_UINT(0, 15), SETARG_UINT(1, 15)}, + {.caseValue.sptr = "string_quoted", .truthValue.sptr = "quoted string", SETARG_UINT(0, 13), SETARG_UINT(1, 13)}, + {.caseValue.sptr = "inline_comment_semicolon", .truthValue.sptr = "inline semicolon comment", SETARG_UINT(0, 24), SETARG_UINT(1, 24)}, + {.caseValue.sptr = "inline_comment_hash", .truthValue.sptr = "inline hash comment", SETARG_UINT(0, 19), SETARG_UINT(1, 19)}, + {.caseValue.sptr = "whitespace", .truthValue.sptr = "ignored", SETARG_UINT(0, 10), SETARG_UINT(1, 7)}, + {.caseValue.sptr = "integer", .truthValue.sptr = "1", SETARG_UINT(0, 7), SETARG_UINT(1, 1)}, + {.caseValue.sptr = "decimal", .truthValue.sptr = "1.0", SETARG_UINT(0, 7), SETARG_UINT(1, 3)}, + {.caseValue.sptr = "missing", .truthValue.sptr = NULL, SETARG_UINT(0, 0), SETARG_UINT(1, 0)}, +}; +size_t numCases = sizeof(testCase) / sizeof(struct TestCase); + +int main(int argc, char *argv[]) { + ConfigItem **config = NULL; + char filename[NAME_MAX]; + + memset(filename, '\0', sizeof(filename)); + sprintf(filename, "%s.%s.mock", basename(__FILE__), __FUNCTION__); + + mock(filename, (void *) mockConfig[0], sizeof(char), strlen(mockConfig[0])); + config = config_read(filename); + + for (size_t i = 0; i < numCases; i++) { + ConfigItem *item = NULL; + const char *caseValue = testCase[i].caseValue.sptr; + const char *truthValue = testCase[i].truthValue.sptr; + + item = config_get(config, caseValue); + if (testCase[i].truthValue.sptr == NULL) { + myassert(item == NULL, "item was not NULL\n"); + continue; + } + myassert(strcmp(caseValue, item->key) == 0, testFmt, caseValue, item->key, caseValue); + myassert(strcmp(truthValue, item->value) == 0, testFmt, caseValue, item->value, truthValue); + myassert(GETARG_UINT(testCase[i], 0) == item->key_length, testSizesFmt, caseValue, item->key_length, GETARG_UINT(testCase[i], 0)); + myassert(GETARG_UINT(testCase[i], 1) == item->value_length, testSizesFmt, caseValue, item->value_length, GETARG_UINT(testCase[i], 1)); + } + + config_free(config); + unlink(filename); + + // Now throw some bad input data at it + mock(filename, (void *) mockConfig[1], sizeof(char), strlen(mockConfig[1])); + config = config_read(filename); + myassert(config == NULL, "input data was invalid and config_read(\"%s\") did not return NULL.", filename); + + config_free(config); + unlink(filename); + + return 0; +}
\ No newline at end of file |