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