4 * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com>
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files
8 * (the "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 * Implementation of the configuration parsers.
40 #include <xkbcommon/xkbcommon.h>
45 #define LOG_SUBSYSTEM "config"
47 void conf_free_value(struct conf_option *opt)
49 if (*(void**)opt->mem && *(void**)opt->mem != opt->def) {
50 free(*(void**)opt->mem);
51 *(void**)opt->mem = NULL;
55 int conf_parse_bool(struct conf_option *opt, bool on, const char *arg)
57 *(bool*)opt->mem = on;
61 void conf_default_bool(struct conf_option *opt)
63 *(bool*)opt->mem = (bool)opt->def;
66 int conf_parse_int(struct conf_option *opt, bool on, const char *arg)
68 *(int*)opt->mem = atoi(arg);
72 void conf_default_int(struct conf_option *opt)
74 *(int*)opt->mem = (int)(unsigned long)opt->def;
77 int conf_parse_uint(struct conf_option *opt, bool on, const char *arg)
79 *(unsigned int*)opt->mem = atoi(arg);
83 void conf_default_uint(struct conf_option *opt)
85 *(unsigned int*)opt->mem = (unsigned int)(unsigned long)opt->def;
88 int conf_parse_string(struct conf_option *opt, bool on, const char *arg)
90 char *val = strdup(arg);
95 *(void**)opt->mem = val;
99 void conf_default_string(struct conf_option *opt)
101 *(void**)opt->mem = opt->def;
104 int conf_parse_string_list(struct conf_option *opt, bool on, const char *arg)
109 ret = shl_split_string(arg, &list, NULL, ',', true);
113 opt->type->free(opt);
114 *(void**)opt->mem = list;
118 void conf_default_string_list(struct conf_option *opt)
120 *(void**)opt->mem = opt->def;
123 static int parse_single_grab(char *arg, unsigned int *mods,
124 uint32_t *keysym, bool allow_mods)
126 char *tmp, *start, *end;
138 while (*tmp && *tmp != '>')
142 log_error("missing '>' near '%s'", start);
148 if (!strcasecmp(start, "shift")) {
149 *mods |= SHL_SHIFT_MASK;
150 } else if (!strcasecmp(start, "lock")) {
151 *mods |= SHL_LOCK_MASK;
152 } else if (!strcasecmp(start, "control") ||
153 !strcasecmp(start, "ctrl")) {
154 *mods |= SHL_CONTROL_MASK;
155 } else if (!strcasecmp(start, "alt")) {
156 *mods |= SHL_ALT_MASK;
157 } else if (!strcasecmp(start, "logo")) {
158 *mods |= SHL_LOGO_MASK;
160 log_error("invalid modifier '%s'", start);
171 while (*tmp && *tmp != ' ')
185 *keysym = xkb_keysym_from_name(start);
187 log_error("invalid key '%s'", start);
194 int conf_parse_grab(struct conf_option *opt, bool on, const char *arg)
197 unsigned int list_num, key_num, i, j, k, l;
199 struct conf_grab *grab;
201 ret = shl_split_string(arg, &list, &list_num, ',', false);
205 grab = malloc(sizeof(*grab));
210 memset(grab, 0, sizeof(*grab));
213 grab->mods = malloc(sizeof(*grab->mods) * list_num);
218 memset(grab->mods, 0, sizeof(*grab->mods) * list_num);
220 grab->num_syms = malloc(sizeof(*grab->num_syms) * list_num);
221 if (!grab->num_syms) {
225 memset(grab->num_syms, 0, sizeof(*grab->num_syms) * list_num);
227 grab->keysyms = malloc(sizeof(*grab->keysyms) * list_num);
228 if (!grab->keysyms) {
232 memset(grab->keysyms, 0, sizeof(*grab->keysyms) * list_num);
236 for (i = 0; i < list_num; ++i) {
237 ret = shl_split_string(list[i], &keys, &key_num, '+', false);
245 grab->keysyms[l] = malloc(sizeof(*grab->keysyms[l] * key_num));
246 if (!grab->keysyms[l]) {
253 for (j = 0; j < key_num; ++j) {
254 ret = parse_single_grab(keys[j], &grab->mods[l],
255 &grab->keysyms[l][k],
258 log_error("cannot parse grab '%s' in '%s'",
269 grab->num_syms[l] = k;
275 opt->type->free(opt);
276 *(void**)opt->mem = grab;
280 for (i = 0; i < list_num; ++i)
281 free(grab->keysyms[i]);
284 free(grab->num_syms);
292 void conf_free_grab(struct conf_option *opt)
294 struct conf_grab *grab;
297 if (!*(void**)opt->mem || *(void**)opt->mem == opt->def)
300 grab = *(void**)opt->mem;
301 *(void**)opt->mem = NULL;
303 for (i = 0; i < grab->num; ++i)
304 free(grab->keysyms[i]);
307 free(grab->num_syms);
312 void conf_default_grab(struct conf_option *opt)
314 *(void**)opt->mem = opt->def;
317 const struct conf_type conf_bool = {
319 .parse = conf_parse_bool,
321 .set_default = conf_default_bool,
324 const struct conf_type conf_int = {
325 .flags = CONF_HAS_ARG,
326 .parse = conf_parse_int,
328 .set_default = conf_default_int,
331 const struct conf_type conf_uint = {
332 .flags = CONF_HAS_ARG,
333 .parse = conf_parse_uint,
335 .set_default = conf_default_uint,
338 const struct conf_type conf_string = {
339 .flags = CONF_HAS_ARG,
340 .parse = conf_parse_string,
341 .free = conf_free_value,
342 .set_default = conf_default_string,
345 const struct conf_type conf_string_list = {
346 .flags = CONF_HAS_ARG,
347 .parse = conf_parse_string_list,
348 .free = conf_free_value,
349 .set_default = conf_default_string_list,
352 const struct conf_type conf_grab = {
353 .flags = CONF_HAS_ARG,
354 .parse = conf_parse_grab,
355 .free = conf_free_grab,
356 .set_default = conf_default_grab,
359 /* free all memory that we allocated and reset to initial state */
360 void conf_free(struct conf_option *opts, size_t len)
364 for (i = 0; i < len; ++i) {
365 if (opts[i].type->free)
366 opts[i].type->free(&opts[i]);
371 * Parse command line arguments
372 * This temporarily allocates the short_options and long_options arrays so we
373 * can use the getopt_long() library call. It locks all arguments after they
374 * have been set so command-line options will always overwrite config-options.
376 int conf_parse_argv(struct conf_option *opts, size_t len,
377 int argc, char **argv)
380 struct option *long_options;
385 if (!argv || argc < 1)
388 short_options = malloc(sizeof(char) * (len + 1) * 2);
389 if (!short_options) {
390 log_error("cannot allocate enough memory to parse command line arguments (%d): %m", errno);
394 long_options = malloc(sizeof(struct option) * len * 2);
396 log_error("cannot allocate enough memory to parse command line arguments (%d): %m", errno);
402 short_options[pos++] = ':';
404 for (i = 0; i < len; ++i) {
405 if (opts[i].short_name) {
406 short_options[pos++] = opts[i].short_name;
407 if (opts[i].type->flags & CONF_HAS_ARG)
408 short_options[pos++] = ':';
411 if (opts[i].long_name) {
412 /* skip the "no-" prefix */
413 opt->name = &opts[i].long_name[3];
414 opt->has_arg = !!(opts[i].type->flags & CONF_HAS_ARG);
416 opt->val = 100000 + i;
419 /* boolean args are also added with "no-" prefix */
420 if (!(opts[i].type->flags & CONF_HAS_ARG)) {
421 opt->name = opts[i].long_name;
424 opt->val = 200000 + i;
429 short_options[pos++] = 0;
433 c = getopt_long(argc, argv, short_options,
437 } else if (c == ':') {
438 fprintf(stderr, "Missing argument for: %s\n",
441 } else if (c == '?') {
442 if (optopt && optopt < 100000)
443 fprintf(stderr, "Unknown argument: -%c\n",
446 fprintf(stderr, "Unknown argument: %s\n",
449 fprintf(stderr, "Parameter takes no argument: %s\n",
452 } else if (c < 100000) {
453 for (i = 0; i < len; ++i) {
454 if (opts[i].short_name == c) {
455 ret = opts[i].type->parse(&opts[i],
460 opts[i].flags |= CONF_LOCKED;
461 opts[i].flags |= CONF_DONE;
465 } else if (c < 200000) {
467 ret = opts[i].type->parse(&opts[i], true, optarg);
470 opts[i].flags |= CONF_LOCKED;
471 opts[i].flags |= CONF_DONE;
474 ret = opts[i].type->parse(&opts[i], false, NULL);
477 opts[i].flags |= CONF_LOCKED;
478 opts[i].flags |= CONF_DONE;
485 /* set default values if not configured */
486 for (i = 0; i < len; ++i) {
487 if (!(opts[i].flags & CONF_DONE) &&
488 opts[i].type->set_default) {
489 opts[i].type->set_default(&opts[i]);
493 /* Perform aftercheck:
494 * All arguments that provide an aftercheck will be passed the remaining
495 * arguments in order. If they return a negative error code, it is
496 * interpreted as fatal error and returned to the caller. A positive
497 * error code is interpreted as the amount of remaining arguments that
498 * have been consumed by this aftercheck. 0 means nothing has been
500 * The next argument's aftercheck will only get the now remaning
501 * arguments passed in. If not all arguments are consumed, then this
502 * function will report an error to the caller. */
503 for (i = 0; i < len; ++i) {
504 if (opts[i].aftercheck) {
505 ret = opts[i].aftercheck(&opts[i], argc, argv, optind);
513 fprintf(stderr, "Unparsed remaining arguments starting with: %s\n",
521 static int parse_kv_pair(struct conf_option *opts, size_t len,
522 const char *key, const char *value)
527 struct conf_option *opt;
529 for (i = 0; i < len; ++i) {
534 if (!strcmp(key, opt->long_name))
536 else if (!strcmp(key, &opt->long_name[3]))
541 if (opt->type->flags & CONF_HAS_ARG && !value) {
542 log_error("config option '%s' requires an argument", key);
544 } else if (!(opt->type->flags & CONF_HAS_ARG) && value) {
545 log_error("config option '%s' does not take arguments", key);
549 /* ignore if already set by command-line arguments */
550 if (opt->flags & CONF_LOCKED)
553 ret = opt->type->parse(opt, set, value);
557 opt->flags |= CONF_DONE;
561 log_error("unknown config option '%s'", key);
565 static void strip_spaces(char **buf)
569 while (**buf == ' ' || **buf == '\r' || **buf == '\t')
581 while (*tail == ' ' || *tail == '\r' || *tail == '\t')
585 static int parse_line(struct conf_option *opts, size_t olen,
586 char **buf, size_t *size)
613 } else if (*line == '\n') {
616 } else if (*line == '#') {
619 } else if (*line != '=') {
640 } else if (*line == '\n') {
643 } else if (*line == '#') {
665 strip_spaces(&value);
667 ret = parse_kv_pair(opts, olen, key, value);
683 static int parse_buffer(struct conf_option *opts, size_t len,
684 char *buf, size_t size)
688 while (!ret && size > 0)
689 ret = parse_line(opts, len, &buf, &size);
694 /* chunk size when reading config files */
695 #define CONF_BUFSIZE 4096
697 /* This reads the file at \path in memory and parses it as if it was given as
698 * command line options. */
699 int conf_parse_file(struct conf_option *opts, size_t len, const char *path)
705 if (!opts || !len || !path)
708 if (access(path, F_OK))
711 if (access(path, R_OK)) {
712 log_error("read access to config file %s denied", path);
716 log_info("reading config file %s", path);
717 fd = open(path, O_RDONLY | O_CLOEXEC | O_NOCTTY);
719 log_error("cannot open %s (%d): %m", path, errno);
728 if (size - pos < CONF_BUFSIZE) {
729 tmp = realloc(buf, size + CONF_BUFSIZE + 1);
731 log_error("cannot allocate enough memory to parse config file %s (%d): %m",
737 size += CONF_BUFSIZE;
740 ret = read(fd, &buf[pos], CONF_BUFSIZE);
742 log_error("cannot read from config file %s (%d): %m",
751 ret = parse_buffer(opts, len, buf, pos);
759 int conf_parse_file_f(struct conf_option *opts, size_t len,
760 const char *format, ...)
766 if (!opts || !len || !format)
769 va_start(list, format);
770 ret = vasprintf(&path, format, list);
774 log_error("cannot allocate memory for config-file path");
778 ret = conf_parse_file(opts, len, path);
783 int conf_parse_standard_files(struct conf_option *opts, size_t len,
789 ret = conf_parse_file_f(opts, len, "/etc/%s.conf", fname);
793 home = getenv("HOME");
795 ret = conf_parse_file_f(opts, len, "%s/.%s.conf", home, fname);