2 * Copyright (C) 2013 Andy Spencer <andy753421@gmail.com>
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27 /* Saved formattnig struct */
28 typedef struct line_t {
39 static const char *booleans[] = {
45 static char *filename;
46 static parser_t parser;
51 static line_t *settings;
53 /* Parsing and saving */
57 static char *lastgroup;
58 static char *lastname;
61 /* Helper functions */
62 static void conf_error(const char *value)
65 error("invalid value '%s' for %s.%s.%s",
66 value, lastgroup, lastname, lastkey);
68 error("invalid value '%s' for %s.%s",
69 value, lastgroup, lastkey);
72 static void set_value(const char *group, const char *name,
73 const char *key, const char *value)
76 line_t *groupend = NULL;
77 line_t *fileend = NULL;
79 /* Queue a save next time around */
82 /* Look though for existing items */
83 for (line_t *line = settings; line; line = line->next) {
84 /* Search for the correct group */
86 ingroup = match(line->group, group) &&
87 match(line->name, name);
89 if (ingroup && match(line->key, key)) {
91 line->value = strcopy(value);
95 /* Record positions for new keys */
96 if (ingroup && line->key && line->value)
102 /* Create new items */
104 /* Append to group */
105 line_t *line = new0(line_t);
106 groupend->next = line;
107 line->key = strcopy(key);
108 line->value = strcopy(value);
109 line->next = groupend->next;
110 } else if (fileend) {
111 /* Create new group */
112 line_t *blank = new0(line_t);
113 line_t *header = new0(line_t);
114 line_t *line = new0(line_t);
115 fileend->next = blank;
116 blank->next = header;
117 header->group = strcopy(group);
118 header->name = strcopy(name);
120 line->key = strcopy(key);
121 line->value = strcopy(value);
123 /* Create new file */
124 line_t *header = new0(line_t);
125 line_t *line = new0(line_t);
127 header->group = strcopy(group);
128 header->name = strcopy(name);
130 line->key = strcopy(key);
131 line->value = strcopy(value);
135 /* Parsing functions */
136 static char *scan_white(char *src)
138 while (*src == ' ' || *src == '\t')
143 static char *scan_word(char *dst, char *src)
145 while (islower(*src))
151 static char *scan_value(char *dst, char *src)
154 while (*src != '#' && *src != '\0')
158 while (dst > start && (*dst == ' ' || *dst == '\t'));
162 static char *scan_quote(char *dst, char *src)
164 while (*src != '"' && *src != '\0') {
167 case '\0': *dst++ = '\0'; break;
168 case '\\': *dst++ = '\\'; src++; break;
169 case 't': *dst++ = '\t'; src++; break;
170 case 'r': *dst++ = '\r'; src++; break;
171 case 'n': *dst++ = '\n'; src++; break;
172 default: *dst++ = *src; src++; break;
184 static void parse_line(line_t *dst, char *text, const char *file, int lnum)
186 char *chr = scan_white(text);
188 dst->group = malloc(strlen(chr)+1);
189 chr = scan_white(chr+1);
190 chr = scan_word(dst->group, chr);
191 chr = scan_white(chr);
193 dst->name = malloc(strlen(chr)+1);
194 chr = scan_quote(dst->name, chr+1);
195 chr = scan_white(chr);
198 error("parsing group at %s:%d,%d -- '%.8s..'",
199 file, lnum, 1+chr-text, chr);
201 else if (islower(*chr)) {
202 dst->key = malloc(strlen(chr)+1);
203 chr = scan_white(chr);
204 chr = scan_word(dst->key, chr);
205 chr = scan_white(chr);
207 error("parsing key at %s:%d,%d -- '%.8s..'",
208 file, lnum, 1+chr-text, chr);
211 chr = scan_white(chr);
212 dst->value = malloc(strlen(chr)+1);
214 chr = scan_quote(dst->value, chr+1);
216 chr = scan_value(dst->value, chr);
217 } else if (*chr != '#' && *chr != '\n' && *chr != '\0') {
218 error("parsing file at %s:%d,%d -- '%.8s..'",
219 file, lnum, 1+chr-text, chr);
223 /* File I/O functions */
224 static void conf_load(const char *path, parser_t parser)
227 char *group = NULL, *name = NULL;
229 /* read the whole file */
231 char *start = read_file(path, &len);
238 for (lnum = 1, sol = start; sol < (start+len); lnum++, sol = eol+1) {
239 eol = strchr(sol, '\n') ?: &start[len];
242 /* update current group info */
243 line_t *line = new0(line_t);
244 line->text = strcopy(sol);
245 parse_line(line, sol, path, lnum);
246 group = line->group ? line->group : group;
247 name = line->group ? line->name : name;
249 /* Parse dynamic groups */
250 if (line->group && line->name) {
252 lastgroup = line->group;
253 lastname = line->name;
255 parser(line->group, line->name, "", "");
257 error("unknown group: line %d - [%s \"%s\"]",
258 lnum, group, name ?: "");
261 /* Parse static key/value pairs */
262 if (group && line->key && line->value) {
267 parser(group, name?:"", line->key, line->value);
269 error("unknown setting: line %d - %s.%s.%s = '%s'\n",
270 lnum, group, name?:"", line->key, line->value);
274 printf("parse: %s.%s.%s = '%s'\n", group, name, line->key, line->value);
276 /* save line formatting for the next write */
287 void conf_save(const char *path)
289 FILE *fd = fopen(path, "wt+");
293 for (line_t *cur = settings; cur; cur = cur->next) {
294 /* Output existing items */
295 if (cur->text && !cur->dirty)
296 fprintf(fd, "%s\n", cur->text);
298 /* Output group and name headers */
299 else if (cur->group && cur->name)
300 fprintf(fd, "[%s \"%s\"]\n", cur->group, cur->name);
302 /* Output group only headers */
304 fprintf(fd, "[%s]\n", cur->group);
306 /* Output key/value pairs - todo: add quotes */
307 else if (cur->key && cur->value)
308 fprintf(fd, "\t%s = %s\n", cur->key, cur->value);
310 /* Output blank lines */
319 void conf_setup(int _argc, char **_argv, const char *_name, parser_t _parser)
321 const char *home = getenv("HOME");
322 filename = alloc0(strlen(home) + 1 + strlen(_name) + 1);
323 sprintf(filename, "%s/%s", home, _name);
332 conf_load(filename, parser);
344 int get_enum(const char *value, const char **map, int n)
347 for (int i = 0; i < n; i++)
348 if (match(map[i], value))
354 int get_bool(const char *value)
357 return get_enum(value, booleans, N_ELEMENTS(booleans));
360 int get_number(const char *value)
364 int rval = atoi(value);
370 char *get_string(const char *value)
376 char *get_name(const char *name)
383 void set_enum(const char *group, const char *name,
384 const char *key, int value,
385 const char **map, int n)
387 if (value >= 0 && value < n)
388 set_value(group, name, key, map[value]);
391 void set_bool(const char *group, const char *name,
392 const char *key, int value)
394 set_enum(group, name, key, value,
395 booleans, N_ELEMENTS(booleans));
398 void set_number(const char *group, const char *name,
399 const char *key, int value)
402 snprintf(buf, sizeof(buf), "%d", value);
403 set_value(group, name, key, buf);
406 void set_string(const char *group, const char *name,
407 const char *key, const char *value)
409 set_value(group, name, key, value);
412 void set_name(const char *group, const char *name, const char *value)
414 for (line_t *line = settings; line; line = line->next) {
415 if (match(line->group, group) &&
416 match(line->name, name)) {
418 line->name = strcopy(value);
426 static const char *colors[] = {"red", "green", "blue"};
428 static int test_bin = 1;
429 static int test_num = 42;
430 static char *test_str = "str";
431 static int test_clr = 0;
433 static void test_parser(const char *group, const char *name,
434 const char *key, const char *value)
436 if (match(group, "test")) {
437 if (match(key, "bin"))
438 test_bin = get_bool(value);
439 else if (match(key, "clr"))
440 test_clr = get_enum(value, colors, N_ELEMENTS(colors));
441 else if (match(key, "num"))
442 test_num = get_number(value);
443 else if (match(key, "str"))
444 test_str = get_string(value);
450 printf("conf_test:\n");
452 /* Read values from a file */
453 conf_load("data/test.rc", test_parser);
456 printf(" bin: %-8d\n", test_bin);
457 printf(" clr: %-8s\n", colors[test_clr]);
458 printf(" num: %-8d\n", test_num);
459 printf(" str: %-8s\n", test_str);
462 set_bool ("test", 0, "bin", 1);
463 set_enum ("test", 0, "clr", 2, colors, N_ELEMENTS(colors));
464 set_number("test", 0, "num", -9999);
465 set_string("test", 0, "str", "hello");
467 set_string("test", 0, "new", "new0");
468 set_string("test", "new", "new", "new1");
469 set_string("new", 0, "newa", "new2");
470 set_string("new", 0, "newb", "new3");
471 set_string("new", "new", "newa", "new4");
472 set_string("new", "new", "newb", "new5");
474 set_name ("func", "name", "test");
476 /* Write back to file */
477 conf_save("data/test_out.rc");