blob: 5ef6f03b8d4871c26882e5a79517de1b07aa2dfd [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
265 wl_list_for_each(s, &config->section_list, link) {
266 if (strcmp(s->name, section) != 0)
267 continue;
268 if (key == NULL)
269 return s;
270 e = config_section_get_entry(s, key);
271 if (e && strcmp(e->value, value) == 0)
272 return s;
273 }
274
275 return NULL;
276}
277
278int
279weston_config_section_get_int(struct weston_config_section *section,
280 const char *key,
281 int32_t *value, int32_t default_value)
282{
283 struct weston_config_entry *entry;
284 char *end;
285
286 entry = config_section_get_entry(section, key);
287 if (entry == NULL) {
288 *value = default_value;
289 errno = ENOENT;
290 return -1;
291 }
292
293 *value = strtol(entry->value, &end, 0);
294 if (*end != '\0') {
295 *value = default_value;
296 errno = EINVAL;
297 return -1;
298 }
299
300 return 0;
301}
302
303int
304weston_config_section_get_uint(struct weston_config_section *section,
305 const char *key,
306 uint32_t *value, uint32_t default_value)
307{
308 struct weston_config_entry *entry;
309 char *end;
310
311 entry = config_section_get_entry(section, key);
312 if (entry == NULL) {
313 *value = default_value;
314 errno = ENOENT;
315 return -1;
316 }
317
318 *value = strtoul(entry->value, &end, 0);
319 if (*end != '\0') {
320 *value = default_value;
321 errno = EINVAL;
322 return -1;
323 }
324
325 return 0;
326}
327
328int
329weston_config_section_get_string(struct weston_config_section *section,
330 const char *key,
331 char **value, const char *default_value)
332{
333 struct weston_config_entry *entry;
334
335 entry = config_section_get_entry(section, key);
336 if (entry == NULL) {
337 if (default_value)
338 *value = strdup(default_value);
339 else
340 *value = NULL;
341 errno = ENOENT;
342 return -1;
343 }
344
345 *value = strdup(entry->value);
346
347 return 0;
348}
349
350int
351weston_config_section_get_bool(struct weston_config_section *section,
352 const char *key,
353 int *value, int default_value)
354{
355 struct weston_config_entry *entry;
356
357 entry = config_section_get_entry(section, key);
358 if (entry == NULL) {
359 *value = default_value;
360 errno = ENOENT;
361 return -1;
362 }
363
364 if (strcmp(entry->value, "false") == 0)
365 *value = 0;
366 else if (strcmp(entry->value, "true") == 0)
367 *value = 1;
368 else {
369 *value = default_value;
370 errno = EINVAL;
371 return -1;
372 }
373
374 return 0;
375}
376
377static struct weston_config_section *
378config_add_section(struct weston_config *config, const char *name)
379{
380 struct weston_config_section *section;
381
382 section = malloc(sizeof *section);
383 section->name = strdup(name);
384 wl_list_init(&section->entry_list);
385 wl_list_insert(config->section_list.prev, &section->link);
386
387 return section;
388}
389
390static struct weston_config_entry *
391section_add_entry(struct weston_config_section *section,
392 const char *key, const char *value)
393{
394 struct weston_config_entry *entry;
395
396 entry = malloc(sizeof *entry);
397 entry->key = strdup(key);
398 entry->value = strdup(value);
399 wl_list_insert(section->entry_list.prev, &entry->link);
400
401 return entry;
402}
403
404struct weston_config *
405weston_config_parse(int fd)
406{
407 FILE *fp;
408 char line[512], *p;
409 struct weston_config *config;
410 struct weston_config_section *section = NULL;
411 int i;
412
413 config = malloc(sizeof *config);
414 if (config == NULL)
415 return NULL;
416
417 wl_list_init(&config->section_list);
418
419 fp = fdopen(dup(fd), "r");
420 if (fp == NULL) {
421 free(config);
422 return NULL;
423 }
424
425 rewind(fp);
426
427 while (fgets(line, sizeof line, fp)) {
428 switch (line[0]) {
429 case '#':
430 case '\n':
431 continue;
432 case '[':
433 p = strchr(&line[1], ']');
434 if (!p || p[1] != '\n') {
435 fprintf(stderr, "malformed "
436 "section header: %s\n", line);
437 fclose(fp);
438 weston_config_destroy(config);
439 return NULL;
440 }
441 p[0] = '\0';
442 section = config_add_section(config, &line[1]);
443 continue;
444 default:
445 p = strchr(line, '=');
446 if (!p || p == line || !section) {
447 fprintf(stderr, "malformed "
448 "config line: %s\n", line);
449 fclose(fp);
450 weston_config_destroy(config);
451 return NULL;
452 }
453
454 p[0] = '\0';
455 p++;
456 while (isspace(*p))
457 p++;
458 i = strlen(p);
459 while (i > 0 && isspace(p[i - 1])) {
460 p[i - 1] = '\0';
461 i--;
462 }
463 section_add_entry(section, line, p);
464 continue;
465 }
466 }
467
468 fclose(fp);
469
470 return config;
471}
472
473void
474weston_config_destroy(struct weston_config *config)
475{
476 struct weston_config_section *s, *next_s;
477 struct weston_config_entry *e, *next_e;
478
479 wl_list_for_each_safe(s, next_s, &config->section_list, link) {
480 wl_list_for_each_safe(e, next_e, &s->entry_list, link) {
481 free(e->key);
482 free(e->value);
483 free(e);
484 }
485 free(s->name);
486 free(s);
487 }
488
489 free(config);
490}