big dialogs: set_curosr2 -> set_dlg_cursor.
[elinks.git] / src / config / options.c
blob237a8cf68ba4f3c94f9f9b8a2ceb89dd1548deb4
1 /* Options variables manipulation core */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <ctype.h>
8 #include <string.h>
10 #include "elinks.h"
12 #include "bfu/dialog.h"
13 #include "cache/cache.h"
14 #include "config/conf.h"
15 #include "config/dialogs.h"
16 #include "config/domain.h"
17 #include "config/options.h"
18 #include "config/opttypes.h"
19 #include "dialogs/status.h"
20 #include "document/document.h"
21 #include "globhist/globhist.h"
22 #include "intl/charsets.h"
23 #include "intl/gettext/libintl.h"
24 #include "main/main.h" /* shrink_memory() */
25 #include "main/select.h"
26 #include "network/connection.h"
27 #include "session/session.h"
28 #include "terminal/color.h"
29 #include "terminal/screen.h"
30 #include "terminal/terminal.h"
31 #include "util/color.h"
32 #include "util/error.h"
33 #include "util/memory.h"
34 #include "util/string.h"
35 #include "viewer/text/draw.h"
38 /* TODO? In the past, covered by shadow and legends, remembered only by the
39 * ELinks Elders now, options were in hashes (it was not for a long time, after
40 * we started to use dynamic options lists and before we really started to use
41 * hierarchic options). Hashes might be swift and deft, but they had a flaw and
42 * the flaw showed up as the fatal flaw. They were unsorted, and it was
43 * unfriendly to mere mortal users, without pasky's options handlers in their
44 * brain, but their own poor-written software. And thus pasky went and rewrote
45 * options so that they were in lists from then to now and for all the ages of
46 * men, to the glory of mankind. However, one true hero may arise in future
47 * fabulous and implement possibility to have both lists and hashes for trees,
48 * as it may be useful for some supernatural entities. And when that age will
49 * come... */
52 /* TODO: We should remove special case for root options and use some auxiliary
53 * (struct option *) instead. This applies to bookmarks, global history and
54 * listbox items as well, though. --pasky */
56 static INIT_LIST_OF(struct option, options_root_tree);
58 static struct option options_root = INIT_OPTION(
59 /* name: */ "",
60 /* flags: */ 0,
61 /* type: */ OPT_TREE,
62 /* min, max: */ 0, 0,
63 /* value: */ &options_root_tree,
64 /* desc: */ "",
65 /* capt: */ NULL
68 struct option *config_options;
69 struct option *cmdline_options;
71 static void add_opt_rec(struct option *, unsigned char *, struct option *);
72 static void free_options_tree(LIST_OF(struct option) *, int recursive);
74 #ifdef CONFIG_DEBUG
75 /* Detect ending '.' (and some others) in options captions.
76 * It will emit a message in debug mode only. --Zas */
78 #define bad_punct(c) (c != ')' && c != '>' && !isquote(c) && ispunct(c))
80 static void
81 check_caption(unsigned char *caption)
83 int len;
84 unsigned char c;
86 if (!caption) return;
88 len = strlen(caption);
89 if (!len) return;
91 c = caption[len - 1];
92 if (isspace(c) || bad_punct(c))
93 DBG("bad char at end of caption [%s]", caption);
95 #ifdef CONFIG_NLS
96 caption = gettext(caption);
97 len = strlen(caption);
98 if (!len) return;
100 c = caption[len - 1];
101 if (isspace(c) || bad_punct(c))
102 DBG("bad char at end of i18n caption [%s]", caption);
103 #endif
106 #undef bad_punct
108 static void
109 check_description(unsigned char *desc)
111 int len;
112 unsigned char c;
114 if (!desc) return;
116 len = strlen(desc);
117 if (!len) return;
119 c = desc[len - 1];
120 if (isspace(c))
121 DBG("bad char at end of description [%s]", desc);
123 #ifdef CONFIG_NLS
124 desc = gettext(desc);
125 len = strlen(desc);
126 if (!len) return;
128 if (ispunct(c) != ispunct(desc[len - 1]))
129 DBG("punctuation char possibly missing at end of i18n description [%s]", desc);
131 c = desc[len - 1];
132 if (isspace(c))
133 DBG("bad char at end of i18n description [%s]", desc);
134 #endif
137 static void
138 debug_check_option_syntax(struct option *option)
140 if (!option) return;
141 check_caption(option->capt);
142 check_description(option->desc);
145 #else
146 #define debug_check_option_syntax(option)
147 #endif
150 /**********************************************************************
151 Options interface
152 **********************************************************************/
154 /* If option name contains dots, they are created as "categories" - first,
155 * first category is retrieved from list, taken as a list, second category
156 * is retrieved etc. */
158 /* Ugly kludge */
159 static int no_autocreate = 0;
161 /** Get record of option of given name, or NULL if there's no such option.
163 * If the specified option is an ::OPT_ALIAS, this function returns the
164 * alias, rather than the option to which the alias refers. It must
165 * work this way because the alias may have the ::OPT_ALIAS_NEGATE flag.
166 * Instead, if the caller tries to read or set the value of the alias,
167 * the functions associated with ::OPT_ALIAS will forward the operation
168 * to the underlying option. However, see indirect_option(). */
169 struct option *
170 get_opt_rec(struct option *tree, const unsigned char *name_)
172 struct option *option;
173 unsigned char *aname = stracpy(name_);
174 unsigned char *name = aname;
175 unsigned char *sep;
177 if (!aname) return NULL;
179 /* We iteratively call get_opt_rec() each for path_elements-1, getting
180 * appropriate tree for it and then resolving [path_elements]. */
181 if ((sep = strrchr(name, '.'))) {
182 *sep = '\0';
184 tree = get_opt_rec(tree, name);
185 if (!tree || tree->type != OPT_TREE || tree->flags & OPT_HIDDEN) {
186 #if 0
187 DBG("ERROR in get_opt_rec() crawl: %s (%d) -> %s",
188 name, tree ? tree->type : -1, sep + 1);
189 #endif
190 mem_free(aname);
191 return NULL;
194 *sep = '.';
195 name = sep + 1;
198 foreach (option, *tree->value.tree) {
199 if (option->name && !strcmp(option->name, name)) {
200 mem_free(aname);
201 return option;
205 if (tree && tree->flags & OPT_AUTOCREATE && !no_autocreate) {
206 struct option *template = get_opt_rec(tree, "_template_");
208 assertm(template != NULL, "Requested %s should be autocreated but "
209 "%.*s._template_ is missing!", name_, sep - name_,
210 name_);
211 if_assert_failed {
212 mem_free(aname);
213 return NULL;
216 /* We will just create the option and return pointer to it
217 * automagically. And, we will create it by cloning _template_
218 * option. By having _template_ OPT_AUTOCREATE and _template_
219 * inside, you can have even multi-level autocreating. */
221 option = copy_option(template, 0);
222 if (!option) {
223 mem_free(aname);
224 return NULL;
226 mem_free_set(&option->name, stracpy(name));
228 add_opt_rec(tree, "", option);
230 mem_free(aname);
231 return option;
234 mem_free(aname);
235 return NULL;
238 /* Get record of option of given name, or NULL if there's no such option. But
239 * do not create the option if it doesn't exist and there's autocreation
240 * enabled. */
241 struct option *
242 get_opt_rec_real(struct option *tree, const unsigned char *name)
244 struct option *opt;
246 no_autocreate = 1;
247 opt = get_opt_rec(tree, name);
248 no_autocreate = 0;
249 return opt;
252 /** If @a opt is an alias, return the option to which it refers.
254 * @warning Because the alias may have the ::OPT_ALIAS_NEGATE flag,
255 * the caller must not access the value of the returned option as if
256 * it were also the value of the alias. However, it is safe to access
257 * flags such as ::OPT_MUST_SAVE and ::OPT_DELETED. */
258 struct option *
259 indirect_option(struct option *alias)
261 struct option *real;
263 if (alias->type != OPT_ALIAS) return alias; /* not an error */
265 real = get_opt_rec(config_options, alias->value.string);
266 assertm(real != NULL, "%s aliased to unknown option %s!",
267 alias->name, alias->value.string);
268 if_assert_failed return alias;
270 return real;
273 /* Fetch pointer to value of certain option. It is guaranteed to never return
274 * NULL. Note that you are supposed to use wrapper get_opt(). */
275 union option_value *
276 get_opt_(
277 #ifdef CONFIG_DEBUG
278 unsigned char *file, int line, enum option_type option_type,
279 #endif
280 struct option *tree, unsigned char *name, struct session *ses)
282 struct option *opt = NULL;
284 /* If given a session and the option is shadowed in that session's
285 * options tree, return the shadow. */
286 if (ses && ses->option)
287 opt = get_opt_rec_real(ses->option, name);
289 /* If given a session, no session-specific option was found, and the
290 * option has a shadow in the domain tree that matches the current
291 * document in that session, return that shadow. */
292 if (!opt && ses)
293 opt = get_domain_option_from_session(name, ses);
295 /* Else, return the real option. */
296 if (!opt)
297 opt = get_opt_rec(tree, name);
299 #ifdef CONFIG_DEBUG
300 errfile = file;
301 errline = line;
302 if (!opt) elinks_internal("Attempted to fetch nonexisting option %s!", name);
304 /* Various sanity checks. */
305 if (option_type != opt->type)
306 DBG("get_opt_*(\"%s\") @ %s:%d: call with wrapper for %s for option of type %s",
307 name, file, line,
308 get_option_type_name(option_type),
309 get_option_type_name(opt->type));
311 switch (opt->type) {
312 case OPT_TREE:
313 if (!opt->value.tree)
314 elinks_internal("Option %s has no value!", name);
315 break;
316 case OPT_ALIAS:
317 elinks_internal("Invalid use of alias %s for option %s!",
318 name, opt->value.string);
319 break;
320 case OPT_STRING:
321 if (!opt->value.string)
322 elinks_internal("Option %s has no value!", name);
323 break;
324 case OPT_BOOL:
325 case OPT_INT:
326 if (opt->value.number < opt->min
327 || opt->value.number > opt->max)
328 elinks_internal("Option %s has invalid value %d!", name, opt->value.number);
329 break;
330 case OPT_LONG:
331 if (opt->value.big_number < opt->min
332 || opt->value.big_number > opt->max)
333 elinks_internal("Option %s has invalid value %ld!", name, opt->value.big_number);
334 break;
335 case OPT_COMMAND:
336 if (!opt->value.command)
337 elinks_internal("Option %s has no value!", name);
338 break;
339 case OPT_CODEPAGE: /* TODO: check these too. */
340 case OPT_LANGUAGE:
341 case OPT_COLOR:
342 break;
344 #endif
346 return &opt->value;
349 static void
350 add_opt_sort(struct option *tree, struct option *option, int abi)
352 LIST_OF(struct option) *cat = tree->value.tree;
353 LIST_OF(struct listbox_item) *bcat = &tree->box_item->child;
354 struct option *pos;
356 /* The list is empty, just add it there. */
357 if (list_empty(*cat)) {
358 add_to_list(*cat, option);
359 if (abi) add_to_list(*bcat, option->box_item);
361 /* This fits as the last list entry, add it there. This
362 * optimizes the most expensive BUT most common case ;-). */
363 } else if ((option->type != OPT_TREE
364 || ((struct option *) cat->prev)->type == OPT_TREE)
365 && strcmp(((struct option *) cat->prev)->name,
366 option->name) <= 0) {
367 append:
368 add_to_list_end(*cat, option);
369 if (abi) add_to_list_end(*bcat, option->box_item);
371 /* At the end of the list is tree and we are ordinary. That's
372 * clear case then. */
373 } else if (option->type != OPT_TREE
374 && ((struct option *) cat->prev)->type == OPT_TREE) {
375 goto append;
377 /* Scan the list linearly. This could be probably optimized ie.
378 * to choose direction based on the first letter or so. */
379 } else {
380 struct listbox_item *bpos = (struct listbox_item *) bcat;
382 foreach (pos, *cat) {
383 /* First move the box item to the current position but
384 * only if the position has not been marked as deleted
385 * and actually has a box_item -- else we will end up
386 * 'overflowing' and causing assertion failure. */
387 if (!(pos->flags & OPT_DELETED) && pos->box_item) {
388 bpos = bpos->next;
389 assert(bpos != (struct listbox_item *) bcat);
392 if ((option->type != OPT_TREE
393 || pos->type == OPT_TREE)
394 && strcmp(pos->name, option->name) <= 0)
395 continue;
397 /* Ordinary options always sort behind trees. */
398 if (option->type != OPT_TREE
399 && pos->type == OPT_TREE)
400 continue;
402 /* The (struct option) add_at_pos() can mess up the
403 * order so that we add the box_item to itself, so
404 * better do it first. */
406 /* Always ensure that them _template_ options are
407 * before anything else so a lonely autocreated option
408 * (with _template_ options set to invisible) will be
409 * connected with an upper corner (ascii: `-) instead
410 * of a rotated T (ascii: +-) when displaying it. */
411 if (option->type == pos->type
412 && *option->name <= '_'
413 && !strcmp(pos->name, "_template_")) {
414 if (abi) add_at_pos(bpos, option->box_item);
415 add_at_pos(pos, option);
416 break;
419 if (abi) add_at_pos(bpos->prev, option->box_item);
420 add_at_pos(pos->prev, option);
421 break;
424 assert(pos != (struct option *) cat);
425 assert(bpos != (struct listbox_item *) bcat);
429 /* Add option to tree. */
430 static void
431 add_opt_rec(struct option *tree, unsigned char *path, struct option *option)
433 int abi = 0;
435 assert(path && option && tree);
436 if (*path) tree = get_opt_rec(tree, path);
438 assertm(tree != NULL, "Missing option tree for '%s'", path);
439 if (!tree->value.tree) return;
441 object_nolock(option, "option");
443 if (option->box_item && option->name && !strcmp(option->name, "_template_"))
444 option->box_item->visible = get_opt_bool("config.show_template", NULL);
446 if (tree->flags & OPT_AUTOCREATE && !option->desc) {
447 struct option *template = get_opt_rec(tree, "_template_");
449 assert(template);
450 option->desc = template->desc;
453 option->root = tree;
455 abi = (tree->box_item && option->box_item);
457 if (abi) {
458 /* The config_root tree is a just a placeholder for the
459 * box_items, it actually isn't a real box_item by itself;
460 * these ghosts are indicated by the fact that they have
461 * NULL @next. */
462 if (tree->box_item->next) {
463 option->box_item->depth = tree->box_item->depth + 1;
467 if (tree->flags & OPT_SORT) {
468 add_opt_sort(tree, option, abi);
470 } else {
471 add_to_list_end(*tree->value.tree, option);
472 if (abi) add_to_list_end(tree->box_item->child, option->box_item);
475 update_hierbox_browser(&option_browser);
478 static inline struct listbox_item *
479 init_option_listbox_item(struct option *option)
481 struct listbox_item *item = mem_calloc(1, sizeof(*item));
483 if (!item) return NULL;
485 init_list(item->child);
486 item->visible = 1;
487 item->udata = option;
488 item->type = (option->type == OPT_TREE) ? BI_FOLDER : BI_LEAF;
490 return item;
493 struct option *
494 add_opt(struct option *tree, unsigned char *path, unsigned char *capt,
495 unsigned char *name, enum option_flags flags, enum option_type type,
496 long min, long max, longptr_T value, unsigned char *desc)
498 struct option *option = mem_calloc(1, sizeof(*option));
500 if (!option) return NULL;
502 option->name = stracpy(name);
503 if (!option->name) {
504 mem_free(option);
505 return NULL;
507 option->flags = (flags | OPT_ALLOC);
508 option->type = type;
509 option->min = min;
510 option->max = max;
511 option->capt = capt;
512 option->desc = desc;
514 debug_check_option_syntax(option);
516 /* XXX: For allocated values we allocate in the add_opt_<type>() macro.
517 * This involves OPT_TREE and OPT_STRING. */
518 switch (type) {
519 case OPT_TREE:
520 if (!value) {
521 mem_free(option);
522 return NULL;
524 option->value.tree = (LIST_OF(struct option) *) value;
525 break;
526 case OPT_STRING:
527 if (!value) {
528 mem_free(option);
529 return NULL;
531 option->value.string = (unsigned char *) value;
532 break;
533 case OPT_ALIAS:
534 option->value.string = (unsigned char *) value;
535 break;
536 case OPT_BOOL:
537 case OPT_INT:
538 case OPT_CODEPAGE:
539 option->value.number = (int) value;
540 break;
541 case OPT_LONG:
542 option->value.big_number = (long) value; /* FIXME: cast from void * */
543 break;
544 case OPT_COLOR:
545 decode_color((unsigned char *) value, strlen((unsigned char *) value),
546 &option->value.color);
547 break;
548 case OPT_COMMAND:
549 option->value.command = (void *) value;
550 break;
551 case OPT_LANGUAGE:
552 break;
555 if (option->type != OPT_ALIAS
556 && ((tree->flags & OPT_LISTBOX) || (option->flags & OPT_LISTBOX))) {
557 option->box_item = init_option_listbox_item(option);
558 if (!option->box_item) {
559 mem_free(option);
560 return NULL;
564 add_opt_rec(tree, path, option);
565 return option;
568 static void
569 done_option(struct option *option)
571 switch (option->type) {
572 case OPT_STRING:
573 mem_free_if(option->value.string);
574 break;
575 case OPT_TREE:
576 mem_free_if(option->value.tree);
577 break;
578 default:
579 break;
582 if (option->box_item)
583 done_listbox_item(&option_browser, option->box_item);
585 if (option->flags & OPT_ALLOC) {
586 mem_free_if(option->name);
587 mem_free(option);
588 } else if (!option->capt) {
589 /* We are probably dealing with a built-in autocreated option
590 * that will be attempted to be deleted when shutting down. */
591 /* Clear it so nothing will be done later. */
592 memset(option, 0, sizeof(*option));
596 /* The namespace may start to seem a bit chaotic here; it indeed is, maybe the
597 * function names above should be renamed and only macros should keep their old
598 * short names. */
599 /* The simple rule I took as an apologize is that functions which take already
600 * completely filled (struct option *) have long name and functions which take
601 * only option specs have short name. */
603 static void
604 delete_option_do(struct option *option, int recursive)
606 if (option->next) {
607 del_from_list(option);
608 option->prev = option->next = NULL;
611 if (recursive == -1) {
612 ERROR("Orphaned option %s", option->name);
615 if (option->type == OPT_TREE && option->value.tree
616 && !list_empty(*option->value.tree)) {
617 if (!recursive) {
618 if (option->flags & OPT_AUTOCREATE) {
619 recursive = 1;
620 } else {
621 ERROR("Orphaned unregistered "
622 "option in subtree %s!",
623 option->name);
624 recursive = -1;
627 free_options_tree(option->value.tree, recursive);
630 done_option(option);
633 void
634 mark_option_as_deleted(struct option *option)
636 if (option->type == OPT_TREE) {
637 struct option *unmarked;
639 assert(option->value.tree);
641 foreach (unmarked, *option->value.tree)
642 mark_option_as_deleted(unmarked);
645 option->box_item->visible = 0;
647 option->flags |= (OPT_TOUCHED | OPT_DELETED);
650 void
651 delete_option(struct option *option)
653 delete_option_do(option, 1);
656 struct option *
657 copy_option(struct option *template, int flags)
659 struct option *option = mem_calloc(1, sizeof(*option));
661 if (!option) return NULL;
663 option->name = null_or_stracpy(template->name);
664 option->flags = (template->flags | OPT_ALLOC);
665 option->type = template->type;
666 option->min = template->min;
667 option->max = template->max;
668 option->capt = template->capt;
669 option->desc = template->desc;
670 option->change_hook = template->change_hook;
672 if (!(flags & CO_NO_LISTBOX_ITEM))
673 option->box_item = init_option_listbox_item(option);
674 if (option->box_item) {
675 if (template->box_item) {
676 option->box_item->type = template->box_item->type;
677 option->box_item->depth = template->box_item->depth;
681 if (option_types[template->type].dup) {
682 option_types[template->type].dup(option, template, flags);
683 } else {
684 option->value = template->value;
687 return option;
690 /* Return the shadow option in @shadow_tree of @option in @tree. If @option
691 * isn't yet shadowed in @shadow_tree, shadow it (i.e. create a copy
692 * in @shadow_tree) along with any ancestors that aren't shadowed. */
693 struct option *
694 get_option_shadow(struct option *option, struct option *tree,
695 struct option *shadow_tree)
698 struct option *shadow_option = NULL;
700 assert(option);
701 assert(tree);
702 assert(shadow_tree);
704 if (option == tree) {
705 shadow_option = shadow_tree;
706 } else if (option->root && option->name) {
707 struct option *shadow_root;
709 shadow_root = get_option_shadow(option->root, tree,
710 shadow_tree);
711 if (!shadow_root) return NULL;
713 shadow_option = get_opt_rec_real(shadow_root, option->name);
714 if (!shadow_option) {
715 shadow_option = copy_option(option,
716 CO_SHALLOW
717 | CO_NO_LISTBOX_ITEM);
718 if (shadow_option) {
719 shadow_option->root = shadow_root;
720 /* No need to sort, is there? It isn't shown
721 * in the options manager. -- Miciah */
722 add_to_list_end(*shadow_root->value.tree,
723 shadow_option);
725 shadow_option->flags |= OPT_TOUCHED;
731 return shadow_option;
735 LIST_OF(struct option) *
736 init_options_tree(void)
738 LIST_OF(struct option) *ptr = mem_alloc(sizeof(*ptr));
740 if (ptr) init_list(*ptr);
741 return ptr;
744 /* Some default pre-autocreated options. Doh. */
745 static inline void
746 register_autocreated_options(void)
748 /* TODO: Use table-driven initialization. --jonas */
749 get_opt_int("terminal.linux.type", NULL) = 2;
750 get_opt_int("terminal.linux.colors", NULL) = 1;
751 get_opt_bool("terminal.linux.m11_hack", NULL) = 1;
752 get_opt_int("terminal.vt100.type", NULL) = 1;
753 get_opt_int("terminal.vt110.type", NULL) = 1;
754 get_opt_int("terminal.xterm.type", NULL) = 1;
755 get_opt_bool("terminal.xterm.underline", NULL) = 1;
756 get_opt_int("terminal.xterm-color.type", NULL) = 1;
757 get_opt_int("terminal.xterm-color.colors", NULL) = COLOR_MODE_16;
758 get_opt_bool("terminal.xterm-color.underline", NULL) = 1;
759 #ifdef CONFIG_88_COLORS
760 get_opt_int("terminal.xterm-88color.type", NULL) = 1;
761 get_opt_int("terminal.xterm-88color.colors", NULL) = COLOR_MODE_88;
762 get_opt_bool("terminal.xterm-88color.underline", NULL) = 1;
763 #endif
764 #ifdef CONFIG_256_COLORS
765 get_opt_int("terminal.xterm-256color.type", NULL) = 1;
766 get_opt_int("terminal.xterm-256color.colors", NULL) = COLOR_MODE_256;
767 get_opt_bool("terminal.xterm-256color.underline", NULL) = 1;
768 #endif
771 static struct option_info config_options_info[];
772 extern struct option_info cmdline_options_info[];
773 static const struct change_hook_info change_hooks[];
775 void
776 init_options(void)
778 cmdline_options = add_opt_tree_tree(&options_root, "", "",
779 "cmdline", 0, "");
780 register_options(cmdline_options_info, cmdline_options);
782 config_options = add_opt_tree_tree(&options_root, "", "",
783 "config", OPT_SORT, "");
784 config_options->flags |= OPT_LISTBOX;
785 config_options->box_item = &option_browser.root;
786 register_options(config_options_info, config_options);
788 register_autocreated_options();
789 register_change_hooks(change_hooks);
792 static void
793 free_options_tree(LIST_OF(struct option) *tree, int recursive)
795 while (!list_empty(*tree))
796 delete_option_do(tree->next, recursive);
799 void
800 done_options(void)
802 done_domain_trees();
803 unregister_options(config_options_info, config_options);
804 unregister_options(cmdline_options_info, cmdline_options);
805 config_options->box_item = NULL;
806 free_options_tree(&options_root_tree, 0);
809 void
810 register_change_hooks(const struct change_hook_info *change_hooks)
812 int i;
814 for (i = 0; change_hooks[i].name; i++) {
815 struct option *option = get_opt_rec(config_options,
816 change_hooks[i].name);
818 assert(option);
819 option->change_hook = change_hooks[i].change_hook;
823 void
824 prepare_mustsave_flags(LIST_OF(struct option) *tree, int set_all)
826 struct option *option;
828 foreach (option, *tree) {
829 /* XXX: OPT_LANGUAGE shouldn't have any bussiness
830 * here, but we're just weird in that area. */
831 if (set_all
832 || (option->flags & (OPT_TOUCHED | OPT_DELETED))
833 || option->type == OPT_LANGUAGE)
834 option->flags |= OPT_MUST_SAVE;
835 else
836 option->flags &= ~OPT_MUST_SAVE;
838 if (option->type == OPT_TREE)
839 prepare_mustsave_flags(option->value.tree, set_all);
843 void
844 untouch_options(LIST_OF(struct option) *tree)
846 struct option *option;
848 foreach (option, *tree) {
849 option->flags &= ~OPT_TOUCHED;
851 if (option->type == OPT_TREE)
852 untouch_options(option->value.tree);
856 static int
857 check_nonempty_tree(LIST_OF(struct option) *options)
859 struct option *opt;
861 foreach (opt, *options) {
862 if (opt->type == OPT_TREE) {
863 if (check_nonempty_tree(opt->value.tree))
864 return 1;
865 } else if (opt->flags & OPT_MUST_SAVE) {
866 return 1;
870 return 0;
873 void
874 smart_config_string(struct string *str, int print_comment, int i18n,
875 LIST_OF(struct option) *options,
876 unsigned char *path, int depth,
877 void (*fn)(struct string *, struct option *,
878 unsigned char *, int, int, int, int))
880 struct option *option;
882 foreach (option, *options) {
883 int do_print_comment = 1;
885 if (option->flags & OPT_HIDDEN ||
886 option->type == OPT_ALIAS ||
887 !strcmp(option->name, "_template_"))
888 continue;
890 /* Is there anything to be printed anyway? */
891 if (option->type == OPT_TREE
892 ? !check_nonempty_tree(option->value.tree)
893 : !(option->flags & OPT_MUST_SAVE))
894 continue;
896 /* We won't pop out the description when we're in autocreate
897 * category and not template. It'd be boring flood of
898 * repetitive comments otherwise ;). */
900 /* This print_comment parameter is weird. If it is negative, it
901 * means that we shouldn't print comments at all. If it is 1,
902 * we shouldn't print comment UNLESS the option is _template_
903 * or not-an-autocreating-tree (it is set for the first-level
904 * autocreation tree). When it is 2, we can print out comments
905 * normally. */
906 /* It is still broken somehow, as it didn't work for terminal.*
907 * (the first autocreated level) by the time I wrote this. Good
908 * summer job for bored mad hackers with spare boolean mental
909 * power. I have better things to think about, personally.
910 * Maybe we should just mark autocreated options somehow ;). */
911 if (!print_comment || (print_comment == 1
912 && (strcmp(option->name, "_template_")
913 && (option->flags & OPT_AUTOCREATE
914 && option->type == OPT_TREE))))
915 do_print_comment = 0;
917 /* Pop out the comment */
919 /* For config file, we ignore do_print_comment everywhere
920 * except 1, but sometimes we want to skip the option totally.
922 fn(str, option, path, depth,
923 option->type == OPT_TREE ? print_comment
924 : do_print_comment,
925 0, i18n);
927 fn(str, option, path, depth, do_print_comment, 1, i18n);
929 /* And the option itself */
931 if (option_types[option->type].write) {
932 fn(str, option, path, depth,
933 do_print_comment, 2, i18n);
935 } else if (option->type == OPT_TREE) {
936 struct string newpath;
937 int pc = print_comment;
939 if (!init_string(&newpath)) continue; /* OK? */
941 if (pc == 2 && option->flags & OPT_AUTOCREATE)
942 pc = 1;
943 else if (pc == 1 && strcmp(option->name, "_template_"))
944 pc = 0;
946 fn(str, option, path, depth, /*pc*/1, 3, i18n);
948 if (path) {
949 add_to_string(&newpath, path);
950 add_char_to_string(&newpath, '.');
952 add_to_string(&newpath, option->name);
953 smart_config_string(str, pc, i18n, option->value.tree,
954 newpath.source, depth + 1, fn);
955 done_string(&newpath);
957 fn(str, option, path, depth, /*pc*/1, 3, i18n);
963 static int
964 change_hook_cache(struct session *ses, struct option *current, struct option *changed)
966 shrink_memory(0);
967 return 0;
970 static int
971 change_hook_connection(struct session *ses, struct option *current, struct option *changed)
973 register_check_queue();
974 return 0;
977 static int
978 change_hook_html(struct session *ses, struct option *current, struct option *changed)
980 foreach (ses, sessions) ses->tab->resize = 1;
982 return 0;
985 static int
986 change_hook_insert_mode(struct session *ses, struct option *current, struct option *changed)
988 update_status();
989 return 0;
992 static int
993 change_hook_active_link(struct session *ses, struct option *current, struct option *changed)
995 update_cached_document_options(ses);
996 return 0;
999 static int
1000 change_hook_terminal(struct session *ses, struct option *current, struct option *changed)
1002 cls_redraw_all_terminals();
1003 return 0;
1006 static int
1007 change_hook_ui(struct session *ses, struct option *current, struct option *changed)
1009 update_status();
1010 return 0;
1013 /* Bit 2 of show means we should always set visibility, otherwise we set it
1014 * only on templates. */
1015 static void
1016 update_visibility(LIST_OF(struct option) *tree, int show)
1018 struct option *opt;
1020 foreach (opt, *tree) {
1021 if (opt->flags & OPT_DELETED) continue;
1023 if (!strcmp(opt->name, "_template_")) {
1024 if (opt->box_item)
1025 opt->box_item->visible = (show & 1);
1027 if (opt->type == OPT_TREE)
1028 update_visibility(opt->value.tree, show | 2);
1029 } else {
1030 if (opt->box_item && (show & 2))
1031 opt->box_item->visible = (show & 1);
1033 if (opt->type == OPT_TREE)
1034 update_visibility(opt->value.tree, show);
1039 void
1040 update_options_visibility(void)
1042 update_visibility(config_options->value.tree,
1043 get_opt_bool("config.show_template", NULL));
1046 void
1047 toggle_option(struct session *ses, struct option *option)
1049 long number = option->value.number + 1;
1051 assert(option->type == OPT_BOOL || option->type == OPT_INT);
1052 assert(option->max);
1054 option->value.number = (number <= option->max) ? number : option->min;
1055 option_changed(ses, option);
1058 static int
1059 change_hook_stemplate(struct session *ses, struct option *current, struct option *changed)
1061 update_visibility(config_options->value.tree, changed->value.number);
1062 return 0;
1065 static int
1066 change_hook_language(struct session *ses, struct option *current, struct option *changed)
1068 #ifdef CONFIG_NLS
1069 set_language(changed->value.number);
1070 #endif
1071 return 0;
1074 static const struct change_hook_info change_hooks[] = {
1075 { "config.show_template", change_hook_stemplate },
1076 { "connection", change_hook_connection },
1077 { "document.browse", change_hook_html },
1078 { "document.browse.forms.insert_mode",
1079 change_hook_insert_mode },
1080 { "document.browse.links.active_link",
1081 change_hook_active_link },
1082 { "document.cache", change_hook_cache },
1083 { "document.codepage", change_hook_html },
1084 { "document.colors", change_hook_html },
1085 { "document.html", change_hook_html },
1086 { "document.plain", change_hook_html },
1087 { "terminal", change_hook_terminal },
1088 { "ui.language", change_hook_language },
1089 { "ui", change_hook_ui },
1090 { NULL, NULL },
1093 void
1094 call_change_hooks(struct session *ses, struct option *current, struct option *option)
1096 /* This boolean thing can look a little weird - it
1097 * basically says that we should proceed when there's
1098 * no change_hook or there's one and its return value
1099 * was zero. */
1100 while (current && (!current->change_hook ||
1101 !current->change_hook(ses, current, option))) {
1102 if (!current->root)
1103 break;
1105 current = current->root;
1109 void
1110 option_changed(struct session *ses, struct option *option)
1112 option->flags |= OPT_TOUCHED;
1113 /* Notify everyone out there! */
1114 call_change_hooks(ses, option, option);
1118 commit_option_values(struct option_resolver *resolvers,
1119 struct option *root, union option_value *values, int size)
1121 int touched = 0;
1122 int i;
1124 assert(resolvers && root && values && size);
1126 for (i = 0; i < size; i++) {
1127 unsigned char *name = resolvers[i].name;
1128 struct option *option = get_opt_rec(root, name);
1129 int id = resolvers[i].id;
1131 assertm(option, "Bad option '%s' in options resolver", name);
1133 if (memcmp(&option->value, &values[id], sizeof(union option_value))) {
1134 option->value = values[id];
1135 option->flags |= OPT_TOUCHED;
1136 /* Speed hack: Directly call the change-hook for each
1137 * option in resolvers and later call call_change_hooks
1138 * on the root; if we were to call call_change_hooks
1139 * on each option in resolvers, we would end up calling
1140 * the change-hooks of root, its parents, its
1141 * grandparents, and so on for each option in resolvers
1142 * because call_change_hooks is recursive. -- Miciah */
1143 if (option->change_hook)
1144 option->change_hook(NULL, option, NULL);
1145 touched++;
1149 /* See above 'Speed hack' comment. */
1150 call_change_hooks(NULL, root, NULL);
1152 return touched;
1155 void
1156 checkout_option_values(struct option_resolver *resolvers,
1157 struct option *root,
1158 union option_value *values, int size)
1160 int i;
1162 for (i = 0; i < size; i++) {
1163 unsigned char *name = resolvers[i].name;
1164 struct option *option = get_opt_rec(root, name);
1165 int id = resolvers[i].id;
1167 assertm(option, "Bad option '%s' in options resolver", name);
1169 values[id] = option->value;
1173 /**********************************************************************
1174 Options values
1175 **********************************************************************/
1177 #include "config/options.inc"
1179 void
1180 register_options(struct option_info info[], struct option *tree)
1182 int i;
1184 for (i = 0; info[i].path; i++) {
1185 struct option *option = &info[i].option;
1186 unsigned char *string;
1188 debug_check_option_syntax(option);
1190 if (option->type != OPT_ALIAS
1191 && ((tree->flags & OPT_LISTBOX)
1192 || (option->flags & OPT_LISTBOX))) {
1193 option->box_item = init_option_listbox_item(option);
1194 if (!option->box_item) {
1195 delete_option(option);
1196 continue;
1200 switch (option->type) {
1201 case OPT_TREE:
1202 option->value.tree = init_options_tree();
1203 if (!option->value.tree) {
1204 delete_option(option);
1205 continue;
1207 break;
1208 case OPT_STRING:
1209 string = mem_alloc(MAX_STR_LEN);
1210 if (!string) {
1211 delete_option(option);
1212 continue;
1214 safe_strncpy(string, option->value.string, MAX_STR_LEN);
1215 option->value.string = string;
1216 break;
1217 case OPT_COLOR:
1218 string = option->value.string;
1219 assert(string);
1220 decode_color(string, strlen(string),
1221 &option->value.color);
1222 break;
1223 case OPT_CODEPAGE:
1224 string = option->value.string;
1225 assert(string);
1226 option->value.number = get_cp_index(string);
1227 break;
1228 case OPT_BOOL:
1229 case OPT_INT:
1230 case OPT_LONG:
1231 case OPT_LANGUAGE:
1232 case OPT_COMMAND:
1233 case OPT_ALIAS:
1234 break;
1237 add_opt_rec(tree, info[i].path, option);
1241 void
1242 unregister_options(struct option_info info[], struct option *tree)
1244 int i = 0;
1246 /* We need to remove the options in inverse order to the order how we
1247 * added them. */
1249 while (info[i].path) i++;
1251 for (i--; i >= 0; i--)
1252 delete_option_do(&info[i].option, 0);