1 #include <assert.h>
2 #include <errno.h>
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <strings.h>
6 #include <string.h>
7 #include <ctype.h>
8
9 #ifndef bool
10 #define bool char
11 #define false 0
12 #define true 1
13 #endif
14
15 #include <memcached/config_parser.h>
16 #include <memcached/util.h>
17
18 static int read_config_file(const char *fname, struct config_item items[],
19 FILE *error);
20
21 /**
22 * Copy a string and trim of leading and trailing white space characters.
23 * Allow the user to escape out the stop character by putting a backslash before
24 * the character.
25 * @param dest where to store the result
26 * @param size size of the result buffer
27 * @param src where to copy data from
28 * @param end the last character parsed is returned here
29 * @param stop the character to stop copying.
30 * @return 0 if success, -1 otherwise
31 */
trim_copy(char * dest,size_t size,const char * src,const char ** end,char stop)32 static int trim_copy(char *dest, size_t size, const char *src,
33 const char **end, char stop) {
34 while (isspace(*src)) {
35 ++src;
36 }
37 size_t n = 0;
38 bool escape = false;
39 int ret = 0;
40
41 /* Find the last non-escaped non-space character */
42 const char *lastchar = src + strlen(src) - 1;
43 while (lastchar > src && isspace(*lastchar)) {
44 lastchar--;
45 }
46 if (lastchar < src || *lastchar == '\\') {
47 lastchar++;
48 }
49 assert(lastchar >= src);
50
51 do {
52 if ((*dest = *src) == '\\') {
53 escape = true;
54 } else {
55 escape = false;
56 ++dest;
57 }
58 ++n;
59 ++src;
60
61 } while (!(n == size || src > lastchar || ((*src == stop) && !escape) || *src == '\0'));
62 *end = src;
63
64 if (n == size) {
65 --dest;
66 ret = -1;
67 }
68 *dest = '\0';
69
70 return ret;
71 }
72
73
parse_config(const char * str,struct config_item * items,FILE * error)74 int parse_config(const char *str, struct config_item *items, FILE *error) {
75 int ret = 0;
76 const char *ptr = str;
77
78 while (*ptr != '\0') {
79 while (isspace(*ptr)) {
80 ++ptr;
81 }
82 if (*ptr == '\0') {
83 /* end of parameters */
84 return 0;
85 }
86
87 const char *end;
88 char key[80];
89 if (trim_copy(key, sizeof(key), ptr, &end, '=') == -1) {
90 if (error != NULL) {
91 fprintf(error, "ERROR: Invalid key, starting at: <%s>\n", ptr);
92 }
93 return -1;
94 }
95
96 ptr = end + 1;
97 char value[1024];
98 if (trim_copy(value, sizeof(value), ptr, &end, ';') == -1) {
99 if (error != NULL) {
100 fprintf(error, "ERROR: Invalid value, starting at: <%s>\n", ptr);
101 }
102 return -1;
103 }
104 if (*end == ';') {
105 ptr = end + 1;
106 } else {
107 ptr = end;
108 }
109
110 int ii = 0;
111 while (items[ii].key != NULL) {
112 if (strcmp(key, items[ii].key) == 0) {
113 if (items[ii].found) {
114 if (error != NULL) {
115 fprintf(error, "WARNING: Found duplicate entry for \"%s\"\n",
116 items[ii].key);
117 }
118 }
119
120 switch (items[ii].datatype) {
121 case DT_SIZE:
122 {
123 const char *sfx = "kmgt";
124 int multiplier = 1;
125 int m = 1;
126 for (const char *p = sfx; *p != '\0'; ++p) {
127 m *= 1024;
128 char *ptr = strchr(value, *p);
129 if (ptr == NULL) {
130 ptr = strchr(value, toupper(*p));
131 }
132 if (ptr != NULL) {
133 multiplier = m;
134 *ptr = '\0';
135 break;
136 }
137 }
138
139 uint64_t val;
140 if (safe_strtoull(value, &val)) {
141 *items[ii].value.dt_size = (size_t)(val * multiplier);
142 items[ii].found = true;
143 } else {
144 ret = -1;
145 }
146 }
147 break;
148 case DT_FLOAT:
149 {
150 float val;
151 if (safe_strtof(value, &val)) {
152 *items[ii].value.dt_float = val;
153 items[ii].found = true;
154 } else {
155 ret = -1;
156 }
157 }
158 break;
159 case DT_STRING:
160 *items[ii].value.dt_string = strdup(value);
161 items[ii].found = true;
162 break;
163 case DT_BOOL:
164 if (strcasecmp(value, "true") == 0 || strcasecmp(value, "on") == 0) {
165 *items[ii].value.dt_bool = true;
166 items[ii].found = true;
167 } else if (strcasecmp(value, "false") == 0 || strcasecmp(value, "off") == 0) {
168 *items[ii].value.dt_bool = false;
169 items[ii].found = true;
170 } else {
171 ret = -1;
172 }
173 break;
174 case DT_CONFIGFILE:
175 {
176 int r = read_config_file(value, items, error);
177 if (r != 0) {
178 ret = r;
179 }
180 }
181 break;
182 default:
183 /* You need to fix your code!!! */
184 abort();
185 }
186 if (ret == -1) {
187 if (error != NULL) {
188 fprintf(error, "Invalid entry, Key: <%s> Value: <%s>\n",
189 key, value);
190 }
191 return ret;
192 }
193 break;
194 }
195 ++ii;
196 }
197
198 if (items[ii].key == NULL) {
199 if (error != NULL) {
200 fprintf(error, "Unsupported key: <%s>\n", key);
201 }
202 ret = 1;
203 }
204 }
205 return ret;
206 }
207
read_config_file(const char * fname,struct config_item items[],FILE * error)208 static int read_config_file(const char *fname, struct config_item items[],
209 FILE *error) {
210 FILE *fp = fopen(fname, "r");
211 if (fp == NULL) {
212 if (error != NULL) {
213 fprintf(error, "Failed to open file: %s\n", fname);
214 }
215 return -1;
216 }
217
218 int ret = 0;
219 char line[1024];
220 while (fgets(line, sizeof(line), fp) != NULL && ret != -1L) {
221 if (line[0] == '#') {
222 /* Ignore comment line */
223 continue;
224 }
225
226 int r = parse_config(line, items, error);
227 if (r != 0) {
228 ret = r;
229 }
230 }
231
232 (void)fclose(fp);
233
234 return ret;
235 }
236