Python: Give goto_url_hook only one argument, like follow_url_hook.
[elinks.git] / src / config / options.c
blobd98549861456e004ad67496f4566cdbe0249e441
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/options.h"
17 #include "config/opttypes.h"
18 #include "dialogs/status.h"
19 #include "document/document.h"
20 #include "globhist/globhist.h"
21 #include "intl/charsets.h"
22 #include "intl/gettext/libintl.h"
23 #include "main/main.h" /* shrink_memory() */
24 #include "main/select.h"
25 #include "network/connection.h"
26 #include "session/session.h"
27 #include "terminal/color.h"
28 #include "terminal/screen.h"
29 #include "terminal/terminal.h"
30 #include "util/color.h"
31 #include "util/error.h"
32 #include "util/memory.h"
33 #include "util/string.h"
34 #include "viewer/text/draw.h"
37 /* TODO? In the past, covered by shadow and legends, remembered only by the
38 * ELinks Elders now, options were in hashes (it was not for a long time, after
39 * we started to use dynamic options lists and before we really started to use
40 * hierarchic options). Hashes might be swift and deft, but they had a flaw and
41 * the flaw showed up as the fatal flaw. They were unsorted, and it was
42 * unfriendly to mere mortal users, without pasky's options handlers in their
43 * brain, but their own poor-written software. And thus pasky went and rewrote
44 * options so that they were in lists from then to now and for all the ages of
45 * men, to the glory of mankind. However, one true hero may arise in future
46 * fabulous and implement possibility to have both lists and hashes for trees,
47 * as it may be useful for some supernatural entities. And when that age will
48 * come... */
51 /* TODO: We should remove special case for root options and use some auxiliary
52 * (struct option *) instead. This applies to bookmarks, global history and
53 * listbox items as well, though. --pasky */
55 static INIT_LIST_HEAD(options_root_tree);
57 static struct option options_root = INIT_OPTION(
58 /* name: */ "",
59 /* flags: */ 0,
60 /* type: */ OPT_TREE,
61 /* min, max: */ 0, 0,
62 /* value: */ &options_root_tree,
63 /* desc: */ "",
64 /* capt: */ NULL
67 struct option *config_options;
68 struct option *cmdline_options;
70 static void add_opt_rec(struct option *, unsigned char *, struct option *);
71 static void free_options_tree(struct list_head *, int recursive);
73 #ifdef CONFIG_DEBUG
74 /* Detect ending '.' (and some others) in options captions.
75 * It will emit a message in debug mode only. --Zas */
77 #define bad_punct(c) (c != ')' && c != '>' && !isquote(c) && ispunct(c))
79 static void
80 check_caption(unsigned char *caption)
82 int len;
83 unsigned char c;
85 if (!caption) return;
87 len = strlen(caption);
88 if (!len) return;
90 c = caption[len - 1];
91 if (isspace(c) || bad_punct(c))
92 DBG("bad char at end of caption [%s]", caption);
94 #ifdef CONFIG_NLS
95 caption = gettext(caption);
96 len = strlen(caption);
97 if (!len) return;
99 c = caption[len - 1];
100 if (isspace(c) || bad_punct(c))
101 DBG("bad char at end of i18n caption [%s]", caption);
102 #endif
105 #undef bad_punct
107 static void
108 check_description(unsigned char *desc)
110 int len;
111 unsigned char c;
113 if (!desc) return;
115 len = strlen(desc);
116 if (!len) return;
118 c = desc[len - 1];
119 if (isspace(c))
120 DBG("bad char at end of description [%s]", desc);
122 #ifdef CONFIG_NLS
123 desc = gettext(desc);
124 len = strlen(desc);
125 if (!len) return;
127 if (ispunct(c) != ispunct(desc[len - 1]))
128 DBG("punctuation char possibly missing at end of i18n description [%s]", desc);
130 c = desc[len - 1];
131 if (isspace(c))
132 DBG("bad char at end of i18n description [%s]", desc);
133 #endif
136 static void
137 debug_check_option_syntax(struct option *option)
139 if (!option) return;
140 check_caption(option->capt);
141 check_description(option->desc);
144 #else
145 #define debug_check_option_syntax(option)
146 #endif
149 /**********************************************************************
150 Options interface
151 **********************************************************************/
153 /* If option name contains dots, they are created as "categories" - first,
154 * first category is retrieved from list, taken as a list, second category
155 * is retrieved etc. */
157 /* Ugly kludge */
158 static int no_autocreate = 0;
160 /* Get record of option of given name, or NULL if there's no such option. */
161 struct option *
162 get_opt_rec(struct option *tree, unsigned char *name_)
164 struct option *option;
165 unsigned char *aname = stracpy(name_);
166 unsigned char *name = aname;
167 unsigned char *sep;
169 if (!aname) return NULL;
171 /* We iteratively call get_opt_rec() each for path_elements-1, getting
172 * appropriate tree for it and then resolving [path_elements]. */
173 if ((sep = strrchr(name, '.'))) {
174 *sep = '\0';
176 tree = get_opt_rec(tree, name);
177 if (!tree || tree->type != OPT_TREE || tree->flags & OPT_HIDDEN) {
178 #if 0
179 DBG("ERROR in get_opt_rec() crawl: %s (%d) -> %s",
180 name, tree ? tree->type : -1, sep + 1);
181 #endif
182 mem_free(aname);
183 return NULL;
186 *sep = '.';
187 name = sep + 1;
190 foreach (option, *tree->value.tree) {
191 if (option->name && !strcmp(option->name, name)) {
192 mem_free(aname);
193 return option;
197 if (tree && tree->flags & OPT_AUTOCREATE && !no_autocreate) {
198 struct option *template = get_opt_rec(tree, "_template_");
200 assertm(template, "Requested %s should be autocreated but "
201 "%.*s._template_ is missing!", name_, sep - name_,
202 name_);
203 if_assert_failed {
204 mem_free(aname);
205 return NULL;
208 /* We will just create the option and return pointer to it
209 * automagically. And, we will create it by cloning _template_
210 * option. By having _template_ OPT_AUTOCREATE and _template_
211 * inside, you can have even multi-level autocreating. */
213 option = copy_option(template);
214 if (!option) {
215 mem_free(aname);
216 return NULL;
218 mem_free_set(&option->name, stracpy(name));
220 add_opt_rec(tree, "", option);
222 mem_free(aname);
223 return option;
226 mem_free(aname);
227 return NULL;
230 /* Get record of option of given name, or NULL if there's no such option. But
231 * do not create the option if it doesn't exist and there's autocreation
232 * enabled. */
233 struct option *
234 get_opt_rec_real(struct option *tree, unsigned char *name)
236 struct option *opt;
238 no_autocreate = 1;
239 opt = get_opt_rec(tree, name);
240 no_autocreate = 0;
241 return opt;
244 /* Fetch pointer to value of certain option. It is guaranteed to never return
245 * NULL. Note that you are supposed to use wrapper get_opt(). */
246 union option_value *
247 get_opt_(
248 #ifdef CONFIG_DEBUG
249 unsigned char *file, int line, enum option_type option_type,
250 #endif
251 struct option *tree, unsigned char *name)
253 struct option *opt = get_opt_rec(tree, name);
255 #ifdef CONFIG_DEBUG
256 errfile = file;
257 errline = line;
258 if (!opt) elinks_internal("Attempted to fetch nonexisting option %s!", name);
260 /* Various sanity checks. */
261 if (option_type != opt->type)
262 DBG("get_opt_*(\"%s\") @ %s:%d: call with wrapper for %s for option of type %s",
263 name, file, line,
264 get_option_type_name(option_type),
265 get_option_type_name(opt->type));
267 switch (opt->type) {
268 case OPT_TREE:
269 if (!opt->value.tree)
270 elinks_internal("Option %s has no value!", name);
271 break;
272 case OPT_ALIAS:
273 elinks_internal("Invalid use of alias %s for option %s!",
274 name, opt->value.string);
275 break;
276 case OPT_STRING:
277 if (!opt->value.string)
278 elinks_internal("Option %s has no value!", name);
279 break;
280 case OPT_BOOL:
281 case OPT_INT:
282 if (opt->value.number < opt->min
283 || opt->value.number > opt->max)
284 elinks_internal("Option %s has invalid value %d!", name, opt->value.number);
285 break;
286 case OPT_LONG:
287 if (opt->value.big_number < opt->min
288 || opt->value.big_number > opt->max)
289 elinks_internal("Option %s has invalid value %ld!", name, opt->value.big_number);
290 break;
291 case OPT_COMMAND:
292 if (!opt->value.command)
293 elinks_internal("Option %s has no value!", name);
294 break;
295 case OPT_CODEPAGE: /* TODO: check these too. */
296 case OPT_LANGUAGE:
297 case OPT_COLOR:
298 break;
300 #endif
302 return &opt->value;
305 static void
306 add_opt_sort(struct option *tree, struct option *option, int abi)
308 struct list_head *cat = tree->value.tree;
309 struct list_head *bcat = &tree->box_item->child;
310 struct option *pos;
312 /* The list is empty, just add it there. */
313 if (list_empty(*cat)) {
314 add_to_list(*cat, option);
315 if (abi) add_to_list(*bcat, option->box_item);
317 /* This fits as the last list entry, add it there. This
318 * optimizes the most expensive BUT most common case ;-). */
319 } else if ((option->type != OPT_TREE
320 || ((struct option *) cat->prev)->type == OPT_TREE)
321 && strcmp(((struct option *) cat->prev)->name,
322 option->name) <= 0) {
323 append:
324 add_to_list_end(*cat, option);
325 if (abi) add_to_list_end(*bcat, option->box_item);
327 /* At the end of the list is tree and we are ordinary. That's
328 * clear case then. */
329 } else if (option->type != OPT_TREE
330 && ((struct option *) cat->prev)->type == OPT_TREE) {
331 goto append;
333 /* Scan the list linearly. This could be probably optimized ie.
334 * to choose direction based on the first letter or so. */
335 } else {
336 struct listbox_item *bpos = (struct listbox_item *) bcat;
338 foreach (pos, *cat) {
339 /* First move the box item to the current position but
340 * only if the position has not been marked as deleted
341 * and actually has a box_item -- else we will end up
342 * 'overflowing' and causing assertion failure. */
343 if (!(pos->flags & OPT_DELETED) && pos->box_item) {
344 bpos = bpos->next;
345 assert(bpos != (struct listbox_item *) bcat);
348 if ((option->type != OPT_TREE
349 || pos->type == OPT_TREE)
350 && strcmp(pos->name, option->name) <= 0)
351 continue;
353 /* Ordinary options always sort behind trees. */
354 if (option->type != OPT_TREE
355 && pos->type == OPT_TREE)
356 continue;
358 /* The (struct option) add_at_pos() can mess up the
359 * order so that we add the box_item to itself, so
360 * better do it first. */
362 /* Always ensure that them _template_ options are
363 * before anything else so a lonely autocreated option
364 * (with _template_ options set to invisible) will be
365 * connected with an upper corner (ascii: `-) instead
366 * of a rotated T (ascii: +-) when displaying it. */
367 if (option->type == pos->type
368 && *option->name <= '_'
369 && !strcmp(pos->name, "_template_")) {
370 if (abi) add_at_pos(bpos, option->box_item);
371 add_at_pos(pos, option);
372 break;
375 if (abi) add_at_pos(bpos->prev, option->box_item);
376 add_at_pos(pos->prev, option);
377 break;
380 assert(pos != (struct option *) cat);
381 assert(bpos != (struct listbox_item *) bcat);
385 /* Add option to tree. */
386 static void
387 add_opt_rec(struct option *tree, unsigned char *path, struct option *option)
389 int abi = 0;
391 assert(path && option && tree);
392 if (*path) tree = get_opt_rec(tree, path);
394 assertm(tree, "Missing option tree for '%s'", path);
395 if (!tree->value.tree) return;
397 object_nolock(option, "option");
399 if (option->box_item && option->name && !strcmp(option->name, "_template_"))
400 option->box_item->visible = get_opt_bool("config.show_template");
402 if (tree->flags & OPT_AUTOCREATE && !option->desc) {
403 struct option *template = get_opt_rec(tree, "_template_");
405 assert(template);
406 option->desc = template->desc;
409 option->root = tree;
411 abi = (tree->box_item && option->box_item);
413 if (abi) {
414 /* The config_root tree is a just a placeholder for the
415 * box_items, it actually isn't a real box_item by itself;
416 * these ghosts are indicated by the fact that they have
417 * NULL @next. */
418 if (tree->box_item->next) {
419 option->box_item->depth = tree->box_item->depth + 1;
423 if (tree->flags & OPT_SORT) {
424 add_opt_sort(tree, option, abi);
426 } else {
427 add_to_list_end(*tree->value.tree, option);
428 if (abi) add_to_list_end(tree->box_item->child, option->box_item);
431 update_hierbox_browser(&option_browser);
434 static inline struct listbox_item *
435 init_option_listbox_item(struct option *option)
437 struct listbox_item *item = mem_calloc(1, sizeof(*item));
439 if (!item) return NULL;
441 init_list(item->child);
442 item->visible = 1;
443 item->udata = option;
444 item->type = (option->type == OPT_TREE) ? BI_FOLDER : BI_LEAF;
446 return item;
449 struct option *
450 add_opt(struct option *tree, unsigned char *path, unsigned char *capt,
451 unsigned char *name, enum option_flags flags, enum option_type type,
452 long min, long max, longptr_T value, unsigned char *desc)
454 struct option *option = mem_calloc(1, sizeof(*option));
456 if (!option) return NULL;
458 option->name = stracpy(name);
459 if (!option->name) {
460 mem_free(option);
461 return NULL;
463 option->flags = (flags | OPT_ALLOC);
464 option->type = type;
465 option->min = min;
466 option->max = max;
467 option->capt = capt;
468 option->desc = desc;
470 debug_check_option_syntax(option);
472 /* XXX: For allocated values we allocate in the add_opt_<type>() macro.
473 * This involves OPT_TREE and OPT_STRING. */
474 switch (type) {
475 case OPT_TREE:
476 if (!value) {
477 mem_free(option);
478 return NULL;
480 option->value.tree = (struct list_head *) value;
481 break;
482 case OPT_STRING:
483 if (!value) {
484 mem_free(option);
485 return NULL;
487 option->value.string = (unsigned char *) value;
488 break;
489 case OPT_ALIAS:
490 option->value.string = (unsigned char *) value;
491 break;
492 case OPT_BOOL:
493 case OPT_INT:
494 case OPT_CODEPAGE:
495 option->value.number = (int) value;
496 break;
497 case OPT_LONG:
498 option->value.big_number = (long) value; /* FIXME: cast from void * */
499 break;
500 case OPT_COLOR:
501 decode_color((unsigned char *) value, strlen((unsigned char *) value),
502 &option->value.color);
503 break;
504 case OPT_COMMAND:
505 option->value.command = (void *) value;
506 break;
507 case OPT_LANGUAGE:
508 break;
511 if (option->type != OPT_ALIAS
512 && ((tree->flags & OPT_LISTBOX) || (option->flags & OPT_LISTBOX))) {
513 option->box_item = init_option_listbox_item(option);
514 if (!option->box_item) {
515 mem_free(option);
516 return NULL;
520 add_opt_rec(tree, path, option);
521 return option;
524 static void
525 done_option(struct option *option)
527 switch (option->type) {
528 case OPT_STRING:
529 mem_free_if(option->value.string);
530 break;
531 case OPT_TREE:
532 mem_free_if(option->value.tree);
533 break;
534 default:
535 break;
538 if (option->box_item)
539 done_listbox_item(&option_browser, option->box_item);
541 if (option->flags & OPT_ALLOC) {
542 mem_free_if(option->name);
543 mem_free(option);
544 } else if (!option->capt) {
545 /* We are probably dealing with a built-in autocreated option
546 * that will be attempted to be deleted when shutting down. */
547 /* Clear it so nothing will be done later. */
548 memset(option, 0, sizeof(*option));
552 /* The namespace may start to seem a bit chaotic here; it indeed is, maybe the
553 * function names above should be renamed and only macros should keep their old
554 * short names. */
555 /* The simple rule I took as an apologize is that functions which take already
556 * completely filled (struct option *) have long name and functions which take
557 * only option specs have short name. */
559 static void
560 delete_option_do(struct option *option, int recursive)
562 if (option->next) {
563 del_from_list(option);
564 option->prev = option->next = NULL;
567 if (recursive == -1) {
568 ERROR("Orphaned option %s", option->name);
571 if (option->type == OPT_TREE && option->value.tree
572 && !list_empty(*option->value.tree)) {
573 if (!recursive) {
574 if (option->flags & OPT_AUTOCREATE) {
575 recursive = 1;
576 } else {
577 ERROR("Orphaned unregistered "
578 "option in subtree %s!",
579 option->name);
580 recursive = -1;
583 free_options_tree(option->value.tree, recursive);
586 done_option(option);
589 void
590 mark_option_as_deleted(struct option *option)
592 if (option->type == OPT_TREE) {
593 struct option *unmarked;
595 assert(option->value.tree);
597 foreach (unmarked, *option->value.tree)
598 mark_option_as_deleted(unmarked);
601 option->box_item->visible = 0;
603 option->flags |= (OPT_TOUCHED | OPT_DELETED);
606 void
607 delete_option(struct option *option)
609 delete_option_do(option, 1);
612 struct option *
613 copy_option(struct option *template)
615 struct option *option = mem_calloc(1, sizeof(*option));
617 if (!option) return NULL;
619 option->name = null_or_stracpy(template->name);
620 option->flags = (template->flags | OPT_ALLOC);
621 option->type = template->type;
622 option->min = template->min;
623 option->max = template->max;
624 option->capt = template->capt;
625 option->desc = template->desc;
626 option->change_hook = template->change_hook;
628 option->box_item = init_option_listbox_item(option);
629 if (option->box_item) {
630 if (template->box_item) {
631 option->box_item->type = template->box_item->type;
632 option->box_item->depth = template->box_item->depth;
636 if (option_types[template->type].dup) {
637 option_types[template->type].dup(option, template);
638 } else {
639 option->value = template->value;
642 return option;
645 struct list_head *
646 init_options_tree(void)
648 struct list_head *ptr = mem_alloc(sizeof(*ptr));
650 if (ptr) init_list(*ptr);
651 return ptr;
654 /* Some default pre-autocreated options. Doh. */
655 static inline void
656 register_autocreated_options(void)
658 /* TODO: Use table-driven initialization. --jonas */
659 get_opt_int("terminal.linux.type") = 2;
660 get_opt_int("terminal.linux.colors") = 1;
661 get_opt_bool("terminal.linux.m11_hack") = 1;
662 get_opt_int("terminal.vt100.type") = 1;
663 get_opt_int("terminal.vt110.type") = 1;
664 get_opt_int("terminal.xterm.type") = 1;
665 get_opt_bool("terminal.xterm.underline") = 1;
666 get_opt_int("terminal.xterm-color.type") = 1;
667 get_opt_int("terminal.xterm-color.colors") = COLOR_MODE_16;
668 get_opt_bool("terminal.xterm-color.underline") = 1;
669 #ifdef CONFIG_88_COLORS
670 get_opt_int("terminal.xterm-88color.type") = 1;
671 get_opt_int("terminal.xterm-88color.colors") = COLOR_MODE_88;
672 get_opt_bool("terminal.xterm-88color.underline") = 1;
673 #endif
674 #ifdef CONFIG_256_COLORS
675 get_opt_int("terminal.xterm-256color.type") = 1;
676 get_opt_int("terminal.xterm-256color.colors") = COLOR_MODE_256;
677 get_opt_bool("terminal.xterm-256color.underline") = 1;
678 #endif
681 static struct option_info config_options_info[];
682 extern struct option_info cmdline_options_info[];
683 static struct change_hook_info change_hooks[];
685 void
686 init_options(void)
688 cmdline_options = add_opt_tree_tree(&options_root, "", "",
689 "cmdline", 0, "");
690 register_options(cmdline_options_info, cmdline_options);
692 config_options = add_opt_tree_tree(&options_root, "", "",
693 "config", OPT_SORT, "");
694 config_options->flags |= OPT_LISTBOX;
695 config_options->box_item = &option_browser.root;
696 register_options(config_options_info, config_options);
698 register_autocreated_options();
699 register_change_hooks(change_hooks);
702 static void
703 free_options_tree(struct list_head *tree, int recursive)
705 while (!list_empty(*tree))
706 delete_option_do(tree->next, recursive);
709 void
710 done_options(void)
712 unregister_options(config_options_info, config_options);
713 unregister_options(cmdline_options_info, cmdline_options);
714 config_options->box_item = NULL;
715 free_options_tree(&options_root_tree, 0);
718 void
719 register_change_hooks(struct change_hook_info *change_hooks)
721 int i;
723 for (i = 0; change_hooks[i].name; i++) {
724 struct option *option = get_opt_rec(config_options,
725 change_hooks[i].name);
727 assert(option);
728 option->change_hook = change_hooks[i].change_hook;
732 void
733 unmark_options_tree(struct list_head *tree)
735 struct option *option;
737 foreach (option, *tree) {
738 option->flags &= ~OPT_WATERMARK;
739 if (option->type == OPT_TREE)
740 unmark_options_tree(option->value.tree);
744 void
745 watermark_deleted_options(struct list_head *tree)
747 struct option *option;
749 foreach (option, *tree) {
750 if (option->flags & OPT_DELETED)
751 option->flags |= OPT_WATERMARK;
752 else if (option->type == OPT_TREE)
753 watermark_deleted_options(option->value.tree);
757 static int
758 check_nonempty_tree(struct list_head *options)
760 struct option *opt;
762 foreach (opt, *options) {
763 if (opt->type == OPT_TREE) {
764 if (check_nonempty_tree(opt->value.tree))
765 return 1;
766 } else if (!(opt->flags & OPT_WATERMARK)) {
767 return 1;
771 return 0;
774 void
775 smart_config_string(struct string *str, int print_comment, int i18n,
776 struct list_head *options, unsigned char *path, int depth,
777 void (*fn)(struct string *, struct option *,
778 unsigned char *, int, int, int, int))
780 struct option *option;
782 foreach (option, *options) {
783 int do_print_comment = 1;
785 if (option->flags & OPT_HIDDEN ||
786 option->flags & OPT_WATERMARK ||
787 option->type == OPT_ALIAS ||
788 !strcmp(option->name, "_template_"))
789 continue;
791 /* Is there anything to be printed anyway? */
792 if (option->type == OPT_TREE
793 && !check_nonempty_tree(option->value.tree))
794 continue;
796 /* We won't pop out the description when we're in autocreate
797 * category and not template. It'd be boring flood of
798 * repetitive comments otherwise ;). */
800 /* This print_comment parameter is weird. If it is negative, it
801 * means that we shouldn't print comments at all. If it is 1,
802 * we shouldn't print comment UNLESS the option is _template_
803 * or not-an-autocreating-tree (it is set for the first-level
804 * autocreation tree). When it is 2, we can print out comments
805 * normally. */
806 /* It is still broken somehow, as it didn't work for terminal.*
807 * (the first autocreated level) by the time I wrote this. Good
808 * summer job for bored mad hackers with spare boolean mental
809 * power. I have better things to think about, personally.
810 * Maybe we should just mark autocreated options somehow ;). */
811 if (!print_comment || (print_comment == 1
812 && (strcmp(option->name, "_template_")
813 && (option->flags & OPT_AUTOCREATE
814 && option->type == OPT_TREE))))
815 do_print_comment = 0;
817 /* Pop out the comment */
819 /* For config file, we ignore do_print_comment everywhere
820 * except 1, but sometimes we want to skip the option totally.
822 fn(str, option, path, depth,
823 option->type == OPT_TREE ? print_comment
824 : do_print_comment,
825 0, i18n);
827 fn(str, option, path, depth, do_print_comment, 1, i18n);
829 /* And the option itself */
831 if (option_types[option->type].write) {
832 fn(str, option, path, depth,
833 do_print_comment, 2, i18n);
835 } else if (option->type == OPT_TREE) {
836 struct string newpath;
837 int pc = print_comment;
839 if (!init_string(&newpath)) continue; /* OK? */
841 if (pc == 2 && option->flags & OPT_AUTOCREATE)
842 pc = 1;
843 else if (pc == 1 && strcmp(option->name, "_template_"))
844 pc = 0;
846 fn(str, option, path, depth, /*pc*/1, 3, i18n);
848 if (path) {
849 add_to_string(&newpath, path);
850 add_char_to_string(&newpath, '.');
852 add_to_string(&newpath, option->name);
853 smart_config_string(str, pc, i18n, option->value.tree,
854 newpath.source, depth + 1, fn);
855 done_string(&newpath);
857 fn(str, option, path, depth, /*pc*/1, 3, i18n);
860 /* TODO: We should maybe clear the touched flag only when really
861 * saving the stuff...? --pasky */
862 option->flags &= ~OPT_TOUCHED;
867 static int
868 change_hook_cache(struct session *ses, struct option *current, struct option *changed)
870 shrink_memory(0);
871 return 0;
874 static int
875 change_hook_connection(struct session *ses, struct option *current, struct option *changed)
877 register_check_queue();
878 return 0;
881 static int
882 change_hook_html(struct session *ses, struct option *current, struct option *changed)
884 foreach (ses, sessions) ses->tab->resize = 1;
886 return 0;
889 static int
890 change_hook_insert_mode(struct session *ses, struct option *current, struct option *changed)
892 update_status();
893 return 0;
896 static int
897 change_hook_active_link(struct session *ses, struct option *current, struct option *changed)
899 update_cached_document_options();
900 return 0;
903 static int
904 change_hook_terminal(struct session *ses, struct option *current, struct option *changed)
906 cls_redraw_all_terminals();
907 return 0;
910 static int
911 change_hook_ui(struct session *ses, struct option *current, struct option *changed)
913 update_status();
914 return 0;
917 /* Bit 2 of show means we should always set visibility, otherwise we set it
918 * only on templates. */
919 static void
920 update_visibility(struct list_head *tree, int show)
922 struct option *opt;
924 foreach (opt, *tree) {
925 if (opt->flags & OPT_DELETED) continue;
927 if (!strcmp(opt->name, "_template_")) {
928 if (opt->box_item)
929 opt->box_item->visible = (show & 1);
931 if (opt->type == OPT_TREE)
932 update_visibility(opt->value.tree, show | 2);
933 } else {
934 if (opt->box_item && (show & 2))
935 opt->box_item->visible = (show & 1);
937 if (opt->type == OPT_TREE)
938 update_visibility(opt->value.tree, show);
943 void
944 update_options_visibility(void)
946 update_visibility(config_options->value.tree,
947 get_opt_bool("config.show_template"));
950 void
951 toggle_option(struct session *ses, struct option *option)
953 long number = option->value.number + 1;
955 assert(option->type == OPT_BOOL || option->type == OPT_INT);
956 assert(option->max);
958 /* TODO: call change hooks. --jonas */
959 option->value.number = (number <= option->max) ? number : option->min;
960 option_changed(ses, option, option);
963 static int
964 change_hook_stemplate(struct session *ses, struct option *current, struct option *changed)
966 update_visibility(config_options->value.tree, changed->value.number);
967 return 0;
970 static int
971 change_hook_language(struct session *ses, struct option *current, struct option *changed)
973 #ifdef CONFIG_NLS
974 set_language(changed->value.number);
975 #endif
976 return 0;
979 static struct change_hook_info change_hooks[] = {
980 { "config.show_template", change_hook_stemplate },
981 { "connection", change_hook_connection },
982 { "document.browse", change_hook_html },
983 { "document.browse.forms.insert_mode",
984 change_hook_insert_mode },
985 { "document.browse.links.active_link",
986 change_hook_active_link },
987 { "document.cache", change_hook_cache },
988 { "document.codepage", change_hook_html },
989 { "document.colors", change_hook_html },
990 { "document.html", change_hook_html },
991 { "document.plain", change_hook_html },
992 { "terminal", change_hook_terminal },
993 { "ui.language", change_hook_language },
994 { "ui", change_hook_ui },
995 { NULL, NULL },
998 void
999 call_change_hooks(struct session *ses, struct option *current, struct option *option)
1001 /* This boolean thing can look a little weird - it
1002 * basically says that we should proceed when there's
1003 * no change_hook or there's one and its return value
1004 * was zero. */
1005 while (current && (!current->change_hook ||
1006 !current->change_hook(ses, current, option))) {
1007 if (!current->root)
1008 break;
1010 current = current->root;
1014 void
1015 option_changed(struct session *ses, struct option *current, struct option *option)
1017 option->flags |= OPT_TOUCHED;
1018 /* Notify everyone out there! */
1019 call_change_hooks(ses, current, option);
1023 commit_option_values(struct option_resolver *resolvers,
1024 struct option *root, union option_value *values, int size)
1026 int touched = 0;
1027 int i;
1029 assert(resolvers && root && values && size);
1031 for (i = 0; i < size; i++) {
1032 unsigned char *name = resolvers[i].name;
1033 struct option *option = get_opt_rec(root, name);
1034 int id = resolvers[i].id;
1036 if (memcmp(&option->value, &values[id], sizeof(union option_value))) {
1037 option->value = values[id];
1038 option->flags |= OPT_TOUCHED;
1039 /* Speed hack: Directly call the change-hook for each
1040 * option in resolvers and later call call_change_hooks
1041 * on the root; if we were to call call_change_hooks
1042 * on each option in resolvers, we would end up calling
1043 * the change-hooks of root, its parents, its
1044 * grandparents, and so on for each option in resolvers
1045 * because call_change_hooks is recursive. -- Miciah */
1046 if (option->change_hook)
1047 option->change_hook(NULL, option, NULL);
1048 touched++;
1052 /* See above 'Speed hack' comment. */
1053 call_change_hooks(NULL, root, NULL);
1055 return touched;
1058 void
1059 checkout_option_values(struct option_resolver *resolvers,
1060 struct option *root,
1061 union option_value *values, int size)
1063 int i;
1065 for (i = 0; i < size; i++) {
1066 unsigned char *name = resolvers[i].name;
1067 struct option *option = get_opt_rec(root, name);
1068 int id = resolvers[i].id;
1070 values[id] = option->value;
1074 /**********************************************************************
1075 Options values
1076 **********************************************************************/
1078 #include "config/options.inc"
1080 void
1081 register_options(struct option_info info[], struct option *tree)
1083 int i;
1085 for (i = 0; info[i].path; i++) {
1086 struct option *option = &info[i].option;
1087 unsigned char *string;
1089 debug_check_option_syntax(option);
1091 if (option->type != OPT_ALIAS
1092 && ((tree->flags & OPT_LISTBOX)
1093 || (option->flags & OPT_LISTBOX))) {
1094 option->box_item = init_option_listbox_item(option);
1095 if (!option->box_item) {
1096 delete_option(option);
1097 continue;
1101 switch (option->type) {
1102 case OPT_TREE:
1103 option->value.tree = init_options_tree();
1104 if (!option->value.tree) {
1105 delete_option(option);
1106 continue;
1108 break;
1109 case OPT_STRING:
1110 string = mem_alloc(MAX_STR_LEN);
1111 if (!string) {
1112 delete_option(option);
1113 continue;
1115 safe_strncpy(string, option->value.string, MAX_STR_LEN);
1116 option->value.string = string;
1117 break;
1118 case OPT_COLOR:
1119 string = option->value.string;
1120 assert(string);
1121 decode_color(string, strlen(string),
1122 &option->value.color);
1123 break;
1124 case OPT_CODEPAGE:
1125 string = option->value.string;
1126 assert(string);
1127 option->value.number = get_cp_index(string);
1128 break;
1129 case OPT_BOOL:
1130 case OPT_INT:
1131 case OPT_LONG:
1132 case OPT_LANGUAGE:
1133 case OPT_COMMAND:
1134 case OPT_ALIAS:
1135 break;
1138 add_opt_rec(tree, info[i].path, option);
1142 void
1143 unregister_options(struct option_info info[], struct option *tree)
1145 int i = 0;
1147 /* We need to remove the options in inverse order to the order how we
1148 * added them. */
1150 while (info[i].path) i++;
1152 for (i--; i >= 0; i--)
1153 delete_option_do(&info[i].option, 0);