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