blob: 9772c12a2f7e0c8fe7cb106eb5e02794523dda64 [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
39static int
40handle_key(const struct config_key *key, const char *value)
41{
42 char *end, *s;
43 int i, len;
Scott Moreaufa1de692012-01-27 13:25:49 -070044 unsigned int ui;
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -050045
46 switch (key->type) {
47 case CONFIG_KEY_INTEGER:
48 i = strtol(value, &end, 0);
49 if (*end != '\n') {
50 fprintf(stderr, "invalid integer: %s\n", value);
51 return -1;
52 }
53 *(int *)key->data = i;
54 return 0;
55
Scott Moreaufa1de692012-01-27 13:25:49 -070056 case CONFIG_KEY_UNSIGNED_INTEGER:
57 ui = strtoul(value, &end, 0);
58 if (*end != '\n') {
59 fprintf(stderr, "invalid integer: %s\n", value);
60 return -1;
61 }
62 *(unsigned int *)key->data = ui;
63 return 0;
64
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -050065 case CONFIG_KEY_STRING:
66 len = strlen(value);
Kristian Høgsberg3d890492012-08-03 21:56:41 -040067 while (len > 0 && isspace(value[len - 1]))
68 len--;
69 s = malloc(len + 1);
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -050070 if (s == NULL)
71 return -1;
Kristian Høgsberg3d890492012-08-03 21:56:41 -040072 memcpy(s, value, len);
73 s[len] = '\0';
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -050074 *(char **)key->data = s;
75 return 0;
76
Pekka Paalanen28a20702011-12-08 09:24:24 +020077 case CONFIG_KEY_BOOLEAN:
Pekka Paalanen09d65d02011-11-15 11:45:41 +020078 if (strcmp(value, "false\n") == 0)
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -050079 *(int *)key->data = 0;
Pekka Paalanen09d65d02011-11-15 11:45:41 +020080 else if (strcmp(value, "true\n") == 0)
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -050081 *(int *)key->data = 1;
82 else {
83 fprintf(stderr, "invalid bool: %s\n", value);
84 return -1;
85 }
86 return 0;
87
88 default:
89 assert(0);
90 break;
91 }
Pekka Paalanen4ea4d1b2012-03-30 13:54:53 +030092
93 return -1;
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -050094}
95
96int
Ossama Othmana50e6e42013-05-14 09:48:26 -070097parse_config_file(int fd,
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -050098 const struct config_section *sections, int num_sections,
99 void *data)
100{
101 FILE *fp;
102 char line[512], *p;
103 const struct config_section *current = NULL;
104 int i;
105
Ossama Othmana50e6e42013-05-14 09:48:26 -0700106 if (fd == -1)
107 return -1;
108
109 fp = fdopen(dup(fd), "r");
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -0500110 if (fp == NULL) {
Ossama Othmana50e6e42013-05-14 09:48:26 -0700111 perror("couldn't open config file");
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -0500112 return -1;
113 }
114
Ossama Othmana50e6e42013-05-14 09:48:26 -0700115 rewind(fp);
116
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -0500117 while (fgets(line, sizeof line, fp)) {
118 if (line[0] == '#' || line[0] == '\n') {
119 continue;
120 } if (line[0] == '[') {
121 p = strchr(&line[1], ']');
122 if (!p || p[1] != '\n') {
123 fprintf(stderr, "malformed "
124 "section header: %s\n", line);
125 fclose(fp);
126 return -1;
127 }
128 if (current && current->done)
129 current->done(data);
130 p[0] = '\0';
131 for (i = 0; i < num_sections; i++) {
132 if (strcmp(sections[i].name, &line[1]) == 0) {
133 current = &sections[i];
134 break;
135 }
136 }
137 if (i == num_sections)
138 current = NULL;
139 } else if (p = strchr(line, '='), p != NULL) {
140 if (current == NULL)
141 continue;
142 p[0] = '\0';
143 for (i = 0; i < current->num_keys; i++) {
144 if (strcmp(current->keys[i].name, line) == 0) {
145 if (handle_key(&current->keys[i], &p[1]) < 0) {
146 fclose(fp);
147 return -1;
148 }
149 break;
150 }
151 }
152 } else {
153 fprintf(stderr, "malformed config line: %s\n", line);
154 fclose(fp);
155 return -1;
156 }
157 }
158
159 if (current && current->done)
160 current->done(data);
161
162 fclose(fp);
163
164 return 0;
165}
Pekka Paalanen668dd562011-11-15 11:45:40 +0200166
Ossama Othmana50e6e42013-05-14 09:48:26 -0700167int
168open_config_file(const char *name)
Pekka Paalanen668dd562011-11-15 11:45:40 +0200169{
Ossama Othmana50e6e42013-05-14 09:48:26 -0700170 const char *config_dir = getenv("XDG_CONFIG_HOME");
171 const char *home_dir = getenv("HOME");
172 const char *config_dirs = getenv("XDG_CONFIG_DIRS");
173 char path[PATH_MAX];
174 const char *p, *next;
175 int fd;
Pekka Paalanen668dd562011-11-15 11:45:40 +0200176
Ossama Othmana50e6e42013-05-14 09:48:26 -0700177 /* Precedence is given to config files in the home directory,
178 * and then to directories listed in XDG_CONFIG_DIRS and
179 * finally to the current working directory. */
Pekka Paalanen668dd562011-11-15 11:45:40 +0200180
Ossama Othmana50e6e42013-05-14 09:48:26 -0700181 /* $XDG_CONFIG_HOME */
182 if (config_dir) {
183 snprintf(path, sizeof path, "%s/%s", config_dir, name);
184 fd = open(path, O_RDONLY | O_CLOEXEC);
185 if (fd >= 0)
186 return fd;
Pekka Paalanen668dd562011-11-15 11:45:40 +0200187 }
188
Ossama Othmana50e6e42013-05-14 09:48:26 -0700189 /* $HOME/.config */
190 if (home_dir) {
191 snprintf(path, sizeof path, "%s/.config/%s", home_dir, name);
192 fd = open(path, O_RDONLY | O_CLOEXEC);
193 if (fd >= 0)
194 return fd;
195 }
Pekka Paalanen668dd562011-11-15 11:45:40 +0200196
Ossama Othmana50e6e42013-05-14 09:48:26 -0700197 /* For each $XDG_CONFIG_DIRS: weston/<config_file> */
198 if (!config_dirs)
199 config_dirs = "/etc/xdg"; /* See XDG base dir spec. */
200
201 for (p = config_dirs; *p != '\0'; p = next) {
202 next = strchrnul(p, ':');
203 snprintf(path, sizeof path,
204 "%.*s/weston/%s", (int)(next - p), p, name);
205 fd = open(path, O_RDONLY | O_CLOEXEC);
206 if (fd >= 0)
207 return fd;
208
209 if (*next == ':')
210 next++;
211 }
212
213 /* Current working directory. */
214 snprintf(path, sizeof path, "./%s", name);
215 fd = open(path, O_RDONLY | O_CLOEXEC);
216
217 if (fd >= 0)
218 fprintf(stderr,
219 "using config in current working directory: %s\n",
220 path);
221 else
222 fprintf(stderr, "config file \"%s\" not found.\n", name);
223
224 return fd;
Pekka Paalanen668dd562011-11-15 11:45:40 +0200225}
Kristian Høgsberg73274712013-04-01 12:41:23 -0400226
227struct weston_config_entry {
228 char *key;
229 char *value;
230 struct wl_list link;
231};
232
233struct weston_config_section {
234 char *name;
235 struct wl_list entry_list;
236 struct wl_list link;
237};
238
239struct weston_config {
240 struct wl_list section_list;
241};
242
243static struct weston_config_entry *
244config_section_get_entry(struct weston_config_section *section,
245 const char *key)
246{
247 struct weston_config_entry *e;
248
249 if (section == NULL)
250 return NULL;
251 wl_list_for_each(e, &section->entry_list, link)
252 if (strcmp(e->key, key) == 0)
253 return e;
254
255 return NULL;
256}
257
258struct weston_config_section *
259weston_config_get_section(struct weston_config *config, const char *section,
260 const char *key, const char *value)
261{
262 struct weston_config_section *s;
263 struct weston_config_entry *e;
264
Mun Gwan-gyeong72a3ab72013-05-25 02:09:13 +0900265 if (config == NULL)
266 return NULL;
Kristian Høgsberg73274712013-04-01 12:41:23 -0400267 wl_list_for_each(s, &config->section_list, link) {
268 if (strcmp(s->name, section) != 0)
269 continue;
270 if (key == NULL)
271 return s;
272 e = config_section_get_entry(s, key);
273 if (e && strcmp(e->value, value) == 0)
274 return s;
275 }
276
277 return NULL;
278}
279
280int
281weston_config_section_get_int(struct weston_config_section *section,
282 const char *key,
283 int32_t *value, int32_t default_value)
284{
285 struct weston_config_entry *entry;
286 char *end;
287
288 entry = config_section_get_entry(section, key);
289 if (entry == NULL) {
290 *value = default_value;
291 errno = ENOENT;
292 return -1;
293 }
294
295 *value = strtol(entry->value, &end, 0);
296 if (*end != '\0') {
297 *value = default_value;
298 errno = EINVAL;
299 return -1;
300 }
301
302 return 0;
303}
304
305int
306weston_config_section_get_uint(struct weston_config_section *section,
307 const char *key,
308 uint32_t *value, uint32_t default_value)
309{
310 struct weston_config_entry *entry;
311 char *end;
312
313 entry = config_section_get_entry(section, key);
314 if (entry == NULL) {
315 *value = default_value;
316 errno = ENOENT;
317 return -1;
318 }
319
320 *value = strtoul(entry->value, &end, 0);
321 if (*end != '\0') {
322 *value = default_value;
323 errno = EINVAL;
324 return -1;
325 }
326
327 return 0;
328}
329
330int
331weston_config_section_get_string(struct weston_config_section *section,
332 const char *key,
333 char **value, const char *default_value)
334{
335 struct weston_config_entry *entry;
336
337 entry = config_section_get_entry(section, key);
338 if (entry == NULL) {
339 if (default_value)
340 *value = strdup(default_value);
341 else
342 *value = NULL;
343 errno = ENOENT;
344 return -1;
345 }
346
347 *value = strdup(entry->value);
348
349 return 0;
350}
351
352int
353weston_config_section_get_bool(struct weston_config_section *section,
354 const char *key,
355 int *value, int default_value)
356{
357 struct weston_config_entry *entry;
358
359 entry = config_section_get_entry(section, key);
360 if (entry == NULL) {
361 *value = default_value;
362 errno = ENOENT;
363 return -1;
364 }
365
366 if (strcmp(entry->value, "false") == 0)
367 *value = 0;
368 else if (strcmp(entry->value, "true") == 0)
369 *value = 1;
370 else {
371 *value = default_value;
372 errno = EINVAL;
373 return -1;
374 }
375
376 return 0;
377}
378
379static struct weston_config_section *
380config_add_section(struct weston_config *config, const char *name)
381{
382 struct weston_config_section *section;
383
384 section = malloc(sizeof *section);
385 section->name = strdup(name);
386 wl_list_init(&section->entry_list);
387 wl_list_insert(config->section_list.prev, &section->link);
388
389 return section;
390}
391
392static struct weston_config_entry *
393section_add_entry(struct weston_config_section *section,
394 const char *key, const char *value)
395{
396 struct weston_config_entry *entry;
397
398 entry = malloc(sizeof *entry);
399 entry->key = strdup(key);
400 entry->value = strdup(value);
401 wl_list_insert(section->entry_list.prev, &entry->link);
402
403 return entry;
404}
405
406struct weston_config *
407weston_config_parse(int fd)
408{
409 FILE *fp;
410 char line[512], *p;
411 struct weston_config *config;
412 struct weston_config_section *section = NULL;
413 int i;
414
415 config = malloc(sizeof *config);
416 if (config == NULL)
417 return NULL;
418
419 wl_list_init(&config->section_list);
420
421 fp = fdopen(dup(fd), "r");
422 if (fp == NULL) {
423 free(config);
424 return NULL;
425 }
426
427 rewind(fp);
428
429 while (fgets(line, sizeof line, fp)) {
430 switch (line[0]) {
431 case '#':
432 case '\n':
433 continue;
434 case '[':
435 p = strchr(&line[1], ']');
436 if (!p || p[1] != '\n') {
437 fprintf(stderr, "malformed "
438 "section header: %s\n", line);
439 fclose(fp);
440 weston_config_destroy(config);
441 return NULL;
442 }
443 p[0] = '\0';
444 section = config_add_section(config, &line[1]);
445 continue;
446 default:
447 p = strchr(line, '=');
448 if (!p || p == line || !section) {
449 fprintf(stderr, "malformed "
450 "config line: %s\n", line);
451 fclose(fp);
452 weston_config_destroy(config);
453 return NULL;
454 }
455
456 p[0] = '\0';
457 p++;
458 while (isspace(*p))
459 p++;
460 i = strlen(p);
461 while (i > 0 && isspace(p[i - 1])) {
462 p[i - 1] = '\0';
463 i--;
464 }
465 section_add_entry(section, line, p);
466 continue;
467 }
468 }
469
470 fclose(fp);
471
472 return config;
473}
474
475void
476weston_config_destroy(struct weston_config *config)
477{
478 struct weston_config_section *s, *next_s;
479 struct weston_config_entry *e, *next_e;
480
481 wl_list_for_each_safe(s, next_s, &config->section_list, link) {
482 wl_list_for_each_safe(e, next_e, &s->entry_list, link) {
483 free(e->key);
484 free(e->value);
485 free(e);
486 }
487 free(s->name);
488 free(s);
489 }
490
491 free(config);
492}