1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 #include "config.h"
3 
4 #include <cJSON.h>
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <errno.h>
8 #include <stdint.h>
9 #include <string.h>
10 #include <strings.h>
11 #include <sys/stat.h>
12 
13 #include "config_util.h"
14 
config_strerror(const char *file, config_error_t err)15 char *config_strerror(const char *file, config_error_t err)
16 {
17     char buffer[1024];
18     switch (err) {
19     case CONFIG_SUCCESS:
20         return strdup("success");
21     case CONFIG_INVALID_ARGUMENTS:
22         return strdup("Invalid arguments supplied to config_load_file");
23     case CONFIG_NO_SUCH_FILE:
24         snprintf(buffer, sizeof(buffer), "Failed to look up \"%s\": %s",
25                  file, strerror(errno));
26         return strdup(buffer);
27     case CONFIG_OPEN_FAILED:
28         snprintf(buffer, sizeof(buffer), "Failed to open \"%s\": %s",
29                  file, strerror(errno));
30         return strdup(buffer);
31     case CONFIG_MALLOC_FAILED:
32         return strdup("Failed to allocate memory");
33     case CONFIG_IO_ERROR:
34         snprintf(buffer, sizeof(buffer), "Failed to read \"%s\": %s",
35                  file, strerror(errno));
36         return strdup(buffer);
37     case CONFIG_PARSE_ERROR:
38         snprintf(buffer, sizeof(buffer),
39                  "Failed to parse JSON in \"%s\"\nMost likely syntax "
40                 "error in the file.", file);
41         return strdup(buffer);
42 
43     default:
44         snprintf(buffer, sizeof(buffer), "Unknown error code %u", err);
45         return strdup(buffer);
46     }
47 }
48 
spool(FILE *fp, char *dest, size_t size)49 static int spool(FILE *fp, char *dest, size_t size)
50 {
51     size_t offset = 0;
52 
53     if (getenv("CONFIG_TEST_MOCK_SPOOL_FAILURE") != NULL) {
54         return -1;
55     }
56 
57     clearerr(fp);
58     while (offset < size) {
59         offset += fread(dest + offset, 1, size - offset, fp);
60         if (ferror(fp)) {
61             return -1;
62         }
63     }
64 
65     return 0;
66 }
67 
config_malloc(size_t size)68 static void *config_malloc(size_t size) {
69     if (getenv("CONFIG_TEST_MOCK_MALLOC_FAILURE") != NULL) {
70         return NULL;
71     } else {
72         return malloc(size);
73     }
74 }
75 
config_load_file(const char *file, cJSON **json)76 config_error_t config_load_file(const char *file, cJSON **json)
77 {
78     FILE *fp;
79     struct stat st;
80     char *data;
81 
82     if (file == NULL || json == NULL) {
83         return CONFIG_INVALID_ARGUMENTS;
84     }
85 
86     if (stat(file, &st) == -1) {
87         return CONFIG_NO_SUCH_FILE;
88     }
89 
90     fp = fopen(file, "rb");
91     if (fp == NULL) {
92         return CONFIG_OPEN_FAILED;
93     }
94 
95     data = config_malloc(st.st_size + 1);
96     if (data == NULL) {
97         fclose(fp);
98         return CONFIG_MALLOC_FAILED;
99     }
100 
101     if (spool(fp, data, st.st_size) == -1) {
102         free(data);
103         fclose(fp);
104         return CONFIG_IO_ERROR;
105     }
106 
107     fclose(fp);
108     data[st.st_size] = 0;
109 
110     *json = cJSON_Parse(data);
111     free(data);
112     if (*json == NULL) {
113         return CONFIG_PARSE_ERROR;
114     }
115 
116     return CONFIG_SUCCESS;
117 }
118