1 /* Config file manipulation */
10 #include <sys/types.h>
11 #include <sys/stat.h> /* OS/2 needs this after sys/types.h */
13 #include <fcntl.h> /* OS/2 needs this after sys/types.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/
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
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. */
68 /** The line number corresponding to #look. This is
69 * shown in error messages. */
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
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
]);
106 /** Skip comments and whitespace. */
108 skip_white(struct conf_parsing_pos
*pos
)
110 unsigned char *start
= pos
->look
;
113 while (isspace(*start
)) {
114 if (*start
== '\n') {
121 start
+= strcspn(start
, "\n");
131 /** Skip a quoted string.
132 * This function allows "mismatching quotes' because str_rd() does so. */
134 skip_quoted(struct conf_parsing_pos
*pos
)
136 assert(isquote(*pos
->look
));
137 if_assert_failed
return;
143 if (isquote(*pos
->look
)) {
147 if (*pos
->look
== '\\' && pos
->look
[1])
149 if (*pos
->look
== '\n')
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. */
162 skip_option_value(struct conf_parsing_pos
*pos
)
164 if (isquote(*pos
->look
)) {
165 /* Looks like OPT_STRING, OPT_CODEPAGE, OPT_LANGUAGE,
169 /* Looks like OPT_BOOL, OPT_INT, or OPT_LONG. */
170 while (isasciialnum(*pos
->look
) || *pos
->look
== '.'
171 || *pos
->look
== '+' || *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
);
192 optname
= state
->pos
.look
;
193 while (isident(*state
->pos
.look
) || *state
->pos
.look
== '*'
194 || *state
->pos
.look
== '.' || *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
);
203 if (*state
->pos
.look
!= '=') {
205 return show_parse_error(state
, ERROR_PARSE
);
207 state
->pos
.look
++; /* '=' */
208 skip_white(&state
->pos
);
209 if (!*state
->pos
.look
) {
211 return show_parse_error(state
, ERROR_VALUE
);
214 /* Mirror what we already have */
216 add_bytes_to_string(mirror
, state
->mirrored
,
217 state
->pos
.look
- state
->mirrored
);
218 state
->mirrored
= state
->pos
.look
;
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
);
230 if (!opt
|| (opt
->flags
& OPT_HIDDEN
)) {
231 show_parse_error(state
, ERROR_OPTION
);
232 skip_option_value(&state
->pos
);
236 if (!option_types
[opt
->type
].read
) {
237 show_parse_error(state
, ERROR_VALUE
);
238 skip_option_value(&state
->pos
);
242 val
= option_types
[opt
->type
].read(opt
, &state
->pos
.look
,
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
);
258 if (opt
->flags
& OPT_DELETED
)
259 opt
->flags
&= ~OPT_WATERMARK
;
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
)) {
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; */
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
);
293 optname
= state
->pos
.look
;
294 while (isident(*state
->pos
.look
) || *state
->pos
.look
== '*'
295 || *state
->pos
.look
== '.' || *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 */
303 add_bytes_to_string(mirror
, state
->mirrored
,
304 state
->pos
.look
- state
->mirrored
);
305 state
->mirrored
= state
->pos
.look
;
311 opt
= get_opt_rec_real(opt_tree
, optname
);
314 if (!opt
|| (opt
->flags
& OPT_HIDDEN
))
315 return show_parse_error(state
, ERROR_OPTION
);
318 if (opt
->flags
& OPT_ALLOC
) delete_option(opt
);
320 if (opt
->flags
& OPT_DELETED
)
321 opt
->flags
|= OPT_WATERMARK
;
323 opt
->flags
&= ~OPT_WATERMARK
;
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
);
342 keymap
= option_types
[OPT_STRING
].read(NULL
, &state
->pos
.look
,
344 skip_white(&state
->pos
);
345 if (!keymap
|| !*state
->pos
.look
)
346 return show_parse_error(state
, ERROR_OPTION
);
349 keystroke
= option_types
[OPT_STRING
].read(NULL
, &state
->pos
.look
,
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
);
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
);
372 next_pos
= state
->pos
.look
;
373 action
= option_types
[OPT_STRING
].read(NULL
, &state
->pos
.look
,
376 mem_free(keymap
); mem_free(keystroke
);
377 return show_parse_error(state
, ERROR_VALUE
);
381 /* Mirror what we already have */
382 unsigned char *act_str
= bind_act(keymap
, keystroke
);
385 add_bytes_to_string(mirror
, state
->mirrored
,
386 next_pos
- state
->mirrored
);
387 add_to_string(mirror
, act_str
);
389 state
->mirrored
= state
->pos
.look
;
391 err
= show_parse_error(state
, ERROR_VALUE
);
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
);
403 mem_free(keymap
); mem_free(keystroke
); mem_free(action
);
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
);
427 fname
= option_types
[OPT_STRING
].read(NULL
, &state
->pos
.look
,
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 *) ""
443 fname
, opt_tree
, &dumbstring
, is_system_conf
)) {
444 done_string(&dumbstring
);
446 return show_parse_error(state
, ERROR_VALUE
);
449 done_string(&dumbstring
);
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
},
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
;
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
,
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
;
504 return show_parse_error(state
, ERROR_COMMAND
);
509 parse_config_exmode_command(unsigned char *cmd
)
511 struct conf_parsing_state state
= {{ 0 }};
513 state
.pos
.look
= cmd
;
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 */
523 parse_config_file(struct option
*options
, unsigned char *name
,
524 unsigned char *file
, struct string
*mirror
,
527 struct conf_parsing_state state
= {{ 0 }};
528 int error_occurred
= 0;
530 state
.pos
.look
= file
;
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 */
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
,
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
)
561 /* Mirror what we already have */
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
)
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) */
584 if (get_cmd_opt_bool("dump")
585 || get_cmd_opt_bool("source"))
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
;
602 fd
= open(name
, O_RDONLY
| O_NOCTTY
);
603 if (fd
< 0) return NULL
;
606 if (!init_string(&string
)) return NULL
;
608 while ((r
= safe_read(fd
, cfg_buffer
, FILE_BUF
)) > 0) {
611 /* Clear problems ;). */
612 for (i
= 0; i
< r
; i
++)
616 add_bytes_to_string(&string
, cfg_buffer
, r
);
619 if (r
< 0) done_string(&string
);
622 return string
.source
;
626 /* Return 0 on success. */
628 load_config_file(unsigned char *prefix
, unsigned char *name
,
629 struct option
*options
, struct string
*mirror
,
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
);
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
);
647 mem_free(config_file
);
652 parse_config_file(options
, config_file
, config_str
, mirror
,
655 mem_free(config_str
);
656 mem_free(config_file
);
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);
671 load_config_from(get_cmd_opt_str("config-file"),
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
);
689 add_indent_to_string(struct string
*string
, int depth
)
693 add_xchar_to_string(string
, ' ', depth
* indentation
);
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
)
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
)
714 if (!(comments
& 1)) break;
716 add_indent_to_string(string
, depth
);
718 add_to_string(string
, "## ");
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');
730 if (!(comments
& 2)) break;
732 if (!option
->desc
|| !do_print_comment
)
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
++) {
754 if (*i
== ' ') last_space
= i
;
756 if (n
>= config_width
&& last_space
) {
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
, "# ");
763 n
= depth
* indentation
+ 2 + i
- last_space
;
767 add_to_string(string
, j
);
769 add_char_to_string(string
, '\n');
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 ");
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
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');
797 if (do_print_comment
< 2)
798 add_char_to_string(string
, '\n');
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");
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
;
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) {
838 if (savestyle
== 2) watermark_deleted_options(options
->value
.tree
);
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.
848 add_to_string(&config
, "## ELinks " VERSION
" configuration file\n\n");
849 assert(savestyle
>= 0 && savestyle
<= 2);
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"),
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
));
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"),
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
);
916 unmark_options_tree(options
->value
.tree
);
918 return config
.source
;
922 write_config_file(unsigned char *prefix
, unsigned char *name
,
923 struct option
*options
, struct terminal
*term
)
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
);
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
);
957 write_config(struct terminal
*term
)
962 write_config_dialog(term
, get_cmd_opt_str("config-file"),
967 return write_config_file(elinks_home
, get_cmd_opt_str("config-file"),
968 config_options
, term
);