kmscon: implement per-seat configuration
[platform/upstream/kmscon.git] / src / conf.c
1 /*
2  * App Configuration
3  *
4  * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com>
5  *
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:
13  *
14  * The above copyright notice and this permission notice shall be included
15  * in all copies or substantial portions of the Software.
16  *
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.
24  */
25
26 /*
27  * Configuration
28  * Implementation of the configuration parsers.
29  */
30
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <getopt.h>
34 #include <stdarg.h>
35 #include <stdbool.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <xkbcommon/xkbcommon.h>
41 #include "conf.h"
42 #include "log.h"
43 #include "shl_misc.h"
44
45 #define LOG_SUBSYSTEM "config"
46
47 /*
48  * Config Contexts
49  */
50
51 struct conf_ctx {
52         struct conf_option *opts;
53         size_t onum;
54         void *mem;
55 };
56
57 int conf_ctx_new(struct conf_ctx **out, const struct conf_option *opts,
58                   size_t onum, void *mem)
59 {
60         struct conf_ctx *ctx;
61         size_t size;
62
63         if (!out || !opts || !onum)
64                 return -EINVAL;
65
66         size = sizeof(*ctx) + onum * sizeof(*opts);
67         ctx = malloc(size);
68         if (!ctx) {
69                 log_error("cannot allocate memory for config context");
70                 return -ENOMEM;
71         }
72         memset(ctx, 0, size);
73         ctx->opts = (void*)((char*)ctx + sizeof(*ctx));
74         ctx->onum = onum;
75         ctx->mem = mem;
76         memcpy(ctx->opts, opts, onum * sizeof(*opts));
77
78         conf_ctx_reset(ctx);
79
80         *out = ctx;
81         return 0;
82 }
83
84 void conf_ctx_free(struct conf_ctx *ctx)
85 {
86         if (!ctx)
87                 return;
88
89         conf_ctx_reset(ctx);
90         free(ctx);
91 }
92
93 void conf_ctx_reset(struct conf_ctx *ctx)
94 {
95         unsigned int i;
96
97         if (!ctx)
98                 return;
99
100         for (i = 0; i < ctx->onum; ++i) {
101                 if (ctx->opts[i].type->free)
102                         ctx->opts[i].type->free(&ctx->opts[i]);
103                 ctx->opts[i].flags = 0;
104                 if (ctx->opts[i].type->set_default)
105                         ctx->opts[i].type->set_default(&ctx->opts[i]);
106         }
107 }
108
109 void *conf_ctx_get_mem(struct conf_ctx *ctx)
110 {
111         if (!ctx)
112                 return NULL;
113
114         return ctx->mem;
115 }
116
117 /*
118  * Copy all entries from \src into \ctx
119  * This calls the "copy" callback for each option inside of \ctx with the
120  * corresponding option inside of \src if both have the same type. If the types
121  * do not match, nothing is done.
122  */
123 int conf_ctx_parse_ctx(struct conf_ctx *ctx, const struct conf_ctx *src)
124 {
125         unsigned int i;
126         struct conf_option *d, *s;
127         int ret;
128
129         if (!ctx || !src)
130                 return -EINVAL;
131
132         for (i = 0; i < ctx->onum && i < src->onum; ++i) {
133                 d = &ctx->opts[i];
134                 s = &src->opts[i];
135
136                 if (d->type != s->type)
137                         continue;
138                 if (!d->type->copy)
139                         continue;
140
141                 ret = d->type->copy(d, s);
142                 if (ret)
143                         return ret;
144         }
145
146         return 0;
147 }
148
149 /*
150  * Parse command line arguments
151  * This temporarily allocates the short_options and long_options arrays so we
152  * can use the getopt_long() library call. It locks all arguments after they
153  * have been set so command-line options will always overwrite config-options.
154  */
155 int conf_ctx_parse_argv(struct conf_ctx *ctx, int argc, char **argv)
156 {
157         char *short_options;
158         struct option *long_options;
159         struct option *opt;
160         struct conf_option *o;
161         size_t i, pos;
162         int c, ret;
163
164         if (!ctx || !argv)
165                 return -EINVAL;
166
167         short_options = malloc(sizeof(char) * (ctx->onum + 1) * 2);
168         if (!short_options) {
169                 log_error("out of memory to parse cmd-line arguments (%d): %m",
170                           errno);
171                 return -ENOMEM;
172         }
173
174         long_options = malloc(sizeof(struct option) * ctx->onum * 2);
175         if (!long_options) {
176                 log_error("out of memory to parse cmd-line arguments (%d): %m",
177                           errno);
178                 free(short_options);
179                 return -ENOMEM;
180         }
181
182         pos = 0;
183         short_options[pos++] = ':';
184         opt = long_options;
185         for (i = 0; i < ctx->onum; ++i) {
186                 if (ctx->opts[i].short_name) {
187                         short_options[pos++] = ctx->opts[i].short_name;
188                         if (ctx->opts[i].type->flags & CONF_HAS_ARG)
189                                 short_options[pos++] = ':';
190                 }
191
192                 if (ctx->opts[i].long_name) {
193                         /* skip the "no-" prefix */
194                         opt->name = &ctx->opts[i].long_name[3];
195                         opt->has_arg = !!(ctx->opts[i].type->flags &
196                                                                 CONF_HAS_ARG);
197                         opt->flag = NULL;
198                         opt->val = 100000 + i;
199                         ++opt;
200
201                         /* boolean args are also added with "no-" prefix */
202                         if (!(ctx->opts[i].type->flags & CONF_HAS_ARG)) {
203                                 opt->name = ctx->opts[i].long_name;
204                                 opt->has_arg = 0;
205                                 opt->flag = NULL;
206                                 opt->val = 200000 + i;
207                                 ++opt;
208                         }
209                 }
210         }
211         short_options[pos++] = 0;
212
213         opterr = 0;
214         while (1) {
215                 c = getopt_long(argc, argv, short_options,
216                                 long_options, NULL);
217                 if (c <= 0) {
218                         break;
219                 } else if (c == ':') {
220                         fprintf(stderr, "Missing argument for: %s\n",
221                                 argv[optind - 1]);
222                         return -EFAULT;
223                 } else if (c == '?') {
224                         if (optopt && optopt < 100000)
225                                 fprintf(stderr, "Unknown argument: -%c\n",
226                                         optopt);
227                         else if (!optopt)
228                                 fprintf(stderr, "Unknown argument: %s\n",
229                                         argv[optind - 1]);
230                         else
231                                 fprintf(stderr, "Option takes no arg: %s\n",
232                                         argv[optind - 1]);
233                         return -EFAULT;
234                 } else if (c < 100000) {
235                         for (i = 0; i < ctx->onum; ++i) {
236                                 o = &ctx->opts[i];
237
238                                 if (o->short_name != c)
239                                         continue;
240
241                                 if (o->type->parse) {
242                                         ret = o->type->parse(o, true, optarg);
243                                         if (ret)
244                                                 return ret;
245                                 }
246
247                                 o->flags |= CONF_LOCKED;
248                                 o->flags |= CONF_DONE;
249                                 break;
250                         }
251                 } else if (c < 200000) {
252                         i = c - 100000;
253                         o = &ctx->opts[i];
254
255                         if (o->type->parse) {
256                                 ret = o->type->parse(o, true, optarg);
257                                 if (ret)
258                                         return ret;
259                         }
260
261                         o->flags |= CONF_LOCKED;
262                         o->flags |= CONF_DONE;
263                 } else {
264                         i = c - 200000;
265                         o = &ctx->opts[i];
266
267                         if (o->type->parse) {
268                                 ret = o->type->parse(o, false, NULL);
269                                 if (ret)
270                                         return ret;
271                         }
272
273                         o->flags |= CONF_LOCKED;
274                         o->flags |= CONF_DONE;
275                 }
276         }
277
278         free(long_options);
279         free(short_options);
280
281         /* Perform aftercheck:
282          * All arguments that provide an aftercheck will be passed the remaining
283          * arguments in order. If they return a negative error code, it is
284          * interpreted as fatal error and returned to the caller. A positive
285          * error code is interpreted as the amount of remaining arguments that
286          * have been consumed by this aftercheck. 0 means nothing has been
287          * consumed.
288          * The next argument's aftercheck will only get the now remaning
289          * arguments passed in. If not all arguments are consumed, then this
290          * function will report an error to the caller. */
291         for (i = 0; i < ctx->onum; ++i) {
292                 o = &ctx->opts[i];
293                 if (o->aftercheck) {
294                         ret = o->aftercheck(o, argc, argv, optind);
295                         if (ret < 0)
296                                 return ret;
297                         optind += ret;
298                 }
299         }
300
301         if (optind < argc) {
302                 fprintf(stderr, "Unparsed remaining args starting with: %s\n",
303                         argv[optind]);
304                 return -EFAULT;
305         }
306
307         return 0;
308 }
309
310 static int parse_kv_pair(struct conf_option *opts, size_t len,
311                          const char *key, const char *value)
312 {
313         unsigned int i;
314         int ret;
315         bool set;
316         struct conf_option *opt;
317
318         for (i = 0; i < len; ++i) {
319                 opt = &opts[i];
320                 if (!opt->long_name)
321                         continue;
322
323                 if (!strcmp(key, opt->long_name))
324                         set = false;
325                 else if (!strcmp(key, &opt->long_name[3]))
326                         set = true;
327                 else
328                         continue;
329
330                 if (opt->type->flags & CONF_HAS_ARG && !value) {
331                         log_error("config option '%s' requires an argument",
332                                   key);
333                         return -EFAULT;
334                 } else if (!(opt->type->flags & CONF_HAS_ARG) && value) {
335                         log_error("config option '%s' does not take arguments",
336                                   key);
337                         return -EFAULT;
338                 }
339
340                 /* ignore if already set by command-line arguments */
341                 if (opt->flags & CONF_LOCKED)
342                         return 0;
343
344                 if (opt->type->parse) {
345                         ret = opt->type->parse(opt, set, value);
346                         if (ret)
347                                 return ret;
348                 }
349
350                 opt->flags |= CONF_DONE;
351                 return 0;
352         }
353
354         log_error("unknown config option '%s'", key);
355         return -EFAULT;
356 }
357
358 static void strip_spaces(char **buf)
359 {
360         char *tail;
361
362         while (**buf == ' ' || **buf == '\r' || **buf == '\t')
363                 ++*buf;
364
365         if (!**buf)
366                 return;
367
368         tail = *buf;
369         while (*tail)
370                 ++tail;
371
372         --tail;
373
374         while (*tail == ' ' || *tail == '\r' || *tail == '\t')
375                 *tail-- = 0;
376 }
377
378 static int parse_line(struct conf_option *opts, size_t olen,
379                       char **buf, size_t *size)
380 {
381         char *key;
382         char *value = NULL;
383         char *line;
384         char c;
385         size_t len, klen;
386         int ret;
387
388         line = *buf;
389         len = *size;
390
391         /* parse key */
392         key = line;
393         while (len) {
394                 c = *line;
395                 if (c == '\n' ||
396                     c == '#' ||
397                     c == '=')
398                         break;
399                 ++line;
400                 --len;
401         }
402
403         if (!len) {
404                 *line = 0;
405                 goto done;
406         } else if (*line == '\n') {
407                 *line = 0;
408                 goto done;
409         } else if (*line == '#') {
410                 *line = 0;
411                 goto skip_comment;
412         } else if (*line != '=') {
413                 return -EFAULT;
414         }
415
416         *line++ = 0;
417         --len;
418
419         /* parse value */
420         value = line;
421         while (len) {
422                 c = *line;
423                 if (c == '\n' ||
424                     c == '#')
425                         break;
426                 ++line;
427                 --len;
428         }
429
430         if (!len) {
431                 *line = 0;
432                 goto done;
433         } else if (*line == '\n') {
434                 *line = 0;
435                 goto done;
436         } else if (*line == '#') {
437                 *line = 0;
438                 goto skip_comment;
439         } else {
440                 return -EFAULT;
441         }
442
443 skip_comment:
444         while (len) {
445                 c = *line;
446                 if (c == '\n')
447                         break;
448                 ++line;
449                 --len;
450         }
451
452 done:
453         strip_spaces(&key);
454         
455         klen = strlen(key);
456         if (klen > 0) {
457                 if (value)
458                         strip_spaces(&value);
459
460                 ret = parse_kv_pair(opts, olen, key, value);
461                 if (ret)
462                         return ret;
463         }
464
465         if (!len) {
466                 *buf = NULL;
467                 *size = 0;
468         } else {
469                 *buf = ++line;
470                 *size = --len;
471         }
472
473         return 0;
474 }
475
476 static int parse_buffer(struct conf_option *opts, size_t len,
477                         char *buf, size_t size)
478 {
479         int ret = 0;
480
481         while (!ret && size > 0)
482                 ret = parse_line(opts, len, &buf, &size);
483
484         return ret;
485 }
486
487 /* chunk size when reading config files */
488 #define CONF_BUFSIZE 4096
489
490 /* This reads the file at \path in memory and parses it as if it was given as
491  * command line options. */
492 static int conf_parse_file(struct conf_option *opts, size_t len,
493                            const char *path)
494 {
495         int fd, ret;
496         size_t size, pos;
497         char *buf, *tmp;
498
499         if (!opts || !len || !path)
500                 return -EINVAL;
501
502         if (access(path, F_OK))
503                 return 0;
504
505         if (access(path, R_OK)) {
506                 log_error("read access to config file %s denied", path);
507                 return -EACCES;
508         }
509
510         log_info("reading config file %s", path);
511         fd = open(path, O_RDONLY | O_CLOEXEC | O_NOCTTY);
512         if (fd < 0) {
513                 log_error("cannot open %s (%d): %m", path, errno);
514                 return -EFAULT;
515         }
516
517         buf = NULL;
518         size = 0;
519         pos = 0;
520
521         do {
522                 if (size - pos < CONF_BUFSIZE) {
523                         tmp = realloc(buf, size + CONF_BUFSIZE + 1);
524                         if (!tmp) {
525                                 log_error("cannot allocate enough memory to parse config file %s (%d): %m",
526                                           path, errno);
527                                 ret = -ENOMEM;
528                                 goto out_free;
529                         }
530                         buf = tmp;
531                         size += CONF_BUFSIZE;
532                 }
533
534                 ret = read(fd, &buf[pos], CONF_BUFSIZE);
535                 if (ret < 0) {
536                         log_error("cannot read from config file %s (%d): %m",
537                                   path, errno);
538                         ret = -EFAULT;
539                         goto out_free;
540                 }
541                 pos += ret;
542         } while (ret > 0);
543
544         buf[pos] = 0;
545         ret = parse_buffer(opts, len, buf, pos);
546
547 out_free:
548         free(buf);
549         close(fd);
550         return ret;
551 }
552
553 int conf_ctx_parse_file(struct conf_ctx *ctx, const char *format, ...)
554 {
555         va_list list;
556         char *path;
557         int ret;
558
559         if (!ctx || !format)
560                 return -EINVAL;
561
562         va_start(list, format);
563         ret = vasprintf(&path, format, list);
564         va_end(list);
565
566         if (ret < 0) {
567                 log_error("cannot allocate memory for config-file path");
568                 return -ENOMEM;
569         }
570
571         ret = conf_parse_file(ctx->opts, ctx->onum, path);
572         free(path);
573         return ret;
574 }
575
576 /*
577  * Config Types
578  * Each option that can be parsed must be a specific config-type. A config-type
579  * is used to parse, free and reset the value. It must implement the following
580  * callbacks:
581  *   set_default: This should link opt->mem to opt->def and must not fail. It is
582  *                called during initialization and reset.
583  *   free: This should free any allocated memory and reset the option to the
584  *         initial state. It must not fail.
585  *   parse: This should parse a command-line option. Return 0 on success.
586  *   copy: Copy data from source into destination. Return 0 on success.
587  *
588  * The backing memory is zeroed on reset so a config-type must be able to handle
589  * this as "not set". Also, the "free" callback should reset it to zero (which
590  * is the initial state).
591  */
592
593 /* Miscellaneous helper */
594
595 static void conf_free_value(struct conf_option *opt)
596 {
597         if (*(void**)opt->mem) {
598                 if (*(void**)opt->mem != opt->def)
599                         free(*(void**)opt->mem);
600                 *(void**)opt->mem = NULL;
601         }
602 }
603
604 /* Boolean Option */
605
606 static void conf_default_bool(struct conf_option *opt)
607 {
608         *(bool*)opt->mem = (bool)opt->def;
609 }
610
611 static void conf_free_bool(struct conf_option *opt)
612 {
613         *(bool*)opt->mem = false;
614 }
615
616 static int conf_parse_bool(struct conf_option *opt, bool on, const char *arg)
617 {
618         *(bool*)opt->mem = on;
619         return 0;
620 }
621
622 static int conf_copy_bool(struct conf_option *opt,
623                           const struct conf_option *src)
624 {
625         *(bool*)opt->mem = *(bool*)src->mem;
626         return 0;
627 }
628
629 const struct conf_type conf_bool = {
630         .flags = 0,
631         .set_default = conf_default_bool,
632         .free = conf_free_bool,
633         .parse = conf_parse_bool,
634         .copy = conf_copy_bool,
635 };
636
637 /* Int Option */
638
639 static int conf_parse_int(struct conf_option *opt, bool on, const char *arg)
640 {
641         *(int*)opt->mem = atoi(arg);
642         return 0;
643 }
644
645 static void conf_free_int(struct conf_option *opt)
646 {
647         *(int*)opt->mem = 0;
648 }
649
650 static void conf_default_int(struct conf_option *opt)
651 {
652         *(int*)opt->mem = (int)(unsigned long)opt->def;
653 }
654
655 static int conf_copy_int(struct conf_option *opt,
656                          const struct conf_option *src)
657 {
658         *(int*)opt->mem = *(int*)src->mem;
659         return 0;
660 }
661
662 const struct conf_type conf_int = {
663         .flags = CONF_HAS_ARG,
664         .set_default = conf_default_int,
665         .free = conf_free_int,
666         .parse = conf_parse_int,
667         .copy = conf_copy_int,
668 };
669
670 /* Unsigned Int Option */
671
672 static void conf_default_uint(struct conf_option *opt)
673 {
674         *(unsigned int*)opt->mem = (unsigned int)(unsigned long)opt->def;
675 }
676
677 static void conf_free_uint(struct conf_option *opt)
678 {
679         *(unsigned int*)opt->mem = 0;
680 }
681
682 static int conf_parse_uint(struct conf_option *opt, bool on, const char *arg)
683 {
684         *(unsigned int*)opt->mem = atoi(arg);
685         return 0;
686 }
687
688 static int conf_copy_uint(struct conf_option *opt,
689                           const struct conf_option *src)
690 {
691         *(unsigned int*)opt->mem = *(unsigned int*)src->mem;
692         return 0;
693 }
694
695 const struct conf_type conf_uint = {
696         .flags = CONF_HAS_ARG,
697         .set_default = conf_default_uint,
698         .free = conf_free_uint,
699         .parse = conf_parse_uint,
700         .copy = conf_copy_uint,
701 };
702
703 /* String Option */
704
705 static void conf_default_string(struct conf_option *opt)
706 {
707         opt->type->free(opt);
708         *(void**)opt->mem = opt->def;
709 }
710
711 static int conf_parse_string(struct conf_option *opt, bool on, const char *arg)
712 {
713         char *val = strdup(arg);
714         if (!val)
715                 return -ENOMEM;
716
717         opt->type->free(opt);
718         *(void**)opt->mem = val;
719         return 0;
720 }
721
722 static int conf_copy_string(struct conf_option *opt,
723                             const struct conf_option *src)
724 {
725         char *val;
726
727         if (!*(void**)src->mem) {
728                 val = NULL;
729         } else {
730                 val = strdup(*(void**)src->mem);
731                 if (!val)
732                         return -ENOMEM;
733         }
734
735         opt->type->free(opt);
736         *(void**)opt->mem = val;
737         return 0;
738 }
739
740 const struct conf_type conf_string = {
741         .flags = CONF_HAS_ARG,
742         .set_default = conf_default_string,
743         .free = conf_free_value,
744         .parse = conf_parse_string,
745         .copy = conf_copy_string,
746 };
747
748 /* Stringlist Option */
749
750 static void conf_default_string_list(struct conf_option *opt)
751 {
752         opt->type->free(opt);
753         *(void**)opt->mem = opt->def;
754 }
755
756 static int conf_parse_string_list(struct conf_option *opt, bool on,
757                                   const char *arg)
758 {
759         int ret;
760         char **list;
761
762         ret = shl_split_string(arg, &list, NULL, ',', true);
763         if (ret)
764                 return ret;
765
766         opt->type->free(opt);
767         *(char***)opt->mem = list;
768         return 0;
769 }
770
771 static int conf_copy_string_list(struct conf_option *opt,
772                                  const struct conf_option *src)
773 {
774         int ret;
775         char **t;
776
777         if (!(void***)src->mem) {
778                 t = NULL;
779         } else {
780                 ret = shl_dup_array(&t, *(char***)src->mem);
781                 if (ret)
782                         return ret;
783         }
784
785         opt->type->free(opt);
786         *(char***)opt->mem = t;
787         return 0;
788 }
789
790 const struct conf_type conf_string_list = {
791         .flags = CONF_HAS_ARG,
792         .set_default = conf_default_string_list,
793         .free = conf_free_value,
794         .parse = conf_parse_string_list,
795         .copy = conf_copy_string_list,
796 };
797
798 /* Grab Option */
799
800 static void conf_default_grab(struct conf_option *opt)
801 {
802         opt->type->free(opt);
803         *(void**)opt->mem = opt->def;
804 }
805
806 static void conf_free_grab(struct conf_option *opt)
807 {
808         struct conf_grab *grab;
809         unsigned int i;
810
811         grab = *(void**)opt->mem;
812         *(void**)opt->mem = NULL;
813
814         if (!grab || grab == opt->def)
815                 return;
816
817         for (i = 0; i < grab->num; ++i)
818                 free(grab->keysyms[i]);
819
820         free(grab->keysyms);
821         free(grab->num_syms);
822         free(grab->mods);
823         free(grab);
824 }
825
826 static int parse_single_grab(char *arg, unsigned int *mods,
827                              uint32_t *keysym, bool allow_mods)
828 {
829         char *tmp, *start, *end;
830
831         tmp = arg;
832         do {
833                 while (*tmp == ' ')
834                         ++tmp;
835                 if (!allow_mods)
836                         break;
837                 if (*tmp != '<')
838                         break;
839
840                 start = tmp;
841                 while (*tmp && *tmp != '>')
842                         ++tmp;
843
844                 if (*tmp != '>') {
845                         log_error("missing '>' near '%s'", start);
846                         return -EFAULT;
847                 }
848
849                 *tmp++ = 0;
850                 ++start;
851                 if (!strcasecmp(start, "shift")) {
852                         *mods |= SHL_SHIFT_MASK;
853                 } else if (!strcasecmp(start, "lock")) {
854                         *mods |= SHL_LOCK_MASK;
855                 } else if (!strcasecmp(start, "control") ||
856                            !strcasecmp(start, "ctrl")) {
857                         *mods |= SHL_CONTROL_MASK;
858                 } else if (!strcasecmp(start, "alt")) {
859                         *mods |= SHL_ALT_MASK;
860                 } else if (!strcasecmp(start, "logo")) {
861                         *mods |= SHL_LOGO_MASK;
862                 } else {
863                         log_error("invalid modifier '%s'", start);
864                         return -EFAULT;
865                 }
866         } while (1);
867
868         while (*tmp == ' ')
869                 ++tmp;
870
871         start = tmp;
872         end = start;
873         do {
874                 while (*tmp && *tmp != ' ')
875                         ++tmp;
876                 end = tmp;
877                 if (!*tmp)
878                         break;
879                 while (*tmp == ' ')
880                         ++tmp;
881         } while (1);
882
883         if (start == end)
884                 return 0;
885         if (*end)
886                 *end = 0;
887
888         *keysym = xkb_keysym_from_name(start);
889         if (!*keysym) {
890                 log_error("invalid key '%s'", start);
891                 return -EFAULT;
892         }
893
894         return 1;
895 }
896
897 static int conf_parse_grab(struct conf_option *opt, bool on, const char *arg)
898 {
899         char **list, **keys;
900         unsigned int list_num, key_num, i, j, k, l;
901         int ret;
902         struct conf_grab *grab;
903
904         ret = shl_split_string(arg, &list, &list_num, ',', false);
905         if (ret)
906                 return ret;
907
908         grab = malloc(sizeof(*grab));
909         if (!grab) {
910                 ret = -ENOMEM;
911                 goto err_list;
912         }
913         memset(grab, 0, sizeof(*grab));
914
915         if (list_num) {
916                 grab->mods = malloc(sizeof(*grab->mods) * list_num);
917                 if (!grab->mods) {
918                         ret = -ENOMEM;
919                         goto err_grab;
920                 }
921                 memset(grab->mods, 0, sizeof(*grab->mods) * list_num);
922
923                 grab->num_syms = malloc(sizeof(*grab->num_syms) * list_num);
924                 if (!grab->num_syms) {
925                         ret = -ENOMEM;
926                         goto err_grab;
927                 }
928                 memset(grab->num_syms, 0, sizeof(*grab->num_syms) * list_num);
929
930                 grab->keysyms = malloc(sizeof(*grab->keysyms) * list_num);
931                 if (!grab->keysyms) {
932                         ret = -ENOMEM;
933                         goto err_grab;
934                 }
935                 memset(grab->keysyms, 0, sizeof(*grab->keysyms) * list_num);
936         }
937
938         l = 0;
939         for (i = 0; i < list_num; ++i) {
940                 ret = shl_split_string(list[i], &keys, &key_num, '+', false);
941                 if (ret)
942                         goto err_all;
943                 if (!key_num) {
944                         free(keys);
945                         continue;
946                 }
947
948                 grab->keysyms[l] = malloc(sizeof(*grab->keysyms[l]) * key_num);
949                 if (!grab->keysyms[l]) {
950                         ret = -ENOMEM;
951                         free(keys);
952                         goto err_all;
953                 }
954
955                 k = 0;
956                 for (j = 0; j < key_num; ++j) {
957                         ret = parse_single_grab(keys[j], &grab->mods[l],
958                                                 &grab->keysyms[l][k],
959                                                 j == 0);
960                         if (ret < 0) {
961                                 log_error("cannot parse grab '%s' in '%s'",
962                                           list[i], arg);
963                                 free(keys);
964                                 goto err_all;
965                         }
966                         k += ret;
967                 }
968
969                 free(keys);
970                 if (!k)
971                         continue;
972                 grab->num_syms[l] = k;
973                 ++l;
974                 ++grab->num;
975         }
976
977         free(list);
978         opt->type->free(opt);
979         *(void**)opt->mem = grab;
980         return 0;
981
982 err_all:
983         for (i = 0; i < list_num; ++i)
984                 free(grab->keysyms[i]);
985 err_grab:
986         free(grab->keysyms);
987         free(grab->num_syms);
988         free(grab->mods);
989         free(grab);
990 err_list:
991         free(list);
992         return ret;
993 }
994
995 static int conf_copy_grab(struct conf_option *opt,
996                           const struct conf_option *src)
997 {
998         struct conf_grab *grab, *s;
999         int ret;
1000         unsigned int i;
1001
1002         s = *(void**)src->mem;
1003
1004         if (!s) {
1005                 opt->type->free(opt);
1006                 *(void**)opt->mem = NULL;
1007                 return 0;
1008         }
1009
1010         grab = malloc(sizeof(*grab));
1011         if (!grab)
1012                 return -ENOMEM;
1013         memset(grab, 0, sizeof(*grab));
1014         grab->num = s->num;
1015
1016         if (grab->num) {
1017                 grab->mods = malloc(sizeof(*grab->mods) * grab->num);
1018                 if (!grab->mods) {
1019                         ret = -ENOMEM;
1020                         goto err_grab;
1021                 }
1022                 memcpy(grab->mods, s->mods, sizeof(*grab->mods) * grab->num);
1023
1024                 grab->num_syms = malloc(sizeof(*grab->num_syms) * grab->num);
1025                 if (!grab->num_syms) {
1026                         ret = -ENOMEM;
1027                         goto err_grab;
1028                 }
1029                 memcpy(grab->num_syms, s->num_syms,
1030                        sizeof(*grab->num_syms) * grab->num);
1031
1032                 grab->keysyms = malloc(sizeof(*grab->keysyms) * grab->num);
1033                 if (!grab->keysyms) {
1034                         ret = -ENOMEM;
1035                         goto err_grab;
1036                 }
1037                 memset(grab->keysyms, 0, sizeof(*grab->keysyms) * grab->num);
1038         }
1039
1040         for (i = 0; i < grab->num; ++i) {
1041                 grab->keysyms[i] = malloc(sizeof(*s->keysyms[i]) *
1042                                           s->num_syms[i]);
1043                 if (!grab->keysyms[i]) {
1044                         ret = -ENOMEM;
1045                         goto err_all;
1046                 }
1047                 memcpy(grab->keysyms[i], s->keysyms[i],
1048                        sizeof(*s->keysyms[i]) * s->num_syms[i]);
1049         }
1050
1051         opt->type->free(opt);
1052         *(void**)opt->mem = grab;
1053         return 0;
1054
1055 err_all:
1056         for (i = 0; i < grab->num; ++i)
1057                 free(grab->keysyms[i]);
1058 err_grab:
1059         free(grab->keysyms);
1060         free(grab->num_syms);
1061         free(grab->mods);
1062         free(grab);
1063         return ret;
1064 }
1065
1066 const struct conf_type conf_grab = {
1067         .flags = CONF_HAS_ARG,
1068         .set_default = conf_default_grab,
1069         .free = conf_free_grab,
1070         .parse = conf_parse_grab,
1071         .copy = conf_copy_grab,
1072 };