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 } |