big dialogs: set_curosr2 -> set_dlg_cursor.
[elinks.git] / src / config / conf.c
blob95c6ee13a0072609d20060ce5abb11264f067a07
1 /* Config file manipulation */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <sys/types.h>
11 #include <sys/stat.h> /* OS/2 needs this after sys/types.h */
12 #ifdef HAVE_FCNTL_H
13 #include <fcntl.h> /* OS/2 needs this after sys/types.h */
14 #endif
15 #ifdef HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
19 #include "elinks.h"
21 #include "config/conf.h"
22 #include "config/dialogs.h"
23 #include "config/domain.h"
24 #include "config/home.h"
25 #include "config/kbdbind.h"
26 #include "config/options.h"
27 #include "config/opttypes.h"
28 #include "intl/gettext/libintl.h"
29 #include "osdep/osdep.h"
30 #include "terminal/terminal.h"
31 #include "util/error.h"
32 #include "util/memory.h"
33 #include "util/secsave.h"
34 #include "util/string.h"
37 /* Config file has only very simple grammar:
39 * /set *option *= *value/
40 * /bind *keymap *keystroke *= *action/
41 * /include *file/
42 * /#.*$/
44 * Where option consists from any number of categories separated by dots and
45 * name of the option itself. Both category and option name consists from
46 * [a-zA-Z0-9_-*] - using uppercase letters is not recommended, though. '*' is
47 * reserved and is used only as escape character in place of '.' originally in
48 * option name.
50 * Value can consist from:
51 * - number (it will be converted to int/long)
52 * - enum (like on, off; true, fake, last_url; etc ;) - in planning state yet
53 * - string - "blah blah" (keymap, keystroke and action and file looks like that too)
55 * "set" command is parsed first, and then type-specific function is called,
56 * with option as one parameter and value as a second. Usually it just assigns
57 * value to an option, but sometimes you may want to first create the option
58 * ;). Then this will come handy. */
60 struct conf_parsing_state {
61 /** This part may be copied to a local variable as a bookmark
62 * and restored later. So it must not contain any pointers
63 * that would have to be freed in that situation. */
64 struct conf_parsing_pos {
65 /** Points to the next character to be parsed from the
66 * configuration file. */
67 unsigned char *look;
69 /** The line number corresponding to #look. This is
70 * shown in error messages. */
71 int line;
72 } pos;
74 /** When ELinks is rewriting the configuration file, @c mirrored
75 * indicates the end of the part that has already been copied
76 * to the mirror string. Otherwise, @c mirrored is not used.
78 * @invariant @c mirrored @<= @c pos.look */
79 unsigned char *mirrored;
81 /** File name for error messages. If NULL then do not display
82 * error messages. */
83 const unsigned char *filename;
86 /** Tell the user about an error in the configuration file.
87 * @return @a err, for convenience. */
88 static enum parse_error
89 show_parse_error(const struct conf_parsing_state *state, enum parse_error err)
91 static const unsigned char error_msg[][40] = {
92 "no error", /* ERROR_NONE */
93 "unknown command", /* ERROR_COMMAND */
94 "parse error", /* ERROR_PARSE */
95 "unknown option", /* ERROR_OPTION */
96 "bad value", /* ERROR_VALUE */
97 "no memory left", /* ERROR_NOMEM */
100 if (state->filename) {
101 fprintf(stderr, "%s:%d: %s\n",
102 state->filename, state->pos.line, error_msg[err]);
104 return err;
107 /** Skip comments and whitespace. */
108 static void
109 skip_white(struct conf_parsing_pos *pos)
111 unsigned char *start = pos->look;
113 while (*start) {
114 while (isspace(*start)) {
115 if (*start == '\n') {
116 pos->line++;
118 start++;
121 if (*start == '#') {
122 start += strcspn(start, "\n");
123 } else {
124 pos->look = start;
125 return;
129 pos->look = start;
132 /** Skip a quoted string.
133 * This function allows "mismatching quotes' because str_rd() does so. */
134 static void
135 skip_quoted(struct conf_parsing_pos *pos)
137 assert(isquote(*pos->look));
138 if_assert_failed return;
139 pos->look++;
141 for (;;) {
142 if (!*pos->look)
143 return;
144 if (isquote(*pos->look)) {
145 pos->look++;
146 return;
148 if (*pos->look == '\\' && pos->look[1])
149 pos->look++;
150 if (*pos->look == '\n')
151 pos->line++;
152 pos->look++;
156 /** Skip the value of an option.
158 * This job is normally done by the reader function that corresponds
159 * to the type of the option. However, if ELinks does not recognize
160 * the name of the option, it cannot look up the type and has to use
161 * this function instead. */
162 static void
163 skip_option_value(struct conf_parsing_pos *pos)
165 if (isquote(*pos->look)) {
166 /* Looks like OPT_STRING, OPT_CODEPAGE, OPT_LANGUAGE,
167 * or OPT_COLOR. */
168 skip_quoted(pos);
169 } else {
170 /* Looks like OPT_BOOL, OPT_INT, or OPT_LONG. */
171 while (isasciialnum(*pos->look) || *pos->look == '.'
172 || *pos->look == '+' || *pos->look == '-')
173 pos->look++;
177 /** Skip to the next newline or comment that is not part of a quoted
178 * string. When ELinks hits a parse error in the configuration file,
179 * it calls this in order to find the place where is should resume
180 * parsing. This is intended to prevent ELinks from treating words
181 * in strings as commands. */
182 static void
183 skip_to_unquoted_newline_or_comment(struct conf_parsing_pos *pos)
185 while (*pos->look && *pos->look != '#' && *pos->look != '\n') {
186 if (isquote(*pos->look))
187 skip_quoted(pos);
188 else
189 pos->look++;
193 /* Parse a command. Returns error code. */
194 /* If dynamic string credentials are supplied, we will mirror the command at
195 * the end of the string; however, we won't load the option value to the tree,
196 * and we will even write option value from the tree to the output string.
197 * We will only possibly set or clear OPT_MUST_SAVE flag in the option. */
199 static enum parse_error
200 parse_set_common(struct option *opt_tree, struct conf_parsing_state *state,
201 struct string *mirror, int is_system_conf, int want_domain)
203 const unsigned char *domain_orig = NULL;
204 size_t domain_len = 0;
205 unsigned char *domain_copy = NULL;
206 const unsigned char *optname_orig;
207 size_t optname_len;
208 unsigned char *optname_copy;
210 skip_white(&state->pos);
211 if (!*state->pos.look) return show_parse_error(state, ERROR_PARSE);
213 if (want_domain) {
214 domain_orig = state->pos.look;
215 while (isident(*state->pos.look) || *state->pos.look == '*'
216 || *state->pos.look == '.' || *state->pos.look == '+')
217 state->pos.look++;
218 domain_len = state->pos.look - domain_orig;
220 skip_white(&state->pos);
223 /* Option name */
224 optname_orig = state->pos.look;
225 while (isident(*state->pos.look) || *state->pos.look == '*'
226 || *state->pos.look == '.' || *state->pos.look == '+')
227 state->pos.look++;
228 optname_len = state->pos.look - optname_orig;
230 skip_white(&state->pos);
232 /* Equal sign */
233 if (*state->pos.look != '=')
234 return show_parse_error(state, ERROR_PARSE);
235 state->pos.look++; /* '=' */
236 skip_white(&state->pos);
237 if (!*state->pos.look)
238 return show_parse_error(state, ERROR_VALUE);
240 optname_copy = memacpy(optname_orig, optname_len);
241 if (!optname_copy) return show_parse_error(state, ERROR_NOMEM);
242 if (want_domain) {
243 domain_copy = memacpy(domain_orig, domain_len);
244 if (!domain_copy) {
245 mem_free(optname_copy);
246 return show_parse_error(state, ERROR_NOMEM);
250 /* Option value */
252 struct option *opt;
253 unsigned char *val;
254 const struct conf_parsing_pos pos_before_value = state->pos;
256 if (want_domain && *domain_copy) {
257 struct option *domain_tree;
259 domain_tree = get_domain_tree(domain_copy);
260 if (!domain_tree) {
261 mem_free(domain_copy);
262 mem_free(optname_copy);
263 skip_option_value(&state->pos);
264 return show_parse_error(state, ERROR_NOMEM);
267 if (mirror) {
268 opt = get_opt_rec_real(domain_tree,
269 optname_copy);
270 } else {
271 opt = get_opt_rec(opt_tree, optname_copy);
272 if (opt) {
273 opt = get_option_shadow(opt, opt_tree,
274 domain_tree);
275 if (!opt) {
276 mem_free(domain_copy);
277 mem_free(optname_copy);
278 skip_option_value(&state->pos);
279 return show_parse_error(state,
280 ERROR_NOMEM);
284 } else {
285 opt = mirror
286 ? get_opt_rec_real(opt_tree, optname_copy)
287 : get_opt_rec(opt_tree, optname_copy);
289 if (want_domain)
290 mem_free(domain_copy);
291 domain_copy = NULL;
292 mem_free(optname_copy);
293 optname_copy = NULL;
295 if (!opt || (opt->flags & OPT_HIDDEN)) {
296 show_parse_error(state, ERROR_OPTION);
297 skip_option_value(&state->pos);
298 return ERROR_OPTION;
299 /* TODO: Distinguish between two scenarios:
300 * - A newer version of ELinks has saved an
301 * option that this version does not recognize.
302 * The option must be preserved. (This works.)
303 * - The user has added an option, saved
304 * elinks.conf, restarted ELinks, deleted the
305 * option, and is now saving elinks.conf again.
306 * The option should be rewritten to "unset".
307 * (This does not work yet.)
308 * In both cases, ELinks has no struct option
309 * for that name. Possible fixes:
310 * - If the tree has OPT_AUTOCREATE, then
311 * assume the user had created that option,
312 * and rewrite it to "unset". Otherwise,
313 * keep it.
314 * - When the user deletes an option, just mark
315 * it with OPT_DELETED, and keep it in memory
316 * as long as OPT_TOUCHED is set. */
319 if (!option_types[opt->type].read) {
320 show_parse_error(state, ERROR_VALUE);
321 skip_option_value(&state->pos);
322 return ERROR_VALUE;
325 val = option_types[opt->type].read(opt, &state->pos.look,
326 &state->pos.line);
327 if (!val) {
328 /* The reader function failed. Jump back to
329 * the beginning of the value and skip it with
330 * the generic code. For the error message,
331 * use the line number at the beginning of the
332 * value, because the ending position is not
333 * interesting if there is an unclosed quote. */
334 state->pos = pos_before_value;
335 show_parse_error(state, ERROR_VALUE);
336 skip_option_value(&state->pos);
337 return ERROR_VALUE;
340 if (!mirror) {
341 /* loading a configuration file */
342 if (!option_types[opt->type].set
343 || !option_types[opt->type].set(opt, val)) {
344 mem_free(val);
345 return show_parse_error(state, ERROR_VALUE);
347 } else if (is_system_conf) {
348 /* scanning a file that will not be rewritten */
349 struct option *flagsite = indirect_option(opt);
351 if (!(flagsite->flags & OPT_DELETED)
352 && option_types[opt->type].equals
353 && option_types[opt->type].equals(opt, val))
354 flagsite->flags &= ~OPT_MUST_SAVE;
355 else
356 flagsite->flags |= OPT_MUST_SAVE;
357 } else {
358 /* rewriting a configuration file */
359 struct option *flagsite = indirect_option(opt);
361 if (flagsite->flags & OPT_DELETED) {
362 /* Replace the "set" command with an
363 * "unset" command. */
364 add_to_string(mirror, "unset ");
365 add_bytes_to_string(mirror, optname_orig,
366 optname_len);
367 state->mirrored = state->pos.look;
368 } else if (option_types[opt->type].write) {
369 add_bytes_to_string(mirror, state->mirrored,
370 pos_before_value.look
371 - state->mirrored);
372 option_types[opt->type].write(opt, mirror);
373 state->mirrored = state->pos.look;
375 /* Remember that the option need not be
376 * written to the end of the file. */
377 flagsite->flags &= ~OPT_MUST_SAVE;
379 mem_free(val);
382 return ERROR_NONE;
385 static enum parse_error
386 parse_set_domain(struct option *opt_tree, struct conf_parsing_state *state,
387 struct string *mirror, int is_system_conf)
389 return parse_set_common(opt_tree, state, mirror, is_system_conf, 1);
392 static enum parse_error
393 parse_set(struct option *opt_tree, struct conf_parsing_state *state,
394 struct string *mirror, int is_system_conf)
396 return parse_set_common(opt_tree, state, mirror, is_system_conf, 0);
400 static enum parse_error
401 parse_unset(struct option *opt_tree, struct conf_parsing_state *state,
402 struct string *mirror, int is_system_conf)
404 const unsigned char *optname_orig;
405 size_t optname_len;
406 unsigned char *optname_copy;
408 skip_white(&state->pos);
409 if (!*state->pos.look) return show_parse_error(state, ERROR_PARSE);
411 /* Option name */
412 optname_orig = state->pos.look;
413 while (isident(*state->pos.look) || *state->pos.look == '*'
414 || *state->pos.look == '.' || *state->pos.look == '+')
415 state->pos.look++;
416 optname_len = state->pos.look - optname_orig;
418 optname_copy = memacpy(optname_orig, optname_len);
419 if (!optname_copy) return show_parse_error(state, ERROR_NOMEM);
422 struct option *opt;
424 opt = get_opt_rec_real(opt_tree, optname_copy);
425 mem_free(optname_copy);
426 optname_copy = NULL;
428 if (!opt || (opt->flags & OPT_HIDDEN)) {
429 /* The user wanted to delete the option, and
430 * it has already been deleted; this is not an
431 * error. This might happen if a version of
432 * ELinks has a built-in URL rewriting rule,
433 * the user disables it, and a later version
434 * no longer has it. */
435 return ERROR_NONE;
438 if (!mirror) {
439 /* loading a configuration file */
440 if (opt->flags & OPT_ALLOC) delete_option(opt);
441 else mark_option_as_deleted(opt);
442 } else if (is_system_conf) {
443 /* scanning a file that will not be rewritten */
444 struct option *flagsite = indirect_option(opt);
446 if (flagsite->flags & OPT_DELETED)
447 flagsite->flags &= ~OPT_MUST_SAVE;
448 else
449 flagsite->flags |= OPT_MUST_SAVE;
450 } else {
451 /* rewriting a configuration file */
452 struct option *flagsite = indirect_option(opt);
454 if (flagsite->flags & OPT_DELETED) {
455 /* The "unset" command is already in the file,
456 * and unlike with "set", there is no value
457 * to be updated. */
458 } else if (option_types[opt->type].write) {
459 /* Replace the "unset" command with a
460 * "set" command. */
461 add_to_string(mirror, "set ");
462 add_bytes_to_string(mirror, optname_orig,
463 optname_len);
464 add_to_string(mirror, " = ");
465 option_types[opt->type].write(opt, mirror);
466 state->mirrored = state->pos.look;
468 /* Remember that the option need not be
469 * written to the end of the file. */
470 flagsite->flags &= ~OPT_MUST_SAVE;
474 return ERROR_NONE;
477 static enum parse_error
478 parse_bind(struct option *opt_tree, struct conf_parsing_state *state,
479 struct string *mirror, int is_system_conf)
481 unsigned char *keymap, *keystroke, *action;
482 enum parse_error err = ERROR_NONE;
483 struct conf_parsing_pos before_error;
485 skip_white(&state->pos);
486 if (!*state->pos.look) return show_parse_error(state, ERROR_PARSE);
488 /* Keymap */
489 before_error = state->pos;
490 keymap = option_types[OPT_STRING].read(NULL, &state->pos.look,
491 &state->pos.line);
492 skip_white(&state->pos);
493 if (!keymap || !*state->pos.look) {
494 state->pos = before_error;
495 return show_parse_error(state, ERROR_PARSE);
498 /* Keystroke */
499 before_error = state->pos;
500 keystroke = option_types[OPT_STRING].read(NULL, &state->pos.look,
501 &state->pos.line);
502 skip_white(&state->pos);
503 if (!keystroke || !*state->pos.look) {
504 mem_free(keymap); mem_free_if(keystroke);
505 state->pos = before_error;
506 return show_parse_error(state, ERROR_PARSE);
509 /* Equal sign */
510 skip_white(&state->pos);
511 if (*state->pos.look != '=') {
512 mem_free(keymap); mem_free(keystroke);
513 return show_parse_error(state, ERROR_PARSE);
515 state->pos.look++; /* '=' */
517 skip_white(&state->pos);
518 if (!*state->pos.look) {
519 mem_free(keymap); mem_free(keystroke);
520 return show_parse_error(state, ERROR_PARSE);
523 /* Action */
524 before_error = state->pos;
525 action = option_types[OPT_STRING].read(NULL, &state->pos.look,
526 &state->pos.line);
527 if (!action) {
528 mem_free(keymap); mem_free(keystroke);
529 state->pos = before_error;
530 return show_parse_error(state, ERROR_PARSE);
533 if (!mirror) {
534 /* loading a configuration file */
535 /* We don't bother to bind() if -default-keys. */
536 if (!get_cmd_opt_bool("default-keys")
537 && bind_do(keymap, keystroke, action, is_system_conf)) {
538 /* bind_do() tried but failed. */
539 err = show_parse_error(state, ERROR_VALUE);
540 } else {
541 err = ERROR_NONE;
543 } else if (is_system_conf) {
544 /* scanning a file that will not be rewritten */
545 /* TODO */
546 } else {
547 /* rewriting a configuration file */
548 /* Mirror what we already have. If the keystroke has
549 * been unbound, then act_str is simply "none" and
550 * this does not require special handling. */
551 unsigned char *act_str = bind_act(keymap, keystroke);
553 if (act_str) {
554 add_bytes_to_string(mirror, state->mirrored,
555 before_error.look - state->mirrored);
556 add_to_string(mirror, act_str);
557 mem_free(act_str);
558 state->mirrored = state->pos.look;
559 } else {
560 err = show_parse_error(state, ERROR_VALUE);
563 mem_free(keymap); mem_free(keystroke); mem_free(action);
564 return err;
567 static int load_config_file(unsigned char *, unsigned char *, struct option *,
568 struct string *, int);
570 static enum parse_error
571 parse_include(struct option *opt_tree, struct conf_parsing_state *state,
572 struct string *mirror, int is_system_conf)
574 unsigned char *fname;
575 struct string dumbstring;
576 struct conf_parsing_pos before_error;
578 if (!init_string(&dumbstring))
579 return show_parse_error(state, ERROR_NOMEM);
581 skip_white(&state->pos);
582 if (!*state->pos.look) {
583 done_string(&dumbstring);
584 return show_parse_error(state, ERROR_PARSE);
587 /* File name */
588 before_error = state->pos;
589 fname = option_types[OPT_STRING].read(NULL, &state->pos.look,
590 &state->pos.line);
591 if (!fname) {
592 done_string(&dumbstring);
593 state->pos = before_error;
594 return show_parse_error(state, ERROR_PARSE);
597 /* We want load_config_file() to watermark stuff, but not to load
598 * anything, polluting our beloved options tree - thus, we will feed it
599 * with some dummy string which we will destroy later; still better
600 * than cloning whole options tree or polluting interface with another
601 * rarely-used option ;). */
602 /* XXX: We should try CONFDIR/<file> when proceeding
603 * CONFDIR/<otherfile> ;). --pasky */
604 if (load_config_file(fname[0] == '/' ? (unsigned char *) ""
605 : elinks_home,
606 fname, opt_tree,
607 mirror ? &dumbstring : NULL, 1)) {
608 done_string(&dumbstring);
609 mem_free(fname);
610 return show_parse_error(state, ERROR_VALUE);
613 done_string(&dumbstring);
614 mem_free(fname);
615 return ERROR_NONE;
619 struct parse_handler {
620 const unsigned char *command;
621 enum parse_error (*handler)(struct option *opt_tree,
622 struct conf_parsing_state *state,
623 struct string *mirror, int is_system_conf);
626 static const struct parse_handler parse_handlers[] = {
627 { "set_domain", parse_set_domain },
628 { "set", parse_set },
629 { "unset", parse_unset },
630 { "bind", parse_bind },
631 { "include", parse_include },
632 { NULL, NULL }
636 static enum parse_error
637 parse_config_command(struct option *options, struct conf_parsing_state *state,
638 struct string *mirror, int is_system_conf)
640 const struct parse_handler *handler;
642 /* If we're mirroring, then everything up to this point must
643 * have already been mirrored. */
644 assert(mirror == NULL || state->mirrored == state->pos.look);
645 if_assert_failed return show_parse_error(state, ERROR_PARSE);
647 for (handler = parse_handlers; handler->command;
648 handler++) {
649 int cmdlen = strlen(handler->command);
651 if (!strncmp(state->pos.look, handler->command, cmdlen)
652 && isspace(state->pos.look[cmdlen])) {
653 enum parse_error err;
655 state->pos.look += cmdlen;
656 err = handler->handler(options, state, mirror,
657 is_system_conf);
658 if (mirror) {
659 /* Mirror any characters that the handler
660 * consumed but did not already mirror. */
661 add_bytes_to_string(mirror, state->mirrored,
662 state->pos.look - state->mirrored);
663 state->mirrored = state->pos.look;
665 return err;
669 return show_parse_error(state, ERROR_COMMAND);
672 #ifdef CONFIG_EXMODE
673 enum parse_error
674 parse_config_exmode_command(unsigned char *cmd)
676 struct conf_parsing_state state = {{ 0 }};
678 state.pos.look = cmd;
679 state.pos.line = 0;
680 state.mirrored = NULL; /* not read because mirror is NULL too */
681 state.filename = NULL; /* prevent error messages */
683 return parse_config_command(config_options, &state, NULL, 0);
685 #endif /* CONFIG_EXMODE */
687 void
688 parse_config_file(struct option *options, unsigned char *name,
689 unsigned char *file, struct string *mirror,
690 int is_system_conf)
692 struct conf_parsing_state state = {{ 0 }};
693 int error_occurred = 0;
695 state.pos.look = file;
696 state.pos.line = 1;
697 state.mirrored = file;
698 if (!mirror && get_cmd_opt_int("verbose") >= VERBOSE_WARNINGS)
699 state.filename = name;
701 while (state.pos.look && *state.pos.look) {
702 enum parse_error err = ERROR_NONE;
704 /* Skip all possible comments and whitespace. */
705 skip_white(&state.pos);
707 /* Mirror what we already have */
708 if (mirror) {
709 add_bytes_to_string(mirror, state.mirrored,
710 state.pos.look - state.mirrored);
711 state.mirrored = state.pos.look;
714 /* Second chance to escape from the hell. */
715 if (!*state.pos.look) break;
717 err = parse_config_command(options, &state, mirror,
718 is_system_conf);
719 switch (err) {
720 case ERROR_NONE:
721 break;
723 case ERROR_COMMAND:
724 case ERROR_PARSE:
725 /* Jump over this crap we can't understand. */
726 skip_to_unquoted_newline_or_comment(&state.pos);
728 /* Mirror what we already have */
729 if (mirror) {
730 add_bytes_to_string(mirror, state.mirrored,
731 state.pos.look - state.mirrored);
732 state.mirrored = state.pos.look;
735 /* fall through */
736 default:
737 error_occurred = 1;
738 break;
742 if (!error_occurred || !state.filename) return;
744 /* If an error occurred make sure that the user is notified and is able
745 * to see it. First sound the bell. Then, if the text viewer is going to
746 * be started, sleep for a while so the message will be visible before
747 * ELinks starts drawing to on the screen and overwriting any error
748 * messages. This should not be necessary for terminals supporting the
749 * alternate screen mode but better to be safe. (debian bug 305017) */
751 fputc('\a', stderr);
753 if (get_cmd_opt_bool("dump")
754 || get_cmd_opt_bool("source"))
755 return;
757 sleep(1);
762 static unsigned char *
763 read_config_file(unsigned char *name)
765 #define FILE_BUF 1024
766 unsigned char cfg_buffer[FILE_BUF];
767 struct string string;
768 int fd;
769 ssize_t r;
771 fd = open(name, O_RDONLY | O_NOCTTY);
772 if (fd < 0) return NULL;
773 set_bin(fd);
775 if (!init_string(&string)) return NULL;
777 while ((r = safe_read(fd, cfg_buffer, FILE_BUF)) > 0) {
778 int i;
780 /* Clear problems ;). */
781 for (i = 0; i < r; i++)
782 if (!cfg_buffer[i])
783 cfg_buffer[i] = ' ';
785 add_bytes_to_string(&string, cfg_buffer, r);
788 if (r < 0) done_string(&string);
789 close(fd);
791 return string.source;
792 #undef FILE_BUF
795 /* Return 0 on success. */
796 static int
797 load_config_file(unsigned char *prefix, unsigned char *name,
798 struct option *options, struct string *mirror,
799 int is_system_conf)
801 unsigned char *config_str, *config_file;
803 config_file = straconcat(prefix, STRING_DIR_SEP, name,
804 (unsigned char *) NULL);
805 if (!config_file) return 1;
807 config_str = read_config_file(config_file);
808 if (!config_str) {
809 mem_free(config_file);
810 config_file = straconcat(prefix, STRING_DIR_SEP, ".", name,
811 (unsigned char *) NULL);
812 if (!config_file) return 2;
814 config_str = read_config_file(config_file);
815 if (!config_str) {
816 mem_free(config_file);
817 return 3;
821 parse_config_file(options, config_file, config_str, mirror,
822 is_system_conf);
824 mem_free(config_str);
825 mem_free(config_file);
827 return 0;
830 static void
831 load_config_from(unsigned char *file, struct option *tree)
833 load_config_file(CONFDIR, file, tree, NULL, 1);
834 load_config_file(empty_string_or_(elinks_home), file, tree, NULL, 0);
837 void
838 load_config(void)
840 load_config_from(get_cmd_opt_str("config-file"),
841 config_options);
845 static int indentation = 2;
846 /* 0 -> none, 1 -> only option full name+type, 2 -> only desc, 3 -> both */
847 static int comments = 3;
849 static inline unsigned char *
850 conf_i18n(unsigned char *s, int i18n)
852 if (i18n) return gettext(s);
853 return s;
856 static void
857 add_indent_to_string(struct string *string, int depth)
859 if (!depth) return;
861 add_xchar_to_string(string, ' ', depth * indentation);
864 static unsigned char *smart_config_output_fn_domain;
866 static void
867 smart_config_output_fn(struct string *string, struct option *option,
868 unsigned char *path, int depth, int do_print_comment,
869 int action, int i18n)
871 unsigned char *desc_i18n;
873 if (option->type == OPT_ALIAS)
874 return;
876 switch (action) {
877 case 0:
878 if (!(comments & 1)) break;
880 add_indent_to_string(string, depth);
882 add_to_string(string, "## ");
883 if (path) {
884 add_to_string(string, path);
885 add_char_to_string(string, '.');
887 add_to_string(string, option->name);
888 add_char_to_string(string, ' ');
889 add_to_string(string, option_types[option->type].help_str);
890 add_char_to_string(string, '\n');
891 break;
893 case 1:
894 if (!(comments & 2)) break;
896 if (!option->desc || !do_print_comment)
897 break;
899 desc_i18n = conf_i18n(option->desc, i18n);
901 if (!*desc_i18n) break;
903 add_indent_to_string(string, depth);
904 add_to_string(string, "# ");
906 unsigned char *i = desc_i18n;
907 unsigned char *j = i;
908 unsigned char *last_space = NULL;
909 int config_width = 80;
910 int n = depth * indentation + 3;
912 for (; *i; i++, n++) {
913 if (*i == '\n') {
914 last_space = i;
915 goto split;
918 if (*i == ' ') last_space = i;
920 if (n >= config_width && last_space) {
921 split:
922 add_bytes_to_string(string, j, last_space - j);
923 add_char_to_string(string, '\n');
924 add_indent_to_string(string, depth);
925 add_to_string(string, "# ");
926 j = last_space + 1;
927 n = depth * indentation + 2 + i - last_space;
928 last_space = NULL;
931 add_to_string(string, j);
933 add_char_to_string(string, '\n');
934 break;
936 case 2:
938 add_indent_to_string(string, depth);
939 if (option->flags & OPT_DELETED) {
940 add_to_string(string, "un");
942 if (smart_config_output_fn_domain) {
943 add_to_string(string, "set_domain ");
944 add_to_string(string, smart_config_output_fn_domain);
945 add_char_to_string(string, ' ');
946 } else {
947 add_to_string(string, "set ");
949 if (path) {
950 add_to_string(string, path);
951 add_char_to_string(string, '.');
953 add_to_string(string, option->name);
954 if (!(option->flags & OPT_DELETED)) {
955 add_to_string(string, " = ");
956 /* OPT_ALIAS won't ever. OPT_TREE won't reach action 2.
957 * OPT_SPECIAL makes no sense in the configuration
958 * context. */
959 assert(option_types[option->type].write);
960 option_types[option->type].write(option, string);
962 add_char_to_string(string, '\n');
963 if (do_print_comment) add_char_to_string(string, '\n');
964 break;
966 case 3:
967 if (do_print_comment < 2)
968 add_char_to_string(string, '\n');
969 break;
974 static void
975 add_cfg_header_to_string(struct string *string, unsigned char *text)
977 int n = strlen(text) + 2;
979 int_bounds(&n, 10, 80);
981 add_to_string(string, "\n\n\n");
982 add_xchar_to_string(string, '#', n);
983 add_to_string(string, "\n# ");
984 add_to_string(string, text);
985 add_to_string(string, "#\n\n");
988 unsigned char *
989 create_config_string(unsigned char *prefix, unsigned char *name)
991 struct option *options = config_options;
992 struct string config;
993 /* Don't write headers if nothing will be added anyway. */
994 struct string tmpstring;
995 int origlen;
996 int savestyle = get_opt_int("config.saving_style", NULL);
997 int i18n = get_opt_bool("config.i18n", NULL);
999 if (!init_string(&config)) return NULL;
1002 int set_all = (savestyle == 1 || savestyle == 2);
1003 struct domain_tree *domain;
1005 prepare_mustsave_flags(options->value.tree, set_all);
1006 foreach (domain, domain_trees) {
1007 prepare_mustsave_flags(domain->tree->value.tree,
1008 set_all);
1012 /* Scaring. */
1013 if (savestyle == 2
1014 || load_config_file(prefix, name, options, &config, 0)
1015 || !config.length) {
1016 /* At first line, and in English, write ELinks version, may be
1017 * of some help in future. Please keep that format for it.
1018 * --Zas */
1019 add_to_string(&config, "## ELinks " VERSION " configuration file\n\n");
1020 assert(savestyle >= 0 && savestyle <= 3);
1021 switch (savestyle) {
1022 case 0:
1023 add_to_string(&config, conf_i18n(N_(
1024 "## This is ELinks configuration file. You can edit it manually,\n"
1025 "## if you wish so; this file is edited by ELinks when you save\n"
1026 "## options through UI, however only option values will be altered\n"
1027 "## and all your formatting, own comments etc will be kept as-is.\n"),
1028 i18n));
1029 break;
1030 case 1: case 3:
1031 add_to_string(&config, conf_i18n(N_(
1032 "## This is ELinks configuration file. You can edit it manually,\n"
1033 "## if you wish so; this file is edited by ELinks when you save\n"
1034 "## options through UI, however only option values will be altered\n"
1035 "## and missing options will be added at the end of file; if option\n"
1036 "## is not written in this file, but in some file included from it,\n"
1037 "## it is NOT counted as missing. Note that all your formatting,\n"
1038 "## own comments and so on will be kept as-is.\n"), i18n));
1039 break;
1040 case 2:
1041 add_to_string(&config, conf_i18n(N_(
1042 "## This is ELinks configuration file. You can edit it manually,\n"
1043 "## if you wish so, but keep in mind that this file is overwritten\n"
1044 "## by ELinks when you save options through UI and you are out of\n"
1045 "## luck with your formatting and own comments then, so beware.\n"),
1046 i18n));
1047 break;
1050 add_to_string(&config, "##\n");
1052 add_to_string(&config, conf_i18n(N_(
1053 "## Obviously, if you don't like what ELinks is going to do with\n"
1054 "## this file, you can change it by altering the config.saving_style\n"
1055 "## option. Come on, aren't we friendly guys after all?\n"), i18n));
1058 if (savestyle == 0) goto get_me_out;
1060 indentation = get_opt_int("config.indentation", NULL);
1061 comments = get_opt_int("config.comments", NULL);
1063 if (!init_string(&tmpstring)) goto get_me_out;
1065 add_cfg_header_to_string(&tmpstring,
1066 conf_i18n(N_("Automatically saved options\n"), i18n));
1068 origlen = tmpstring.length;
1069 smart_config_string(&tmpstring, 2, i18n, options->value.tree, NULL, 0,
1070 smart_config_output_fn);
1073 struct domain_tree *domain;
1075 foreach (domain, domain_trees) {
1076 smart_config_output_fn_domain = domain->name;
1077 smart_config_string(&tmpstring, 2, i18n,
1078 domain->tree->value.tree,
1079 NULL, 0,
1080 smart_config_output_fn);
1083 smart_config_output_fn_domain = NULL;
1086 if (tmpstring.length > origlen)
1087 add_string_to_string(&config, &tmpstring);
1088 done_string(&tmpstring);
1090 if (!init_string(&tmpstring)) goto get_me_out;
1092 add_cfg_header_to_string(&tmpstring,
1093 conf_i18n(N_("Automatically saved keybindings\n"), i18n));
1095 origlen = tmpstring.length;
1096 bind_config_string(&tmpstring);
1097 if (tmpstring.length > origlen)
1098 add_string_to_string(&config, &tmpstring);
1099 done_string(&tmpstring);
1101 get_me_out:
1102 return config.source;
1105 static int
1106 write_config_file(unsigned char *prefix, unsigned char *name,
1107 struct terminal *term)
1109 int ret = -1;
1110 struct secure_save_info *ssi;
1111 unsigned char *config_file = NULL;
1112 unsigned char *cfg_str = create_config_string(prefix, name);
1113 int prefixlen = strlen(prefix);
1114 int prefix_has_slash = (prefixlen && dir_sep(prefix[prefixlen - 1]));
1115 int name_has_slash = dir_sep(name[0]);
1116 unsigned char *slash = name_has_slash || prefix_has_slash ? "" : STRING_DIR_SEP;
1118 if (!cfg_str) return -1;
1120 if (name_has_slash && prefix_has_slash) name++;
1122 config_file = straconcat(prefix, slash, name, (unsigned char *) NULL);
1123 if (!config_file) goto free_cfg_str;
1125 ssi = secure_open(config_file);
1126 if (ssi) {
1127 secure_fputs(ssi, cfg_str);
1128 ret = secure_close(ssi);
1129 if (!ret) {
1130 struct domain_tree *domain;
1132 untouch_options(config_options->value.tree);
1133 foreach (domain, domain_trees)
1134 untouch_options(domain->tree->value.tree);
1138 write_config_dialog(term, config_file, secsave_errno, ret);
1139 mem_free(config_file);
1141 free_cfg_str:
1142 mem_free(cfg_str);
1144 return ret;
1148 write_config(struct terminal *term)
1150 assert(term);
1152 if (!elinks_home) {
1153 write_config_dialog(term, get_cmd_opt_str("config-file"),
1154 SS_ERR_DISABLED, 0);
1155 return -1;
1158 return write_config_file(elinks_home, get_cmd_opt_str("config-file"),
1159 term);