blob: 1d88eed07aabf57c042fce82674962b3aa7bd3de [file] [log] [blame]
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -05001/*
2 * Copyright © 2011 Intel Corporation
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission. The copyright holders make no representations
11 * about the suitability of this software for any purpose. It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
Ossama Othmana50e6e42013-05-14 09:48:26 -070023#define _GNU_SOURCE /* for stchrnul() */
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -050024#include <string.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <assert.h>
Kristian Høgsberg3d890492012-08-03 21:56:41 -040028#include <ctype.h>
Ossama Othmana50e6e42013-05-14 09:48:26 -070029#include <limits.h>
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <fcntl.h>
33#include <unistd.h>
Kristian Høgsberg73274712013-04-01 12:41:23 -040034#include <errno.h>
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -050035
Kristian Høgsberg73274712013-04-01 12:41:23 -040036#include <wayland-util.h>
Kristian Høgsberg9b935c82011-12-08 12:44:27 -050037#include "config-parser.h"
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -050038
Kristian Høgsbergf73f3162013-05-26 20:50:53 -040039#define container_of(ptr, type, member) ({ \
40 const __typeof__( ((type *)0)->member ) *__mptr = (ptr); \
41 (type *)( (char *)__mptr - offsetof(type,member) );})
42
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -050043static int
44handle_key(const struct config_key *key, const char *value)
45{
46 char *end, *s;
47 int i, len;
Scott Moreaufa1de692012-01-27 13:25:49 -070048 unsigned int ui;
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -050049
50 switch (key->type) {
51 case CONFIG_KEY_INTEGER:
52 i = strtol(value, &end, 0);
53 if (*end != '\n') {
54 fprintf(stderr, "invalid integer: %s\n", value);
55 return -1;
56 }
57 *(int *)key->data = i;
58 return 0;
59
Scott Moreaufa1de692012-01-27 13:25:49 -070060 case CONFIG_KEY_UNSIGNED_INTEGER:
61 ui = strtoul(value, &end, 0);
62 if (*end != '\n') {
63 fprintf(stderr, "invalid integer: %s\n", value);
64 return -1;
65 }
66 *(unsigned int *)key->data = ui;
67 return 0;
68
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -050069 case CONFIG_KEY_STRING:
70 len = strlen(value);
Kristian Høgsberg3d890492012-08-03 21:56:41 -040071 while (len > 0 && isspace(value[len - 1]))
72 len--;
73 s = malloc(len + 1);
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -050074 if (s == NULL)
75 return -1;
Kristian Høgsberg3d890492012-08-03 21:56:41 -040076 memcpy(s, value, len);
77 s[len] = '\0';
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -050078 *(char **)key->data = s;
79 return 0;
80
Pekka Paalanen28a20702011-12-08 09:24:24 +020081 case CONFIG_KEY_BOOLEAN:
Pekka Paalanen09d65d02011-11-15 11:45:41 +020082 if (strcmp(value, "false\n") == 0)
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -050083 *(int *)key->data = 0;
Pekka Paalanen09d65d02011-11-15 11:45:41 +020084 else if (strcmp(value, "true\n") == 0)
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -050085 *(int *)key->data = 1;
86 else {
87 fprintf(stderr, "invalid bool: %s\n", value);
88 return -1;
89 }
90 return 0;
91
92 default:
93 assert(0);
94 break;
95 }
Pekka Paalanen4ea4d1b2012-03-30 13:54:53 +030096
97 return -1;
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -050098}
99
100int
Ossama Othmana50e6e42013-05-14 09:48:26 -0700101parse_config_file(int fd,
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -0500102 const struct config_section *sections, int num_sections,
103 void *data)
104{
105 FILE *fp;
106 char line[512], *p;
107 const struct config_section *current = NULL;
108 int i;
109
Ossama Othmana50e6e42013-05-14 09:48:26 -0700110 if (fd == -1)
111 return -1;
112
113 fp = fdopen(dup(fd), "r");
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -0500114 if (fp == NULL) {
Ossama Othmana50e6e42013-05-14 09:48:26 -0700115 perror("couldn't open config file");
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -0500116 return -1;
117 }
118
Ossama Othmana50e6e42013-05-14 09:48:26 -0700119 rewind(fp);
120
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -0500121 while (fgets(line, sizeof line, fp)) {
122 if (line[0] == '#' || line[0] == '\n') {
123 continue;
124 } if (line[0] == '[') {
125 p = strchr(&line[1], ']');
126 if (!p || p[1] != '\n') {
127 fprintf(stderr, "malformed "
128 "section header: %s\n", line);
129 fclose(fp);
130 return -1;
131 }
132 if (current && current->done)
133 current->done(data);
134 p[0] = '\0';
135 for (i = 0; i < num_sections; i++) {
136 if (strcmp(sections[i].name, &line[1]) == 0) {
137 current = &sections[i];
138 break;
139 }
140 }
141 if (i == num_sections)
142 current = NULL;
143 } else if (p = strchr(line, '='), p != NULL) {
144 if (current == NULL)
145 continue;
146 p[0] = '\0';
147 for (i = 0; i < current->num_keys; i++) {
148 if (strcmp(current->keys[i].name, line) == 0) {
149 if (handle_key(&current->keys[i], &p[1]) < 0) {
150 fclose(fp);
151 return -1;
152 }
153 break;
154 }
155 }
156 } else {
157 fprintf(stderr, "malformed config line: %s\n", line);
158 fclose(fp);
159 return -1;
160 }
161 }
162
163 if (current && current->done)
164 current->done(data);
165
166 fclose(fp);
167
168 return 0;
169}
Pekka Paalanen668dd562011-11-15 11:45:40 +0200170
Ossama Othmana50e6e42013-05-14 09:48:26 -0700171int
172open_config_file(const char *name)
Pekka Paalanen668dd562011-11-15 11:45:40 +0200173{
Ossama Othmana50e6e42013-05-14 09:48:26 -0700174 const char *config_dir = getenv("XDG_CONFIG_HOME");
175 const char *home_dir = getenv("HOME");
176 const char *config_dirs = getenv("XDG_CONFIG_DIRS");
177 char path[PATH_MAX];
178 const char *p, *next;
179 int fd;
Pekka Paalanen668dd562011-11-15 11:45:40 +0200180
Ossama Othmana50e6e42013-05-14 09:48:26 -0700181 /* Precedence is given to config files in the home directory,
182 * and then to directories listed in XDG_CONFIG_DIRS and
183 * finally to the current working directory. */
Pekka Paalanen668dd562011-11-15 11:45:40 +0200184
Ossama Othmana50e6e42013-05-14 09:48:26 -0700185 /* $XDG_CONFIG_HOME */
186 if (config_dir) {
187 snprintf(path, sizeof path, "%s/%s", config_dir, name);
188 fd = open(path, O_RDONLY | O_CLOEXEC);
189 if (fd >= 0)
190 return fd;
Pekka Paalanen668dd562011-11-15 11:45:40 +0200191 }
192
Ossama Othmana50e6e42013-05-14 09:48:26 -0700193 /* $HOME/.config */
194 if (home_dir) {
195 snprintf(path, sizeof path, "%s/.config/%s", home_dir, name);
196 fd = open(path, O_RDONLY | O_CLOEXEC);
197 if (fd >= 0)
198 return fd;
199 }
Pekka Paalanen668dd562011-11-15 11:45:40 +0200200
Ossama Othmana50e6e42013-05-14 09:48:26 -0700201 /* For each $XDG_CONFIG_DIRS: weston/<config_file> */
202 if (!config_dirs)
203 config_dirs = "/etc/xdg"; /* See XDG base dir spec. */
204
205 for (p = config_dirs; *p != '\0'; p = next) {
206 next = strchrnul(p, ':');
207 snprintf(path, sizeof path,
208 "%.*s/weston/%s", (int)(next - p), p, name);
209 fd = open(path, O_RDONLY | O_CLOEXEC);
210 if (fd >= 0)
211 return fd;
212
213 if (*next == ':')
214 next++;
215 }
216
217 /* Current working directory. */
218 snprintf(path, sizeof path, "./%s", name);
219 fd = open(path, O_RDONLY | O_CLOEXEC);
220
221 if (fd >= 0)
222 fprintf(stderr,
223 "using config in current working directory: %s\n",
224 path);
225 else
226 fprintf(stderr, "config file \"%s\" not found.\n", name);
227
228 return fd;
Pekka Paalanen668dd562011-11-15 11:45:40 +0200229}
Kristian Høgsberg73274712013-04-01 12:41:23 -0400230
231struct weston_config_entry {
232 char *key;
233 char *value;
234 struct wl_list link;
235};
236
237struct weston_config_section {
238 char *name;
239 struct wl_list entry_list;
240 struct wl_list link;
241};
242
243struct weston_config {
244 struct wl_list section_list;
245};
246
247static struct weston_config_entry *
248config_section_get_entry(struct weston_config_section *section,
249 const char *key)
250{
251 struct weston_config_entry *e;
252
253 if (section == NULL)
254 return NULL;
255 wl_list_for_each(e, &section->entry_list, link)
256 if (strcmp(e->key, key) == 0)
257 return e;
258
259 return NULL;
260}
261
262struct weston_config_section *
263weston_config_get_section(struct weston_config *config, const char *section,
264 const char *key, const char *value)
265{
266 struct weston_config_section *s;
267 struct weston_config_entry *e;
268
Mun Gwan-gyeong72a3ab72013-05-25 02:09:13 +0900269 if (config == NULL)
270 return NULL;
Kristian Høgsberg73274712013-04-01 12:41:23 -0400271 wl_list_for_each(s, &config->section_list, link) {
272 if (strcmp(s->name, section) != 0)
273 continue;
274 if (key == NULL)
275 return s;
276 e = config_section_get_entry(s, key);
277 if (e && strcmp(e->value, value) == 0)
278 return s;
279 }
280
281 return NULL;
282}
283
284int
285weston_config_section_get_int(struct weston_config_section *section,
286 const char *key,
287 int32_t *value, int32_t default_value)
288{
289 struct weston_config_entry *entry;
290 char *end;
291
292 entry = config_section_get_entry(section, key);
293 if (entry == NULL) {
294 *value = default_value;
295 errno = ENOENT;
296 return -1;
297 }
298
299 *value = strtol(entry->value, &end, 0);
300 if (*end != '\0') {
301 *value = default_value;
302 errno = EINVAL;
303 return -1;
304 }
305
306 return 0;
307}
308
309int
310weston_config_section_get_uint(struct weston_config_section *section,
311 const char *key,
312 uint32_t *value, uint32_t default_value)
313{
314 struct weston_config_entry *entry;
315 char *end;
316
317 entry = config_section_get_entry(section, key);
318 if (entry == NULL) {
319 *value = default_value;
320 errno = ENOENT;
321 return -1;
322 }
323
324 *value = strtoul(entry->value, &end, 0);
325 if (*end != '\0') {
326 *value = default_value;
327 errno = EINVAL;
328 return -1;
329 }
330
331 return 0;
332}
333
334int
335weston_config_section_get_string(struct weston_config_section *section,
336 const char *key,
337 char **value, const char *default_value)
338{
339 struct weston_config_entry *entry;
340
341 entry = config_section_get_entry(section, key);
342 if (entry == NULL) {
343 if (default_value)
344 *value = strdup(default_value);
345 else
346 *value = NULL;
347 errno = ENOENT;
348 return -1;
349 }
350
351 *value = strdup(entry->value);
352
353 return 0;
354}
355
356int
357weston_config_section_get_bool(struct weston_config_section *section,
358 const char *key,
359 int *value, int default_value)
360{
361 struct weston_config_entry *entry;
362
363 entry = config_section_get_entry(section, key);
364 if (entry == NULL) {
365 *value = default_value;
366 errno = ENOENT;
367 return -1;
368 }
369
370 if (strcmp(entry->value, "false") == 0)
371 *value = 0;
372 else if (strcmp(entry->value, "true") == 0)
373 *value = 1;
374 else {
375 *value = default_value;
376 errno = EINVAL;
377 return -1;
378 }
379
380 return 0;
381}
382
383static struct weston_config_section *
384config_add_section(struct weston_config *config, const char *name)
385{
386 struct weston_config_section *section;
387
388 section = malloc(sizeof *section);
389 section->name = strdup(name);
390 wl_list_init(&section->entry_list);
391 wl_list_insert(config->section_list.prev, &section->link);
392
393 return section;
394}
395
396static struct weston_config_entry *
397section_add_entry(struct weston_config_section *section,
398 const char *key, const char *value)
399{
400 struct weston_config_entry *entry;
401
402 entry = malloc(sizeof *entry);
403 entry->key = strdup(key);
404 entry->value = strdup(value);
405 wl_list_insert(section->entry_list.prev, &entry->link);
406
407 return entry;
408}
409
410struct weston_config *
411weston_config_parse(int fd)
412{
413 FILE *fp;
414 char line[512], *p;
415 struct weston_config *config;
416 struct weston_config_section *section = NULL;
417 int i;
418
419 config = malloc(sizeof *config);
420 if (config == NULL)
421 return NULL;
422
423 wl_list_init(&config->section_list);
424
425 fp = fdopen(dup(fd), "r");
426 if (fp == NULL) {
427 free(config);
428 return NULL;
429 }
430
431 rewind(fp);
432
433 while (fgets(line, sizeof line, fp)) {
434 switch (line[0]) {
435 case '#':
436 case '\n':
437 continue;
438 case '[':
439 p = strchr(&line[1], ']');
440 if (!p || p[1] != '\n') {
441 fprintf(stderr, "malformed "
442 "section header: %s\n", line);
443 fclose(fp);
444 weston_config_destroy(config);
445 return NULL;
446 }
447 p[0] = '\0';
448 section = config_add_section(config, &line[1]);
449 continue;
450 default:
451 p = strchr(line, '=');
452 if (!p || p == line || !section) {
453 fprintf(stderr, "malformed "
454 "config line: %s\n", line);
455 fclose(fp);
456 weston_config_destroy(config);
457 return NULL;
458 }
459
460 p[0] = '\0';
461 p++;
462 while (isspace(*p))
463 p++;
464 i = strlen(p);
465 while (i > 0 && isspace(p[i - 1])) {
466 p[i - 1] = '\0';
467 i--;
468 }
469 section_add_entry(section, line, p);
470 continue;
471 }
472 }
473
474 fclose(fp);
475
476 return config;
477}
478
Kristian Høgsbergf73f3162013-05-26 20:50:53 -0400479int
480weston_config_next_section(struct weston_config *config,
481 struct weston_config_section **section,
482 const char **name)
483{
Mun Gwan-gyeong151a5282013-05-28 00:04:26 +0900484 if (config == NULL)
485 return 0;
486
Kristian Høgsbergf73f3162013-05-26 20:50:53 -0400487 if (*section == NULL)
488 *section = container_of(config->section_list.next,
489 struct weston_config_section, link);
490 else
491 *section = container_of((*section)->link.next,
492 struct weston_config_section, link);
493
494 if (&(*section)->link == &config->section_list)
495 return 0;
496
497 *name = (*section)->name;
498
499 return 1;
500}
501
Kristian Høgsberg73274712013-04-01 12:41:23 -0400502void
503weston_config_destroy(struct weston_config *config)
504{
505 struct weston_config_section *s, *next_s;
506 struct weston_config_entry *e, *next_e;
507
Mun Gwan-gyeong77325402013-05-28 00:20:04 +0900508 if (config == NULL)
509 return;
510
Kristian Høgsberg73274712013-04-01 12:41:23 -0400511 wl_list_for_each_safe(s, next_s, &config->section_list, link) {
512 wl_list_for_each_safe(e, next_e, &s->entry_list, link) {
513 free(e->key);
514 free(e->value);
515 free(e);
516 }
517 free(s->name);
518 free(s);
519 }
520
521 free(config);
522}