config: Skip values of unrecognized options.
[elinks.git] / src / config / conf.c
blob8f3a3b9cb99ea6505c63bc656fa463ae5198f156
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/home.h"
24 #include "config/kbdbind.h"
25 #include "config/options.h"
26 #include "config/opttypes.h"
27 #include "intl/gettext/libintl.h"
28 #include "osdep/osdep.h"
29 #include "terminal/terminal.h"
30 #include "util/error.h"
31 #include "util/memory.h"
32 #include "util/secsave.h"
33 #include "util/string.h"
36 /* Config file has only very simple grammar:
38 * /set *option *= *value/
39 * /bind *keymap *keystroke *= *action/
40 * /include *file/
41 * /#.*$/
43 * Where option consists from any number of categories separated by dots and
44 * name of the option itself. Both category and option name consists from
45 * [a-zA-Z0-9_-*] - using uppercase letters is not recommended, though. '*' is
46 * reserved and is used only as escape character in place of '.' originally in
47 * option name.
49 * Value can consist from:
50 * - number (it will be converted to int/long)
51 * - enum (like on, off; true, fake, last_url; etc ;) - in planning state yet
52 * - string - "blah blah" (keymap, keystroke and action and file looks like that too)
54 * "set" command is parsed first, and then type-specific function is called,
55 * with option as one parameter and value as a second. Usually it just assigns
56 * value to an option, but sometimes you may want to first create the option
57 * ;). Then this will come handy. */
59 struct conf_parsing_state {
60 /** This part may be copied to a local variable as a bookmark
61 * and restored later. So it must not contain any pointers
62 * that would have to be freed in that situation. */
63 struct conf_parsing_pos {
64 /** Points to the next character to be parsed from the
65 * configuration file. */
66 unsigned char *look;
68 /** The line number corresponding to #look. This is
69 * shown in error messages. */
70 int line;
71 } pos;
73 /** When ELinks is rewriting the configuration file, @c mirrored
74 * indicates the end of the part that has already been copied
75 * to the mirror string. Otherwise, @c mirrored is not used.
77 * @invariant @c mirrored @<= @c pos.look */
78 unsigned char *mirrored;
80 /** File name for error messages. If NULL then do not display
81 * error messages. */
82 const unsigned char *filename;
85 /** Tell the user about an error in the configuration file.
86 * @return @a err, for convenience. */
87 static enum parse_error
88 show_parse_error(const struct conf_parsing_state *state, enum parse_error err)
90 static const unsigned char error_msg[][40] = {
91 "no error", /* ERROR_NONE */
92 "unknown command", /* ERROR_COMMAND */
93 "parse error", /* ERROR_PARSE */
94 "unknown option", /* ERROR_OPTION */
95 "bad value", /* ERROR_VALUE */
96 "no memory left", /* ERROR_NOMEM */
99 if (state->filename) {
100 fprintf(stderr, "%s:%d: %s\n",
101 state->filename, state->pos.line, error_msg[err]);
103 return err;
106 /** Skip comments and whitespace. */
107 static void
108 skip_white(struct conf_parsing_pos *pos)
110 unsigned char *start = pos->look;
112 while (*start) {
113 while (isspace(*start)) {
114 if (*start == '\n') {
115 pos->line++;
117 start++;
120 if (*start == '#') {
121 start += strcspn(start, "\n");
122 } else {
123 pos->look = start;
124 return;
128 pos->look = start;
131 /** Skip a quoted string.
132 * This function allows "mismatching quotes' because str_rd() does so. */
133 static void
134 skip_quoted(struct conf_parsing_pos *pos)
136 assert(isquote(*pos->look));
137 if_assert_failed return;
138 pos->look++;
140 for (;;) {
141 if (!*pos->look)
142 return;
143 if (isquote(*pos->look)) {
144 pos->look++;
145 return;
147 if (*pos->look == '\\' && pos->look[1])
148 pos->look++;
149 if (*pos->look == '\n')
150 pos->line++;
151 pos->look++;
155 /** Skip the value of an option.
157 * This job is normally done by the reader function that corresponds
158 * to the type of the option. However, if ELinks does not recognize
159 * the name of the option, it cannot look up the type and has to use
160 * this function instead. */
161 static void
162 skip_option_value(struct conf_parsing_pos *pos)
164 if (isquote(*pos->look)) {
165 /* Looks like OPT_STRING, OPT_CODEPAGE, OPT_LANGUAGE,
166 * or OPT_COLOR. */
167 skip_quoted(pos);
168 } else {
169 /* Looks like OPT_BOOL, OPT_INT, or OPT_LONG. */
170 while (isasciialnum(*pos->look) || *pos->look == '.'
171 || *pos->look == '+' || *pos->look == '-')
172 pos->look++;
176 /* Parse a command. Returns error code. */
177 /* If dynamic string credentials are supplied, we will mirror the command at
178 * the end of the string; however, we won't load the option value to the tree,
179 * and we will even write option value from the tree to the output string. We
180 * will only possibly set OPT_WATERMARK flag to the option (if enabled). */
182 static enum parse_error
183 parse_set(struct option *opt_tree, struct conf_parsing_state *state,
184 struct string *mirror, int is_system_conf)
186 unsigned char *optname;
188 skip_white(&state->pos);
189 if (!*state->pos.look) return show_parse_error(state, ERROR_PARSE);
191 /* Option name */
192 optname = state->pos.look;
193 while (isident(*state->pos.look) || *state->pos.look == '*'
194 || *state->pos.look == '.' || *state->pos.look == '+')
195 state->pos.look++;
197 optname = memacpy(optname, state->pos.look - optname);
198 if (!optname) return show_parse_error(state, ERROR_NOMEM);
200 skip_white(&state->pos);
202 /* Equal sign */
203 if (*state->pos.look != '=') {
204 mem_free(optname);
205 return show_parse_error(state, ERROR_PARSE);
207 state->pos.look++; /* '=' */
208 skip_white(&state->pos);
209 if (!*state->pos.look) {
210 mem_free(optname);
211 return show_parse_error(state, ERROR_VALUE);
214 /* Mirror what we already have */
215 if (mirror) {
216 add_bytes_to_string(mirror, state->mirrored,
217 state->pos.look - state->mirrored);
218 state->mirrored = state->pos.look;
221 /* Option value */
223 struct option *opt;
224 unsigned char *val;
225 const struct conf_parsing_pos pos_before_value = state->pos;
227 opt = mirror ? get_opt_rec_real(opt_tree, optname) : get_opt_rec(opt_tree, optname);
228 mem_free(optname);
230 if (!opt || (opt->flags & OPT_HIDDEN)) {
231 show_parse_error(state, ERROR_OPTION);
232 skip_option_value(&state->pos);
233 return ERROR_OPTION;
236 if (!option_types[opt->type].read) {
237 show_parse_error(state, ERROR_VALUE);
238 skip_option_value(&state->pos);
239 return ERROR_VALUE;
242 val = option_types[opt->type].read(opt, &state->pos.look,
243 &state->pos.line);
244 if (!val) {
245 /* The reader function failed. Jump back to
246 * the beginning of the value and skip it with
247 * the generic code. For the error message,
248 * use the line number at the beginning of the
249 * value, because the ending position is not
250 * interesting if there is an unclosed quote. */
251 state->pos = pos_before_value;
252 show_parse_error(state, ERROR_VALUE);
253 skip_option_value(&state->pos);
254 return ERROR_VALUE;
257 if (mirror) {
258 if (opt->flags & OPT_DELETED)
259 opt->flags &= ~OPT_WATERMARK;
260 else
261 opt->flags |= OPT_WATERMARK;
262 if (option_types[opt->type].write) {
263 option_types[opt->type].write(opt, mirror);
264 state->mirrored = state->pos.look;
266 } else if (!option_types[opt->type].set
267 || !option_types[opt->type].set(opt, val)) {
268 mem_free(val);
269 return show_parse_error(state, ERROR_VALUE);
271 /* This is not needed since this will be WATERMARK'd when
272 * saving it. We won't need to save it as touched. */
273 /* if (!str) opt->flags |= OPT_TOUCHED; */
274 mem_free(val);
277 return ERROR_NONE;
280 static enum parse_error
281 parse_unset(struct option *opt_tree, struct conf_parsing_state *state,
282 struct string *mirror, int is_system_conf)
284 unsigned char *optname;
286 /* XXX: This does not handle the autorewriting well and is mostly a
287 * quick hack than anything now. --pasky */
289 skip_white(&state->pos);
290 if (!*state->pos.look) return show_parse_error(state, ERROR_PARSE);
292 /* Option name */
293 optname = state->pos.look;
294 while (isident(*state->pos.look) || *state->pos.look == '*'
295 || *state->pos.look == '.' || *state->pos.look == '+')
296 state->pos.look++;
298 optname = memacpy(optname, state->pos.look - optname);
299 if (!optname) return show_parse_error(state, ERROR_NOMEM);
301 /* Mirror what we have */
302 if (mirror) {
303 add_bytes_to_string(mirror, state->mirrored,
304 state->pos.look - state->mirrored);
305 state->mirrored = state->pos.look;
309 struct option *opt;
311 opt = get_opt_rec_real(opt_tree, optname);
312 mem_free(optname);
314 if (!opt || (opt->flags & OPT_HIDDEN))
315 return show_parse_error(state, ERROR_OPTION);
317 if (!mirror) {
318 if (opt->flags & OPT_ALLOC) delete_option(opt);
319 } else {
320 if (opt->flags & OPT_DELETED)
321 opt->flags |= OPT_WATERMARK;
322 else
323 opt->flags &= ~OPT_WATERMARK;
327 return ERROR_NONE;
330 static enum parse_error
331 parse_bind(struct option *opt_tree, struct conf_parsing_state *state,
332 struct string *mirror, int is_system_conf)
334 unsigned char *next_pos;
335 unsigned char *keymap, *keystroke, *action;
336 enum parse_error err = ERROR_NONE;
338 skip_white(&state->pos);
339 if (!*state->pos.look) return show_parse_error(state, ERROR_PARSE);
341 /* Keymap */
342 keymap = option_types[OPT_STRING].read(NULL, &state->pos.look,
343 &state->pos.line);
344 skip_white(&state->pos);
345 if (!keymap || !*state->pos.look)
346 return show_parse_error(state, ERROR_OPTION);
348 /* Keystroke */
349 keystroke = option_types[OPT_STRING].read(NULL, &state->pos.look,
350 &state->pos.line);
351 skip_white(&state->pos);
352 if (!keystroke || !*state->pos.look) {
353 mem_free(keymap); mem_free_if(keystroke);
354 return show_parse_error(state, ERROR_OPTION);
357 /* Equal sign */
358 skip_white(&state->pos);
359 if (*state->pos.look != '=') {
360 mem_free(keymap); mem_free(keystroke);
361 return show_parse_error(state, ERROR_PARSE);
363 state->pos.look++; /* '=' */
365 skip_white(&state->pos);
366 if (!*state->pos.look) {
367 mem_free(keymap); mem_free(keystroke);
368 return show_parse_error(state, ERROR_PARSE);
371 /* Action */
372 next_pos = state->pos.look;
373 action = option_types[OPT_STRING].read(NULL, &state->pos.look,
374 &state->pos.line);
375 if (!action) {
376 mem_free(keymap); mem_free(keystroke);
377 return show_parse_error(state, ERROR_VALUE);
380 if (mirror) {
381 /* Mirror what we already have */
382 unsigned char *act_str = bind_act(keymap, keystroke);
384 if (act_str) {
385 add_bytes_to_string(mirror, state->mirrored,
386 next_pos - state->mirrored);
387 add_to_string(mirror, act_str);
388 mem_free(act_str);
389 state->mirrored = state->pos.look;
390 } else {
391 err = show_parse_error(state, ERROR_VALUE);
393 } else {
394 /* We don't bother to bind() if -default-keys. */
395 if (!get_cmd_opt_bool("default-keys")
396 && bind_do(keymap, keystroke, action, is_system_conf)) {
397 /* bind_do() tried but failed. */
398 err = show_parse_error(state, ERROR_VALUE);
399 } else {
400 err = ERROR_NONE;
403 mem_free(keymap); mem_free(keystroke); mem_free(action);
404 return err;
407 static int load_config_file(unsigned char *, unsigned char *, struct option *,
408 struct string *, int);
410 static enum parse_error
411 parse_include(struct option *opt_tree, struct conf_parsing_state *state,
412 struct string *mirror, int is_system_conf)
414 unsigned char *fname;
415 struct string dumbstring;
417 if (!init_string(&dumbstring))
418 return show_parse_error(state, ERROR_NOMEM);
420 skip_white(&state->pos);
421 if (!*state->pos.look) {
422 done_string(&dumbstring);
423 return show_parse_error(state, ERROR_PARSE);
426 /* File name */
427 fname = option_types[OPT_STRING].read(NULL, &state->pos.look,
428 &state->pos.line);
429 if (!fname) {
430 done_string(&dumbstring);
431 return show_parse_error(state, ERROR_VALUE);
434 /* We want load_config_file() to watermark stuff, but not to load
435 * anything, polluting our beloved options tree - thus, we will feed it
436 * with some dummy string which we will destroy later; still better
437 * than cloning whole options tree or polluting interface with another
438 * rarely-used option ;). */
439 /* XXX: We should try CONFDIR/<file> when proceeding
440 * CONFDIR/<otherfile> ;). --pasky */
441 if (load_config_file(fname[0] == '/' ? (unsigned char *) ""
442 : elinks_home,
443 fname, opt_tree, &dumbstring, is_system_conf)) {
444 done_string(&dumbstring);
445 mem_free(fname);
446 return show_parse_error(state, ERROR_VALUE);
449 done_string(&dumbstring);
450 mem_free(fname);
451 return ERROR_NONE;
455 struct parse_handler {
456 const unsigned char *command;
457 enum parse_error (*handler)(struct option *opt_tree,
458 struct conf_parsing_state *state,
459 struct string *mirror, int is_system_conf);
462 static const struct parse_handler parse_handlers[] = {
463 { "set", parse_set },
464 { "unset", parse_unset },
465 { "bind", parse_bind },
466 { "include", parse_include },
467 { NULL, NULL }
471 static enum parse_error
472 parse_config_command(struct option *options, struct conf_parsing_state *state,
473 struct string *mirror, int is_system_conf)
475 const struct parse_handler *handler;
477 /* If we're mirroring, then everything up to this point must
478 * have already been mirrored. */
479 assert(mirror == NULL || state->mirrored == state->pos.look);
480 if_assert_failed return show_parse_error(state, ERROR_PARSE);
482 for (handler = parse_handlers; handler->command;
483 handler++) {
484 int cmdlen = strlen(handler->command);
486 if (!strncmp(state->pos.look, handler->command, cmdlen)
487 && isspace(state->pos.look[cmdlen])) {
488 enum parse_error err;
490 state->pos.look += cmdlen;
491 err = handler->handler(options, state, mirror,
492 is_system_conf);
493 if (mirror) {
494 /* Mirror any characters that the handler
495 * consumed but did not already mirror. */
496 add_bytes_to_string(mirror, state->mirrored,
497 state->pos.look - state->mirrored);
498 state->mirrored = state->pos.look;
500 return err;
504 return show_parse_error(state, ERROR_COMMAND);
507 #ifdef CONFIG_EXMODE
508 enum parse_error
509 parse_config_exmode_command(unsigned char *cmd)
511 struct conf_parsing_state state = {{ 0 }};
513 state.pos.look = cmd;
514 state.pos.line = 0;
515 state.mirrored = NULL; /* not read because mirror is NULL too */
516 state.filename = NULL; /* prevent error messages */
518 return parse_config_command(config_options, &state, NULL, 0);
520 #endif /* CONFIG_EXMODE */
522 void
523 parse_config_file(struct option *options, unsigned char *name,
524 unsigned char *file, struct string *mirror,
525 int is_system_conf)
527 struct conf_parsing_state state = {{ 0 }};
528 int error_occurred = 0;
530 state.pos.look = file;
531 state.pos.line = 1;
532 state.mirrored = file;
533 if (!mirror && get_cmd_opt_int("verbose") >= VERBOSE_WARNINGS)
534 state.filename = name;
536 while (state.pos.look && *state.pos.look) {
537 enum parse_error err = ERROR_NONE;
539 /* Skip all possible comments and whitespace. */
540 skip_white(&state.pos);
542 /* Mirror what we already have */
543 if (mirror) {
544 add_bytes_to_string(mirror, state.mirrored,
545 state.pos.look - state.mirrored);
546 state.mirrored = state.pos.look;
549 /* Second chance to escape from the hell. */
550 if (!*state.pos.look) break;
552 err = parse_config_command(options, &state, mirror,
553 is_system_conf);
555 if (err == ERROR_COMMAND) {
556 /* Jump over this crap we can't understand. */
557 while (!isspace(*state.pos.look)
558 && *state.pos.look != '#' && *state.pos.look)
559 state.pos.look++;
561 /* Mirror what we already have */
562 if (mirror) {
563 add_bytes_to_string(mirror, state.mirrored,
564 state.pos.look - state.mirrored);
565 state.mirrored = state.pos.look;
569 if (err != ERROR_NONE)
570 error_occurred = 1;
573 if (!error_occurred || !state.filename) return;
575 /* If an error occurred make sure that the user is notified and is able
576 * to see it. First sound the bell. Then, if the text viewer is going to
577 * be started, sleep for a while so the message will be visible before
578 * ELinks starts drawing to on the screen and overwriting any error
579 * messages. This should not be necessary for terminals supporting the
580 * alternate screen mode but better to be safe. (debian bug 305017) */
582 fputc('\a', stderr);
584 if (get_cmd_opt_bool("dump")
585 || get_cmd_opt_bool("source"))
586 return;
588 sleep(1);
593 static unsigned char *
594 read_config_file(unsigned char *name)
596 #define FILE_BUF 1024
597 unsigned char cfg_buffer[FILE_BUF];
598 struct string string;
599 int fd;
600 ssize_t r;
602 fd = open(name, O_RDONLY | O_NOCTTY);
603 if (fd < 0) return NULL;
604 set_bin(fd);
606 if (!init_string(&string)) return NULL;
608 while ((r = safe_read(fd, cfg_buffer, FILE_BUF)) > 0) {
609 int i;
611 /* Clear problems ;). */
612 for (i = 0; i < r; i++)
613 if (!cfg_buffer[i])
614 cfg_buffer[i] = ' ';
616 add_bytes_to_string(&string, cfg_buffer, r);
619 if (r < 0) done_string(&string);
620 close(fd);
622 return string.source;
623 #undef FILE_BUF
626 /* Return 0 on success. */
627 static int
628 load_config_file(unsigned char *prefix, unsigned char *name,
629 struct option *options, struct string *mirror,
630 int is_system_conf)
632 unsigned char *config_str, *config_file;
634 config_file = straconcat(prefix, STRING_DIR_SEP, name,
635 (unsigned char *) NULL);
636 if (!config_file) return 1;
638 config_str = read_config_file(config_file);
639 if (!config_str) {
640 mem_free(config_file);
641 config_file = straconcat(prefix, STRING_DIR_SEP, ".", name,
642 (unsigned char *) NULL);
643 if (!config_file) return 2;
645 config_str = read_config_file(config_file);
646 if (!config_str) {
647 mem_free(config_file);
648 return 3;
652 parse_config_file(options, config_file, config_str, mirror,
653 is_system_conf);
655 mem_free(config_str);
656 mem_free(config_file);
658 return 0;
661 static void
662 load_config_from(unsigned char *file, struct option *tree)
664 load_config_file(CONFDIR, file, tree, NULL, 1);
665 load_config_file(empty_string_or_(elinks_home), file, tree, NULL, 0);
668 void
669 load_config(void)
671 load_config_from(get_cmd_opt_str("config-file"),
672 config_options);
676 static int indentation = 2;
677 /* 0 -> none, 1 -> only option full name+type, 2 -> only desc, 3 -> both */
678 static int comments = 3;
679 static int touching = 0;
681 static inline unsigned char *
682 conf_i18n(unsigned char *s, int i18n)
684 if (i18n) return gettext(s);
685 return s;
688 static void
689 add_indent_to_string(struct string *string, int depth)
691 if (!depth) return;
693 add_xchar_to_string(string, ' ', depth * indentation);
696 static void
697 smart_config_output_fn(struct string *string, struct option *option,
698 unsigned char *path, int depth, int do_print_comment,
699 int action, int i18n)
701 unsigned char *desc_i18n;
703 if (option->type == OPT_ALIAS)
704 return;
706 /* XXX: OPT_LANGUAGE shouldn't have any bussiness here, but we're just
707 * weird in that area. */
708 if (touching && !(option->flags & OPT_TOUCHED)
709 && option->type != OPT_LANGUAGE)
710 return;
712 switch (action) {
713 case 0:
714 if (!(comments & 1)) break;
716 add_indent_to_string(string, depth);
718 add_to_string(string, "## ");
719 if (path) {
720 add_to_string(string, path);
721 add_char_to_string(string, '.');
723 add_to_string(string, option->name);
724 add_char_to_string(string, ' ');
725 add_to_string(string, option_types[option->type].help_str);
726 add_char_to_string(string, '\n');
727 break;
729 case 1:
730 if (!(comments & 2)) break;
732 if (!option->desc || !do_print_comment)
733 break;
735 desc_i18n = conf_i18n(option->desc, i18n);
737 if (!*desc_i18n) break;
739 add_indent_to_string(string, depth);
740 add_to_string(string, "# ");
742 unsigned char *i = desc_i18n;
743 unsigned char *j = i;
744 unsigned char *last_space = NULL;
745 int config_width = 80;
746 int n = depth * indentation + 3;
748 for (; *i; i++, n++) {
749 if (*i == '\n') {
750 last_space = i;
751 goto split;
754 if (*i == ' ') last_space = i;
756 if (n >= config_width && last_space) {
757 split:
758 add_bytes_to_string(string, j, last_space - j);
759 add_char_to_string(string, '\n');
760 add_indent_to_string(string, depth);
761 add_to_string(string, "# ");
762 j = last_space + 1;
763 n = depth * indentation + 2 + i - last_space;
764 last_space = NULL;
767 add_to_string(string, j);
769 add_char_to_string(string, '\n');
770 break;
772 case 2:
774 add_indent_to_string(string, depth);
775 if (option->flags & OPT_DELETED) {
776 add_to_string(string, "un");
778 add_to_string(string, "set ");
779 if (path) {
780 add_to_string(string, path);
781 add_char_to_string(string, '.');
783 add_to_string(string, option->name);
784 if (!(option->flags & OPT_DELETED)) {
785 add_to_string(string, " = ");
786 /* OPT_ALIAS won't ever. OPT_TREE won't reach action 2.
787 * OPT_SPECIAL makes no sense in the configuration
788 * context. */
789 assert(option_types[option->type].write);
790 option_types[option->type].write(option, string);
792 add_char_to_string(string, '\n');
793 if (do_print_comment) add_char_to_string(string, '\n');
794 break;
796 case 3:
797 if (do_print_comment < 2)
798 add_char_to_string(string, '\n');
799 break;
804 static void
805 add_cfg_header_to_string(struct string *string, unsigned char *text)
807 int n = strlen(text) + 2;
809 int_bounds(&n, 10, 80);
811 add_to_string(string, "\n\n\n");
812 add_xchar_to_string(string, '#', n);
813 add_to_string(string, "\n# ");
814 add_to_string(string, text);
815 add_to_string(string, "#\n\n");
818 unsigned char *
819 create_config_string(unsigned char *prefix, unsigned char *name,
820 struct option *options)
822 struct string config;
823 /* Don't write headers if nothing will be added anyway. */
824 struct string tmpstring;
825 int origlen;
826 int savestyle = get_opt_int("config.saving_style");
827 int i18n = get_opt_bool("config.i18n");
829 if (!init_string(&config)) return NULL;
831 if (savestyle == 3) {
832 touching = 1;
833 savestyle = 1;
834 } else {
835 touching = 0;
838 if (savestyle == 2) watermark_deleted_options(options->value.tree);
840 /* Scaring. */
841 if (savestyle == 2
842 || (savestyle < 2
843 && (load_config_file(prefix, name, options, &config, 0)
844 || !config.length))) {
845 /* At first line, and in English, write ELinks version, may be
846 * of some help in future. Please keep that format for it.
847 * --Zas */
848 add_to_string(&config, "## ELinks " VERSION " configuration file\n\n");
849 assert(savestyle >= 0 && savestyle <= 2);
850 switch (savestyle) {
851 case 0:
852 add_to_string(&config, conf_i18n(N_(
853 "## This is ELinks configuration file. You can edit it manually,\n"
854 "## if you wish so; this file is edited by ELinks when you save\n"
855 "## options through UI, however only option values will be altered\n"
856 "## and all your formatting, own comments etc will be kept as-is.\n"),
857 i18n));
858 break;
859 case 1:
860 add_to_string(&config, conf_i18n(N_(
861 "## This is ELinks configuration file. You can edit it manually,\n"
862 "## if you wish so; this file is edited by ELinks when you save\n"
863 "## options through UI, however only option values will be altered\n"
864 "## and missing options will be added at the end of file; if option\n"
865 "## is not written in this file, but in some file included from it,\n"
866 "## it is NOT counted as missing. Note that all your formatting,\n"
867 "## own comments and so on will be kept as-is.\n"), i18n));
868 break;
869 case 2:
870 add_to_string(&config, conf_i18n(N_(
871 "## This is ELinks configuration file. You can edit it manually,\n"
872 "## if you wish so, but keep in mind that this file is overwritten\n"
873 "## by ELinks when you save options through UI and you are out of\n"
874 "## luck with your formatting and own comments then, so beware.\n"),
875 i18n));
876 break;
879 add_to_string(&config, "##\n");
881 add_to_string(&config, conf_i18n(N_(
882 "## Obviously, if you don't like what ELinks is going to do with\n"
883 "## this file, you can change it by altering the config.saving_style\n"
884 "## option. Come on, aren't we friendly guys after all?\n"), i18n));
887 if (savestyle == 0) goto get_me_out;
889 indentation = get_opt_int("config.indentation");
890 comments = get_opt_int("config.comments");
892 if (!init_string(&tmpstring)) goto get_me_out;
894 add_cfg_header_to_string(&tmpstring,
895 conf_i18n(N_("Automatically saved options\n"), i18n));
897 origlen = tmpstring.length;
898 smart_config_string(&tmpstring, 2, i18n, options->value.tree, NULL, 0,
899 smart_config_output_fn);
900 if (tmpstring.length > origlen)
901 add_string_to_string(&config, &tmpstring);
902 done_string(&tmpstring);
904 if (!init_string(&tmpstring)) goto get_me_out;
906 add_cfg_header_to_string(&tmpstring,
907 conf_i18n(N_("Automatically saved keybindings\n"), i18n));
909 origlen = tmpstring.length;
910 bind_config_string(&tmpstring);
911 if (tmpstring.length > origlen)
912 add_string_to_string(&config, &tmpstring);
913 done_string(&tmpstring);
915 get_me_out:
916 unmark_options_tree(options->value.tree);
918 return config.source;
921 static int
922 write_config_file(unsigned char *prefix, unsigned char *name,
923 struct option *options, struct terminal *term)
925 int ret = -1;
926 struct secure_save_info *ssi;
927 unsigned char *config_file = NULL;
928 unsigned char *cfg_str = create_config_string(prefix, name, options);
929 int prefixlen = strlen(prefix);
930 int prefix_has_slash = (prefixlen && dir_sep(prefix[prefixlen - 1]));
931 int name_has_slash = dir_sep(name[0]);
932 unsigned char *slash = name_has_slash || prefix_has_slash ? "" : STRING_DIR_SEP;
934 if (!cfg_str) return -1;
936 if (name_has_slash && prefix_has_slash) name++;
938 config_file = straconcat(prefix, slash, name, (unsigned char *) NULL);
939 if (!config_file) goto free_cfg_str;
941 ssi = secure_open(config_file);
942 if (ssi) {
943 secure_fputs(ssi, cfg_str);
944 ret = secure_close(ssi);
947 write_config_dialog(term, config_file, secsave_errno, ret);
948 mem_free(config_file);
950 free_cfg_str:
951 mem_free(cfg_str);
953 return ret;
957 write_config(struct terminal *term)
959 assert(term);
961 if (!elinks_home) {
962 write_config_dialog(term, get_cmd_opt_str("config-file"),
963 SS_ERR_DISABLED, 0);
964 return -1;
967 return write_config_file(elinks_home, get_cmd_opt_str("config-file"),
968 config_options, term);