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.
39 #include <xkbcommon/xkbcommon.h>
44 #define LOG_SUBSYSTEM "config"
46 void conf_free_value(struct conf_option *opt)
48 if (*(void**)opt->mem && *(void**)opt->mem != opt->def) {
49 free(*(void**)opt->mem);
50 *(void**)opt->mem = NULL;
54 int conf_parse_bool(struct conf_option *opt, bool on, const char *arg)
56 *(bool*)opt->mem = on;
60 void conf_default_bool(struct conf_option *opt)
62 *(bool*)opt->mem = (bool)opt->def;
65 int conf_parse_int(struct conf_option *opt, bool on, const char *arg)
67 *(int*)opt->mem = atoi(arg);
71 void conf_default_int(struct conf_option *opt)
73 *(int*)opt->mem = (int)(unsigned long)opt->def;
76 int conf_parse_uint(struct conf_option *opt, bool on, const char *arg)
78 *(unsigned int*)opt->mem = atoi(arg);
82 void conf_default_uint(struct conf_option *opt)
84 *(unsigned int*)opt->mem = (unsigned int)(unsigned long)opt->def;
87 int conf_parse_string(struct conf_option *opt, bool on, const char *arg)
89 char *val = strdup(arg);
94 *(void**)opt->mem = val;
98 void conf_default_string(struct conf_option *opt)
100 *(void**)opt->mem = opt->def;
103 int conf_parse_string_list(struct conf_option *opt, bool on, const char *arg)
108 ret = shl_split_string(arg, &list, NULL, ',', true);
112 opt->type->free(opt);
113 *(void**)opt->mem = list;
117 void conf_default_string_list(struct conf_option *opt)
119 *(void**)opt->mem = opt->def;
122 static int parse_single_grab(char *arg, unsigned int *mods,
123 uint32_t *keysym, bool allow_mods)
125 char *tmp, *start, *end;
137 while (*tmp && *tmp != '>')
141 log_error("missing '>' near '%s'", start);
147 if (!strcasecmp(start, "shift")) {
148 *mods |= SHL_SHIFT_MASK;
149 } else if (!strcasecmp(start, "lock")) {
150 *mods |= SHL_LOCK_MASK;
151 } else if (!strcasecmp(start, "control") ||
152 !strcasecmp(start, "ctrl")) {
153 *mods |= SHL_CONTROL_MASK;
154 } else if (!strcasecmp(start, "alt")) {
155 *mods |= SHL_ALT_MASK;
156 } else if (!strcasecmp(start, "logo")) {
157 *mods |= SHL_LOGO_MASK;
159 log_error("invalid modifier '%s'", start);
170 while (*tmp && *tmp != ' ')
184 *keysym = xkb_keysym_from_name(start);
186 log_error("invalid key '%s'", start);
193 int conf_parse_grab(struct conf_option *opt, bool on, const char *arg)
196 unsigned int list_num, key_num, i, j, k, l;
198 struct conf_grab *grab;
200 ret = shl_split_string(arg, &list, &list_num, ',', false);
204 grab = malloc(sizeof(*grab));
209 memset(grab, 0, sizeof(*grab));
212 grab->mods = malloc(sizeof(*grab->mods) * list_num);
217 memset(grab->mods, 0, sizeof(*grab->mods) * list_num);
219 grab->num_syms = malloc(sizeof(*grab->num_syms) * list_num);
220 if (!grab->num_syms) {
224 memset(grab->num_syms, 0, sizeof(*grab->num_syms) * list_num);
226 grab->keysyms = malloc(sizeof(*grab->keysyms) * list_num);
227 if (!grab->keysyms) {
231 memset(grab->keysyms, 0, sizeof(*grab->keysyms) * list_num);
235 for (i = 0; i < list_num; ++i) {
236 ret = shl_split_string(list[i], &keys, &key_num, '+', false);
244 grab->keysyms[l] = malloc(sizeof(*grab->keysyms[l] * key_num));
245 if (!grab->keysyms[l]) {
252 for (j = 0; j < key_num; ++j) {
253 ret = parse_single_grab(keys[j], &grab->mods[l],
254 &grab->keysyms[l][k],
257 log_error("cannot parse grab '%s' in '%s'",
268 grab->num_syms[l] = k;
274 opt->type->free(opt);
275 *(void**)opt->mem = grab;
279 for (i = 0; i < list_num; ++i)
280 free(grab->keysyms[i]);
283 free(grab->num_syms);
291 void conf_free_grab(struct conf_option *opt)
293 struct conf_grab *grab;
296 if (!*(void**)opt->mem || *(void**)opt->mem == opt->def)
299 grab = *(void**)opt->mem;
300 *(void**)opt->mem = NULL;
302 for (i = 0; i < grab->num; ++i)
303 free(grab->keysyms[i]);
306 free(grab->num_syms);
311 void conf_default_grab(struct conf_option *opt)
313 *(void**)opt->mem = opt->def;
316 const struct conf_type conf_bool = {
318 .parse = conf_parse_bool,
320 .set_default = conf_default_bool,
323 const struct conf_type conf_int = {
324 .flags = CONF_HAS_ARG,
325 .parse = conf_parse_int,
327 .set_default = conf_default_int,
330 const struct conf_type conf_uint = {
331 .flags = CONF_HAS_ARG,
332 .parse = conf_parse_uint,
334 .set_default = conf_default_uint,
337 const struct conf_type conf_string = {
338 .flags = CONF_HAS_ARG,
339 .parse = conf_parse_string,
340 .free = conf_free_value,
341 .set_default = conf_default_string,
344 const struct conf_type conf_string_list = {
345 .flags = CONF_HAS_ARG,
346 .parse = conf_parse_string_list,
347 .free = conf_free_value,
348 .set_default = conf_default_string_list,
351 const struct conf_type conf_grab = {
352 .flags = CONF_HAS_ARG,
353 .parse = conf_parse_grab,
354 .free = conf_free_grab,
355 .set_default = conf_default_grab,
358 /* free all memory that we allocated and reset to initial state */
359 void conf_free(struct conf_option *opts, size_t len)
363 for (i = 0; i < len; ++i) {
364 if (opts[i].type->free)
365 opts[i].type->free(&opts[i]);
370 * Parse command line arguments
371 * This temporarily allocates the short_options and long_options arrays so we
372 * can use the getopt_long() library call. It locks all arguments after they
373 * have been set so command-line options will always overwrite config-options.
375 int conf_parse_argv(struct conf_option *opts, size_t len,
376 int argc, char **argv)
379 struct option *long_options;
384 if (!argv || argc < 1)
387 short_options = malloc(sizeof(char) * (len + 1) * 2);
388 if (!short_options) {
389 log_error("cannot allocate enough memory to parse command line arguments (%d): %m", errno);
393 long_options = malloc(sizeof(struct option) * len * 2);
395 log_error("cannot allocate enough memory to parse command line arguments (%d): %m", errno);
401 short_options[pos++] = ':';
403 for (i = 0; i < len; ++i) {
404 if (opts[i].short_name) {
405 short_options[pos++] = opts[i].short_name;
406 if (opts[i].type->flags & CONF_HAS_ARG)
407 short_options[pos++] = ':';
410 if (opts[i].long_name) {
411 /* skip the "no-" prefix */
412 opt->name = &opts[i].long_name[3];
413 opt->has_arg = !!(opts[i].type->flags & CONF_HAS_ARG);
415 opt->val = 100000 + i;
418 /* boolean args are also added with "no-" prefix */
419 if (!(opts[i].type->flags & CONF_HAS_ARG)) {
420 opt->name = opts[i].long_name;
423 opt->val = 200000 + i;
428 short_options[pos++] = 0;
432 c = getopt_long(argc, argv, short_options,
436 } else if (c == ':') {
437 fprintf(stderr, "Missing argument for: %s\n",
440 } else if (c == '?') {
441 if (optopt && optopt < 100000)
442 fprintf(stderr, "Unknown argument: -%c\n",
445 fprintf(stderr, "Unknown argument: %s\n",
448 fprintf(stderr, "Parameter takes no argument: %s\n",
451 } else if (c < 100000) {
452 for (i = 0; i < len; ++i) {
453 if (opts[i].short_name == c) {
454 ret = opts[i].type->parse(&opts[i],
459 opts[i].flags |= CONF_LOCKED;
460 opts[i].flags |= CONF_DONE;
464 } else if (c < 200000) {
466 ret = opts[i].type->parse(&opts[i], true, optarg);
469 opts[i].flags |= CONF_LOCKED;
470 opts[i].flags |= CONF_DONE;
473 ret = opts[i].type->parse(&opts[i], false, NULL);
476 opts[i].flags |= CONF_LOCKED;
477 opts[i].flags |= CONF_DONE;
484 /* set default values if not configured */
485 for (i = 0; i < len; ++i) {
486 if (!(opts[i].flags & CONF_DONE) &&
487 opts[i].type->set_default) {
488 opts[i].type->set_default(&opts[i]);
492 /* Perform aftercheck:
493 * All arguments that provide an aftercheck will be passed the remaining
494 * arguments in order. If they return a negative error code, it is
495 * interpreted as fatal error and returned to the caller. A positive
496 * error code is interpreted as the amount of remaining arguments that
497 * have been consumed by this aftercheck. 0 means nothing has been
499 * The next argument's aftercheck will only get the now remaning
500 * arguments passed in. If not all arguments are consumed, then this
501 * function will report an error to the caller. */
502 for (i = 0; i < len; ++i) {
503 if (opts[i].aftercheck) {
504 ret = opts[i].aftercheck(&opts[i], argc, argv, optind);
512 fprintf(stderr, "Unparsed remaining arguments starting with: %s\n",
520 static int parse_kv_pair(struct conf_option *opts, size_t len,
521 const char *key, const char *value)
526 struct conf_option *opt;
528 for (i = 0; i < len; ++i) {
533 if (!strcmp(key, opt->long_name))
535 else if (!strcmp(key, &opt->long_name[3]))
540 if (opt->type->flags & CONF_HAS_ARG && !value) {
541 log_error("config option '%s' requires an argument", key);
543 } else if (!(opt->type->flags & CONF_HAS_ARG) && value) {
544 log_error("config option '%s' does not take arguments", key);
548 /* ignore if already set by command-line arguments */
549 if (opt->flags & CONF_LOCKED)
552 ret = opt->type->parse(opt, set, value);
556 opt->flags |= CONF_DONE;
560 log_error("unknown config option '%s'", key);
564 static void strip_spaces(char **buf)
568 while (**buf == ' ' || **buf == '\r' || **buf == '\t')
580 while (*tail == ' ' || *tail == '\r' || *tail == '\t')
584 static int parse_line(struct conf_option *opts, size_t olen,
585 char **buf, size_t *size)
612 } else if (*line == '\n') {
615 } else if (*line == '#') {
618 } else if (*line != '=') {
639 } else if (*line == '\n') {
642 } else if (*line == '#') {
664 strip_spaces(&value);
666 ret = parse_kv_pair(opts, olen, key, value);
682 static int parse_buffer(struct conf_option *opts, size_t len,
683 char *buf, size_t size)
687 while (!ret && size > 0)
688 ret = parse_line(opts, len, &buf, &size);
693 /* chunk size when reading config files */
694 #define CONF_BUFSIZE 4096
696 /* This reads the file at \path in memory and parses it as if it was given as
697 * command line options. */
698 int conf_parse_file(struct conf_option *opts, size_t len, const char *path)
707 log_info("reading config file %s", path);
708 fd = open(path, O_RDONLY | O_CLOEXEC | O_NOCTTY);
710 log_error("cannot open %s (%d): %m", path, errno);
719 if (size - pos < CONF_BUFSIZE) {
720 tmp = realloc(buf, size + CONF_BUFSIZE + 1);
722 log_error("cannot allocate enough memory to parse config file %s (%d): %m",
728 size += CONF_BUFSIZE;
731 ret = read(fd, &buf[pos], CONF_BUFSIZE);
733 log_error("cannot read from config file %s (%d): %m",
742 ret = parse_buffer(opts, len, buf, pos);
750 int conf_parse_all_files(struct conf_option *opts, size_t len)
753 const char *file, *home;
758 file = "/etc/kmscon.conf";
759 if (!access(file, F_OK)) {
760 if (access(file, R_OK))
761 log_warning("config file %s exists but read access was denied",
764 ret = conf_parse_file(opts, len, file);
770 home = getenv("HOME");
772 ret = asprintf(&path, "%s/.kmscon.conf", home);
774 log_warning("cannot allocate enough resources to build a config-path");
778 if (!access(path, F_OK)) {
779 if (access(path, R_OK))
780 log_warning("config file %s exists but read access was denied",
783 ret = conf_parse_file(opts, len, path);