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;
48 static line_t *settings;
50 /* Parsing and saving */
54 static char *lastgroup;
55 static char *lastname;
58 /* Helper functions */
59 static void conf_error(const char *value)
62 error("invalid value '%s' for %s.%s.%s",
63 value, lastgroup, lastname, lastkey);
65 error("invalid value '%s' for %s.%s",
66 value, lastgroup, lastkey);
69 static void set_value(const char *group, const char *name,
70 const char *key, const char *value)
73 line_t *groupend = NULL;
74 line_t *fileend = NULL;
76 /* Queue a save next time around */
79 /* Look though for existing items */
80 for (line_t *line = settings; line; line = line->next) {
81 /* Search for the correct group */
83 ingroup = match(line->group, group) &&
84 match(line->name, name);
86 if (ingroup && match(line->key, key)) {
88 line->value = strcopy(value);
92 /* Record positions for new keys */
93 if (ingroup && line->key && line->value)
99 /* Create new items */
101 /* Append to group */
102 line_t *line = new0(line_t);
103 line->key = strcopy(key);
104 line->value = strcopy(value);
105 line->next = groupend->next;
106 groupend->next = line;
107 } else if (fileend) {
108 /* Create new group */
109 line_t *blank = new0(line_t);
110 line_t *header = new0(line_t);
111 line_t *line = new0(line_t);
112 fileend->next = blank;
113 blank->next = header;
114 header->group = strcopy(group);
115 header->name = strcopy(name);
117 line->key = strcopy(key);
118 line->value = strcopy(value);
120 /* Create new file */
121 line_t *header = new0(line_t);
122 line_t *line = new0(line_t);
124 header->group = strcopy(group);
125 header->name = strcopy(name);
127 line->key = strcopy(key);
128 line->value = strcopy(value);
132 /* Parsing functions */
133 static char *scan_white(char *src)
135 while (*src == ' ' || *src == '\t')
140 static char *scan_word(char *dst, char *src)
142 while (islower(*src))
148 static char *scan_value(char *dst, char *src)
151 while (*src != '#' && *src != '\0')
155 while (dst > start && (*dst == ' ' || *dst == '\t'));
159 static char *scan_quote(char *dst, char *src)
161 while (*src != '"' && *src != '\0') {
164 case '\0': *dst++ = '\0'; break;
165 case '\\': *dst++ = '\\'; src++; break;
166 case 't': *dst++ = '\t'; src++; break;
167 case 'r': *dst++ = '\r'; src++; break;
168 case 'n': *dst++ = '\n'; src++; break;
169 default: *dst++ = *src; src++; break;
181 static void parse_line(line_t *dst, char *text, const char *file, int lnum)
183 char *chr = scan_white(text);
185 dst->group = malloc(strlen(chr)+1);
186 chr = scan_white(chr+1);
187 chr = scan_word(dst->group, chr);
188 chr = scan_white(chr);
190 dst->name = malloc(strlen(chr)+1);
191 chr = scan_quote(dst->name, chr+1);
192 chr = scan_white(chr);
195 error("parsing group at %s:%d,%d -- '%.8s..'",
196 file, lnum, 1+chr-text, chr);
198 else if (islower(*chr)) {
199 dst->key = malloc(strlen(chr)+1);
200 chr = scan_white(chr);
201 chr = scan_word(dst->key, chr);
202 chr = scan_white(chr);
204 error("parsing key at %s:%d,%d -- '%.8s..'",
205 file, lnum, 1+chr-text, chr);
208 chr = scan_white(chr);
209 dst->value = malloc(strlen(chr)+1);
211 chr = scan_quote(dst->value, chr+1);
213 chr = scan_value(dst->value, chr);
214 } else if (*chr != '#' && *chr != '\n' && *chr != '\0') {
215 error("parsing file at %s:%d,%d -- '%.8s..'",
216 file, lnum, 1+chr-text, chr);
220 /* File I/O functions */
221 static void conf_load(const char *path, parser_t parser)
224 char *group = NULL, *name = NULL;
226 /* read the whole file */
228 char *start = read_file(path, &len);
235 for (lnum = 1, sol = start; sol < (start+len); lnum++, sol = eol+1) {
236 eol = strchr(sol, '\n') ?: &start[len];
239 /* update current group info */
240 line_t *line = new0(line_t);
241 line->text = strcopy(sol);
242 parse_line(line, sol, path, lnum);
243 group = line->group ? line->group : group;
244 name = line->group ? line->name : name;
246 /* Parse dynamic groups */
247 if (line->group && line->name) {
249 lastgroup = line->group;
250 lastname = line->name;
252 parser(line->group, line->name, "", "");
254 error("unknown group: line %d - [%s \"%s\"]",
255 lnum, group, name ?: "");
258 /* Parse static key/value pairs */
259 if (group && line->key && line->value) {
264 parser(group, name?:"", line->key, line->value);
266 error("unknown setting: line %d - %s.%s.%s = '%s'\n",
267 lnum, group, name?:"", line->key, line->value);
271 debug("parse: %s.%s.%s = '%s'", group, name, line->key, line->value);
273 /* save line formatting for the next write */
284 void conf_save(const char *path)
286 FILE *fd = fopen(path, "wt+");
290 for (line_t *cur = settings; cur; cur = cur->next) {
291 /* Output existing items */
292 if (cur->text && !cur->dirty)
293 fprintf(fd, "%s\n", cur->text);
295 /* Output group and name headers */
296 else if (cur->group && cur->name)
297 fprintf(fd, "[%s \"%s\"]\n", cur->group, cur->name);
299 /* Output group only headers */
301 fprintf(fd, "[%s]\n", cur->group);
303 /* Output key/value pairs - todo: add quotes */
304 else if (cur->key && cur->value)
305 fprintf(fd, "\t%s = %s\n", cur->key, cur->value);
307 /* Output blank lines */
316 void conf_setup(const char *name, parser_t parser)
318 const char *home = getenv("HOME");
319 filename = alloc0(strlen(home) + 1 + strlen(name) + 1);
320 sprintf(filename, "%s/%s", home, name);
321 conf_load(filename, parser);
324 void conf_start(void)
337 int get_enum(const char *value, const char **map, int n)
340 for (int i = 0; i < n; i++)
341 if (match(map[i], value))
347 int get_bool(const char *value)
350 return get_enum(value, booleans, N_ELEMENTS(booleans));
353 int get_number(const char *value)
357 int rval = atoi(value);
363 char *get_string(const char *value)
369 char *get_name(const char *name)
376 void set_enum(const char *group, const char *name,
377 const char *key, int value,
378 const char **map, int n)
380 if (value >= 0 && value < n)
381 set_value(group, name, key, map[value]);
384 void set_bool(const char *group, const char *name,
385 const char *key, int value)
387 set_enum(group, name, key, value,
388 booleans, N_ELEMENTS(booleans));
391 void set_number(const char *group, const char *name,
392 const char *key, int value)
395 snprintf(buf, sizeof(buf), "%d", value);
396 set_value(group, name, key, buf);
399 void set_string(const char *group, const char *name,
400 const char *key, const char *value)
402 set_value(group, name, key, value);
405 void set_name(const char *group, const char *name, const char *value)
407 for (line_t *line = settings; line; line = line->next) {
408 if (match(line->group, group) &&
409 match(line->name, name)) {
411 line->name = strcopy(value);
419 static const char *colors[] = {"red", "green", "blue"};
421 static int test_bin = 1;
422 static int test_num = 42;
423 static char *test_str = "str";
424 static int test_clr = 0;
426 static void test_parser(const char *group, const char *name,
427 const char *key, const char *value)
429 if (match(group, "test")) {
430 if (match(key, "bin"))
431 test_bin = get_bool(value);
432 else if (match(key, "clr"))
433 test_clr = get_enum(value, colors, N_ELEMENTS(colors));
434 else if (match(key, "num"))
435 test_num = get_number(value);
436 else if (match(key, "str"))
437 test_str = get_string(value);
443 printf("conf_test:\n");
445 /* Read values from a file */
446 conf_load("data/test.rc", test_parser);
449 printf(" bin: %-8d\n", test_bin);
450 printf(" clr: %-8s\n", colors[test_clr]);
451 printf(" num: %-8d\n", test_num);
452 printf(" str: %-8s\n", test_str);
455 set_bool ("test", 0, "bin", 1);
456 set_enum ("test", 0, "clr", 2, colors, N_ELEMENTS(colors));
457 set_number("test", 0, "num", -9999);
458 set_string("test", 0, "str", "hello");
460 set_string("test", 0, "new", "new0");
461 set_string("test", "new", "new", "new1");
462 set_string("new", 0, "newa", "new2");
463 set_string("new", 0, "newb", "new3");
464 set_string("new", "new", "newa", "new4");
465 set_string("new", "new", "newb", "new5");
467 set_name ("func", "name", "test");
469 /* Write back to file */
470 conf_save("data/test_out.rc");