blob: d5491c2765f42802aaae98d6a241fb3e817eb45a [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
Daniel Stonec228e232013-05-22 18:03:19 +030023#include "config.h"
24
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -050025#include <string.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <assert.h>
Kristian Høgsberg3d890492012-08-03 21:56:41 -040029#include <ctype.h>
Ossama Othmana50e6e42013-05-14 09:48:26 -070030#include <limits.h>
31#include <sys/types.h>
32#include <sys/stat.h>
33#include <fcntl.h>
34#include <unistd.h>
Kristian Høgsberg73274712013-04-01 12:41:23 -040035#include <errno.h>
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -050036
Kristian Høgsberg73274712013-04-01 12:41:23 -040037#include <wayland-util.h>
Kristian Høgsberg9b935c82011-12-08 12:44:27 -050038#include "config-parser.h"
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -050039
Kristian Høgsbergf73f3162013-05-26 20:50:53 -040040#define container_of(ptr, type, member) ({ \
41 const __typeof__( ((type *)0)->member ) *__mptr = (ptr); \
42 (type *)( (char *)__mptr - offsetof(type,member) );})
43
Ossama Othmana50e6e42013-05-14 09:48:26 -070044int
45open_config_file(const char *name)
Pekka Paalanen668dd562011-11-15 11:45:40 +020046{
Ossama Othmana50e6e42013-05-14 09:48:26 -070047 const char *config_dir = getenv("XDG_CONFIG_HOME");
48 const char *home_dir = getenv("HOME");
49 const char *config_dirs = getenv("XDG_CONFIG_DIRS");
50 char path[PATH_MAX];
51 const char *p, *next;
52 int fd;
Pekka Paalanen668dd562011-11-15 11:45:40 +020053
Ossama Othmana50e6e42013-05-14 09:48:26 -070054 /* Precedence is given to config files in the home directory,
55 * and then to directories listed in XDG_CONFIG_DIRS and
56 * finally to the current working directory. */
Pekka Paalanen668dd562011-11-15 11:45:40 +020057
Ossama Othmana50e6e42013-05-14 09:48:26 -070058 /* $XDG_CONFIG_HOME */
59 if (config_dir) {
60 snprintf(path, sizeof path, "%s/%s", config_dir, name);
61 fd = open(path, O_RDONLY | O_CLOEXEC);
62 if (fd >= 0)
63 return fd;
Pekka Paalanen668dd562011-11-15 11:45:40 +020064 }
65
Ossama Othmana50e6e42013-05-14 09:48:26 -070066 /* $HOME/.config */
67 if (home_dir) {
68 snprintf(path, sizeof path, "%s/.config/%s", home_dir, name);
69 fd = open(path, O_RDONLY | O_CLOEXEC);
70 if (fd >= 0)
71 return fd;
72 }
Pekka Paalanen668dd562011-11-15 11:45:40 +020073
Ossama Othmana50e6e42013-05-14 09:48:26 -070074 /* For each $XDG_CONFIG_DIRS: weston/<config_file> */
75 if (!config_dirs)
76 config_dirs = "/etc/xdg"; /* See XDG base dir spec. */
77
78 for (p = config_dirs; *p != '\0'; p = next) {
79 next = strchrnul(p, ':');
80 snprintf(path, sizeof path,
81 "%.*s/weston/%s", (int)(next - p), p, name);
82 fd = open(path, O_RDONLY | O_CLOEXEC);
83 if (fd >= 0)
84 return fd;
85
86 if (*next == ':')
87 next++;
88 }
89
90 /* Current working directory. */
91 snprintf(path, sizeof path, "./%s", name);
92 fd = open(path, O_RDONLY | O_CLOEXEC);
93
94 if (fd >= 0)
95 fprintf(stderr,
96 "using config in current working directory: %s\n",
97 path);
98 else
99 fprintf(stderr, "config file \"%s\" not found.\n", name);
100
101 return fd;
Pekka Paalanen668dd562011-11-15 11:45:40 +0200102}
Kristian Høgsberg73274712013-04-01 12:41:23 -0400103
104struct weston_config_entry {
105 char *key;
106 char *value;
107 struct wl_list link;
108};
109
110struct weston_config_section {
111 char *name;
112 struct wl_list entry_list;
113 struct wl_list link;
114};
115
116struct weston_config {
117 struct wl_list section_list;
118};
119
120static struct weston_config_entry *
121config_section_get_entry(struct weston_config_section *section,
122 const char *key)
123{
124 struct weston_config_entry *e;
125
126 if (section == NULL)
127 return NULL;
128 wl_list_for_each(e, &section->entry_list, link)
129 if (strcmp(e->key, key) == 0)
130 return e;
131
132 return NULL;
133}
134
Quentin Glidic6e2c1242013-07-01 17:03:08 +0200135WL_EXPORT
Kristian Høgsberg73274712013-04-01 12:41:23 -0400136struct weston_config_section *
137weston_config_get_section(struct weston_config *config, const char *section,
138 const char *key, const char *value)
139{
140 struct weston_config_section *s;
141 struct weston_config_entry *e;
142
Mun Gwan-gyeong72a3ab72013-05-25 02:09:13 +0900143 if (config == NULL)
144 return NULL;
Kristian Høgsberg73274712013-04-01 12:41:23 -0400145 wl_list_for_each(s, &config->section_list, link) {
146 if (strcmp(s->name, section) != 0)
147 continue;
148 if (key == NULL)
149 return s;
150 e = config_section_get_entry(s, key);
151 if (e && strcmp(e->value, value) == 0)
152 return s;
153 }
154
155 return NULL;
156}
157
Quentin Glidic6e2c1242013-07-01 17:03:08 +0200158WL_EXPORT
Kristian Høgsberg73274712013-04-01 12:41:23 -0400159int
160weston_config_section_get_int(struct weston_config_section *section,
161 const char *key,
162 int32_t *value, int32_t default_value)
163{
164 struct weston_config_entry *entry;
165 char *end;
166
167 entry = config_section_get_entry(section, key);
168 if (entry == NULL) {
169 *value = default_value;
170 errno = ENOENT;
171 return -1;
172 }
173
174 *value = strtol(entry->value, &end, 0);
175 if (*end != '\0') {
176 *value = default_value;
177 errno = EINVAL;
178 return -1;
179 }
180
181 return 0;
182}
183
Quentin Glidic6e2c1242013-07-01 17:03:08 +0200184WL_EXPORT
Kristian Høgsberg73274712013-04-01 12:41:23 -0400185int
186weston_config_section_get_uint(struct weston_config_section *section,
187 const char *key,
188 uint32_t *value, uint32_t default_value)
189{
190 struct weston_config_entry *entry;
191 char *end;
192
193 entry = config_section_get_entry(section, key);
194 if (entry == NULL) {
195 *value = default_value;
196 errno = ENOENT;
197 return -1;
198 }
199
200 *value = strtoul(entry->value, &end, 0);
201 if (*end != '\0') {
202 *value = default_value;
203 errno = EINVAL;
204 return -1;
205 }
206
207 return 0;
208}
209
Quentin Glidic6e2c1242013-07-01 17:03:08 +0200210WL_EXPORT
Kristian Høgsberg73274712013-04-01 12:41:23 -0400211int
Armin Kb502f902013-07-31 01:41:03 +0200212weston_config_section_get_double(struct weston_config_section *section,
213 const char *key,
214 double *value, double default_value)
215{
216 struct weston_config_entry *entry;
217 char *end;
218
219 entry = config_section_get_entry(section, key);
220 if (entry == NULL) {
221 *value = default_value;
222 errno = ENOENT;
223 return -1;
224 }
225
226 *value = strtod(entry->value, &end);
227 if (*end != '\0') {
228 *value = default_value;
229 errno = EINVAL;
230 return -1;
231 }
232
233 return 0;
234}
235
236WL_EXPORT
237int
Kristian Høgsberg73274712013-04-01 12:41:23 -0400238weston_config_section_get_string(struct weston_config_section *section,
239 const char *key,
240 char **value, const char *default_value)
241{
242 struct weston_config_entry *entry;
243
244 entry = config_section_get_entry(section, key);
245 if (entry == NULL) {
246 if (default_value)
247 *value = strdup(default_value);
248 else
249 *value = NULL;
250 errno = ENOENT;
251 return -1;
252 }
253
254 *value = strdup(entry->value);
255
256 return 0;
257}
258
Quentin Glidic6e2c1242013-07-01 17:03:08 +0200259WL_EXPORT
Kristian Høgsberg73274712013-04-01 12:41:23 -0400260int
261weston_config_section_get_bool(struct weston_config_section *section,
262 const char *key,
263 int *value, int default_value)
264{
265 struct weston_config_entry *entry;
266
267 entry = config_section_get_entry(section, key);
268 if (entry == NULL) {
269 *value = default_value;
270 errno = ENOENT;
271 return -1;
272 }
273
274 if (strcmp(entry->value, "false") == 0)
275 *value = 0;
276 else if (strcmp(entry->value, "true") == 0)
277 *value = 1;
278 else {
279 *value = default_value;
280 errno = EINVAL;
281 return -1;
282 }
283
284 return 0;
285}
286
287static struct weston_config_section *
288config_add_section(struct weston_config *config, const char *name)
289{
290 struct weston_config_section *section;
291
292 section = malloc(sizeof *section);
293 section->name = strdup(name);
294 wl_list_init(&section->entry_list);
295 wl_list_insert(config->section_list.prev, &section->link);
296
297 return section;
298}
299
300static struct weston_config_entry *
301section_add_entry(struct weston_config_section *section,
302 const char *key, const char *value)
303{
304 struct weston_config_entry *entry;
305
306 entry = malloc(sizeof *entry);
307 entry->key = strdup(key);
308 entry->value = strdup(value);
309 wl_list_insert(section->entry_list.prev, &entry->link);
310
311 return entry;
312}
313
314struct weston_config *
315weston_config_parse(int fd)
316{
317 FILE *fp;
318 char line[512], *p;
319 struct weston_config *config;
320 struct weston_config_section *section = NULL;
321 int i;
322
323 config = malloc(sizeof *config);
324 if (config == NULL)
325 return NULL;
326
327 wl_list_init(&config->section_list);
328
329 fp = fdopen(dup(fd), "r");
330 if (fp == NULL) {
331 free(config);
332 return NULL;
333 }
334
335 rewind(fp);
336
337 while (fgets(line, sizeof line, fp)) {
338 switch (line[0]) {
339 case '#':
340 case '\n':
341 continue;
342 case '[':
343 p = strchr(&line[1], ']');
344 if (!p || p[1] != '\n') {
345 fprintf(stderr, "malformed "
346 "section header: %s\n", line);
347 fclose(fp);
348 weston_config_destroy(config);
349 return NULL;
350 }
351 p[0] = '\0';
352 section = config_add_section(config, &line[1]);
353 continue;
354 default:
355 p = strchr(line, '=');
356 if (!p || p == line || !section) {
357 fprintf(stderr, "malformed "
358 "config line: %s\n", line);
359 fclose(fp);
360 weston_config_destroy(config);
361 return NULL;
362 }
363
364 p[0] = '\0';
365 p++;
366 while (isspace(*p))
367 p++;
368 i = strlen(p);
369 while (i > 0 && isspace(p[i - 1])) {
370 p[i - 1] = '\0';
371 i--;
372 }
373 section_add_entry(section, line, p);
374 continue;
375 }
376 }
377
378 fclose(fp);
379
380 return config;
381}
382
Kristian Høgsbergf73f3162013-05-26 20:50:53 -0400383int
384weston_config_next_section(struct weston_config *config,
385 struct weston_config_section **section,
386 const char **name)
387{
Mun Gwan-gyeong151a5282013-05-28 00:04:26 +0900388 if (config == NULL)
389 return 0;
390
Kristian Høgsbergf73f3162013-05-26 20:50:53 -0400391 if (*section == NULL)
392 *section = container_of(config->section_list.next,
393 struct weston_config_section, link);
394 else
395 *section = container_of((*section)->link.next,
396 struct weston_config_section, link);
397
398 if (&(*section)->link == &config->section_list)
399 return 0;
400
401 *name = (*section)->name;
402
403 return 1;
404}
405
Kristian Høgsberg73274712013-04-01 12:41:23 -0400406void
407weston_config_destroy(struct weston_config *config)
408{
409 struct weston_config_section *s, *next_s;
410 struct weston_config_entry *e, *next_e;
411
Mun Gwan-gyeong77325402013-05-28 00:20:04 +0900412 if (config == NULL)
413 return;
414
Kristian Høgsberg73274712013-04-01 12:41:23 -0400415 wl_list_for_each_safe(s, next_s, &config->section_list, link) {
416 wl_list_for_each_safe(e, next_e, &s->entry_list, link) {
417 free(e->key);
418 free(e->value);
419 free(e);
420 }
421 free(s->name);
422 free(s);
423 }
424
425 free(config);
426}