1 /* Options variables manipulation core */
2 
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
6 
7 #include <ctype.h>
8 #include <string.h>
9 
10 #include "elinks.h"
11 
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"
35 
36 
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... */
49 
50 
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 */
54 
55 static INIT_LIST_HEAD(options_root_tree);
56 
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
65 );
66 
67 struct option *config_options;
68 struct option *cmdline_options;
69 
70 static void add_opt_rec(struct option *, unsigned char *, struct option *);
71 static void free_options_tree(struct list_head *, int recursive);
72 
73 #ifdef CONFIG_DEBUG
74 /* Detect ending '.' (and some others) in options captions.
75  * It will emit a message in debug mode only. --Zas */
76 
77 #define bad_punct(c) (c != ')' && c != '>' && !isquote(c) && ispunct(c))
78 
79 static void
check_caption(unsigned char * caption)80 check_caption(unsigned char *caption)
81 {
82 	int len;
83 	unsigned char c;
84 
85 	if (!caption) return;
86 
87 	len = strlen(caption);
88 	if (!len) return;
89 
90 	c = caption[len - 1];
91 	if (isspace(c) || bad_punct(c))
92 		DBG("bad char at end of caption [%s]", caption);
93 
94 #ifdef CONFIG_NLS
95 	caption = gettext(caption);
96 	len = strlen(caption);
97 	if (!len) return;
98 
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
103 }
104 
105 #undef bad_punct
106 
107 static void
check_description(unsigned char * desc)108 check_description(unsigned char *desc)
109 {
110 	int len;
111 	unsigned char c;
112 
113 	if (!desc) return;
114 
115 	len = strlen(desc);
116 	if (!len) return;
117 
118 	c = desc[len - 1];
119 	if (isspace(c))
120 		DBG("bad char at end of description [%s]", desc);
121 
122 #ifdef CONFIG_NLS
123 	desc = gettext(desc);
124 	len = strlen(desc);
125 	if (!len) return;
126 
127 	if (ispunct(c) != ispunct(desc[len - 1]))
128 		DBG("punctuation char possibly missing at end of i18n description [%s]", desc);
129 
130 	c = desc[len - 1];
131 	if (isspace(c))
132 		DBG("bad char at end of i18n description [%s]", desc);
133 #endif
134 }
135 
136 static void
debug_check_option_syntax(struct option * option)137 debug_check_option_syntax(struct option *option)
138 {
139 	if (!option) return;
140 	check_caption(option->capt);
141 	check_description(option->desc);
142 }
143 
144 #else
145 #define debug_check_option_syntax(option)
146 #endif
147 
148 
149 /**********************************************************************
150  Options interface
151 **********************************************************************/
152 
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. */
156 
157 /* Ugly kludge */
158 static int no_autocreate = 0;
159 
160 /** Get record of option of given name, or NULL if there's no such option.
161  *
162  * If the specified option is an ::OPT_ALIAS, this function returns the
163  * alias, rather than the option to which the alias refers.  It must
164  * work this way because the alias may have the ::OPT_ALIAS_NEGATE flag.
165  * Instead, if the caller tries to read or set the value of the alias,
166  * the functions associated with ::OPT_ALIAS will forward the operation
167  * to the underlying option.  However, see indirect_option().  */
168 struct option *
get_opt_rec(struct option * tree,unsigned char * name_)169 get_opt_rec(struct option *tree, unsigned char *name_)
170 {
171 	struct option *option;
172 	unsigned char *aname = stracpy(name_);
173 	unsigned char *name = aname;
174 	unsigned char *sep;
175 
176 	if (!aname) return NULL;
177 
178 	/* We iteratively call get_opt_rec() each for path_elements-1, getting
179 	 * appropriate tree for it and then resolving [path_elements]. */
180 	if ((sep = strrchr(name, '.'))) {
181 		*sep = '\0';
182 
183 		tree = get_opt_rec(tree, name);
184 		if (!tree || tree->type != OPT_TREE || tree->flags & OPT_HIDDEN) {
185 #if 0
186 			DBG("ERROR in get_opt_rec() crawl: %s (%d) -> %s",
187 			      name, tree ? tree->type : -1, sep + 1);
188 #endif
189 			mem_free(aname);
190 			return NULL;
191 		}
192 
193 		*sep = '.';
194 		name = sep + 1;
195 	}
196 
197 	foreach (option, *tree->value.tree) {
198 		if (option->name && !strcmp(option->name, name)) {
199 			mem_free(aname);
200 			return option;
201 		}
202 	}
203 
204 	if (tree && tree->flags & OPT_AUTOCREATE && !no_autocreate) {
205 		struct option *template = get_opt_rec(tree, "_template_");
206 
207 		assertm(template, "Requested %s should be autocreated but "
208 			"%.*s._template_ is missing!", name_, sep - name_,
209 			name_);
210 		if_assert_failed {
211 			mem_free(aname);
212 			return NULL;
213 		}
214 
215 		/* We will just create the option and return pointer to it
216 		 * automagically. And, we will create it by cloning _template_
217 		 * option. By having _template_ OPT_AUTOCREATE and _template_
218 		 * inside, you can have even multi-level autocreating. */
219 
220 		option = copy_option(template);
221 		if (!option) {
222 			mem_free(aname);
223 			return NULL;
224 		}
225 		mem_free_set(&option->name, stracpy(name));
226 
227 		add_opt_rec(tree, "", option);
228 
229 		mem_free(aname);
230 		return option;
231 	}
232 
233 	mem_free(aname);
234 	return NULL;
235 }
236 
237 /* Get record of option of given name, or NULL if there's no such option. But
238  * do not create the option if it doesn't exist and there's autocreation
239  * enabled. */
240 struct option *
get_opt_rec_real(struct option * tree,unsigned char * name)241 get_opt_rec_real(struct option *tree, unsigned char *name)
242 {
243 	struct option *opt;
244 
245 	no_autocreate = 1;
246 	opt = get_opt_rec(tree, name);
247 	no_autocreate = 0;
248 	return opt;
249 }
250 
251 /** If @a opt is an alias, return the option to which it refers.
252  *
253  * @warning Because the alias may have the ::OPT_ALIAS_NEGATE flag,
254  * the caller must not access the value of the returned option as if
255  * it were also the value of the alias.  However, it is safe to access
256  * flags such as ::OPT_MUST_SAVE and ::OPT_DELETED.  */
257 struct option *
indirect_option(struct option * alias)258 indirect_option(struct option *alias)
259 {
260 	struct option *real;
261 
262 	if (alias->type != OPT_ALIAS) return alias; /* not an error */
263 
264 	real = get_opt_rec(config_options, alias->value.string);
265 	assertm(real != NULL, "%s aliased to unknown option %s!",
266 		alias->name, alias->value.string);
267 	if_assert_failed return alias;
268 
269 	return real;
270 }
271 
272 /* Fetch pointer to value of certain option. It is guaranteed to never return
273  * NULL. Note that you are supposed to use wrapper get_opt(). */
274 union option_value *
get_opt_(unsigned char * file,int line,enum option_type option_type,struct option * tree,unsigned char * name)275 get_opt_(
276 #ifdef CONFIG_DEBUG
277 	 unsigned char *file, int line, enum option_type option_type,
278 #endif
279 	 struct option *tree, unsigned char *name)
280 {
281 	struct option *opt = get_opt_rec(tree, name);
282 
283 #ifdef CONFIG_DEBUG
284 	errfile = file;
285 	errline = line;
286 	if (!opt) elinks_internal("Attempted to fetch nonexisting option %s!", name);
287 
288 	/* Various sanity checks. */
289 	if (option_type != opt->type)
290 		DBG("get_opt_*(\"%s\") @ %s:%d: call with wrapper for %s for option of type %s",
291 		    name, file, line,
292 		    get_option_type_name(option_type),
293 		    get_option_type_name(opt->type));
294 
295 	switch (opt->type) {
296 	case OPT_TREE:
297 		if (!opt->value.tree)
298 			elinks_internal("Option %s has no value!", name);
299 		break;
300 	case OPT_ALIAS:
301 		elinks_internal("Invalid use of alias %s for option %s!",
302 				name, opt->value.string);
303 		break;
304 	case OPT_STRING:
305 		if (!opt->value.string)
306 			elinks_internal("Option %s has no value!", name);
307 		break;
308 	case OPT_BOOL:
309 	case OPT_INT:
310 		if (opt->value.number < opt->min
311 		    || opt->value.number > opt->max)
312 			elinks_internal("Option %s has invalid value %d!", name, opt->value.number);
313 		break;
314 	case OPT_LONG:
315 		if (opt->value.big_number < opt->min
316 		    || opt->value.big_number > opt->max)
317 			elinks_internal("Option %s has invalid value %ld!", name, opt->value.big_number);
318 		break;
319 	case OPT_COMMAND:
320 		if (!opt->value.command)
321 			elinks_internal("Option %s has no value!", name);
322 		break;
323 	case OPT_CODEPAGE: /* TODO: check these too. */
324 	case OPT_LANGUAGE:
325 	case OPT_COLOR:
326 		break;
327 	}
328 #endif
329 
330 	return &opt->value;
331 }
332 
333 static void
add_opt_sort(struct option * tree,struct option * option,int abi)334 add_opt_sort(struct option *tree, struct option *option, int abi)
335 {
336 	struct list_head *cat = tree->value.tree;
337 	struct list_head *bcat = &tree->box_item->child;
338 	struct option *pos;
339 
340 	/* The list is empty, just add it there. */
341 	if (list_empty(*cat)) {
342 		add_to_list(*cat, option);
343 		if (abi) add_to_list(*bcat, option->box_item);
344 
345 	/* This fits as the last list entry, add it there. This
346 	 * optimizes the most expensive BUT most common case ;-). */
347 	} else if ((option->type != OPT_TREE
348 		    || ((struct option *) cat->prev)->type == OPT_TREE)
349 		   && strcmp(((struct option *) cat->prev)->name,
350 			     option->name) <= 0) {
351 append:
352 		add_to_list_end(*cat, option);
353 		if (abi) add_to_list_end(*bcat, option->box_item);
354 
355 	/* At the end of the list is tree and we are ordinary. That's
356 	 * clear case then. */
357 	} else if (option->type != OPT_TREE
358 		   && ((struct option *) cat->prev)->type == OPT_TREE) {
359 		goto append;
360 
361 	/* Scan the list linearly. This could be probably optimized ie.
362 	 * to choose direction based on the first letter or so. */
363 	} else {
364 		struct listbox_item *bpos = (struct listbox_item *) bcat;
365 
366 		foreach (pos, *cat) {
367 			/* First move the box item to the current position but
368 			 * only if the position has not been marked as deleted
369 			 * and actually has a box_item -- else we will end up
370 			 * 'overflowing' and causing assertion failure. */
371 			if (!(pos->flags & OPT_DELETED) && pos->box_item) {
372 				bpos = bpos->next;
373 				assert(bpos != (struct listbox_item *) bcat);
374 			}
375 
376 			if ((option->type != OPT_TREE
377 			     || pos->type == OPT_TREE)
378 			    && strcmp(pos->name, option->name) <= 0)
379 				continue;
380 
381 			/* Ordinary options always sort behind trees. */
382 			if (option->type != OPT_TREE
383 			    && pos->type == OPT_TREE)
384 				continue;
385 
386 			/* The (struct option) add_at_pos() can mess up the
387 			 * order so that we add the box_item to itself, so
388 			 * better do it first. */
389 
390 			/* Always ensure that them _template_ options are
391 			 * before anything else so a lonely autocreated option
392 			 * (with _template_ options set to invisible) will be
393 			 * connected with an upper corner (ascii: `-) instead
394 			 * of a rotated T (ascii: +-) when displaying it. */
395 			if (option->type == pos->type
396 			    && *option->name <= '_'
397 			    && !strcmp(pos->name, "_template_")) {
398 				if (abi) add_at_pos(bpos, option->box_item);
399 				add_at_pos(pos, option);
400 				break;
401 			}
402 
403 			if (abi) add_at_pos(bpos->prev, option->box_item);
404 			add_at_pos(pos->prev, option);
405 			break;
406 		}
407 
408 		assert(pos != (struct option *) cat);
409 		assert(bpos != (struct listbox_item *) bcat);
410 	}
411 }
412 
413 /* Add option to tree. */
414 static void
add_opt_rec(struct option * tree,unsigned char * path,struct option * option)415 add_opt_rec(struct option *tree, unsigned char *path, struct option *option)
416 {
417 	int abi = 0;
418 
419 	assert(path && option && tree);
420 	if (*path) tree = get_opt_rec(tree, path);
421 
422 	assertm(tree, "Missing option tree for '%s'", path);
423 	if (!tree->value.tree) return;
424 
425 	object_nolock(option, "option");
426 
427 	if (option->box_item && option->name && !strcmp(option->name, "_template_"))
428 		option->box_item->visible = get_opt_bool("config.show_template");
429 
430 	if (tree->flags & OPT_AUTOCREATE && !option->desc) {
431 		struct option *template = get_opt_rec(tree, "_template_");
432 
433 		assert(template);
434 		option->desc = template->desc;
435 	}
436 
437 	option->root = tree;
438 
439 	abi = (tree->box_item && option->box_item);
440 
441 	if (abi) {
442 		/* The config_root tree is a just a placeholder for the
443 		 * box_items, it actually isn't a real box_item by itself;
444 		 * these ghosts are indicated by the fact that they have
445 		 * NULL @next. */
446 		if (tree->box_item->next) {
447 			option->box_item->depth = tree->box_item->depth + 1;
448 		}
449 	}
450 
451 	if (tree->flags & OPT_SORT) {
452 		add_opt_sort(tree, option, abi);
453 
454 	} else {
455 		add_to_list_end(*tree->value.tree, option);
456 		if (abi) add_to_list_end(tree->box_item->child, option->box_item);
457 	}
458 
459 	update_hierbox_browser(&option_browser);
460 }
461 
462 static inline struct listbox_item *
init_option_listbox_item(struct option * option)463 init_option_listbox_item(struct option *option)
464 {
465 	struct listbox_item *item = mem_calloc(1, sizeof(*item));
466 
467 	if (!item) return NULL;
468 
469 	init_list(item->child);
470 	item->visible = 1;
471 	item->udata = option;
472 	item->type = (option->type == OPT_TREE) ? BI_FOLDER : BI_LEAF;
473 
474 	return item;
475 }
476 
477 struct option *
add_opt(struct option * tree,unsigned char * path,unsigned char * capt,unsigned char * name,enum option_flags flags,enum option_type type,long min,long max,longptr_T value,unsigned char * desc)478 add_opt(struct option *tree, unsigned char *path, unsigned char *capt,
479 	unsigned char *name, enum option_flags flags, enum option_type type,
480 	long min, long max, longptr_T value, unsigned char *desc)
481 {
482 	struct option *option = mem_calloc(1, sizeof(*option));
483 
484 	if (!option) return NULL;
485 
486 	option->name = stracpy(name);
487 	if (!option->name) {
488 		mem_free(option);
489 		return NULL;
490 	}
491 	option->flags = (flags | OPT_ALLOC);
492 	option->type = type;
493 	option->min = min;
494 	option->max = max;
495 	option->capt = capt;
496 	option->desc = desc;
497 
498 	debug_check_option_syntax(option);
499 
500 	/* XXX: For allocated values we allocate in the add_opt_<type>() macro.
501 	 * This involves OPT_TREE and OPT_STRING. */
502 	switch (type) {
503 		case OPT_TREE:
504 			if (!value) {
505 				mem_free(option);
506 				return NULL;
507 			}
508 			option->value.tree = (struct list_head *) value;
509 			break;
510 		case OPT_STRING:
511 			if (!value) {
512 				mem_free(option);
513 				return NULL;
514 			}
515 			option->value.string = (unsigned char *) value;
516 			break;
517 		case OPT_ALIAS:
518 			option->value.string = (unsigned char *) value;
519 			break;
520 		case OPT_BOOL:
521 		case OPT_INT:
522 		case OPT_CODEPAGE:
523 			option->value.number = (int) value;
524 			break;
525 		case OPT_LONG:
526 			option->value.big_number = (long) value; /* FIXME: cast from void * */
527 			break;
528 		case OPT_COLOR:
529 			decode_color((unsigned char *) value, strlen((unsigned char *) value),
530 					&option->value.color);
531 			break;
532 		case OPT_COMMAND:
533 			option->value.command = (void *) value;
534 			break;
535 		case OPT_LANGUAGE:
536 			break;
537 	}
538 
539 	if (option->type != OPT_ALIAS
540 	    && ((tree->flags & OPT_LISTBOX) || (option->flags & OPT_LISTBOX))) {
541 		option->box_item = init_option_listbox_item(option);
542 		if (!option->box_item) {
543 			mem_free(option);
544 			return NULL;
545 		}
546 	}
547 
548 	add_opt_rec(tree, path, option);
549 	return option;
550 }
551 
552 static void
done_option(struct option * option)553 done_option(struct option *option)
554 {
555 	switch (option->type) {
556 		case OPT_STRING:
557 			mem_free_if(option->value.string);
558 			break;
559 		case OPT_TREE:
560 			mem_free_if(option->value.tree);
561 			break;
562 		default:
563 			break;
564 	}
565 
566 	if (option->box_item)
567 		done_listbox_item(&option_browser, option->box_item);
568 
569 	if (option->flags & OPT_ALLOC) {
570 		mem_free_if(option->name);
571 		mem_free(option);
572 	} else if (!option->capt) {
573 		/* We are probably dealing with a built-in autocreated option
574 		 * that will be attempted to be deleted when shutting down. */
575 		/* Clear it so nothing will be done later. */
576 		memset(option, 0, sizeof(*option));
577 	}
578 }
579 
580 /* The namespace may start to seem a bit chaotic here; it indeed is, maybe the
581  * function names above should be renamed and only macros should keep their old
582  * short names. */
583 /* The simple rule I took as an apologize is that functions which take already
584  * completely filled (struct option *) have long name and functions which take
585  * only option specs have short name. */
586 
587 static void
delete_option_do(struct option * option,int recursive)588 delete_option_do(struct option *option, int recursive)
589 {
590 	if (option->next) {
591 		del_from_list(option);
592 		option->prev = option->next = NULL;
593 	}
594 
595 	if (recursive == -1) {
596 		ERROR("Orphaned option %s", option->name);
597 	}
598 
599 	if (option->type == OPT_TREE && option->value.tree
600 	    && !list_empty(*option->value.tree)) {
601 		if (!recursive) {
602 			if (option->flags & OPT_AUTOCREATE) {
603 				recursive = 1;
604 			} else {
605 				ERROR("Orphaned unregistered "
606 					"option in subtree %s!",
607 					option->name);
608 				recursive = -1;
609 			}
610 		}
611 		free_options_tree(option->value.tree, recursive);
612 	}
613 
614 	done_option(option);
615 }
616 
617 void
mark_option_as_deleted(struct option * option)618 mark_option_as_deleted(struct option *option)
619 {
620 	if (option->type == OPT_TREE) {
621 		struct option *unmarked;
622 
623 		assert(option->value.tree);
624 
625 		foreach (unmarked, *option->value.tree)
626 			mark_option_as_deleted(unmarked);
627 	}
628 
629 	option->box_item->visible = 0;
630 
631 	option->flags |= (OPT_TOUCHED | OPT_DELETED);
632 }
633 
634 void
delete_option(struct option * option)635 delete_option(struct option *option)
636 {
637 	delete_option_do(option, 1);
638 }
639 
640 struct option *
copy_option(struct option * template)641 copy_option(struct option *template)
642 {
643 	struct option *option = mem_calloc(1, sizeof(*option));
644 
645 	if (!option) return NULL;
646 
647 	option->name = null_or_stracpy(template->name);
648 	option->flags = (template->flags | OPT_ALLOC);
649 	option->type = template->type;
650 	option->min = template->min;
651 	option->max = template->max;
652 	option->capt = template->capt;
653 	option->desc = template->desc;
654 	option->change_hook = template->change_hook;
655 
656 	option->box_item = init_option_listbox_item(option);
657 	if (option->box_item) {
658 		if (template->box_item) {
659 			option->box_item->type = template->box_item->type;
660 			option->box_item->depth = template->box_item->depth;
661 		}
662 	}
663 
664 	if (option_types[template->type].dup) {
665 		option_types[template->type].dup(option, template);
666 	} else {
667 		option->value = template->value;
668 	}
669 
670 	return option;
671 }
672 
673 struct list_head *
init_options_tree(void)674 init_options_tree(void)
675 {
676 	struct list_head *ptr = mem_alloc(sizeof(*ptr));
677 
678 	if (ptr) init_list(*ptr);
679 	return ptr;
680 }
681 
682 /* Some default pre-autocreated options. Doh. */
683 static inline void
register_autocreated_options(void)684 register_autocreated_options(void)
685 {
686 	/* TODO: Use table-driven initialization. --jonas */
687 	get_opt_int("terminal.linux.type") = 2;
688 	get_opt_int("terminal.linux.colors") = 1;
689 	get_opt_bool("terminal.linux.m11_hack") = 1;
690 	get_opt_int("terminal.vt100.type") = 1;
691 	get_opt_int("terminal.vt110.type") = 1;
692 	get_opt_int("terminal.xterm.type") = 1;
693 	get_opt_bool("terminal.xterm.underline") = 1;
694 	get_opt_int("terminal.xterm-color.type") = 1;
695 	get_opt_int("terminal.xterm-color.colors") = COLOR_MODE_16;
696 	get_opt_bool("terminal.xterm-color.underline") = 1;
697 #ifdef CONFIG_88_COLORS
698 	get_opt_int("terminal.xterm-88color.type") = 1;
699 	get_opt_int("terminal.xterm-88color.colors") = COLOR_MODE_88;
700 	get_opt_bool("terminal.xterm-88color.underline") = 1;
701 #endif
702 #ifdef CONFIG_256_COLORS
703 	get_opt_int("terminal.xterm-256color.type") = 1;
704 	get_opt_int("terminal.xterm-256color.colors") = COLOR_MODE_256;
705 	get_opt_bool("terminal.xterm-256color.underline") = 1;
706 #endif
707 }
708 
709 static struct option_info config_options_info[];
710 extern struct option_info cmdline_options_info[];
711 static struct change_hook_info change_hooks[];
712 
713 void
init_options(void)714 init_options(void)
715 {
716 	/* The following assignment is needed if void * and struct
717 	 * list_head * have different representations.  Otherwise,
718 	 * the compiler should be able to optimize it out.  */
719 	options_root.value.tree = options_root.value.compile_time_init;
720 
721 	cmdline_options = add_opt_tree_tree(&options_root, "", "",
722 					    "cmdline", 0, "");
723 	register_options(cmdline_options_info, cmdline_options);
724 
725 	config_options = add_opt_tree_tree(&options_root, "", "",
726 					 "config", OPT_SORT, "");
727 	config_options->flags |= OPT_LISTBOX;
728 	config_options->box_item = &option_browser.root;
729 	register_options(config_options_info, config_options);
730 
731 	register_autocreated_options();
732 	register_change_hooks(change_hooks);
733 }
734 
735 static void
free_options_tree(struct list_head * tree,int recursive)736 free_options_tree(struct list_head *tree, int recursive)
737 {
738 	while (!list_empty(*tree))
739 		delete_option_do(tree->next, recursive);
740 }
741 
742 void
done_options(void)743 done_options(void)
744 {
745 	unregister_options(config_options_info, config_options);
746 	unregister_options(cmdline_options_info, cmdline_options);
747 	config_options->box_item = NULL;
748 	free_options_tree(&options_root_tree, 0);
749 }
750 
751 void
register_change_hooks(struct change_hook_info * change_hooks)752 register_change_hooks(struct change_hook_info *change_hooks)
753 {
754 	int i;
755 
756 	for (i = 0; change_hooks[i].name; i++) {
757 		struct option *option = get_opt_rec(config_options,
758 						    change_hooks[i].name);
759 
760 		assert(option);
761 		option->change_hook = change_hooks[i].change_hook;
762 	}
763 }
764 
765 void
prepare_mustsave_flags(struct list_head * tree,int set_all)766 prepare_mustsave_flags(struct list_head *tree, int set_all)
767 {
768 	struct option *option;
769 
770 	foreach (option, *tree) {
771 		/* XXX: OPT_LANGUAGE shouldn't have any bussiness
772 		 * here, but we're just weird in that area. */
773 		if (set_all
774 		    || (option->flags & (OPT_TOUCHED | OPT_DELETED))
775 		    || option->type == OPT_LANGUAGE)
776 			option->flags |= OPT_MUST_SAVE;
777 		else
778 			option->flags &= ~OPT_MUST_SAVE;
779 
780 		if (option->type == OPT_TREE)
781 			prepare_mustsave_flags(option->value.tree, set_all);
782 	}
783 }
784 
785 void
untouch_options(struct list_head * tree)786 untouch_options(struct list_head *tree)
787 {
788 	struct option *option;
789 
790 	foreach (option, *tree) {
791 		option->flags &= ~OPT_TOUCHED;
792 
793 		if (option->type == OPT_TREE)
794 			untouch_options(option->value.tree);
795 	}
796 }
797 
798 static int
check_nonempty_tree(struct list_head * options)799 check_nonempty_tree(struct list_head *options)
800 {
801 	struct option *opt;
802 
803 	foreach (opt, *options) {
804 		if (opt->type == OPT_TREE) {
805 			if (check_nonempty_tree(opt->value.tree))
806 				return 1;
807 		} else if (opt->flags & OPT_MUST_SAVE) {
808 			return 1;
809 		}
810 	}
811 
812 	return 0;
813 }
814 
815 void
smart_config_string(struct string * str,int print_comment,int i18n,struct list_head * options,unsigned char * path,int depth,void (* fn)(struct string *,struct option *,unsigned char *,int,int,int,int))816 smart_config_string(struct string *str, int print_comment, int i18n,
817 		    struct list_head *options, unsigned char *path, int depth,
818 		    void (*fn)(struct string *, struct option *,
819 			       unsigned char *, int, int, int, int))
820 {
821 	struct option *option;
822 
823 	foreach (option, *options) {
824 		int do_print_comment = 1;
825 
826 		if (option->flags & OPT_HIDDEN ||
827 		    option->type == OPT_ALIAS ||
828 		    !strcmp(option->name, "_template_"))
829 			continue;
830 
831 		/* Is there anything to be printed anyway? */
832 		if (option->type == OPT_TREE
833 		    ? !check_nonempty_tree(option->value.tree)
834 		    : !(option->flags & OPT_MUST_SAVE))
835 			continue;
836 
837 		/* We won't pop out the description when we're in autocreate
838 		 * category and not template. It'd be boring flood of
839 		 * repetitive comments otherwise ;). */
840 
841 		/* This print_comment parameter is weird. If it is negative, it
842 		 * means that we shouldn't print comments at all. If it is 1,
843 		 * we shouldn't print comment UNLESS the option is _template_
844 		 * or not-an-autocreating-tree (it is set for the first-level
845 		 * autocreation tree). When it is 2, we can print out comments
846 		 * normally. */
847 		/* It is still broken somehow, as it didn't work for terminal.*
848 		 * (the first autocreated level) by the time I wrote this. Good
849 		 * summer job for bored mad hackers with spare boolean mental
850 		 * power. I have better things to think about, personally.
851 		 * Maybe we should just mark autocreated options somehow ;). */
852 		if (!print_comment || (print_comment == 1
853 					&& (strcmp(option->name, "_template_")
854 					    && (option->flags & OPT_AUTOCREATE
855 					        && option->type == OPT_TREE))))
856 			do_print_comment = 0;
857 
858 		/* Pop out the comment */
859 
860 		/* For config file, we ignore do_print_comment everywhere
861 		 * except 1, but sometimes we want to skip the option totally.
862 		 */
863 		fn(str, option, path, depth,
864 		   option->type == OPT_TREE ? print_comment
865 					    : do_print_comment,
866 		   0, i18n);
867 
868 		fn(str, option, path, depth, do_print_comment, 1, i18n);
869 
870 		/* And the option itself */
871 
872 		if (option_types[option->type].write) {
873 			fn(str, option, path, depth,
874 			   do_print_comment, 2, i18n);
875 
876 		} else if (option->type == OPT_TREE) {
877 			struct string newpath;
878 			int pc = print_comment;
879 
880 			if (!init_string(&newpath)) continue; /* OK? */
881 
882 			if (pc == 2 && option->flags & OPT_AUTOCREATE)
883 				pc = 1;
884 			else if (pc == 1 && strcmp(option->name, "_template_"))
885 				pc = 0;
886 
887 			fn(str, option, path, depth, /*pc*/1, 3, i18n);
888 
889 			if (path) {
890 				add_to_string(&newpath, path);
891 				add_char_to_string(&newpath, '.');
892 			}
893 			add_to_string(&newpath, option->name);
894 			smart_config_string(str, pc, i18n, option->value.tree,
895 					    newpath.source, depth + 1, fn);
896 			done_string(&newpath);
897 
898 			fn(str, option, path, depth, /*pc*/1, 3, i18n);
899 		}
900 	}
901 }
902 
903 
904 static int
change_hook_cache(struct session * ses,struct option * current,struct option * changed)905 change_hook_cache(struct session *ses, struct option *current, struct option *changed)
906 {
907 	shrink_memory(0);
908 	return 0;
909 }
910 
911 static int
change_hook_connection(struct session * ses,struct option * current,struct option * changed)912 change_hook_connection(struct session *ses, struct option *current, struct option *changed)
913 {
914 	register_check_queue();
915 	return 0;
916 }
917 
918 static int
change_hook_html(struct session * ses,struct option * current,struct option * changed)919 change_hook_html(struct session *ses, struct option *current, struct option *changed)
920 {
921 	foreach (ses, sessions) ses->tab->resize = 1;
922 
923 	return 0;
924 }
925 
926 static int
change_hook_insert_mode(struct session * ses,struct option * current,struct option * changed)927 change_hook_insert_mode(struct session *ses, struct option *current, struct option *changed)
928 {
929 	update_status();
930 	return 0;
931 }
932 
933 static int
change_hook_active_link(struct session * ses,struct option * current,struct option * changed)934 change_hook_active_link(struct session *ses, struct option *current, struct option *changed)
935 {
936 	update_cached_document_options();
937 	return 0;
938 }
939 
940 static int
change_hook_terminal(struct session * ses,struct option * current,struct option * changed)941 change_hook_terminal(struct session *ses, struct option *current, struct option *changed)
942 {
943 	cls_redraw_all_terminals();
944 	return 0;
945 }
946 
947 static int
change_hook_ui(struct session * ses,struct option * current,struct option * changed)948 change_hook_ui(struct session *ses, struct option *current, struct option *changed)
949 {
950 	update_status();
951 	return 0;
952 }
953 
954 /* Bit 2 of show means we should always set visibility, otherwise we set it
955  * only on templates. */
956 static void
update_visibility(struct list_head * tree,int show)957 update_visibility(struct list_head *tree, int show)
958 {
959 	struct option *opt;
960 
961 	foreach (opt, *tree) {
962 		if (opt->flags & OPT_DELETED) continue;
963 
964 		if (!strcmp(opt->name, "_template_")) {
965 			if (opt->box_item)
966 				opt->box_item->visible = (show & 1);
967 
968 			if (opt->type == OPT_TREE)
969 				update_visibility(opt->value.tree, show | 2);
970 		} else {
971 			if (opt->box_item && (show & 2))
972 				opt->box_item->visible = (show & 1);
973 
974 			if (opt->type == OPT_TREE)
975 				update_visibility(opt->value.tree, show);
976 		}
977 	}
978 }
979 
980 void
update_options_visibility(void)981 update_options_visibility(void)
982 {
983 	update_visibility(config_options->value.tree,
984 			  get_opt_bool("config.show_template"));
985 }
986 
987 void
toggle_option(struct session * ses,struct option * option)988 toggle_option(struct session *ses, struct option *option)
989 {
990 	long number = option->value.number + 1;
991 
992 	assert(option->type == OPT_BOOL || option->type == OPT_INT);
993 	assert(option->max);
994 
995 	/* TODO: call change hooks. --jonas */
996 	option->value.number = (number <= option->max) ? number : option->min;
997 	option_changed(ses, option, option);
998 }
999 
1000 static int
change_hook_stemplate(struct session * ses,struct option * current,struct option * changed)1001 change_hook_stemplate(struct session *ses, struct option *current, struct option *changed)
1002 {
1003 	update_visibility(config_options->value.tree, changed->value.number);
1004 	return 0;
1005 }
1006 
1007 static int
change_hook_language(struct session * ses,struct option * current,struct option * changed)1008 change_hook_language(struct session *ses, struct option *current, struct option *changed)
1009 {
1010 #ifdef CONFIG_NLS
1011 	set_language(changed->value.number);
1012 #endif
1013 	return 0;
1014 }
1015 
1016 static struct change_hook_info change_hooks[] = {
1017 	{ "config.show_template",	change_hook_stemplate },
1018 	{ "connection",			change_hook_connection },
1019 	{ "document.browse",		change_hook_html },
1020 	{ "document.browse.forms.insert_mode",
1021 					change_hook_insert_mode },
1022 	{ "document.browse.links.active_link",
1023 					change_hook_active_link },
1024 	{ "document.cache",		change_hook_cache },
1025 	{ "document.codepage",		change_hook_html },
1026 	{ "document.colors",		change_hook_html },
1027 	{ "document.html",		change_hook_html },
1028 	{ "document.plain",		change_hook_html },
1029 	{ "terminal",			change_hook_terminal },
1030 	{ "ui.language",		change_hook_language },
1031 	{ "ui",				change_hook_ui },
1032 	{ NULL,				NULL },
1033 };
1034 
1035 void
call_change_hooks(struct session * ses,struct option * current,struct option * option)1036 call_change_hooks(struct session *ses, struct option *current, struct option *option)
1037 {
1038 	/* This boolean thing can look a little weird - it
1039 	 * basically says that we should proceed when there's
1040 	 * no change_hook or there's one and its return value
1041 	 * was zero. */
1042 	while (current && (!current->change_hook ||
1043 		!current->change_hook(ses, current, option))) {
1044 		if (!current->root)
1045 			break;
1046 
1047 		current = current->root;
1048 	}
1049 }
1050 
1051 void
option_changed(struct session * ses,struct option * current,struct option * option)1052 option_changed(struct session *ses, struct option *current, struct option *option)
1053 {
1054 	option->flags |= OPT_TOUCHED;
1055 	/* Notify everyone out there! */
1056 	call_change_hooks(ses, current, option);
1057 }
1058 
1059 int
commit_option_values(struct option_resolver * resolvers,struct option * root,union option_value * values,int size)1060 commit_option_values(struct option_resolver *resolvers,
1061 		     struct option *root, union option_value *values, int size)
1062 {
1063 	int touched = 0;
1064 	int i;
1065 
1066 	assert(resolvers && root && values && size);
1067 
1068 	for (i = 0; i < size; i++) {
1069 		unsigned char *name = resolvers[i].name;
1070 		struct option *option = get_opt_rec(root, name);
1071 		int id = resolvers[i].id;
1072 
1073 		if (memcmp(&option->value, &values[id], sizeof(union option_value))) {
1074 			option->value = values[id];
1075 			option->flags |= OPT_TOUCHED;
1076 			/* Speed hack: Directly call the change-hook for each
1077 			 * option in resolvers and later call call_change_hooks
1078 			 * on the root; if we were to call call_change_hooks
1079 			 * on each option in resolvers, we would end up calling
1080 			 * the change-hooks of root, its parents, its
1081 			 * grandparents, and so on for each option in resolvers
1082 			 * because call_change_hooks is recursive. -- Miciah */
1083 			if (option->change_hook)
1084 				option->change_hook(NULL, option, NULL);
1085 			touched++;
1086 		}
1087 	}
1088 
1089 	/* See above 'Speed hack' comment. */
1090 	call_change_hooks(NULL, root, NULL);
1091 
1092 	return touched;
1093 }
1094 
1095 void
checkout_option_values(struct option_resolver * resolvers,struct option * root,union option_value * values,int size)1096 checkout_option_values(struct option_resolver *resolvers,
1097 		       struct option *root,
1098 		       union option_value *values, int size)
1099 {
1100 	int i;
1101 
1102 	for (i = 0; i < size; i++) {
1103 		unsigned char *name = resolvers[i].name;
1104 		struct option *option = get_opt_rec(root, name);
1105 		int id = resolvers[i].id;
1106 
1107 		values[id] = option->value;
1108 	}
1109 }
1110 
1111 /**********************************************************************
1112  Options values
1113 **********************************************************************/
1114 
1115 #include "config/options.inc"
1116 
1117 void
register_options(struct option_info info[],struct option * tree)1118 register_options(struct option_info info[], struct option *tree)
1119 {
1120 	int i;
1121 
1122 	for (i = 0; info[i].path; i++) {
1123 		struct option *option = &info[i].option;
1124 		unsigned char *string;
1125 
1126 		debug_check_option_syntax(option);
1127 
1128 		if (option->type != OPT_ALIAS
1129 		    && ((tree->flags & OPT_LISTBOX)
1130 			|| (option->flags & OPT_LISTBOX))) {
1131 			option->box_item = init_option_listbox_item(option);
1132 			if (!option->box_item) {
1133 				delete_option(option);
1134 				continue;
1135 			}
1136 		}
1137 
1138 		switch (option->type) {
1139 			case OPT_TREE:
1140 				option->value.tree = init_options_tree();
1141 				if (!option->value.tree) {
1142 					delete_option(option);
1143 					continue;
1144 				}
1145 				break;
1146 			case OPT_STRING:
1147 				string = mem_alloc(MAX_STR_LEN);
1148 				if (!string) {
1149 					delete_option(option);
1150 					continue;
1151 				}
1152 				safe_strncpy(string,
1153 					     option->value.compile_time_init,
1154 					     MAX_STR_LEN);
1155 				option->value.string = string;
1156 				break;
1157 			case OPT_COLOR:
1158 				string = option->value.compile_time_init;
1159 				assert(string);
1160 				decode_color(string, strlen(string),
1161 						&option->value.color);
1162 				break;
1163 			case OPT_CODEPAGE:
1164 				string = option->value.compile_time_init;
1165 				assert(string);
1166 				option->value.number = get_cp_index(string);
1167 				break;
1168 			case OPT_BOOL:
1169 			case OPT_INT:
1170 			case OPT_LANGUAGE:
1171 				/* Some compilers warn if we cast a
1172 				 * pointer directly to an integer with
1173 				 * a different size.  */
1174 				option->value.number = (int) (longptr_T)
1175 					option->value.compile_time_init;
1176 				break;
1177 			case OPT_LONG:
1178 				option->value.big_number = (long) (longptr_T)
1179 					option->value.compile_time_init;
1180 				break;
1181 			case OPT_COMMAND:
1182 				option->value.command =
1183 					option->value.compile_time_init;
1184 				break;
1185 			case OPT_ALIAS:
1186 				option->value.string =
1187 					option->value.compile_time_init;
1188 				break;
1189 		}
1190 
1191 		add_opt_rec(tree, info[i].path, option);
1192 	}
1193 }
1194 
1195 void
unregister_options(struct option_info info[],struct option * tree)1196 unregister_options(struct option_info info[], struct option *tree)
1197 {
1198 	int i = 0;
1199 
1200 	/* We need to remove the options in inverse order to the order how we
1201 	 * added them. */
1202 
1203 	while (info[i].path) i++;
1204 
1205 	for (i--; i >= 0; i--)
1206 		delete_option_do(&info[i].option, 0);
1207 }
1208