Introduction
Introduction Statistics Contact Development Disclaimer Help
configparser.c - ledit - Text editor (WIP)
git clone git://lumidify.org/ledit.git (fast, but not encrypted)
git clone https://lumidify.org/ledit.git (encrypted, but very slow)
git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/…
Log
Files
Refs
README
LICENSE
---
configparser.c (63996B)
---
1 #ifdef LEDIT_DEBUG
2 #include <time.h>
3 #include "macros.h"
4 #endif
5 #include <stdio.h>
6 #include <ctype.h>
7 #include <errno.h>
8 #include <string.h>
9 #include <stdint.h>
10 #include <stdlib.h>
11 #include <limits.h>
12
13 #include "util.h"
14 #include "memory.h"
15 #include "assert.h"
16 #include "configparser.h"
17 #include "theme_config.h"
18 #include "keys_config.h"
19
20 /* FIXME: Replace this entire parser with something sensible.
21 The current handwritten parser is mainly for the lulz. */
22
23 /* FIXME: standardize error messages */
24 /* FIXME: it isn't entirely correct to give size_t as length for
25 string in print_fmt (supposed to be int) */
26
27 struct config {
28 ledit_theme *theme;
29 basic_key_array *basic_keys;
30 command_key_array *command_keys;
31 command_array *cmds;
32 char **langs;
33 size_t num_langs;
34 size_t alloc_langs;
35 } config = {NULL, NULL, NULL, NULL, NULL, 0, 0};
36
37 enum toktype {
38 STRING,
39 LBRACE,
40 RBRACE,
41 EQUALS,
42 NEWLINE,
43 ERROR,
44 END
45 };
46
47 static const char *
48 toktype_str(enum toktype type) {
49 switch (type) {
50 case STRING:
51 return "string";
52 break;
53 case LBRACE:
54 return "left brace";
55 break;
56 case RBRACE:
57 return "right brace";
58 break;
59 case EQUALS:
60 return "equals";
61 break;
62 case NEWLINE:
63 return "newline";
64 break;
65 case ERROR:
66 return "error";
67 break;
68 case END:
69 return "end of file";
70 break;
71 default:
72 return "unknown";
73 }
74 }
75
76 struct token {
77 char *text;
78 size_t len;
79 enum toktype type;
80 size_t line; /* line in original input */
81 size_t line_offset; /* offset from start of line */
82 };
83
84 struct lexstate {
85 char *text;
86 size_t len; /* length of text */
87 size_t cur; /* current byte position */
88 size_t cur_line; /* current line */
89 size_t line_start; /* byte offset of start of current line */
90 };
91
92 static struct token
93 next_token(struct lexstate *s) {
94 char c;
95 struct token tok;
96 while (1) {
97 if (s->cur >= s->len)
98 return (struct token){NULL, 0, END, s->cur_line,…
99 while (isspace(c = s->text[s->cur])) {
100 s->cur++;
101 if (c == '\n') {
102 struct token tok = (struct token){s->tex…
103 s->cur_line++;
104 s->line_start = s->cur;
105 return tok;
106 }
107 if (s->cur >= s->len)
108 return (struct token){NULL, 0, END, s->c…
109 }
110
111 switch (s->text[s->cur]) {
112 case '#':
113 s->cur++;
114 while (s->cur < s->len && s->text[s->cur] != '\n…
115 s->cur++;
116 continue;
117 case '{':
118 tok = (struct token){s->text + s->cur, 1, LBRACE…
119 s->cur++;
120 break;
121 case '}':
122 tok = (struct token){s->text + s->cur, 1, RBRACE…
123 s->cur++;
124 break;
125 case '=':
126 tok = (struct token){s->text + s->cur, 1, EQUALS…
127 s->cur++;
128 break;
129 case '"':
130 /* FIXME: error if next char is not whitespace o…
131 s->cur++;
132 tok = (struct token){s->text + s->cur, 0, STRING…
133 size_t shift = 0, bs = 0;
134 int finished = 0;
135 while (s->cur < s->len) {
136 char c = s->text[s->cur];
137 if (c == '\n') {
138 break;
139 } else if (c == '\\') {
140 shift += bs;
141 tok.len += bs;
142 bs = (bs + 1) % 2;
143 } else if (c == '"') {
144 if (bs) {
145 shift++;
146 tok.len++;
147 bs = 0;
148 } else {
149 s->cur++;
150 finished = 1;
151 break;
152 }
153 } else {
154 tok.len++;
155 }
156 s->text[s->cur - shift] = s->text[s->cur…
157 s->cur++;
158 }
159 if (!finished) {
160 tok.text = "Unfinished string";
161 tok.len = strlen("Unfinished string");
162 tok.type = ERROR;
163 }
164 break;
165 default:
166 tok = (struct token){s->text + s->cur, 1, STRING…
167 s->cur++;
168 while (s->cur < s->len) {
169 char c = s->text[s->cur];
170 if (isspace(c) || c == '{' || c == '}' |…
171 break;
172 } else if (c == '"') {
173 tok.text = "Unexpected start of …
174 tok.len = strlen("Unexpected sta…
175 tok.type = ERROR;
176 tok.line_offset = s->cur - s->li…
177 }
178 tok.len++;
179 s->cur++;
180 }
181 }
182 return tok;
183 }
184 }
185
186 typedef struct ast_obj ast_obj;
187
188 typedef struct {
189 ast_obj *objs;
190 size_t len, cap;
191 } ast_list;
192
193 typedef struct {
194 struct token tok;
195 } ast_string;
196
197 typedef struct {
198 struct token tok;
199 ast_obj *value;
200 } ast_assignment;
201
202 typedef struct {
203 struct token func_tok;
204 struct token *args;
205 size_t len, cap;
206 } ast_statement;
207
208 enum objtype {
209 OBJ_LIST,
210 OBJ_STRING,
211 OBJ_ASSIGNMENT,
212 OBJ_STATEMENT
213 };
214
215 struct ast_obj {
216 struct token tok;
217 union {
218 ast_list list;
219 ast_string str;
220 ast_assignment assignment;
221 ast_statement statement;
222 } obj;
223 enum objtype type;
224 };
225
226 /* Note: These functions only free everything inside the object
227 so they can be used with stack variables (or array elements)! */
228
229 static void destroy_obj(ast_obj *obj);
230
231 static void
232 destroy_list(ast_list *list) {
233 if (!list)
234 return;
235 for (size_t i = 0; i < list->len; i++) {
236 destroy_obj(&list->objs[i]);
237 }
238 free(list->objs);
239 list->objs = NULL;
240 list->len = list->cap = 0;
241 }
242
243 static void
244 destroy_obj(ast_obj *obj) {
245 if (!obj)
246 return;
247 switch (obj->type) {
248 case OBJ_LIST:
249 destroy_list(&obj->obj.list);
250 break;
251 case OBJ_ASSIGNMENT:
252 destroy_obj(obj->obj.assignment.value);
253 free(obj->obj.assignment.value);
254 obj->obj.assignment.value = NULL;
255 break;
256 case OBJ_STATEMENT:
257 free(obj->obj.statement.args);
258 obj->obj.statement.args = NULL;
259 obj->obj.statement.len = obj->obj.statement.cap …
260 break;
261 default:
262 break;
263 }
264 }
265
266 /* FIXME: overflow */
267 static void
268 list_append(ast_list *list, ast_obj o) {
269 list->cap = ideal_array_size(list->cap, add_sz(list->len, 1));
270 list->objs = ledit_reallocarray(list->objs, list->cap, sizeof(as…
271 list->objs[list->len++] = o;
272 }
273
274 static void
275 statement_append(ast_statement *statement, struct token tok) {
276 statement->cap = ideal_array_size(statement->cap, add_sz(stateme…
277 statement->args = ledit_reallocarray(statement->args, statement-…
278 statement->args[statement->len++] = tok;
279 }
280
281 /* FIXME: make this a bit nicer */
282 /* Note: A lot of the ugliness is because of the
283 (failed) attempt to somewhat optimize everything */
284
285 static int
286 parse_list(struct lexstate *s, ast_list *ret, int implicit_end, char *fi…
287 *ret = (ast_list){NULL, 0, 0};
288 struct token tok = next_token(s);
289 struct token tok2;
290 while (1) {
291 switch (tok.type) {
292 case STRING:
293 tok2 = next_token(s);
294 if (tok2.type == STRING) {
295 ast_statement statement = {tok, NULL, 0,…
296 /* FIXME: maybe allow lists in statement…
297 while (tok2.type == STRING) {
298 statement_append(&statement, tok…
299 tok2 = next_token(s);
300 }
301 list_append(ret, (ast_obj){.tok = tok, .…
302 tok = tok2;
303 } else if (tok2.type == EQUALS) {
304 ast_assignment assignment = {tok, NULL};
305 assignment.value = ledit_malloc(sizeof(a…
306 tok2 = next_token(s);
307 assignment.value->tok = tok2;
308 struct token orig_tok = tok;
309 if (tok2.type == STRING) {
310 assignment.value->obj.str = (ast…
311 assignment.value->type = OBJ_STR…
312 tok = next_token(s);
313 if (tok.type == STRING) {
314 *errstr = print_fmt(
315 "%s: Invalid assignm…
316 filename, tok.line, …
317 );
318 free(assignment.value);
319 goto error;
320 }
321 } else if (tok2.type == LBRACE) {
322 assignment.value->type = OBJ_LIS…
323 /* just in case */
324 assignment.value->obj.list = (as…
325 if (parse_list(s, &assignment.va…
326 free(assignment.value);
327 goto error;
328 }
329 tok = next_token(s);
330 if (tok.type == STRING) {
331 *errstr = print_fmt(
332 "%s: Invalid assignm…
333 filename, tok.line, …
334 );
335 destroy_list(&assignment…
336 free(assignment.value);
337 goto error;
338 }
339 } else {
340 *errstr = print_fmt(
341 "%s: Invalid assignment at l…
342 filename, tok2.line, tok2.li…
343 );
344 free(assignment.value);
345 goto error;
346 }
347 list_append(ret, (ast_obj){.tok = orig_t…
348 } else {
349 *errstr = print_fmt(
350 "%s: Invalid token '%s' at line %zu,…
351 filename, toktype_str(tok2.type), to…
352 );
353 goto error;
354 }
355 break;
356 case NEWLINE:
357 tok = next_token(s);
358 break;
359 case RBRACE:
360 if (implicit_end) {
361 *errstr = print_fmt(
362 "%s: Unexpected right brace at line …
363 filename, tok.line, tok.line_offset
364 );
365 goto error;
366 } else {
367 return 0;
368 }
369 case END:
370 if (!implicit_end) {
371 *errstr = print_fmt(
372 "%s: Unexpected end of file at line …
373 filename, tok.line, tok.line_offset
374 );
375 goto error;
376 } else {
377 return 0;
378 }
379 case LBRACE:
380 case EQUALS:
381 case ERROR:
382 default:
383 *errstr = print_fmt(
384 "%s: Unexpected token '%s' at line %zu, offs…
385 filename, toktype_str(tok.type), tok.line, t…
386 );
387 goto error;
388 }
389 }
390 return 0;
391 error:
392 destroy_list(ret);
393 return 1;
394 }
395
396 static char *
397 load_file(char *filename, size_t *len_ret, char **errstr) {
398 long len;
399 char *file_contents;
400 FILE *file;
401
402 /* FIXME: https://wiki.sei.cmu.edu/confluence/display/c/FIO19-C.…
403 file = fopen(filename, "r");
404 if (!file) goto error;
405 if (fseek(file, 0, SEEK_END)) goto errorclose;
406 len = ftell(file);
407 if (len < 0) goto errorclose;
408 if (fseek(file, 0, SEEK_SET)) goto errorclose;
409 file_contents = ledit_malloc(add_sz((size_t)len, 1));
410 clearerr(file);
411 fread(file_contents, 1, (size_t)len, file);
412 if (ferror(file)) goto errorclose;
413 file_contents[len] = '\0';
414 if (fclose(file)) goto error;
415 *len_ret = (size_t)len;
416 return file_contents;
417 error:
418 if (errstr)
419 *errstr = strerror(errno);
420 return NULL;
421 errorclose:
422 if (errstr)
423 *errstr = strerror(errno);
424 fclose(file);
425 return NULL;
426 }
427
428 /* FIXME: max recursion depth in parser */
429
430 static int
431 parse_theme_color(
432 ledit_common *common,
433 void *obj, const char *val, size_t val_len, char *key,
434 char *filename, size_t line, size_t line_offset, char **errstr) {
435 XftColor *dst = (XftColor *)obj;
436 char col[8]; /* 7 for '#' and 6 hex values + 1 for '\0' */
437 if (val_len == 7 && val[0] == '#') {
438 strncpy(col, val, val_len);
439 col[val_len] = '\0';
440 } else if (val_len == 6) {
441 col[0] = '#';
442 strncpy(col + 1, val, val_len);
443 col[val_len + 1] = '\0';
444 } else {
445 goto error;
446 }
447 /* FIXME: XftColorAllocValue */
448 if (!XftColorAllocName(common->dpy, common->vis, common->cm, col…
449 goto error;
450 return 0;
451 error:
452 *errstr = print_fmt(
453 "%s: Unable to parse color specification "
454 "'%.*s' for '%s' at line %zu, position %zu",
455 filename, val_len, val, key, line, line_offset
456 );
457 return 1;
458 }
459
460 static void
461 destroy_theme_color(ledit_common *common, void *obj) {
462 XftColor *color = (XftColor *)obj;
463 XftColorFree(common->dpy, common->vis, common->cm, color);
464 }
465
466 /* based partially on OpenBSD's strtonum */
467 static int
468 parse_theme_number(
469 ledit_common *common,
470 void *obj, const char *val, size_t val_len, char *key,
471 char *filename, size_t line, size_t line_offset, char **errstr) {
472 (void)common;
473 int *num = (int *)obj;
474 /* the string needs to be nul-terminated
475 if it contains more than 9 digits, it's illegal anyways */
476 if (val_len > 9)
477 goto error;
478 char str[10];
479 strncpy(str, val, val_len);
480 str[val_len] = '\0';
481 char *end;
482 long l = strtol(str, &end, 10);
483 if (str == end || *end != '\0' ||
484 l < 0 || l > INT_MAX || ((l == LONG_MIN ||
485 l == LONG_MAX) && errno == ERANGE)) {
486 goto error;
487 }
488 *num = (int)l;
489 return 0;
490 error:
491 *errstr = print_fmt(
492 "%s: Invalid number '%.*s' "
493 "for '%s' at line %zu, position %zu",
494 filename, val_len, val, key, line, line_offset
495 );
496 return 1;
497 }
498
499 static void
500 destroy_theme_number(ledit_common *common, void *obj) {
501 (void)common;
502 (void)obj;
503 }
504
505 static int
506 parse_theme_string(
507 ledit_common *common,
508 void *obj, const char *val, size_t val_len, char *key,
509 char *filename, size_t line, size_t line_offset, char **errstr) {
510 (void)common; (void)key;
511 (void)filename; (void)line; (void)line_offset; (void)errstr;
512
513 char **obj_str = (char **)obj;
514 *obj_str = ledit_strndup(val, val_len);
515 return 0;
516 }
517
518 static void
519 destroy_theme_string(ledit_common *common, void *obj) {
520 (void)common;
521 char **obj_str = (char **)obj;
522 free(*obj_str);
523 }
524
525 static int
526 parse_theme_bool(
527 ledit_common *common,
528 void *obj, const char *val, size_t val_len, char *key,
529 char *filename, size_t line, size_t line_offset, char **errstr) {
530 (void)common;
531 int *num = (int *)obj;
532 if (str_array_equal("true", val, val_len)) {
533 *num = 1;
534 return 0;
535 } else if (str_array_equal("false", val, val_len)) {
536 *num = 0;
537 return 0;
538 }
539 *errstr = print_fmt(
540 "%s: Invalid boolean '%.*s' "
541 "for '%s' at line %zu, position %zu",
542 filename, val_len, val, key, line, line_offset
543 );
544 return 1;
545 }
546
547 static void
548 destroy_theme_bool(ledit_common *common, void *obj) {
549 (void)common;
550 (void)obj;
551 }
552
553 /* FIXME: This interface is absolutely horrible - it's mainly this way t…
554 theme array for the destroy function */
555 /* If theme is NULL, a new theme is loaded, else it is destroyed */
556 static ledit_theme *
557 load_destroy_theme(ledit_common *common, ast_list *theme_list, ledit_the…
558 *errstr = NULL;
559 int default_init = theme ? 1 : 0;
560 if (!theme)
561 theme = ledit_malloc(sizeof(ledit_theme));
562
563 struct {
564 char *key;
565 void *obj;
566 int (*parse_func)(
567 ledit_common *common,
568 void *obj, const char *val, size_t val_len, char *ke…
569 char *filename, size_t line, size_t line_offset, cha…
570 );
571 void (*destroy_func)(ledit_common *common, void *obj);
572 const char *default_value;
573 int initialized;
574 } settings[] = {
575 {"text-font", &theme->text_font, &parse_theme_string, &d…
576 {"text-size", &theme->text_size, &parse_theme_number, &d…
577 {"scrollbar-width", &theme->scrollbar_width, &parse_them…
578 {"scrollbar-step", &theme->scrollbar_step, &parse_theme_…
579 {"extra-line-spacing", &theme->extra_line_spacing, &pars…
580 {"text-fg", &theme->text_fg, &parse_theme_color, &destro…
581 {"text-bg", &theme->text_bg, &parse_theme_color, &destro…
582 {"cursor-fg", &theme->cursor_fg, &parse_theme_color, &de…
583 {"cursor-bg", &theme->cursor_bg, &parse_theme_color, &de…
584 {"selection-fg", &theme->selection_fg, &parse_theme_colo…
585 {"selection-bg", &theme->selection_bg, &parse_theme_colo…
586 {"bar-fg", &theme->bar_fg, &parse_theme_color, &destroy_…
587 {"bar-bg", &theme->bar_bg, &parse_theme_color, &destroy_…
588 {"bar-cursor", &theme->bar_cursor, &parse_theme_color, &…
589 {"bar-fmt", &theme->bar_fmt, &parse_theme_string, &destr…
590 {"scrollbar-fg", &theme->scrollbar_fg, &parse_theme_colo…
591 {"scrollbar-bg", &theme->scrollbar_bg, &parse_theme_colo…
592 {"highlight-search", &theme->highlight_search, &parse_th…
593 };
594
595 if (default_init)
596 goto cleanup;
597
598 if (theme_list) {
599 for (size_t i = 0; i < theme_list->len; i++) {
600 size_t line = theme_list->objs[i].tok.line;
601 size_t line_offset = theme_list->objs[i].tok.lin…
602 if (theme_list->objs[i].type != OBJ_ASSIGNMENT) {
603 *errstr = print_fmt(
604 "%s: Invalid statement in theme conf…
605 "at line %zu, offset %zu", filename,…
606 );
607 goto cleanup;
608 } else if (theme_list->objs[i].obj.assignment.va…
609 *errstr = print_fmt(
610 "%s: Invalid assignment in theme con…
611 "at line %zu, offset %zu", filename,…
612 );
613 goto cleanup;
614 }
615
616 char *key = theme_list->objs[i].obj.assignment.t…
617 size_t key_len = theme_list->objs[i].obj.assignm…
618 char *val = theme_list->objs[i].obj.assignment.v…
619 size_t val_len = theme_list->objs[i].obj.assignm…
620
621 int found = 0;
622 /* FIXME: use binary search maybe */
623 for (size_t j = 0; j < LENGTH(settings); j++) {
624 if (str_array_equal(settings[j].key, key…
625 /* FIXME: maybe just make this a…
626 if (settings[j].initialized) {
627 *errstr = print_fmt(
628 "%s: Duplicate defin…
629 "'%.*s' at line %zu,…
630 filename, key_len, k…
631 );
632 goto cleanup;
633 }
634 if (settings[j].parse_func(
635 common, settings[j].obj, val…
636 settings[j].key, filename, l…
637 goto cleanup;
638 }
639 settings[j].initialized = 1;
640 found = 1;
641 break;
642 }
643 }
644 if (!found) {
645 *errstr = print_fmt(
646 "%s: Invalid theme setting "
647 "'%.*s' at line %zu, position %zu",
648 filename, key_len, key, line, line_o…
649 );
650 goto cleanup;
651 }
652 }
653 }
654
655 for (size_t i = 0; i < LENGTH(settings); i++) {
656 if (!settings[i].initialized) {
657 /* FIXME: kind of inefficient to calculate strle…
658 /* FIXME: line number doesn't make sense */
659 if (settings[i].parse_func(
660 common, settings[i].obj, settings[i].default…
661 strlen(settings[i].default_value), settings[…
662 "default config", 0, 0, errstr)) {
663 goto cleanup;
664 }
665 }
666 }
667
668 /* FIXME: make this check part of the generic handling above (al…
669 /* FIXME: 100 is completely arbitrary */
670 if (theme->extra_line_spacing < 0 || theme->extra_line_spacing >…
671 *errstr = print_fmt(
672 "%s: Invalid value '%d' for theme setting 'extra-lin…
673 "(allowed values are 0-100)",
674 filename, theme->extra_line_spacing
675 );
676 goto cleanup;
677 }
678
679 return theme;
680 cleanup:
681 for (size_t i = 0; i < LENGTH(settings); i++) {
682 if (settings[i].initialized) {
683 settings[i].destroy_func(common, settings[i].obj…
684 }
685 }
686 free(theme);
687 return NULL;
688 }
689
690 static ledit_theme *
691 load_theme(ledit_common *common, ast_list *theme_list, char *filename, c…
692 return load_destroy_theme(common, theme_list, NULL, filename, er…
693 }
694
695 static void
696 destroy_theme(ledit_common *common, ledit_theme *theme) {
697 char *errstr = NULL;
698 if (!theme)
699 return;
700 (void)load_destroy_theme(common, NULL, theme, NULL, &errstr);
701 /* shouldn't happen... */
702 if (errstr)
703 free(errstr);
704 }
705
706 /* This only destroys the members inside 'cfg' since the config
707 * struct itself is usually not on the heap. */
708 static void
709 config_destroy(ledit_common *common, struct config *cfg) {
710 if (cfg->theme)
711 destroy_theme(common, cfg->theme);
712 cfg->theme = NULL;
713 for (size_t i = 0; i < cfg->num_langs; i++) {
714 for (size_t j = 0; j < cfg->basic_keys[i].num_keys; j++)…
715 free(cfg->basic_keys[i].keys[j].text);
716 }
717 free(cfg->basic_keys[i].keys);
718 for (size_t j = 0; j < cfg->command_keys[i].num_keys; j+…
719 free(cfg->command_keys[i].keys[j].text);
720 }
721 free(cfg->command_keys[i].keys);
722 for (size_t j = 0; j < cfg->cmds[i].num_cmds; j++) {
723 free(cfg->cmds[i].cmds[j].text);
724 }
725 free(cfg->cmds[i].cmds);
726 free(cfg->langs[i]);
727 }
728 free(cfg->basic_keys);
729 free(cfg->command_keys);
730 free(cfg->cmds);
731 free(cfg->langs);
732 cfg->basic_keys = NULL;
733 cfg->command_keys = NULL;
734 cfg->cmds = NULL;
735 cfg->langs = NULL;
736 cfg->num_langs = cfg->alloc_langs = 0;
737 }
738
739 void
740 config_cleanup(ledit_common *common) {
741 config_destroy(common, &config);
742 }
743
744 /* FIXME: which additional ones are needed here? */
745 static struct keysym_mapping {
746 char *name;
747 KeySym keysym;
748 } keysym_map[] = {
749 {"backspace", XK_BackSpace},
750 {"begin", XK_Begin},
751 {"break", XK_Break},
752 {"cancel", XK_Cancel},
753 {"clear", XK_Clear},
754 {"delete", XK_Delete},
755 {"down", XK_Down},
756 {"end", XK_End},
757 {"escape", XK_Escape},
758 {"execute", XK_Execute},
759
760 {"f1", XK_F1},
761 {"f10", XK_F10},
762 {"f11", XK_F11},
763 {"f12", XK_F12},
764 {"f13", XK_F13},
765 {"f14", XK_F14},
766 {"f15", XK_F15},
767 {"f16", XK_F16},
768 {"f17", XK_F17},
769 {"f18", XK_F18},
770 {"f19", XK_F19},
771 {"f2", XK_F2},
772 {"f20", XK_F20},
773 {"f21", XK_F21},
774 {"f22", XK_F22},
775 {"f23", XK_F23},
776 {"f24", XK_F24},
777 {"f25", XK_F25},
778 {"f26", XK_F26},
779 {"f27", XK_F27},
780 {"f28", XK_F28},
781 {"f29", XK_F29},
782 {"f3", XK_F3},
783 {"f30", XK_F30},
784 {"f31", XK_F31},
785 {"f32", XK_F32},
786 {"f33", XK_F33},
787 {"f34", XK_F34},
788 {"f35", XK_F35},
789 {"f4", XK_F4},
790 {"f5", XK_F5},
791 {"f6", XK_F6},
792 {"f7", XK_F7},
793 {"f8", XK_F8},
794 {"f9", XK_F9},
795
796 {"find", XK_Find},
797 {"help", XK_Help},
798 {"home", XK_Home},
799 {"insert", XK_Insert},
800
801 {"kp-0", XK_KP_0},
802 {"kp-1", XK_KP_1},
803 {"kp-2", XK_KP_2},
804 {"kp-3", XK_KP_3},
805 {"kp-4", XK_KP_4},
806 {"kp-5", XK_KP_5},
807 {"kp-6", XK_KP_6},
808 {"kp-7", XK_KP_7},
809 {"kp-8", XK_KP_8},
810 {"kp-9", XK_KP_9},
811 {"kp-add", XK_KP_Add},
812 {"kp-begin", XK_KP_Begin},
813 {"kp-decimal", XK_KP_Decimal},
814 {"kp-delete", XK_KP_Delete},
815 {"kp-divide", XK_KP_Divide},
816 {"kp-down", XK_KP_Down},
817 {"kp-end", XK_KP_End},
818 {"kp-enter", XK_KP_Enter},
819 {"kp-equal", XK_KP_Equal},
820 {"kp-f1", XK_KP_F1},
821 {"kp-f2", XK_KP_F2},
822 {"kp-f3", XK_KP_F3},
823 {"kp-f4", XK_KP_F4},
824 {"kp-home", XK_KP_Home},
825 {"kp-insert", XK_KP_Insert},
826 {"kp-left", XK_KP_Left},
827 {"kp-multiply", XK_KP_Multiply},
828 {"kp-next", XK_KP_Next},
829 {"kp-page-down", XK_KP_Page_Down},
830 {"kp-page-up", XK_KP_Page_Up},
831 {"kp-prior", XK_KP_Prior},
832 {"kp-right", XK_KP_Right},
833 {"kp-separator", XK_KP_Separator},
834 {"kp-space", XK_KP_Space},
835 {"kp-subtract", XK_KP_Subtract},
836 {"kp-tab", XK_KP_Tab},
837 {"kp-up", XK_KP_Up},
838
839 {"l1", XK_L1},
840 {"l10", XK_L10},
841 {"l2", XK_L2},
842 {"l3", XK_L3},
843 {"l4", XK_L4},
844 {"l5", XK_L5},
845 {"l6", XK_L6},
846 {"l7", XK_L7},
847 {"l8", XK_L8},
848 {"l9", XK_L9},
849
850 {"left", XK_Left},
851 {"linefeed", XK_Linefeed},
852 {"menu", XK_Menu},
853 {"mode-switch", XK_Mode_switch},
854 {"next", XK_Next},
855 {"num-lock", XK_Num_Lock},
856 {"page-down", XK_Page_Down},
857 {"page-up", XK_Page_Up},
858 {"pause", XK_Pause},
859 {"print", XK_Print},
860 {"prior", XK_Prior},
861
862 {"r1", XK_R1},
863 {"r10", XK_R10},
864 {"r11", XK_R11},
865 {"r12", XK_R12},
866 {"r13", XK_R13},
867 {"r14", XK_R14},
868 {"r15", XK_R15},
869 {"r2", XK_R2},
870 {"r3", XK_R3},
871 {"r4", XK_R4},
872 {"r5", XK_R5},
873 {"r6", XK_R6},
874 {"r7", XK_R7},
875 {"r8", XK_R8},
876 {"r9", XK_R9},
877
878 {"redo", XK_Redo},
879 {"return", XK_Return},
880 {"right", XK_Right},
881 {"script-switch", XK_script_switch},
882 {"scroll-lock", XK_Scroll_Lock},
883 {"select", XK_Select},
884 {"space", XK_space},
885 {"sysreq", XK_Sys_Req},
886 {"tab", XK_Tab},
887 {"up", XK_Up},
888 {"undo", XK_Undo},
889 };
890
891 GEN_CB_MAP_HELPERS(keysym_map, struct keysym_mapping, name)
892
893 static int
894 parse_keysym(char *keysym_str, size_t len, KeySym *sym) {
895 struct keysym_mapping *km = keysym_map_get_entry(keysym_str, len…
896 if (!km)
897 return 1;
898 *sym = km->keysym;
899 return 0;
900 }
901
902 static int
903 parse_modemask(char *modemask_str, size_t len, ledit_mode *mode_ret) {
904 size_t cur = 0;
905 *mode_ret = 0;
906 while (cur < len) {
907 if (str_array_equal("normal", modemask_str + cur, LEDIT_…
908 cur += 6;
909 *mode_ret |= NORMAL;
910 } else if (str_array_equal("visual", modemask_str + cur,…
911 cur += 6;
912 *mode_ret |= VISUAL;
913 } else if (str_array_equal("insert", modemask_str + cur,…
914 cur += 6;
915 *mode_ret |= INSERT;
916 } else {
917 return 1;
918 }
919 if (cur < len && modemask_str[cur] != '|')
920 return 1;
921 else
922 cur++;
923 }
924 return 0;
925 }
926
927 static int
928 parse_modmask(char *modmask_str, size_t len, unsigned int *mask_ret) {
929 size_t cur = 0;
930 *mask_ret = 0;
931 while (cur < len) {
932 if (str_array_equal("shift", modmask_str + cur, LEDIT_MI…
933 cur += 5;
934 *mask_ret |= ShiftMask;
935 /*
936 } else if (str_array_equal("lock", modmask_str + cur, LE…
937 cur += 4;
938 *mask_ret |= LockMask;
939 */
940 } else if (str_array_equal("control", modmask_str + cur,…
941 cur += 7;
942 *mask_ret |= ControlMask;
943 } else if (str_array_equal("mod1", modmask_str + cur, LE…
944 cur += 4;
945 *mask_ret |= Mod1Mask;
946 /*
947 } else if (str_array_equal("mod2", modmask_str + cur, LE…
948 cur += 4;
949 *mask_ret |= Mod2Mask;
950 */
951 } else if (str_array_equal("mod3", modmask_str + cur, LE…
952 cur += 4;
953 *mask_ret |= Mod3Mask;
954 } else if (str_array_equal("mod4", modmask_str + cur, LE…
955 cur += 4;
956 *mask_ret |= Mod4Mask;
957 } else if (str_array_equal("mod5", modmask_str + cur, LE…
958 cur += 4;
959 *mask_ret |= Mod5Mask;
960 } else if (str_array_equal("any", modmask_str + cur, LED…
961 cur += 3;
962 *mask_ret = UINT_MAX;
963 } else {
964 return 1;
965 }
966 if (cur < len && modmask_str[cur] != '|')
967 return 1;
968 else
969 cur++;
970 }
971 return 0;
972 }
973
974 /* FIXME: it would probably be safer to not write the string lengths by …
975 static int
976 parse_command_modemask(char *mode_str, size_t len, command_mode *mode_re…
977 size_t cur = 0;
978 *mode_ret = 0;
979 /* IMPORTANT: these need to be sorted appropriately so e.g. edit…
980 while (cur < len) {
981 if (str_array_equal("substitute", mode_str + cur, LEDIT_…
982 cur += 10;
983 *mode_ret |= CMD_SUBSTITUTE;
984 } else if (str_array_equal("edit-search-backwards", mode…
985 cur += 21;
986 *mode_ret |= CMD_EDITSEARCHB;
987 } else if (str_array_equal("edit-search", mode_str + cur…
988 cur += 11;
989 *mode_ret |= CMD_EDITSEARCH;
990 } else if (str_array_equal("edit", mode_str + cur, LEDIT…
991 cur += 4;
992 *mode_ret |= CMD_EDIT;
993 } else {
994 return 1;
995 }
996 if (cur < len && mode_str[cur] != '|') {
997 return 1;
998 } else {
999 cur++;
1000 }
1001 }
1002 return 0;
1003 }
1004
1005 /* FIXME: generic dynamic array */
1006
1007 static void
1008 push_lang(struct config *cfg) {
1009 if (cfg->num_langs == cfg->alloc_langs) {
1010 cfg->alloc_langs = ideal_array_size(cfg->alloc_langs, ad…
1011 cfg->basic_keys = ledit_reallocarray(cfg->basic_keys, cf…
1012 cfg->command_keys = ledit_reallocarray(cfg->command_keys…
1013 cfg->cmds = ledit_reallocarray(cfg->cmds, cfg->alloc_lan…
1014 cfg->langs = ledit_reallocarray(cfg->langs, cfg->alloc_l…
1015 }
1016 basic_key_array *arr1 = &cfg->basic_keys[cfg->num_langs];
1017 arr1->keys = NULL;
1018 arr1->num_keys = arr1->alloc_keys = 0;
1019 command_key_array *arr2 = &cfg->command_keys[cfg->num_langs];
1020 arr2->keys = NULL;
1021 arr2->num_keys = arr2->alloc_keys = 0;
1022 command_array *arr3 = &cfg->cmds[cfg->num_langs];
1023 arr3->cmds = NULL;
1024 arr3->num_cmds = arr3->alloc_cmds = 0;
1025 cfg->langs[cfg->num_langs] = NULL;
1026 cfg->num_langs++;
1027 }
1028
1029 #define GEN_PARSE_STATEMENT(name, cb_type, mapping_type, mode_parse_func…
1030 static int …
1031 name(ast_statement *st, mapping_type *m, char *filename, char **errstr) …
1032 size_t line = st->func_tok.line; …
1033 size_t line_offset = st->func_tok.line_offset; …
1034 m->cb = NULL; …
1035 m->text = NULL; …
1036 m->mods = 0; …
1037 m->modes = 0; …
1038 m->keysym = 0; …
1039 char *msg = NULL; …
1040 if (!str_array_equal("bind", st->func_tok.text, st->func_tok.len…
1041 msg = "Invalid statement"; …
1042 goto error; …
1043 } …
1044 m->cb = cb_type##_map_get_entry(st->args[0].text, st->args[0].le…
1045 if (!m->cb) { …
1046 msg = "Invalid function specification"; …
1047 goto error; …
1048 } …
1049 int text_init = 0, keysym_init = 0, modes_init = 0, mods_init = …
1050 for (size_t i = 1; i < st->len; i++) { …
1051 line = st->args[i].line; …
1052 line_offset = st->args[i].line_offset; …
1053 if (str_array_equal("mods", st->args[i].text, st->args[i…
1054 if (mods_init) { …
1055 msg = "Duplicate mods specification"; …
1056 goto error; …
1057 } else if (i == st->len - 1) { …
1058 msg = "Unfinished statement"; …
1059 goto error; …
1060 } …
1061 i++; …
1062 if (parse_modmask(st->args[i].text, st->args[i].…
1063 msg = "Invalid mods specification"; …
1064 goto error; …
1065 } …
1066 mods_init = 1; …
1067 } else if (str_array_equal("modes", st->args[i].text, st…
1068 if (modes_init) { …
1069 msg = "Duplicate modes specification"; …
1070 goto error; …
1071 } else if (i == st->len - 1) { …
1072 msg = "Unfinished statement"; …
1073 goto error; …
1074 } …
1075 i++; …
1076 if (mode_parse_func(st->args[i].text, st->args[i…
1077 msg = "Invalid modes specification"; …
1078 goto error; …
1079 } else if (!cb_type##_modemask_is_valid(m->cb, m…
1080 msg = "Function not defined for all give…
1081 goto error; …
1082 } …
1083 modes_init = 1; …
1084 } else if (str_array_equal("keysym", st->args[i].text, s…
1085 if (text_init) { …
1086 msg = "Text already specified"; …
1087 goto error; …
1088 } else if (keysym_init) { …
1089 msg = "Duplicate keysym specification"; …
1090 goto error; …
1091 } else if (i == st->len - 1) { …
1092 msg = "Unfinished statement"; …
1093 goto error; …
1094 } …
1095 i++; …
1096 if (parse_keysym(st->args[i].text, st->args[i].l…
1097 msg = "Invalid keysym specification"; …
1098 goto error; …
1099 } …
1100 keysym_init = 1; …
1101 } else if (str_array_equal("text", st->args[i].text, st-…
1102 if (keysym_init) { …
1103 msg = "Keysym already specified"; …
1104 goto error; …
1105 } else if (text_init) { …
1106 msg = "Duplicate text specification"; …
1107 goto error; …
1108 } else if (i == st->len - 1) { …
1109 msg = "Unfinished statement"; …
1110 goto error; …
1111 } …
1112 i++; …
1113 m->text = ledit_strndup(st->args[i].text, st->ar…
1114 text_init = 1; …
1115 } else if (str_array_equal("catchall", st->args[i].text,…
1116 if (keysym_init) { …
1117 msg = "Keysym already specified"; …
1118 goto error; …
1119 } else if (text_init) { …
1120 msg = "Duplicate text specification"; …
1121 goto error; …
1122 } …
1123 m->text = ledit_strdup(""); …
1124 text_init = 1; …
1125 } else { …
1126 msg = "Invalid statement"; …
1127 goto error; …
1128 } …
1129 } …
1130 if (!text_init && !keysym_init) { …
1131 msg = "No text or keysym specified"; …
1132 goto error; …
1133 } …
1134 if (!modes_init) { …
1135 msg = "No modes specified"; …
1136 goto error; …
1137 } …
1138 return 0; …
1139 error: …
1140 if (msg) { …
1141 *errstr = print_fmt( …
1142 "%s, line %zu, offset %zu: %s", filename, line, line…
1143 ); …
1144 } …
1145 if (m->text) …
1146 free(m->text); …
1147 return 1; …
1148 }
1149
1150 GEN_PARSE_STATEMENT(parse_basic_key_statement, basic_key_cb, basic_key_m…
1151 GEN_PARSE_STATEMENT(parse_command_key_statement, command_key_cb, command…
1152
1153 static int
1154 parse_command_statement(ast_statement *st, command_mapping *m, char *fil…
1155 size_t line = st->func_tok.line;
1156 size_t line_offset = st->func_tok.line_offset;
1157 m->cb = NULL;
1158 m->text = NULL;
1159 char *msg = NULL;
1160 if (!str_array_equal("bind", st->func_tok.text, st->func_tok.len…
1161 msg = "Invalid statement";
1162 goto error;
1163 }
1164 m->cb = command_cb_map_get_entry(st->args[0].text, st->args[0].l…
1165 if (!m->cb) {
1166 msg = "Invalid function specification";
1167 goto error;
1168 }
1169 m->text = ledit_strndup(st->args[1].text, st->args[1].len);
1170 return 0;
1171 error:
1172 if (msg) {
1173 *errstr = print_fmt(
1174 "%s, line %zu, offset %zu: %s", filename, line, line…
1175 );
1176 }
1177 /* I guess this is unnecessary */
1178 if (m->text)
1179 free(m->text);
1180 return 1;
1181 }
1182
1183 static void
1184 push_basic_key_mapping(basic_key_array *arr, basic_key_mapping m) {
1185 if (arr->num_keys == arr->alloc_keys) {
1186 arr->alloc_keys = ideal_array_size(arr->alloc_keys, add_…
1187 arr->keys = ledit_reallocarray(arr->keys, arr->alloc_key…
1188 }
1189 arr->keys[arr->num_keys] = m;
1190 arr->num_keys++;
1191 }
1192
1193 static void
1194 push_command_key_mapping(command_key_array *arr, command_key_mapping m) {
1195 if (arr->num_keys == arr->alloc_keys) {
1196 arr->alloc_keys = ideal_array_size(arr->alloc_keys, add_…
1197 arr->keys = ledit_reallocarray(arr->keys, arr->alloc_key…
1198 }
1199 arr->keys[arr->num_keys] = m;
1200 arr->num_keys++;
1201 }
1202
1203 static void
1204 push_command_mapping(command_array *arr, command_mapping m) {
1205 if (arr->num_cmds == arr->alloc_cmds) {
1206 arr->alloc_cmds = ideal_array_size(arr->alloc_cmds, add_…
1207 arr->cmds = ledit_reallocarray(arr->cmds, arr->alloc_cmd…
1208 }
1209 arr->cmds[arr->num_cmds] = m;
1210 arr->num_cmds++;
1211 }
1212
1213 /* FIXME: This could be made a lot nicer and less repetitive */
1214 static int
1215 load_bindings(struct config *cfg, ast_list *list, char *filename, char *…
1216 int basic_keys_init = 0, command_keys_init = 0, commands_init = …
1217 size_t cur_lang = cfg->num_langs - 1; /* FIXME: ensure no underf…
1218 for (size_t i = 0; i < list->len; i++) {
1219 size_t line = list->objs[i].tok.line;
1220 size_t line_offset = list->objs[i].tok.line_offset;
1221 if (list->objs[i].type != OBJ_ASSIGNMENT) {
1222 *errstr = print_fmt(
1223 "%s: Invalid statement in bindings configur…
1224 "at list %zu, offset %zu", filename, line, …
1225 );
1226 goto error;
1227 }
1228 char *key = list->objs[i].obj.assignment.tok.text;
1229 size_t key_len = list->objs[i].obj.assignment.tok.len;
1230 if (str_array_equal("language", key, key_len)) {
1231 if (list->objs[i].obj.assignment.value->type != …
1232 *errstr = print_fmt(
1233 "%s: Invalid language setting in bin…
1234 "at line %zu, offset %zu", filename,…
1235 );
1236 goto error;
1237 } else if (cfg->langs[cur_lang]) {
1238 *errstr = print_fmt(
1239 "%s: Duplicate language setting in b…
1240 "at line %zu, offset %zu", filename,…
1241 );
1242 goto error;
1243 }
1244 char *val = list->objs[i].obj.assignment.value->…
1245 size_t val_len = list->objs[i].obj.assignment.va…
1246 cfg->langs[cur_lang] = ledit_strndup(val, val_le…
1247 } else if (str_array_equal("basic-keys", key, key_len)) {
1248 if (list->objs[i].obj.assignment.value->type != …
1249 *errstr = print_fmt(
1250 "%s: Invalid basic-keys setting in b…
1251 "at line %zu, offset %zu", filename,…
1252 );
1253 goto error;
1254 } else if (basic_keys_init) {
1255 *errstr = print_fmt(
1256 "%s: Duplicate basic-keys setting in…
1257 "at line %zu, offset %zu", filename,…
1258 );
1259 goto error;
1260 }
1261 ast_list *slist = &list->objs[i].obj.assignment.…
1262 for (size_t j = 0; j < slist->len; j++) {
1263 line = slist->objs[j].tok.line;
1264 line_offset = slist->objs[j].tok.line_of…
1265 if (slist->objs[j].type != OBJ_STATEMENT…
1266 *errstr = print_fmt(
1267 "%s: Invalid basic-keys sett…
1268 "at line %zu, offset %zu", f…
1269 );
1270 goto error;
1271 }
1272 basic_key_mapping m;
1273 if (parse_basic_key_statement(&slist->ob…
1274 goto error;
1275 push_basic_key_mapping(&cfg->basic_keys[…
1276 }
1277 basic_keys_init = 1;
1278 } else if (str_array_equal("command-keys", key, key_len)…
1279 if (list->objs[i].obj.assignment.value->type != …
1280 *errstr = print_fmt(
1281 "%s: Invalid command-keys setting in…
1282 "at line %zu, offset %zu", filename,…
1283 );
1284 goto error;
1285 } else if (command_keys_init) {
1286 *errstr = print_fmt(
1287 "%s: Duplicate command-keys setting …
1288 "at line %zu, offset %zu", filename,…
1289 );
1290 goto error;
1291 }
1292 ast_list *slist = &list->objs[i].obj.assignment.…
1293 for (size_t j = 0; j < slist->len; j++) {
1294 line = slist->objs[j].tok.line;
1295 line_offset = slist->objs[j].tok.line_of…
1296 if (slist->objs[j].type != OBJ_STATEMENT…
1297 *errstr = print_fmt(
1298 "%s: Invalid command-keys se…
1299 "at line %zu, offset %zu", f…
1300 );
1301 goto error;
1302 }
1303 command_key_mapping m;
1304 if (parse_command_key_statement(&slist->…
1305 goto error;
1306 push_command_key_mapping(&cfg->command_k…
1307 }
1308 command_keys_init = 1;
1309 } else if (str_array_equal("commands", key, key_len)) {
1310 if (list->objs[i].obj.assignment.value->type != …
1311 *errstr = print_fmt(
1312 "%s: Invalid commands setting in bin…
1313 "at line %zu, offset %zu", filename,…
1314 );
1315 goto error;
1316 } else if (commands_init) {
1317 *errstr = print_fmt(
1318 "%s: Duplicate commands setting in b…
1319 "at line %zu, offset %zu", filename,…
1320 );
1321 goto error;
1322 }
1323 ast_list *slist = &list->objs[i].obj.assignment.…
1324 for (size_t j = 0; j < slist->len; j++) {
1325 line = slist->objs[j].tok.line;
1326 line_offset = slist->objs[j].tok.line_of…
1327 if (slist->objs[j].type != OBJ_STATEMENT…
1328 *errstr = print_fmt(
1329 "%s: Invalid commands settin…
1330 "at line %zu, offset %zu", f…
1331 );
1332 goto error;
1333 }
1334 command_mapping m;
1335 if (parse_command_statement(&slist->objs…
1336 goto error;
1337 push_command_mapping(&cfg->cmds[0], m);
1338 }
1339 commands_init = 1;
1340 }
1341 }
1342
1343 /* FIXME: the behavior here is a bit weird - if there is nothing…
1344 setting in the bindings configuration, all actual bindings ar…
1345 associated language is different */
1346 if (!cfg->langs[cur_lang]) {
1347 cfg->langs[cur_lang] = ledit_strdup(language_default);
1348 }
1349 /* FIXME: avoid calling strlen */
1350 if (!basic_keys_init) {
1351 ledit_debug("No basic keys configured in bindings; loadi…
1352 basic_key_mapping m;
1353 for (size_t i = 0; i < LENGTH(basic_keys_default); i++) {
1354 m.cb = basic_key_cb_map_get_entry(basic_keys_def…
1355 if (!m.cb) {
1356 *errstr = print_fmt("default config: Inv…
1357 goto error;
1358 } else if (!basic_key_cb_modemask_is_valid(m.cb,…
1359 *errstr = print_fmt("default config: Fun…
1360 goto error;
1361 }
1362 m.text = basic_keys_default[i].text ? ledit_strd…
1363 m.mods = basic_keys_default[i].mods;
1364 m.modes = basic_keys_default[i].modes;
1365 m.keysym = basic_keys_default[i].keysym;
1366 push_basic_key_mapping(&cfg->basic_keys[0], m);
1367 }
1368 }
1369 if (!command_keys_init) {
1370 ledit_debug("No command keys configured in bindings; loa…
1371 command_key_mapping m;
1372 for (size_t i = 0; i < LENGTH(command_keys_default); i++…
1373 m.cb = command_key_cb_map_get_entry(command_keys…
1374 if (!m.cb) {
1375 *errstr = print_fmt("default config: Inv…
1376 goto error;
1377 } else if (!command_key_cb_modemask_is_valid(m.c…
1378 *errstr = print_fmt("default config: Fun…
1379 goto error;
1380 }
1381 m.text = command_keys_default[i].text ? ledit_st…
1382 m.mods = command_keys_default[i].mods;
1383 m.modes = command_keys_default[i].modes;
1384 m.keysym = command_keys_default[i].keysym;
1385 push_command_key_mapping(&cfg->command_keys[0], …
1386 }
1387 }
1388 /* FIXME: guard against NULL text in default config! */
1389 if (!commands_init) {
1390 ledit_debug("No commands configured in bindings; loading…
1391 command_mapping m;
1392 for (size_t i = 0; i < LENGTH(commands_default); i++) {
1393 m.cb = command_cb_map_get_entry(commands_default…
1394 if (!m.cb) {
1395 *errstr = print_fmt("default config: Inv…
1396 goto error;
1397 }
1398 m.text = ledit_strdup(commands_default[i].text);
1399 push_command_mapping(&cfg->cmds[0], m);
1400 }
1401 }
1402 return 0;
1403 /* FIXME: simplify error handling by doing more here */
1404 error:
1405 return 1;
1406 }
1407
1408 static int
1409 load_mapping(struct config *cfg, ast_list *list, char *filename, char **…
1410 int key_mapping_init = 0, command_mapping_init = 0;
1411 size_t cur_lang = cfg->num_langs - 1; /* FIXME: ensure no underf…
1412 for (size_t i = 0; i < list->len; i++) {
1413 size_t line = list->objs[i].tok.line;
1414 size_t line_offset = list->objs[i].tok.line_offset;
1415 if (list->objs[i].type != OBJ_ASSIGNMENT) {
1416 *errstr = print_fmt(
1417 "%s: Invalid statement in language mapping …
1418 "at list %zu, offset %zu", filename, line, …
1419 );
1420 goto error;
1421 }
1422 char *key = list->objs[i].obj.assignment.tok.text;
1423 size_t key_len = list->objs[i].obj.assignment.tok.len;
1424 basic_key_array *bkmap = &cfg->basic_keys[cur_lang];
1425 command_key_array *ckmap = &cfg->command_keys[cur_lang];
1426 command_array *cmap = &cfg->cmds[cur_lang];
1427 if (str_array_equal("language", key, key_len)) {
1428 if (list->objs[i].obj.assignment.value->type != …
1429 *errstr = print_fmt(
1430 "%s: Invalid language setting in lan…
1431 "at line %zu, offset %zu", filename,…
1432 );
1433 goto error;
1434 } else if (cfg->langs[cur_lang]) {
1435 *errstr = print_fmt(
1436 "%s: Duplicate language setting in l…
1437 "at line %zu, offset %zu", filename,…
1438 );
1439 goto error;
1440 }
1441 char *val = list->objs[i].obj.assignment.value->…
1442 size_t val_len = list->objs[i].obj.assignment.va…
1443 cfg->langs[cur_lang] = ledit_strndup(val, val_le…
1444 } else if (str_array_equal("key-mapping", key, key_len))…
1445 if (list->objs[i].obj.assignment.value->type != …
1446 *errstr = print_fmt(
1447 "%s: Invalid key-mapping setting in …
1448 "at line %zu, offset %zu", filename,…
1449 );
1450 goto error;
1451 } else if (key_mapping_init) {
1452 *errstr = print_fmt(
1453 "%s: Duplicate key-mapping setting i…
1454 "at line %zu, offset %zu", filename,…
1455 );
1456 goto error;
1457 }
1458 ast_list *slist = &list->objs[i].obj.assignment.…
1459 for (size_t j = 0; j < slist->len; j++) {
1460 line = slist->objs[j].tok.line;
1461 line_offset = slist->objs[j].tok.line_of…
1462 if (slist->objs[j].type != OBJ_STATEMENT…
1463 *errstr = print_fmt(
1464 "%s: Invalid key-mapping set…
1465 "at line %zu, offset %zu", f…
1466 );
1467 goto error;
1468 }
1469 ast_statement *st = &slist->objs[j].obj.…
1470 if (!str_array_equal("map", st->func_tok…
1471 *errstr = print_fmt(
1472 "%s: Invalid key-mapping sta…
1473 "at line %zu, offset %zu", f…
1474 );
1475 goto error;
1476 }
1477 /* FIXME: any way to speed this up? I gu…
1478 for (size_t k = 0; k < bkmap->num_keys; …
1479 if (bkmap->keys[k].text && str_a…
1480 free(bkmap->keys[k].text…
1481 bkmap->keys[k].text = le…
1482 }
1483 }
1484 for (size_t k = 0; k < ckmap->num_keys; …
1485 if (ckmap->keys[k].text && str_a…
1486 free(ckmap->keys[k].text…
1487 ckmap->keys[k].text = le…
1488 }
1489 }
1490 }
1491 key_mapping_init = 1;
1492 } else if (str_array_equal("command-mapping", key, key_l…
1493 if (list->objs[i].obj.assignment.value->type != …
1494 *errstr = print_fmt(
1495 "%s: Invalid command-mapping setting…
1496 "at line %zu, offset %zu", filename,…
1497 );
1498 goto error;
1499 } else if (command_mapping_init) {
1500 *errstr = print_fmt(
1501 "%s: Duplicate command-mapping setti…
1502 "at line %zu, offset %zu", filename,…
1503 );
1504 goto error;
1505 }
1506 ast_list *slist = &list->objs[i].obj.assignment.…
1507 for (size_t j = 0; j < slist->len; j++) {
1508 line = slist->objs[j].tok.line;
1509 line_offset = slist->objs[j].tok.line_of…
1510 if (slist->objs[j].type != OBJ_STATEMENT…
1511 *errstr = print_fmt(
1512 "%s: Invalid command-mapping…
1513 "at line %zu, offset %zu", f…
1514 );
1515 goto error;
1516 }
1517 ast_statement *st = &slist->objs[j].obj.…
1518 if (!str_array_equal("map", st->func_tok…
1519 *errstr = print_fmt(
1520 "%s: Invalid command-mapping…
1521 "at line %zu, offset %zu", f…
1522 );
1523 goto error;
1524 }
1525 for (size_t k = 0; k < cmap->num_cmds; k…
1526 if (str_array_equal(cmap->cmds[k…
1527 free(cmap->cmds[k].text);
1528 cmap->cmds[k].text = led…
1529 }
1530 }
1531 }
1532 command_mapping_init = 1;
1533 }
1534 }
1535 if (!cfg->langs[cur_lang]) {
1536 /* FIXME: pass actual beginning line and offset so this …
1537 use the line and offset of the first list element */
1538 if (list->len > 0) {
1539 *errstr = print_fmt(
1540 "%s: Missing language setting in language ma…
1541 "at line %zu, offset %zu", filename, list->o…
1542 );
1543 } else {
1544 *errstr = print_fmt("%s: Missing language settin…
1545 }
1546 goto error;
1547 }
1548 return 0;
1549 error:
1550 return 1;
1551 }
1552
1553 static void
1554 append_mapping(struct config *cfg) {
1555 push_lang(cfg);
1556 ledit_assert(cfg->num_langs > 1);
1557
1558 /* first duplicate original mappings before replacing the text */
1559 /* FIXME: optimize this to avoid useless reallocations */
1560 size_t cur_lang = cfg->num_langs - 1;
1561 basic_key_array *arr1 = &cfg->basic_keys[cur_lang];
1562 arr1->num_keys = arr1->alloc_keys = cfg->basic_keys[0].num_keys;
1563 arr1->keys = ledit_reallocarray(NULL, arr1->num_keys, sizeof(bas…
1564 memmove(arr1->keys, cfg->basic_keys[0].keys, arr1->num_keys * si…
1565 for (size_t i = 0; i < arr1->num_keys; i++) {
1566 if (arr1->keys[i].text)
1567 arr1->keys[i].text = ledit_strdup(arr1->keys[i].…
1568 }
1569
1570
1571 command_key_array *arr2 = &cfg->command_keys[cur_lang];
1572 arr2->num_keys = arr2->alloc_keys = cfg->command_keys[0].num_key…
1573 arr2->keys = ledit_reallocarray(NULL, arr2->num_keys, sizeof(com…
1574 memmove(arr2->keys, cfg->command_keys[0].keys, arr2->num_keys * …
1575 for (size_t i = 0; i < arr2->num_keys; i++) {
1576 if (arr2->keys[i].text)
1577 arr2->keys[i].text = ledit_strdup(arr2->keys[i].…
1578 }
1579
1580 command_array *arr3 = &cfg->cmds[cur_lang];
1581 arr3->num_cmds = arr3->alloc_cmds = cfg->cmds[0].num_cmds;
1582 arr3->cmds = ledit_reallocarray(NULL, arr3->num_cmds, sizeof(com…
1583 memmove(arr3->cmds, cfg->cmds[0].cmds, arr3->num_cmds * sizeof(c…
1584 for (size_t i = 0; i < arr3->num_cmds; i++) {
1585 arr3->cmds[i].text = ledit_strdup(arr3->cmds[i].text);
1586 }
1587 }
1588
1589 #ifdef LEDIT_DEBUG
1590 static void
1591 debug_print_obj(ast_obj *obj, int shiftwidth) {
1592 for (int i = 0; i < shiftwidth; i++) {
1593 fprintf(stderr, " ");
1594 }
1595 switch (obj->type) {
1596 case OBJ_STRING:
1597 fprintf(stderr, "STRING: %.*s\n", (int)obj->obj.str.tok.…
1598 break;
1599 case OBJ_STATEMENT:
1600 fprintf(stderr, "STATEMENT: %.*s ", (int)obj->obj.statem…
1601 for (size_t i = 0; i < obj->obj.statement.len; i++) {
1602 fprintf(stderr, "%.*s ", (int)obj->obj.statement…
1603 }
1604 fprintf(stderr, "\n");
1605 break;
1606 case OBJ_ASSIGNMENT:
1607 fprintf(stderr, "ASSIGNMENT: %.*s =\n", (int)obj->obj.as…
1608 debug_print_obj(obj->obj.assignment.value, shiftwidth + …
1609 break;
1610 case OBJ_LIST:
1611 fprintf(stderr, "LIST:\n");
1612 for (size_t i = 0; i < obj->obj.list.len; i++) {
1613 debug_print_obj(&obj->obj.list.objs[i], shiftwid…
1614 }
1615 break;
1616 }
1617 }
1618 #endif
1619
1620 /* WARNING: *errstr must be freed! */
1621 int
1622 config_loadfile(ledit_common *common, char *filename, char **errstr) {
1623 #ifdef LEDIT_DEBUG
1624 struct timespec now, elapsed, last;
1625 clock_gettime(CLOCK_MONOTONIC, &last);
1626 #endif
1627 size_t len;
1628 *errstr = NULL;
1629 ast_list list = {.objs = NULL, .len = 0, .cap = 0};
1630 char *file_contents = NULL;
1631 if (filename) {
1632 file_contents = load_file(filename, &len, errstr);
1633 if (!file_contents) return 1;
1634 #ifdef LEDIT_DEBUG
1635 clock_gettime(CLOCK_MONOTONIC, &now);
1636 ledit_timespecsub(&now, &last, &elapsed);
1637 ledit_debug_fmt(
1638 "Time to load config file: %lld seconds, %ld nanosec…
1639 (long long)elapsed.tv_sec, elapsed.tv_nsec
1640 );
1641 last = now;
1642 #endif
1643 /* start at line 1 to make error messages more useful */
1644 struct lexstate s = {file_contents, len, 0, 1, 0};
1645 if (parse_list(&s, &list, 1, filename, errstr)) {
1646 free(file_contents);
1647 return 1;
1648 }
1649 #ifdef LEDIT_DEBUG
1650 clock_gettime(CLOCK_MONOTONIC, &now);
1651 ledit_timespecsub(&now, &last, &elapsed);
1652 ledit_debug_fmt(
1653 "Time to parse config file: %lld seconds, %ld nanose…
1654 (long long)elapsed.tv_sec, elapsed.tv_nsec
1655 );
1656 #endif
1657 }
1658
1659 #ifdef LEDIT_DEBUG
1660 clock_gettime(CLOCK_MONOTONIC, &last);
1661 for (size_t i = 0; i < list.len; i++) {
1662 debug_print_obj(&list.objs[i], 0);
1663 }
1664 clock_gettime(CLOCK_MONOTONIC, &now);
1665 ledit_timespecsub(&now, &last, &elapsed);
1666 ledit_debug_fmt(
1667 "Time to print useless information: %lld seconds, %ld nanose…
1668 (long long)elapsed.tv_sec, elapsed.tv_nsec
1669 );
1670 clock_gettime(CLOCK_MONOTONIC, &last);
1671 #endif
1672
1673 struct config cfg = {NULL, NULL, NULL, NULL, NULL, 0, 0};
1674 int theme_init = 0, bindings_init = 0, mappings_init = 0;
1675 ast_assignment *assignment;
1676 for (size_t i = 0; i < list.len; i++) {
1677 switch (list.objs[i].type) {
1678 case OBJ_ASSIGNMENT:
1679 assignment = &list.objs[i].obj.assignment;
1680 if (str_array_equal("theme", assignment->tok.tex…
1681 if (theme_init) {
1682 *errstr = print_fmt(
1683 "%s: Duplicate theme definit…
1684 filename, assignment->tok.li…
1685 );
1686 goto error;
1687 } else if (assignment->value->type != OB…
1688 *errstr = print_fmt(
1689 "%s: Invalid theme definitio…
1690 filename, assignment->tok.li…
1691 );
1692 goto error;
1693 }
1694 cfg.theme = load_theme(common, &assignme…
1695 if (!cfg.theme)
1696 goto error;
1697 theme_init = 1;
1698 } else if (str_array_equal("bindings", assignmen…
1699 if (bindings_init) {
1700 *errstr = print_fmt(
1701 "%s: Duplicate definition of…
1702 filename, assignment->tok.li…
1703 );
1704 goto error;
1705 }
1706 push_lang(&cfg);
1707 if (assignment->value->type != OBJ_LIST)…
1708 *errstr = print_fmt(
1709 "%s: Invalid definition of b…
1710 filename, assignment->tok.li…
1711 );
1712 goto error;
1713 }
1714 if (load_bindings(&cfg, &assignment->val…
1715 goto error;
1716 bindings_init = 1;
1717 } else if (str_array_equal("language-mapping", a…
1718 if (cfg.num_langs == 0) {
1719 ledit_debug("No key/command bind…
1720 push_lang(&cfg);
1721 /* load default config */
1722 ast_list empty_list = {.objs = N…
1723 /* shouldn't usually happen */
1724 if (load_bindings(&cfg, &empty_l…
1725 goto error;
1726 bindings_init = 1;
1727 } else if (assignment->value->type != OB…
1728 *errstr = print_fmt(
1729 "%s: Invalid definition of l…
1730 filename, assignment->tok.li…
1731 );
1732 goto error;
1733 }
1734
1735 append_mapping(&cfg);
1736
1737 if (load_mapping(&cfg, &assignment->valu…
1738 goto error;
1739 mappings_init = 1;
1740 } else {
1741 *errstr = print_fmt(
1742 "%s: Invalid assignment at line %zu,…
1743 filename, assignment->tok.line, assi…
1744 );
1745 goto error;
1746 }
1747 break;
1748 default:
1749 *errstr = print_fmt(
1750 "%s: Invalid statement at line %zu, offset %…
1751 filename, list.objs[i].tok.line, list.objs[i…
1752 );
1753 goto error;
1754 }
1755 }
1756 if (!theme_init) {
1757 ledit_debug("No theme configured; loading defaults\n");
1758 cfg.theme = load_theme(common, NULL, NULL, errstr);
1759 if (!cfg.theme)
1760 goto error;
1761 }
1762 if (!bindings_init) {
1763 ledit_debug("No key/command bindings configured; loading…
1764 push_lang(&cfg);
1765 /* load default config */
1766 ast_list empty_list = {.objs = NULL, .len = 0, .cap = 0};
1767 /* shouldn't usually happen */
1768 if (load_bindings(&cfg, &empty_list, NULL, errstr))
1769 goto error;
1770 }
1771 if (!mappings_init) {
1772 ledit_debug("No key/command mappings configured; loading…
1773 for (size_t i = 0; i < LENGTH(mappings_default); i++) {
1774 append_mapping(&cfg);
1775 size_t cur_lang = cfg.num_langs - 1;
1776 cfg.langs[cur_lang] = ledit_strdup(mappings_defa…
1777 basic_key_array *bkmap = &cfg.basic_keys[cur_lan…
1778 command_key_array *ckmap = &cfg.command_keys[cur…
1779 command_array *cmap = &cfg.cmds[cur_lang];
1780 /* FIXME: any way to speed this up? I guess once…
1781 /* FIXME: duplicated code from above */
1782 for (size_t j = 0; j < mappings_default[i].keys_…
1783 for (size_t k = 0; k < bkmap->num_keys; …
1784 if (bkmap->keys[k].text && !strc…
1785 free(bkmap->keys[k].text…
1786 bkmap->keys[k].text = le…
1787 }
1788 }
1789 for (size_t k = 0; k < ckmap->num_keys; …
1790 if (ckmap->keys[k].text && !strc…
1791 free(ckmap->keys[k].text…
1792 ckmap->keys[k].text = le…
1793 }
1794 }
1795 }
1796 for (size_t j = 0; j < mappings_default[i].cmds_…
1797 for (size_t k = 0; k < cmap->num_cmds; k…
1798 if (!strcmp(cmap->cmds[k].text, …
1799 free(cmap->cmds[k].text);
1800 cmap->cmds[k].text = led…
1801 }
1802 }
1803 }
1804 }
1805 }
1806 destroy_list(&list);
1807 free(file_contents);
1808 config_destroy(common, &config);
1809 config = cfg;
1810 #ifdef LEDIT_DEBUG
1811 clock_gettime(CLOCK_MONOTONIC, &now);
1812 ledit_timespecsub(&now, &last, &elapsed);
1813 ledit_debug_fmt(
1814 "Time to interpret config file: %lld seconds, %ld nanosecond…
1815 (long long)elapsed.tv_sec, elapsed.tv_nsec
1816 );
1817 #endif
1818 return 0;
1819 error:
1820 destroy_list(&list);
1821 free(file_contents);
1822 config_destroy(common, &cfg);
1823 return 1;
1824 }
1825
1826 ledit_theme *
1827 config_get_theme(void) {
1828 ledit_assert(config.theme != NULL);
1829 return config.theme;
1830 }
1831
1832 basic_key_array *
1833 config_get_basic_keys(size_t lang_index) {
1834 ledit_assert(lang_index < config.num_langs);
1835 return &config.basic_keys[lang_index];
1836 }
1837
1838 command_key_array *
1839 config_get_command_keys(size_t lang_index) {
1840 ledit_assert(lang_index < config.num_langs);
1841 return &config.command_keys[lang_index];
1842 }
1843
1844 command_array *
1845 config_get_commands(size_t lang_index) {
1846 ledit_assert(lang_index < config.num_langs);
1847 return &config.cmds[lang_index];
1848 }
1849
1850 int
1851 config_get_language_index(char *lang, size_t *idx_ret) {
1852 for (size_t i = 0; i < config.num_langs; i++) {
1853 if (!strcmp(lang, config.langs[i])) {
1854 *idx_ret = i;
1855 return 0;
1856 }
1857 }
1858 return 1;
1859 }
1860
1861 char *
1862 config_get_language_string(size_t lang_index) {
1863 if (lang_index >= config.num_langs)
1864 return NULL;
1865 return config.langs[lang_index];
1866 }
You are viewing proxied material from lumidify.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.