Add wrap_option_desc() in conf.c, still static
[elinks.git] / src / config / conf.c
blobf90f6fd3f303f6b962fc95810a359b96039708b7
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 /** Skip to the next newline or comment that is not part of a quoted
177 * string. When ELinks hits a parse error in the configuration file,
178 * it calls this in order to find the place where is should resume
179 * parsing. This is intended to prevent ELinks from treating words
180 * in strings as commands. */
181 static void
182 skip_to_unquoted_newline_or_comment(struct conf_parsing_pos *pos)
184 while (*pos->look && *pos->look != '#' && *pos->look != '\n') {
185 if (isquote(*pos->look))
186 skip_quoted(pos);
187 else
188 pos->look++;
192 /* Parse a command. Returns error code. */
193 /* If dynamic string credentials are supplied, we will mirror the command at
194 * the end of the string; however, we won't load the option value to the tree,
195 * and we will even write option value from the tree to the output string.
196 * We will only possibly set or clear OPT_MUST_SAVE flag in the option. */
198 static enum parse_error
199 parse_set(struct option *opt_tree, struct conf_parsing_state *state,
200 struct string *mirror, int is_system_conf)
202 const unsigned char *optname_orig;
203 size_t optname_len;
204 unsigned char *optname_copy;
206 skip_white(&state->pos);
207 if (!*state->pos.look) return show_parse_error(state, ERROR_PARSE);
209 /* Option name */
210 optname_orig = state->pos.look;
211 while (isident(*state->pos.look) || *state->pos.look == '*'
212 || *state->pos.look == '.' || *state->pos.look == '+')
213 state->pos.look++;
214 optname_len = state->pos.look - optname_orig;
216 skip_white(&state->pos);
218 /* Equal sign */
219 if (*state->pos.look != '=')
220 return show_parse_error(state, ERROR_PARSE);
221 state->pos.look++; /* '=' */
222 skip_white(&state->pos);
223 if (!*state->pos.look)
224 return show_parse_error(state, ERROR_VALUE);
226 optname_copy = memacpy(optname_orig, optname_len);
227 if (!optname_copy) return show_parse_error(state, ERROR_NOMEM);
229 /* Option value */
231 struct option *opt;
232 unsigned char *val;
233 const struct conf_parsing_pos pos_before_value = state->pos;
235 opt = mirror
236 ? get_opt_rec_real(opt_tree, optname_copy)
237 : get_opt_rec(opt_tree, optname_copy);
238 mem_free(optname_copy);
239 optname_copy = NULL;
241 if (!opt || (opt->flags & OPT_HIDDEN)) {
242 show_parse_error(state, ERROR_OPTION);
243 skip_option_value(&state->pos);
244 return ERROR_OPTION;
245 /* TODO: Distinguish between two scenarios:
246 * - A newer version of ELinks has saved an
247 * option that this version does not recognize.
248 * The option must be preserved. (This works.)
249 * - The user has added an option, saved
250 * elinks.conf, restarted ELinks, deleted the
251 * option, and is now saving elinks.conf again.
252 * The option should be rewritten to "unset".
253 * (This does not work yet.)
254 * In both cases, ELinks has no struct option
255 * for that name. Possible fixes:
256 * - If the tree has OPT_AUTOCREATE, then
257 * assume the user had created that option,
258 * and rewrite it to "unset". Otherwise,
259 * keep it.
260 * - When the user deletes an option, just mark
261 * it with OPT_DELETED, and keep it in memory
262 * as long as OPT_TOUCHED is set. */
265 if (!option_types[opt->type].read) {
266 show_parse_error(state, ERROR_VALUE);
267 skip_option_value(&state->pos);
268 return ERROR_VALUE;
271 val = option_types[opt->type].read(opt, &state->pos.look,
272 &state->pos.line);
273 if (!val) {
274 /* The reader function failed. Jump back to
275 * the beginning of the value and skip it with
276 * the generic code. For the error message,
277 * use the line number at the beginning of the
278 * value, because the ending position is not
279 * interesting if there is an unclosed quote. */
280 state->pos = pos_before_value;
281 show_parse_error(state, ERROR_VALUE);
282 skip_option_value(&state->pos);
283 return ERROR_VALUE;
286 if (!mirror) {
287 /* loading a configuration file */
288 if (!option_types[opt->type].set
289 || !option_types[opt->type].set(opt, val)) {
290 mem_free(val);
291 return show_parse_error(state, ERROR_VALUE);
293 } else if (is_system_conf) {
294 /* scanning a file that will not be rewritten */
295 struct option *flagsite = indirect_option(opt);
297 if (!(flagsite->flags & OPT_DELETED)
298 && option_types[opt->type].equals
299 && option_types[opt->type].equals(opt, val))
300 flagsite->flags &= ~OPT_MUST_SAVE;
301 else
302 flagsite->flags |= OPT_MUST_SAVE;
303 } else {
304 /* rewriting a configuration file */
305 struct option *flagsite = indirect_option(opt);
307 if (flagsite->flags & OPT_DELETED) {
308 /* Replace the "set" command with an
309 * "unset" command. */
310 add_to_string(mirror, "unset ");
311 add_bytes_to_string(mirror, optname_orig,
312 optname_len);
313 state->mirrored = state->pos.look;
314 } else if (option_types[opt->type].write) {
315 add_bytes_to_string(mirror, state->mirrored,
316 pos_before_value.look
317 - state->mirrored);
318 option_types[opt->type].write(opt, mirror);
319 state->mirrored = state->pos.look;
321 /* Remember that the option need not be
322 * written to the end of the file. */
323 flagsite->flags &= ~OPT_MUST_SAVE;
325 mem_free(val);
328 return ERROR_NONE;
331 static enum parse_error
332 parse_unset(struct option *opt_tree, struct conf_parsing_state *state,
333 struct string *mirror, int is_system_conf)
335 const unsigned char *optname_orig;
336 size_t optname_len;
337 unsigned char *optname_copy;
339 skip_white(&state->pos);
340 if (!*state->pos.look) return show_parse_error(state, ERROR_PARSE);
342 /* Option name */
343 optname_orig = state->pos.look;
344 while (isident(*state->pos.look) || *state->pos.look == '*'
345 || *state->pos.look == '.' || *state->pos.look == '+')
346 state->pos.look++;
347 optname_len = state->pos.look - optname_orig;
349 optname_copy = memacpy(optname_orig, optname_len);
350 if (!optname_copy) return show_parse_error(state, ERROR_NOMEM);
353 struct option *opt;
355 opt = get_opt_rec_real(opt_tree, optname_copy);
356 mem_free(optname_copy);
357 optname_copy = NULL;
359 if (!opt || (opt->flags & OPT_HIDDEN)) {
360 /* The user wanted to delete the option, and
361 * it has already been deleted; this is not an
362 * error. This might happen if a version of
363 * ELinks has a built-in URL rewriting rule,
364 * the user disables it, and a later version
365 * no longer has it. */
366 return ERROR_NONE;
369 if (!mirror) {
370 /* loading a configuration file */
371 if (opt->flags & OPT_ALLOC) delete_option(opt);
372 else mark_option_as_deleted(opt);
373 } else if (is_system_conf) {
374 /* scanning a file that will not be rewritten */
375 struct option *flagsite = indirect_option(opt);
377 if (flagsite->flags & OPT_DELETED)
378 flagsite->flags &= ~OPT_MUST_SAVE;
379 else
380 flagsite->flags |= OPT_MUST_SAVE;
381 } else {
382 /* rewriting a configuration file */
383 struct option *flagsite = indirect_option(opt);
385 if (flagsite->flags & OPT_DELETED) {
386 /* The "unset" command is already in the file,
387 * and unlike with "set", there is no value
388 * to be updated. */
389 } else if (option_types[opt->type].write) {
390 /* Replace the "unset" command with a
391 * "set" command. */
392 add_to_string(mirror, "set ");
393 add_bytes_to_string(mirror, optname_orig,
394 optname_len);
395 add_to_string(mirror, " = ");
396 option_types[opt->type].write(opt, mirror);
397 state->mirrored = state->pos.look;
399 /* Remember that the option need not be
400 * written to the end of the file. */
401 flagsite->flags &= ~OPT_MUST_SAVE;
405 return ERROR_NONE;
408 static enum parse_error
409 parse_bind(struct option *opt_tree, struct conf_parsing_state *state,
410 struct string *mirror, int is_system_conf)
412 unsigned char *keymap, *keystroke, *action;
413 enum parse_error err = ERROR_NONE;
414 struct conf_parsing_pos before_error;
416 skip_white(&state->pos);
417 if (!*state->pos.look) return show_parse_error(state, ERROR_PARSE);
419 /* Keymap */
420 before_error = state->pos;
421 keymap = option_types[OPT_STRING].read(NULL, &state->pos.look,
422 &state->pos.line);
423 skip_white(&state->pos);
424 if (!keymap || !*state->pos.look) {
425 state->pos = before_error;
426 return show_parse_error(state, ERROR_PARSE);
429 /* Keystroke */
430 before_error = state->pos;
431 keystroke = option_types[OPT_STRING].read(NULL, &state->pos.look,
432 &state->pos.line);
433 skip_white(&state->pos);
434 if (!keystroke || !*state->pos.look) {
435 mem_free(keymap); mem_free_if(keystroke);
436 state->pos = before_error;
437 return show_parse_error(state, ERROR_PARSE);
440 /* Equal sign */
441 skip_white(&state->pos);
442 if (*state->pos.look != '=') {
443 mem_free(keymap); mem_free(keystroke);
444 return show_parse_error(state, ERROR_PARSE);
446 state->pos.look++; /* '=' */
448 skip_white(&state->pos);
449 if (!*state->pos.look) {
450 mem_free(keymap); mem_free(keystroke);
451 return show_parse_error(state, ERROR_PARSE);
454 /* Action */
455 before_error = state->pos;
456 action = option_types[OPT_STRING].read(NULL, &state->pos.look,
457 &state->pos.line);
458 if (!action) {
459 mem_free(keymap); mem_free(keystroke);
460 state->pos = before_error;
461 return show_parse_error(state, ERROR_PARSE);
464 if (!mirror) {
465 /* loading a configuration file */
466 /* We don't bother to bind() if -default-keys. */
467 if (!get_cmd_opt_bool("default-keys")
468 && bind_do(keymap, keystroke, action, is_system_conf)) {
469 /* bind_do() tried but failed. */
470 err = show_parse_error(state, ERROR_VALUE);
471 } else {
472 err = ERROR_NONE;
474 } else if (is_system_conf) {
475 /* scanning a file that will not be rewritten */
476 /* TODO */
477 } else {
478 /* rewriting a configuration file */
479 /* Mirror what we already have. If the keystroke has
480 * been unbound, then act_str is simply "none" and
481 * this does not require special handling. */
482 unsigned char *act_str = bind_act(keymap, keystroke);
484 if (act_str) {
485 add_bytes_to_string(mirror, state->mirrored,
486 before_error.look - state->mirrored);
487 add_to_string(mirror, act_str);
488 mem_free(act_str);
489 state->mirrored = state->pos.look;
490 } else {
491 err = show_parse_error(state, ERROR_VALUE);
494 mem_free(keymap); mem_free(keystroke); mem_free(action);
495 return err;
498 static int load_config_file(unsigned char *, unsigned char *, struct option *,
499 struct string *, int);
501 static enum parse_error
502 parse_include(struct option *opt_tree, struct conf_parsing_state *state,
503 struct string *mirror, int is_system_conf)
505 unsigned char *fname;
506 struct string dumbstring;
507 struct conf_parsing_pos before_error;
509 if (!init_string(&dumbstring))
510 return show_parse_error(state, ERROR_NOMEM);
512 skip_white(&state->pos);
513 if (!*state->pos.look) {
514 done_string(&dumbstring);
515 return show_parse_error(state, ERROR_PARSE);
518 /* File name */
519 before_error = state->pos;
520 fname = option_types[OPT_STRING].read(NULL, &state->pos.look,
521 &state->pos.line);
522 if (!fname) {
523 done_string(&dumbstring);
524 state->pos = before_error;
525 return show_parse_error(state, ERROR_PARSE);
528 /* We want load_config_file() to watermark stuff, but not to load
529 * anything, polluting our beloved options tree - thus, we will feed it
530 * with some dummy string which we will destroy later; still better
531 * than cloning whole options tree or polluting interface with another
532 * rarely-used option ;). */
533 /* XXX: We should try CONFDIR/<file> when proceeding
534 * CONFDIR/<otherfile> ;). --pasky */
535 if (load_config_file(fname[0] == '/' ? (unsigned char *) ""
536 : elinks_home,
537 fname, opt_tree,
538 mirror ? &dumbstring : NULL, 1)) {
539 done_string(&dumbstring);
540 mem_free(fname);
541 return show_parse_error(state, ERROR_VALUE);
544 done_string(&dumbstring);
545 mem_free(fname);
546 return ERROR_NONE;
550 struct parse_handler {
551 const unsigned char *command;
552 enum parse_error (*handler)(struct option *opt_tree,
553 struct conf_parsing_state *state,
554 struct string *mirror, int is_system_conf);
557 static const struct parse_handler parse_handlers[] = {
558 { "set", parse_set },
559 { "unset", parse_unset },
560 { "bind", parse_bind },
561 { "include", parse_include },
562 { NULL, NULL }
566 static enum parse_error
567 parse_config_command(struct option *options, struct conf_parsing_state *state,
568 struct string *mirror, int is_system_conf)
570 const struct parse_handler *handler;
572 /* If we're mirroring, then everything up to this point must
573 * have already been mirrored. */
574 assert(mirror == NULL || state->mirrored == state->pos.look);
575 if_assert_failed return show_parse_error(state, ERROR_PARSE);
577 for (handler = parse_handlers; handler->command;
578 handler++) {
579 int cmdlen = strlen(handler->command);
581 if (!strncmp(state->pos.look, handler->command, cmdlen)
582 && isspace(state->pos.look[cmdlen])) {
583 enum parse_error err;
585 state->pos.look += cmdlen;
586 err = handler->handler(options, state, mirror,
587 is_system_conf);
588 if (mirror) {
589 /* Mirror any characters that the handler
590 * consumed but did not already mirror. */
591 add_bytes_to_string(mirror, state->mirrored,
592 state->pos.look - state->mirrored);
593 state->mirrored = state->pos.look;
595 return err;
599 return show_parse_error(state, ERROR_COMMAND);
602 #ifdef CONFIG_EXMODE
603 enum parse_error
604 parse_config_exmode_command(unsigned char *cmd)
606 struct conf_parsing_state state = {{ 0 }};
608 state.pos.look = cmd;
609 state.pos.line = 0;
610 state.mirrored = NULL; /* not read because mirror is NULL too */
611 state.filename = NULL; /* prevent error messages */
613 return parse_config_command(config_options, &state, NULL, 0);
615 #endif /* CONFIG_EXMODE */
617 void
618 parse_config_file(struct option *options, unsigned char *name,
619 unsigned char *file, struct string *mirror,
620 int is_system_conf)
622 struct conf_parsing_state state = {{ 0 }};
623 int error_occurred = 0;
625 state.pos.look = file;
626 state.pos.line = 1;
627 state.mirrored = file;
628 if (!mirror && get_cmd_opt_int("verbose") >= VERBOSE_WARNINGS)
629 state.filename = name;
631 while (state.pos.look && *state.pos.look) {
632 enum parse_error err = ERROR_NONE;
634 /* Skip all possible comments and whitespace. */
635 skip_white(&state.pos);
637 /* Mirror what we already have */
638 if (mirror) {
639 add_bytes_to_string(mirror, state.mirrored,
640 state.pos.look - state.mirrored);
641 state.mirrored = state.pos.look;
644 /* Second chance to escape from the hell. */
645 if (!*state.pos.look) break;
647 err = parse_config_command(options, &state, mirror,
648 is_system_conf);
649 switch (err) {
650 case ERROR_NONE:
651 break;
653 case ERROR_COMMAND:
654 case ERROR_PARSE:
655 /* Jump over this crap we can't understand. */
656 skip_to_unquoted_newline_or_comment(&state.pos);
658 /* Mirror what we already have */
659 if (mirror) {
660 add_bytes_to_string(mirror, state.mirrored,
661 state.pos.look - state.mirrored);
662 state.mirrored = state.pos.look;
665 /* fall through */
666 default:
667 error_occurred = 1;
668 break;
672 if (!error_occurred || !state.filename) return;
674 /* If an error occurred make sure that the user is notified and is able
675 * to see it. First sound the bell. Then, if the text viewer is going to
676 * be started, sleep for a while so the message will be visible before
677 * ELinks starts drawing to on the screen and overwriting any error
678 * messages. This should not be necessary for terminals supporting the
679 * alternate screen mode but better to be safe. (debian bug 305017) */
681 fputc('\a', stderr);
683 if (get_cmd_opt_bool("dump")
684 || get_cmd_opt_bool("source"))
685 return;
687 sleep(1);
692 static unsigned char *
693 read_config_file(unsigned char *name)
695 #define FILE_BUF 1024
696 unsigned char cfg_buffer[FILE_BUF];
697 struct string string;
698 int fd;
699 ssize_t r;
701 fd = open(name, O_RDONLY | O_NOCTTY);
702 if (fd < 0) return NULL;
703 set_bin(fd);
705 if (!init_string(&string)) return NULL;
707 while ((r = safe_read(fd, cfg_buffer, FILE_BUF)) > 0) {
708 int i;
710 /* Clear problems ;). */
711 for (i = 0; i < r; i++)
712 if (!cfg_buffer[i])
713 cfg_buffer[i] = ' ';
715 add_bytes_to_string(&string, cfg_buffer, r);
718 if (r < 0) done_string(&string);
719 close(fd);
721 return string.source;
722 #undef FILE_BUF
725 /* Return 0 on success. */
726 static int
727 load_config_file(unsigned char *prefix, unsigned char *name,
728 struct option *options, struct string *mirror,
729 int is_system_conf)
731 unsigned char *config_str, *config_file;
733 config_file = straconcat(prefix, STRING_DIR_SEP, name,
734 (unsigned char *) NULL);
735 if (!config_file) return 1;
737 config_str = read_config_file(config_file);
738 if (!config_str) {
739 mem_free(config_file);
740 config_file = straconcat(prefix, STRING_DIR_SEP, ".", name,
741 (unsigned char *) NULL);
742 if (!config_file) return 2;
744 config_str = read_config_file(config_file);
745 if (!config_str) {
746 mem_free(config_file);
747 return 3;
751 parse_config_file(options, config_file, config_str, mirror,
752 is_system_conf);
754 mem_free(config_str);
755 mem_free(config_file);
757 return 0;
760 static void
761 load_config_from(unsigned char *file, struct option *tree)
763 load_config_file(CONFDIR, file, tree, NULL, 1);
764 load_config_file(empty_string_or_(elinks_home), file, tree, NULL, 0);
767 void
768 load_config(void)
770 load_config_from(get_cmd_opt_str("config-file"),
771 config_options);
775 static int indentation = 2;
776 /* 0 -> none, 1 -> only option full name+type, 2 -> only desc, 3 -> both */
777 static int comments = 3;
779 static inline unsigned char *
780 conf_i18n(unsigned char *s, int i18n)
782 if (i18n) return gettext(s);
783 return s;
786 static void
787 add_indent_to_string(struct string *string, int depth)
789 if (!depth) return;
791 add_xchar_to_string(string, ' ', depth * indentation);
794 static struct string *
795 wrap_option_desc(struct string *out, const unsigned char *src,
796 const struct string *indent, int maxwidth)
798 const unsigned char *last_space = NULL;
799 const unsigned char *uncopied = src;
800 int width = 0;
802 /* TODO: multibyte or fullwidth characters */
803 for (; *src; src++, width++) {
804 if (*src == '\n') {
805 last_space = src;
806 goto split;
809 if (*src == ' ') last_space = src;
811 if (width >= maxwidth && last_space) {
812 split:
813 if (!add_string_to_string(out, indent))
814 return NULL;
815 if (!add_bytes_to_string(out, uncopied,
816 last_space - uncopied))
817 return NULL;
818 if (!add_char_to_string(out, '\n'))
819 return NULL;
820 uncopied = last_space + 1;
821 width = src - uncopied;
822 last_space = NULL;
825 if (*uncopied) {
826 if (!add_string_to_string(out, indent))
827 return NULL;
828 if (!add_to_string(out, uncopied))
829 return NULL;
830 if (!add_char_to_string(out, '\n'))
831 return NULL;
833 return out;
836 static void
837 output_option_desc_as_comment(struct string *out, const struct option *option,
838 int i18n, int depth)
840 unsigned char *desc_i18n = conf_i18n(option->desc, i18n);
841 struct string indent;
843 if (!init_string(&indent)) return;
845 add_indent_to_string(&indent, depth);
846 if (!add_to_string(&indent, "# ")) goto out_of_memory;
847 if (!wrap_option_desc(out, desc_i18n, &indent, 80 - indent.length))
848 goto out_of_memory;
850 out_of_memory:
851 done_string(&indent);
854 static void
855 smart_config_output_fn(struct string *string, struct option *option,
856 unsigned char *path, int depth, int do_print_comment,
857 int action, int i18n)
859 if (option->type == OPT_ALIAS)
860 return;
862 switch (action) {
863 case 0:
864 if (!(comments & 1)) break;
866 add_indent_to_string(string, depth);
868 add_to_string(string, "## ");
869 if (path) {
870 add_to_string(string, path);
871 add_char_to_string(string, '.');
873 add_to_string(string, option->name);
874 add_char_to_string(string, ' ');
875 add_to_string(string, option_types[option->type].help_str);
876 add_char_to_string(string, '\n');
877 break;
879 case 1:
880 if (!(comments & 2)) break;
882 if (!option->desc || !do_print_comment)
883 break;
885 output_option_desc_as_comment(string, option,
886 i18n, depth);
887 break;
889 case 2:
891 add_indent_to_string(string, depth);
892 if (option->flags & OPT_DELETED) {
893 add_to_string(string, "un");
895 add_to_string(string, "set ");
896 if (path) {
897 add_to_string(string, path);
898 add_char_to_string(string, '.');
900 add_to_string(string, option->name);
901 if (!(option->flags & OPT_DELETED)) {
902 add_to_string(string, " = ");
903 /* OPT_ALIAS won't ever. OPT_TREE won't reach action 2.
904 * OPT_SPECIAL makes no sense in the configuration
905 * context. */
906 assert(option_types[option->type].write);
907 option_types[option->type].write(option, string);
909 add_char_to_string(string, '\n');
910 if (do_print_comment) add_char_to_string(string, '\n');
911 break;
913 case 3:
914 if (do_print_comment < 2)
915 add_char_to_string(string, '\n');
916 break;
921 static void
922 add_cfg_header_to_string(struct string *string, unsigned char *text)
924 int n = strlen(text) + 2;
926 int_bounds(&n, 10, 80);
928 add_to_string(string, "\n\n\n");
929 add_xchar_to_string(string, '#', n);
930 add_to_string(string, "\n# ");
931 add_to_string(string, text);
932 add_to_string(string, "#\n\n");
935 unsigned char *
936 create_config_string(unsigned char *prefix, unsigned char *name,
937 struct option *options)
939 struct string config;
940 /* Don't write headers if nothing will be added anyway. */
941 struct string tmpstring;
942 int origlen;
943 int savestyle = get_opt_int("config.saving_style");
944 int i18n = get_opt_bool("config.i18n");
946 if (!init_string(&config)) return NULL;
948 prepare_mustsave_flags(options->value.tree,
949 savestyle == 1 || savestyle == 2);
951 /* Scaring. */
952 if (savestyle == 2
953 || load_config_file(prefix, name, options, &config, 0)
954 || !config.length) {
955 /* At first line, and in English, write ELinks version, may be
956 * of some help in future. Please keep that format for it.
957 * --Zas */
958 add_to_string(&config, "## ELinks " VERSION " configuration file\n\n");
959 assert(savestyle >= 0 && savestyle <= 3);
960 switch (savestyle) {
961 case 0:
962 add_to_string(&config, conf_i18n(N_(
963 "## This is ELinks configuration file. You can edit it manually,\n"
964 "## if you wish so; this file is edited by ELinks when you save\n"
965 "## options through UI, however only option values will be altered\n"
966 "## and all your formatting, own comments etc will be kept as-is.\n"),
967 i18n));
968 break;
969 case 1: case 3:
970 add_to_string(&config, conf_i18n(N_(
971 "## This is ELinks configuration file. You can edit it manually,\n"
972 "## if you wish so; this file is edited by ELinks when you save\n"
973 "## options through UI, however only option values will be altered\n"
974 "## and missing options will be added at the end of file; if option\n"
975 "## is not written in this file, but in some file included from it,\n"
976 "## it is NOT counted as missing. Note that all your formatting,\n"
977 "## own comments and so on will be kept as-is.\n"), i18n));
978 break;
979 case 2:
980 add_to_string(&config, conf_i18n(N_(
981 "## This is ELinks configuration file. You can edit it manually,\n"
982 "## if you wish so, but keep in mind that this file is overwritten\n"
983 "## by ELinks when you save options through UI and you are out of\n"
984 "## luck with your formatting and own comments then, so beware.\n"),
985 i18n));
986 break;
989 add_to_string(&config, "##\n");
991 add_to_string(&config, conf_i18n(N_(
992 "## Obviously, if you don't like what ELinks is going to do with\n"
993 "## this file, you can change it by altering the config.saving_style\n"
994 "## option. Come on, aren't we friendly guys after all?\n"), i18n));
997 if (savestyle == 0) goto get_me_out;
999 indentation = get_opt_int("config.indentation");
1000 comments = get_opt_int("config.comments");
1002 if (!init_string(&tmpstring)) goto get_me_out;
1004 add_cfg_header_to_string(&tmpstring,
1005 conf_i18n(N_("Automatically saved options\n"), i18n));
1007 origlen = tmpstring.length;
1008 smart_config_string(&tmpstring, 2, i18n, options->value.tree, NULL, 0,
1009 smart_config_output_fn);
1010 if (tmpstring.length > origlen)
1011 add_string_to_string(&config, &tmpstring);
1012 done_string(&tmpstring);
1014 if (!init_string(&tmpstring)) goto get_me_out;
1016 add_cfg_header_to_string(&tmpstring,
1017 conf_i18n(N_("Automatically saved keybindings\n"), i18n));
1019 origlen = tmpstring.length;
1020 bind_config_string(&tmpstring);
1021 if (tmpstring.length > origlen)
1022 add_string_to_string(&config, &tmpstring);
1023 done_string(&tmpstring);
1025 get_me_out:
1026 return config.source;
1029 static int
1030 write_config_file(unsigned char *prefix, unsigned char *name,
1031 struct option *options, struct terminal *term)
1033 int ret = -1;
1034 struct secure_save_info *ssi;
1035 unsigned char *config_file = NULL;
1036 unsigned char *cfg_str = create_config_string(prefix, name, options);
1037 int prefixlen = strlen(prefix);
1038 int prefix_has_slash = (prefixlen && dir_sep(prefix[prefixlen - 1]));
1039 int name_has_slash = dir_sep(name[0]);
1040 unsigned char *slash = name_has_slash || prefix_has_slash ? "" : STRING_DIR_SEP;
1042 if (!cfg_str) return -1;
1044 if (name_has_slash && prefix_has_slash) name++;
1046 config_file = straconcat(prefix, slash, name, (unsigned char *) NULL);
1047 if (!config_file) goto free_cfg_str;
1049 ssi = secure_open(config_file);
1050 if (ssi) {
1051 secure_fputs(ssi, cfg_str);
1052 ret = secure_close(ssi);
1053 if (!ret)
1054 untouch_options(options->value.tree);
1057 write_config_dialog(term, config_file, secsave_errno, ret);
1058 mem_free(config_file);
1060 free_cfg_str:
1061 mem_free(cfg_str);
1063 return ret;
1067 write_config(struct terminal *term)
1069 assert(term);
1071 if (!elinks_home) {
1072 write_config_dialog(term, get_cmd_opt_str("config-file"),
1073 SS_ERR_DISABLED, 0);
1074 return -1;
1077 return write_config_file(elinks_home, get_cmd_opt_str("config-file"),
1078 config_options, term);