⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 options.c

📁 一个很有名的浏览器
💻 C
📖 第 1 页 / 共 2 页
字号:
/* Options variables manipulation core *//* $Id: options.c,v 1.468.2.4 2005/04/05 21:08:40 jonas Exp $ */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <ctype.h>#include <string.h>#include "elinks.h"#include "bfu/dialog.h"#include "cache/cache.h"#include "config/conf.h"#include "config/dialogs.h"#include "config/options.h"#include "config/opttypes.h"#include "dialogs/status.h"#include "document/options.h"#include "globhist/globhist.h"#include "intl/charsets.h"#include "intl/gettext/libintl.h"#include "lowlevel/select.h"#include "main.h" /* shrink_memory() */#include "sched/session.h"#include "terminal/color.h"#include "terminal/screen.h"#include "terminal/terminal.h"#include "util/color.h"#include "util/error.h"#include "util/memory.h"#include "util/string.h"#include "viewer/text/draw.h"/* TODO? In the past, covered by shadow and legends, remembered only by the * ELinks Elders now, options were in hashes (it was not for a long time, after * we started to use dynamic options lists and before we really started to use * hierarchic options). Hashes might be swift and deft, but they had a flaw and * the flaw showed up as the fatal flaw. They were unsorted, and it was * unfriendly to mere mortal users, without pasky's options handlers in their * brain, but their own poor-written software. And thus pasky went and rewrote * options so that they were in lists from then to now and for all the ages of * men, to the glory of mankind. However, one true hero may arise in future * fabulous and implement possibility to have both lists and hashes for trees, * as it may be useful for some supernatural entities. And when that age will * come... *//* TODO: We should remove special case for root options and use some auxiliary * (struct option *) instead. This applies to bookmarks, global history and * listbox items as well, though. --pasky */static INIT_LIST_HEAD(options_root_tree);static struct option options_root = INIT_OPTION(	/* name: */	"",	/* flags: */	0,	/* type: */	OPT_TREE,	/* min, max: */	0, 0,	/* value: */	&options_root_tree,	/* desc: */	"",	/* capt: */	NULL);struct option *config_options;struct option *cmdline_options;static void add_opt_rec(struct option *, unsigned char *, struct option *);static void free_options_tree(struct list_head *, int recursive);#ifdef CONFIG_DEBUG/* Detect ending '.' (and some others) in options captions. * It will emit a message in debug mode only. --Zas */#define bad_punct(c) (c != ')' && !isquote(c) && ispunct(c))static voidcheck_caption(unsigned char *caption){	int len;	unsigned char c;	if (!caption) return;	len = strlen(caption);	if (!len) return;	c = caption[len - 1];	if (isspace(c) || bad_punct(c))		DBG("bad char at end of caption [%s]", caption);#ifdef ENABLE_NLS	caption = gettext(caption);	len = strlen(caption);	if (!len) return;	c = caption[len - 1];	if (isspace(c) || bad_punct(c))		DBG("bad char at end of i18n caption [%s]", caption);#endif}#undef bad_punctstatic voidcheck_description(unsigned char *desc){	int len;	unsigned char c;	if (!desc) return;	len = strlen(desc);	if (!len) return;	c = desc[len - 1];	if (isspace(c))		DBG("bad char at end of description [%s]", desc);#ifdef ENABLE_NLS	desc = gettext(desc);	len = strlen(desc);	if (!len) return;	if (ispunct(c) != ispunct(desc[len - 1]))		DBG("punctuation char possibly missing at end of i18n description [%s]", desc);	c = desc[len - 1];	if (isspace(c))		DBG("bad char at end of i18n description [%s]", desc);#endif}static voiddebug_check_option_syntax(struct option *option){	if (!option) return;	check_caption(option->capt);	check_description(option->desc);}#else#define debug_check_option_syntax(option)#endif/********************************************************************** Options interface**********************************************************************//* If option name contains dots, they are created as "categories" - first, * first category is retrieved from list, taken as a list, second category * is retrieved etc. *//* Ugly kludge */static int no_autocreate = 0;/* Get record of option of given name, or NULL if there's no such option. */struct option *get_opt_rec(struct option *tree, unsigned char *name_){	struct option *option;	unsigned char *aname = stracpy(name_);	unsigned char *name = aname;	unsigned char *sep;	if (!aname) return NULL;	/* We iteratively call get_opt_rec() each for path_elements-1, getting	 * appropriate tree for it and then resolving [path_elements]. */	if ((sep = strrchr(name, '.'))) {		*sep = '\0';		tree = get_opt_rec(tree, name);		if (!tree || tree->type != OPT_TREE || tree->flags & OPT_HIDDEN) {#if 0			DBG("ERROR in get_opt_rec() crawl: %s (%d) -> %s",			      name, tree ? tree->type : -1, sep + 1);#endif			mem_free(aname);			return NULL;		}		*sep = '.';		name = sep + 1;	}	foreach (option, *tree->value.tree) {		if (option->name && !strcmp(option->name, name)) {			mem_free(aname);			return option;		}	}	if (tree && tree->flags & OPT_AUTOCREATE && !no_autocreate) {		struct option *template = get_opt_rec(tree, "_template_");		assertm(template, "Requested %s should be autocreated but "			"%.*s._template_ is missing!", name_, sep - name_,			name_);		if_assert_failed {			mem_free(aname);			return NULL;		}		/* We will just create the option and return pointer to it		 * automagically. And, we will create it by cloning _template_		 * option. By having _template_ OPT_AUTOCREATE and _template_		 * inside, you can have even multi-level autocreating. */		option = copy_option(template);		if (!option) {			mem_free(aname);			return NULL;		}		mem_free_set(&option->name, stracpy(name));		add_opt_rec(tree, "", option);		mem_free(aname);		return option;	}	mem_free(aname);	return NULL;}/* Get record of option of given name, or NULL if there's no such option. But * do not create the option if it doesn't exist and there's autocreation * enabled. */struct option *get_opt_rec_real(struct option *tree, unsigned char *name){	struct option *opt;	no_autocreate = 1;	opt = get_opt_rec(tree, name);	no_autocreate = 0;	return opt;}/* Fetch pointer to value of certain option. It is guaranteed to never return * NULL. Note that you are supposed to use wrapper get_opt(). */union option_value *get_opt_(#ifdef CONFIG_DEBUG	 unsigned char *file, int line, enum option_type option_type,#endif	 struct option *tree, unsigned char *name){	struct option *opt = get_opt_rec(tree, name);#ifdef CONFIG_DEBUG	errfile = file;	errline = line;	if (!opt) elinks_internal("Attempted to fetch nonexisting option %s!", name);	/* Various sanity checks. */	if (option_type != opt->type)		DBG("get_opt_*(\"%s\") @ %s:%d: call with wrapper for %s for option of type %s",		    name, file, line,		    get_option_type_name(option_type),		    get_option_type_name(opt->type));	switch (opt->type) {	case OPT_TREE:		if (!opt->value.tree)			elinks_internal("Option %s has no value!", name);		break;	case OPT_ALIAS:		elinks_internal("Invalid use of alias %s for option %s!",				name, opt->value.string);		break;	case OPT_STRING:		if (!opt->value.string)			elinks_internal("Option %s has no value!", name);		break;	case OPT_BOOL:	case OPT_INT:	case OPT_LONG:		if (opt->value.number < opt->min		    || opt->value.number > opt->max)			elinks_internal("Option %s has invalid value!", name);		break;	case OPT_COMMAND:		if (!opt->value.command)			elinks_internal("Option %s has no value!", name);		break;	case OPT_CODEPAGE: /* TODO: check these too. */	case OPT_LANGUAGE:	case OPT_COLOR:		break;	}#endif	return &opt->value;}static voidadd_opt_sort(struct option *tree, struct option *option, int abi){	struct list_head *cat = tree->value.tree;	struct list_head *bcat = &tree->box_item->child;	struct option *pos;	/* The list is empty, just add it there. */	if (list_empty(*cat)) {		add_to_list(*cat, option);		if (abi) add_to_list(*bcat, option->box_item);	/* This fits as the last list entry, add it there. This	 * optimizes the most expensive BUT most common case ;-). */	} else if ((option->type != OPT_TREE		    || ((struct option *) cat->prev)->type == OPT_TREE)		   && strcmp(((struct option *) cat->prev)->name,			     option->name) <= 0) {append:		add_to_list_end(*cat, option);		if (abi) add_to_list_end(*bcat, option->box_item);	/* At the end of the list is tree and we are ordinary. That's	 * clear case then. */	} else if (option->type != OPT_TREE		   && ((struct option *) cat->prev)->type == OPT_TREE) {		goto append;	/* Scan the list linearly. This could be probably optimized ie.	 * to choose direction based on the first letter or so. */	} else {		struct listbox_item *bpos = (struct listbox_item *) bcat;		foreach (pos, *cat) {			/* First move the box item to the current position but			 * only if the position has not been marked as deleted			 * and actually has a box_item -- else we will end up			 * 'overflowing' and causing assertion failure. */			if (!(pos->flags & OPT_DELETED) && pos->box_item) {				bpos = bpos->next;				assert(bpos != (struct listbox_item *) bcat);			}			if ((option->type != OPT_TREE			     || pos->type == OPT_TREE)			    && strcmp(pos->name, option->name) <= 0)				continue;			/* Ordinary options always sort behind trees. */			if (option->type != OPT_TREE			    && pos->type == OPT_TREE)				continue;			/* The (struct option) add_at_pos() can mess up the			 * order so that we add the box_item to itself, so			 * better do it first. */			/* Always ensure that them _template_ options are			 * before anything else so a lonely autocreated option			 * (with _template_ options set to invisible) will be			 * connected with an upper corner (ascii: `-) instead			 * of a rotated T (ascii: +-) when displaying it. */			if (option->type == pos->type			    && *option->name <= '_'			    && !strcmp(pos->name, "_template_")) {				if (abi) add_at_pos(bpos, option->box_item);				add_at_pos(pos, option);				break;			}			if (abi) add_at_pos(bpos->prev, option->box_item);			add_at_pos(pos->prev, option);			break;		}		assert(pos != (struct option *) cat);		assert(bpos != (struct listbox_item *) bcat);	}}/* Add option to tree. */static voidadd_opt_rec(struct option *tree, unsigned char *path, struct option *option){	int abi = 0;	assert(path && option && tree);	if (*path) tree = get_opt_rec(tree, path);	assertm(tree, "Missing option tree for '%s'", path);	if (!tree->value.tree) return;	object_nolock(option, "option");	if (option->box_item && option->name && !strcmp(option->name, "_template_"))		option->box_item->visible = get_opt_bool("config.show_template");	if (tree->flags & OPT_AUTOCREATE && !option->desc) {		struct option *template = get_opt_rec(tree, "_template_");		assert(template);		option->desc = template->desc;	}	option->root = tree;	abi = (tree->box_item && option->box_item);	if (abi) {		/* The config_root tree is a just a placeholder for the		 * box_items, it actually isn't a real box_item by itself;		 * these ghosts are indicated by the fact that they have		 * NULL @next. */		if (tree->box_item->next) {			option->box_item->depth = tree->box_item->depth + 1;		}	}	if (tree->flags & OPT_SORT) {		add_opt_sort(tree, option, abi);	} else {		add_to_list_end(*tree->value.tree, option);		if (abi) add_to_list_end(tree->box_item->child, option->box_item);	}	update_hierbox_browser(&option_browser);}static inline struct listbox_item *init_option_listbox_item(struct option *option){	struct listbox_item *box = mem_calloc(1, sizeof(*box));	if (!box) return NULL;	init_list(box->child);	box->visible = 1;	box->udata = option;	box->type = (option->type == OPT_TREE) ? BI_FOLDER : BI_LEAF;	return box;}struct option *add_opt(struct option *tree, unsigned char *path, unsigned char *capt,	unsigned char *name, enum option_flags flags, enum option_type type,	int min, int max, void *value, unsigned char *desc){	struct option *option = mem_calloc(1, sizeof(*option));	if (!option) return NULL;	option->name = stracpy(name);	if (!option->name) {		mem_free(option);		return NULL;	}	option->flags = (flags | OPT_ALLOC);	option->type = type;	option->min = min;	option->max = max;	option->capt = capt;	option->desc = desc;	debug_check_option_syntax(option);	if (option->type != OPT_ALIAS	    && ((tree->flags & OPT_LISTBOX) || (option->flags & OPT_LISTBOX))) {		option->box_item = init_option_listbox_item(option);		if (!option->box_item) {			delete_option(option);			return NULL;		}	}	/* XXX: For allocated values we allocate in the add_opt_<type>() macro.	 * This involves OPT_TREE and OPT_STRING. */	switch (type) {		case OPT_TREE:			if (!value) {				delete_option(option);				return NULL;			}			option->value.tree = value;			break;		case OPT_STRING:			if (!value) {				delete_option(option);				return NULL;			}			option->value.string = value;			break;		case OPT_ALIAS:			option->value.string = value;			break;		case OPT_BOOL:		case OPT_INT:		case OPT_CODEPAGE:		case OPT_LONG:			option->value.number = (long) value;			break;		case OPT_COLOR:			decode_color(value, strlen((unsigned char *) value),					&option->value.color);			break;		case OPT_COMMAND:			option->value.command = value;			break;		case OPT_LANGUAGE:			break;	}	add_opt_rec(tree, path, option);	return option;}/* The namespace may start to seem a bit chaotic here; it indeed is, maybe the * function names above should be renamed and only macros should keep their old * short names. *//* The simple rule I took as an apologize is that functions which take already * completely filled (struct option *) have long name and functions which take * only option specs have short name. */static voiddelete_option_do(struct option *option, int recursive){	if (option->next) del_from_list(option);	if (recursive == -1) {		ERROR("Orphaned option %s", option->name);	}	switch (option->type) {		case OPT_STRING:			mem_free_if(option->value.string);			break;		case OPT_TREE:			if (!option->value.tree) break;			if (!recursive && !list_empty(*option->value.tree)) {				if (option->flags & OPT_AUTOCREATE) {					recursive = 1;				} else {					ERROR("Orphaned unregistered "						"option in subtree %s!",						option->name);					recursive = -1;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -