aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/config.c13
-rw-r--r--lib/strlist.c36
-rw-r--r--tests/test_config_read.c80
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