2005-09-15 09:58:31 -04:00
|
|
|
/* Options variables manipulation core */
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2022-08-17 14:48:58 -04:00
|
|
|
#ifdef HAVE_STDINT_H
|
|
|
|
#include <stdint.h>
|
|
|
|
#endif
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
#include <ctype.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
2022-01-02 09:17:53 -05:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
2022-05-11 15:30:33 -04:00
|
|
|
#include <unistd.h>
|
2022-01-02 09:17:53 -05:00
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
#include "elinks.h"
|
|
|
|
|
|
|
|
#include "bfu/dialog.h"
|
2022-03-21 09:11:39 -04:00
|
|
|
#include "bfu/style.h"
|
2005-09-15 09:58:31 -04:00
|
|
|
#include "cache/cache.h"
|
|
|
|
#include "config/conf.h"
|
|
|
|
#include "config/dialogs.h"
|
2007-08-30 18:03:14 -04:00
|
|
|
#include "config/domain.h"
|
2022-01-02 09:17:53 -05:00
|
|
|
#include "config/home.h"
|
2005-09-15 09:58:31 -04:00
|
|
|
#include "config/options.h"
|
|
|
|
#include "config/opttypes.h"
|
|
|
|
#include "dialogs/status.h"
|
|
|
|
#include "document/document.h"
|
|
|
|
#include "globhist/globhist.h"
|
|
|
|
#include "intl/charsets.h"
|
2021-08-08 15:25:08 -04:00
|
|
|
#include "intl/libintl.h"
|
2005-09-15 09:58:31 -04:00
|
|
|
#include "main/main.h" /* shrink_memory() */
|
|
|
|
#include "main/select.h"
|
|
|
|
#include "network/connection.h"
|
|
|
|
#include "session/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"
|
|
|
|
|
2021-12-20 06:15:11 -05:00
|
|
|
extern int ui_double_esc;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
/* 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 */
|
|
|
|
|
2007-07-26 15:39:08 -04:00
|
|
|
static INIT_LIST_OF(struct option, options_root_tree);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
static struct option options_root = INIT_OPTION(
|
|
|
|
/* name: */ "",
|
2022-01-15 14:10:37 -05:00
|
|
|
/* flags: */ OPT_ZERO,
|
2005-09-15 09:58:31 -04:00
|
|
|
/* type: */ OPT_TREE,
|
|
|
|
/* min, max: */ 0, 0,
|
|
|
|
/* value: */ &options_root_tree,
|
|
|
|
/* desc: */ "",
|
|
|
|
/* capt: */ NULL
|
|
|
|
);
|
|
|
|
|
|
|
|
struct option *config_options;
|
|
|
|
struct option *cmdline_options;
|
|
|
|
|
2022-01-29 12:51:45 -05:00
|
|
|
static void add_opt_rec(struct option *, const char *, struct option *);
|
2007-07-26 15:39:08 -04:00
|
|
|
static void free_options_tree(LIST_OF(struct option) *, int recursive);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
#ifdef CONFIG_DEBUG
|
|
|
|
/* Detect ending '.' (and some others) in options captions.
|
|
|
|
* It will emit a message in debug mode only. --Zas */
|
|
|
|
|
2005-11-11 01:17:50 -05:00
|
|
|
#define bad_punct(c) (c != ')' && c != '>' && !isquote(c) && ispunct(c))
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
static void
|
2021-01-02 10:20:27 -05:00
|
|
|
check_caption(char *caption)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
int len;
|
|
|
|
unsigned char c;
|
|
|
|
|
|
|
|
if (!caption) return;
|
|
|
|
|
|
|
|
len = strlen(caption);
|
|
|
|
if (!len) return;
|
|
|
|
|
|
|
|
c = caption[len - 1];
|
2022-06-28 14:25:06 -04:00
|
|
|
if (isspace((unsigned char)c) || bad_punct(c))
|
2005-09-15 09:58:31 -04:00
|
|
|
DBG("bad char at end of caption [%s]", caption);
|
|
|
|
|
|
|
|
#ifdef CONFIG_NLS
|
|
|
|
caption = gettext(caption);
|
|
|
|
len = strlen(caption);
|
|
|
|
if (!len) return;
|
|
|
|
|
|
|
|
c = caption[len - 1];
|
2022-06-28 14:25:06 -04:00
|
|
|
if (isspace((unsigned char)c) || bad_punct(c))
|
2005-09-15 09:58:31 -04:00
|
|
|
DBG("bad char at end of i18n caption [%s]", caption);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef bad_punct
|
|
|
|
|
|
|
|
static void
|
2021-01-02 10:20:27 -05:00
|
|
|
check_description(char *desc)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
int len;
|
|
|
|
unsigned char c;
|
|
|
|
|
|
|
|
if (!desc) return;
|
|
|
|
|
|
|
|
len = strlen(desc);
|
|
|
|
if (!len) return;
|
|
|
|
|
|
|
|
c = desc[len - 1];
|
2022-06-28 14:25:06 -04:00
|
|
|
if (isspace((unsigned char)c))
|
2005-09-15 09:58:31 -04:00
|
|
|
DBG("bad char at end of description [%s]", desc);
|
|
|
|
|
|
|
|
#ifdef CONFIG_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];
|
2022-06-28 14:25:06 -04:00
|
|
|
if (isspace((unsigned char)c))
|
2005-09-15 09:58:31 -04:00
|
|
|
DBG("bad char at end of i18n description [%s]", desc);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/*! @relates option */
|
2005-09-15 09:58:31 -04:00
|
|
|
static void
|
|
|
|
debug_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;
|
|
|
|
|
2008-02-02 08:59:32 -05:00
|
|
|
/** Get record of option of given name, or NULL if there's no such option.
|
|
|
|
*
|
|
|
|
* If the specified option is an ::OPT_ALIAS, this function returns the
|
|
|
|
* alias, rather than the option to which the alias refers. It must
|
|
|
|
* work this way because the alias may have the ::OPT_ALIAS_NEGATE flag.
|
|
|
|
* Instead, if the caller tries to read or set the value of the alias,
|
|
|
|
* the functions associated with ::OPT_ALIAS will forward the operation
|
2009-08-15 21:08:59 -04:00
|
|
|
* to the underlying option. However, see indirect_option().
|
|
|
|
*
|
|
|
|
* @relates option */
|
2005-09-15 09:58:31 -04:00
|
|
|
struct option *
|
2021-01-02 10:20:27 -05:00
|
|
|
get_opt_rec(struct option *tree, const char *name_)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
struct option *option;
|
2021-01-02 10:20:27 -05:00
|
|
|
char *aname = stracpy(name_);
|
|
|
|
char *name = aname;
|
|
|
|
char *sep;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
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]. */
|
2022-01-18 14:42:29 -05:00
|
|
|
if ((sep = strrchr(name, '.'))) {
|
2005-09-15 09:58:31 -04:00
|
|
|
*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) {
|
2016-04-20 12:05:00 -04:00
|
|
|
struct option *template_ = get_opt_rec(tree, "_template_");
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2016-04-20 12:05:00 -04:00
|
|
|
assertm(template_ != NULL, "Requested %s should be autocreated but "
|
2005-09-15 09:58:31 -04:00
|
|
|
"%.*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. */
|
|
|
|
|
2016-04-20 12:05:00 -04:00
|
|
|
option = copy_option(template_, 0);
|
2005-09-15 09:58:31 -04:00
|
|
|
if (!option) {
|
|
|
|
mem_free(aname);
|
|
|
|
return NULL;
|
|
|
|
}
|
2022-01-29 12:17:43 -05:00
|
|
|
mem_free_set(&option->aname, stracpy(name));
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
add_opt_rec(tree, "", option);
|
|
|
|
|
|
|
|
mem_free(aname);
|
|
|
|
return option;
|
|
|
|
}
|
|
|
|
|
|
|
|
mem_free(aname);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/** Get record of option of given name, or NULL if there's no such option. But
|
2005-09-15 09:58:31 -04:00
|
|
|
* do not create the option if it doesn't exist and there's autocreation
|
2009-08-15 21:08:59 -04:00
|
|
|
* enabled.
|
|
|
|
* @relates option */
|
2005-09-15 09:58:31 -04:00
|
|
|
struct option *
|
2021-01-02 10:20:27 -05:00
|
|
|
get_opt_rec_real(struct option *tree, const char *name)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
struct option *opt;
|
|
|
|
|
|
|
|
no_autocreate = 1;
|
|
|
|
opt = get_opt_rec(tree, name);
|
|
|
|
no_autocreate = 0;
|
|
|
|
return opt;
|
|
|
|
}
|
|
|
|
|
2008-02-03 06:57:13 -05:00
|
|
|
/** If @a opt is an alias, return the option to which it refers.
|
|
|
|
*
|
|
|
|
* @warning Because the alias may have the ::OPT_ALIAS_NEGATE flag,
|
|
|
|
* the caller must not access the value of the returned option as if
|
|
|
|
* it were also the value of the alias. However, it is safe to access
|
2009-08-15 21:08:59 -04:00
|
|
|
* flags such as ::OPT_MUST_SAVE and ::OPT_DELETED.
|
|
|
|
*
|
|
|
|
* @relates option */
|
2008-02-03 06:57:13 -05:00
|
|
|
struct option *
|
|
|
|
indirect_option(struct option *alias)
|
|
|
|
{
|
|
|
|
struct option *real;
|
|
|
|
|
|
|
|
if (alias->type != OPT_ALIAS) return alias; /* not an error */
|
|
|
|
|
|
|
|
real = get_opt_rec(config_options, alias->value.string);
|
|
|
|
assertm(real != NULL, "%s aliased to unknown option %s!",
|
|
|
|
alias->name, alias->value.string);
|
|
|
|
if_assert_failed return alias;
|
|
|
|
|
|
|
|
return real;
|
|
|
|
}
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/** Fetch pointer to value of certain option. It is guaranteed to never return
|
|
|
|
* NULL. Note that you are supposed to use wrapper get_opt().
|
|
|
|
* @relates option */
|
2005-09-15 09:58:31 -04:00
|
|
|
union option_value *
|
|
|
|
get_opt_(
|
|
|
|
#ifdef CONFIG_DEBUG
|
2021-01-02 10:20:27 -05:00
|
|
|
char *file, int line, enum option_type option_type,
|
2005-09-15 09:58:31 -04:00
|
|
|
#endif
|
2021-03-01 16:02:50 -05:00
|
|
|
struct option *tree, const char *name, struct session *ses)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
2007-08-28 12:41:18 -04:00
|
|
|
struct option *opt = NULL;
|
|
|
|
|
2007-08-28 13:29:41 -04:00
|
|
|
/* If given a session and the option is shadowed in that session's
|
|
|
|
* options tree, return the shadow. */
|
|
|
|
if (ses && ses->option)
|
|
|
|
opt = get_opt_rec_real(ses->option, name);
|
2007-08-28 12:41:18 -04:00
|
|
|
|
2007-08-31 05:48:58 -04:00
|
|
|
/* If given a session, no session-specific option was found, and the
|
|
|
|
* option has a shadow in the domain tree that matches the current
|
|
|
|
* document in that session, return that shadow. */
|
|
|
|
if (!opt && ses)
|
2007-08-30 17:50:55 -04:00
|
|
|
opt = get_domain_option_from_session(name, ses);
|
2007-08-28 12:41:18 -04:00
|
|
|
|
|
|
|
/* Else, return the real option. */
|
|
|
|
if (!opt)
|
|
|
|
opt = get_opt_rec(tree, name);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
#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:
|
|
|
|
if (opt->value.number < opt->min
|
|
|
|
|| opt->value.number > opt->max)
|
2005-10-25 13:10:30 -04:00
|
|
|
elinks_internal("Option %s has invalid value %d!", name, opt->value.number);
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
2006-06-26 11:49:59 -04:00
|
|
|
case OPT_LONG:
|
|
|
|
if (opt->value.big_number < opt->min
|
|
|
|
|| opt->value.big_number > opt->max)
|
|
|
|
elinks_internal("Option %s has invalid value %ld!", name, opt->value.big_number);
|
|
|
|
break;
|
2005-09-15 09:58:31 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/*! @relates option */
|
2005-09-15 09:58:31 -04:00
|
|
|
static void
|
|
|
|
add_opt_sort(struct option *tree, struct option *option, int abi)
|
|
|
|
{
|
2007-07-26 15:39:08 -04:00
|
|
|
LIST_OF(struct option) *cat = tree->value.tree;
|
|
|
|
LIST_OF(struct listbox_item) *bcat = &tree->box_item->child;
|
2005-09-15 09:58:31 -04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/** Add option to tree.
|
|
|
|
* @relates option */
|
2005-09-15 09:58:31 -04:00
|
|
|
static void
|
2022-01-29 12:51:45 -05:00
|
|
|
add_opt_rec(struct option *tree, const char *path, struct option *option)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
int abi = 0;
|
|
|
|
|
|
|
|
assert(path && option && tree);
|
|
|
|
if (*path) tree = get_opt_rec(tree, path);
|
|
|
|
|
2007-03-11 06:22:02 -04:00
|
|
|
assertm(tree != NULL, "Missing option tree for '%s'", path);
|
2005-09-15 09:58:31 -04:00
|
|
|
if (!tree->value.tree) return;
|
|
|
|
|
|
|
|
object_nolock(option, "option");
|
|
|
|
|
|
|
|
if (option->box_item && option->name && !strcmp(option->name, "_template_"))
|
2007-08-28 12:41:18 -04:00
|
|
|
option->box_item->visible = get_opt_bool("config.show_template", NULL);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
if (tree->flags & OPT_AUTOCREATE && !option->desc) {
|
2016-04-20 12:05:00 -04:00
|
|
|
struct option *template_ = get_opt_rec(tree, "_template_");
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2016-04-20 12:05:00 -04:00
|
|
|
assert(template_);
|
|
|
|
option->desc = template_->desc;
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/*! @relates option */
|
2005-09-15 09:58:31 -04:00
|
|
|
static inline struct listbox_item *
|
|
|
|
init_option_listbox_item(struct option *option)
|
|
|
|
{
|
2022-01-16 15:08:50 -05:00
|
|
|
struct listbox_item *item = (struct listbox_item *)mem_calloc(1, sizeof(*item));
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
if (!item) return NULL;
|
|
|
|
|
|
|
|
init_list(item->child);
|
|
|
|
item->visible = 1;
|
|
|
|
item->udata = option;
|
|
|
|
item->type = (option->type == OPT_TREE) ? BI_FOLDER : BI_LEAF;
|
|
|
|
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/*! @relates option */
|
2005-09-15 09:58:31 -04:00
|
|
|
struct option *
|
2022-01-30 11:27:11 -05:00
|
|
|
add_opt(struct option *tree, const char *path, const char *capt,
|
|
|
|
const char *name, option_flags_T flags, enum option_type type,
|
2022-01-31 10:09:02 -05:00
|
|
|
long min, long max, longptr_T value, const char *desc)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
2022-01-16 15:08:50 -05:00
|
|
|
struct option *option = (struct option *)mem_calloc(1, sizeof(*option));
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
/* 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) {
|
2006-06-26 11:33:00 -04:00
|
|
|
mem_free(option);
|
2005-09-15 09:58:31 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
2007-07-26 15:39:08 -04:00
|
|
|
option->value.tree = (LIST_OF(struct option) *) value;
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
case OPT_STRING:
|
|
|
|
if (!value) {
|
2006-06-26 11:33:00 -04:00
|
|
|
mem_free(option);
|
2005-09-15 09:58:31 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
2021-01-02 10:20:27 -05:00
|
|
|
option->value.string = (char *) value;
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
case OPT_ALIAS:
|
2021-01-02 10:20:27 -05:00
|
|
|
option->value.string = (char *) value;
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
case OPT_BOOL:
|
|
|
|
case OPT_INT:
|
|
|
|
case OPT_CODEPAGE:
|
2006-06-26 11:37:10 -04:00
|
|
|
option->value.number = (int) value;
|
|
|
|
break;
|
2005-09-15 09:58:31 -04:00
|
|
|
case OPT_LONG:
|
2022-08-17 14:48:58 -04:00
|
|
|
option->value.big_number = (intptr_t) value; /* FIXME: cast from void * */
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
case OPT_COLOR:
|
2021-01-02 10:20:27 -05:00
|
|
|
decode_color((char *) value, strlen((char *) value),
|
2005-09-15 09:58:31 -04:00
|
|
|
&option->value.color);
|
|
|
|
break;
|
|
|
|
case OPT_COMMAND:
|
2022-02-18 08:42:25 -05:00
|
|
|
option->value.command = (const char * (*)(struct option_elinks *, char ***, int *))value;
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
case OPT_LANGUAGE:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2006-06-26 11:33:00 -04:00
|
|
|
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) {
|
|
|
|
mem_free(option);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2006-07-27 03:51:10 -04:00
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
add_opt_rec(tree, path, option);
|
|
|
|
return option;
|
|
|
|
}
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/*! @relates option */
|
2005-09-15 09:58:31 -04:00
|
|
|
static void
|
|
|
|
done_option(struct option *option)
|
|
|
|
{
|
|
|
|
switch (option->type) {
|
|
|
|
case OPT_STRING:
|
|
|
|
mem_free_if(option->value.string);
|
|
|
|
break;
|
|
|
|
case OPT_TREE:
|
|
|
|
mem_free_if(option->value.tree);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (option->box_item)
|
|
|
|
done_listbox_item(&option_browser, option->box_item);
|
|
|
|
|
|
|
|
if (option->flags & OPT_ALLOC) {
|
2022-01-29 12:17:43 -05:00
|
|
|
mem_free_if(option->aname);
|
2005-09-15 09:58:31 -04:00
|
|
|
mem_free(option);
|
|
|
|
} else if (!option->capt) {
|
|
|
|
/* We are probably dealing with a built-in autocreated option
|
|
|
|
* that will be attempted to be deleted when shutting down. */
|
|
|
|
/* Clear it so nothing will be done later. */
|
|
|
|
memset(option, 0, sizeof(*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. */
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/*! @relates option */
|
2005-09-15 09:58:31 -04:00
|
|
|
static void
|
|
|
|
delete_option_do(struct option *option, int recursive)
|
|
|
|
{
|
|
|
|
if (option->next) {
|
|
|
|
del_from_list(option);
|
|
|
|
option->prev = option->next = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (recursive == -1) {
|
|
|
|
ERROR("Orphaned option %s", option->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (option->type == OPT_TREE && option->value.tree
|
|
|
|
&& !list_empty(*option->value.tree)) {
|
|
|
|
if (!recursive) {
|
|
|
|
if (option->flags & OPT_AUTOCREATE) {
|
|
|
|
recursive = 1;
|
|
|
|
} else {
|
|
|
|
ERROR("Orphaned unregistered "
|
|
|
|
"option in subtree %s!",
|
|
|
|
option->name);
|
|
|
|
recursive = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free_options_tree(option->value.tree, recursive);
|
|
|
|
}
|
|
|
|
|
|
|
|
done_option(option);
|
|
|
|
}
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/*! @relates option */
|
2005-09-15 09:58:31 -04:00
|
|
|
void
|
|
|
|
mark_option_as_deleted(struct option *option)
|
|
|
|
{
|
|
|
|
if (option->type == OPT_TREE) {
|
|
|
|
struct option *unmarked;
|
|
|
|
|
|
|
|
assert(option->value.tree);
|
|
|
|
|
|
|
|
foreach (unmarked, *option->value.tree)
|
|
|
|
mark_option_as_deleted(unmarked);
|
|
|
|
}
|
|
|
|
|
|
|
|
option->box_item->visible = 0;
|
|
|
|
|
|
|
|
option->flags |= (OPT_TOUCHED | OPT_DELETED);
|
|
|
|
}
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/*! @relates option */
|
2005-09-15 09:58:31 -04:00
|
|
|
void
|
|
|
|
delete_option(struct option *option)
|
|
|
|
{
|
|
|
|
delete_option_do(option, 1);
|
|
|
|
}
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/*! @relates option */
|
2005-09-15 09:58:31 -04:00
|
|
|
struct option *
|
2016-04-20 12:05:00 -04:00
|
|
|
copy_option(struct option *template_, int flags)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
2022-01-16 15:08:50 -05:00
|
|
|
struct option *option = (struct option *)mem_calloc(1, sizeof(*option));
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
if (!option) return NULL;
|
|
|
|
|
2016-04-20 12:05:00 -04:00
|
|
|
option->name = null_or_stracpy(template_->name);
|
|
|
|
option->flags = (template_->flags | OPT_ALLOC);
|
|
|
|
option->type = template_->type;
|
|
|
|
option->min = template_->min;
|
|
|
|
option->max = template_->max;
|
|
|
|
option->capt = template_->capt;
|
|
|
|
option->desc = template_->desc;
|
|
|
|
option->change_hook = template_->change_hook;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2007-08-28 13:11:52 -04:00
|
|
|
if (!(flags & CO_NO_LISTBOX_ITEM))
|
|
|
|
option->box_item = init_option_listbox_item(option);
|
2005-09-15 09:58:31 -04:00
|
|
|
if (option->box_item) {
|
2016-04-20 12:05:00 -04:00
|
|
|
if (template_->box_item) {
|
|
|
|
option->box_item->type = template_->box_item->type;
|
|
|
|
option->box_item->depth = template_->box_item->depth;
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-20 12:05:00 -04:00
|
|
|
if (option_types[template_->type].dup) {
|
|
|
|
option_types[template_->type].dup(option, template_, flags);
|
2005-09-15 09:58:31 -04:00
|
|
|
} else {
|
2016-04-20 12:05:00 -04:00
|
|
|
option->value = template_->value;
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return option;
|
|
|
|
}
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/** Return the shadow option in @a shadow_tree of @a option in @a tree.
|
|
|
|
* If @a option isn't yet shadowed in @a shadow_tree, shadow it
|
|
|
|
* (i.e. create a copy in @a shadow_tree) along with any ancestors
|
|
|
|
* that aren't shadowed. */
|
2007-08-28 13:11:52 -04:00
|
|
|
struct option *
|
|
|
|
get_option_shadow(struct option *option, struct option *tree,
|
|
|
|
struct option *shadow_tree)
|
|
|
|
{
|
|
|
|
|
|
|
|
struct option *shadow_option = NULL;
|
|
|
|
|
|
|
|
assert(option);
|
|
|
|
assert(tree);
|
|
|
|
assert(shadow_tree);
|
|
|
|
|
|
|
|
if (option == tree) {
|
|
|
|
shadow_option = shadow_tree;
|
|
|
|
} else if (option->root && option->name) {
|
|
|
|
struct option *shadow_root;
|
|
|
|
|
|
|
|
shadow_root = get_option_shadow(option->root, tree,
|
|
|
|
shadow_tree);
|
|
|
|
if (!shadow_root) return NULL;
|
|
|
|
|
|
|
|
shadow_option = get_opt_rec_real(shadow_root, option->name);
|
|
|
|
if (!shadow_option) {
|
|
|
|
shadow_option = copy_option(option,
|
|
|
|
CO_SHALLOW
|
|
|
|
| CO_NO_LISTBOX_ITEM);
|
|
|
|
if (shadow_option) {
|
|
|
|
shadow_option->root = shadow_root;
|
|
|
|
/* No need to sort, is there? It isn't shown
|
|
|
|
* in the options manager. -- Miciah */
|
|
|
|
add_to_list_end(*shadow_root->value.tree,
|
|
|
|
shadow_option);
|
2007-08-29 09:34:43 -04:00
|
|
|
|
|
|
|
shadow_option->flags |= OPT_TOUCHED;
|
2007-08-28 13:11:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return shadow_option;
|
|
|
|
}
|
|
|
|
|
2007-08-30 16:29:40 -04:00
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/*! @relates option */
|
2007-07-26 15:39:08 -04:00
|
|
|
LIST_OF(struct option) *
|
2005-09-15 09:58:31 -04:00
|
|
|
init_options_tree(void)
|
|
|
|
{
|
2022-01-16 13:09:27 -05:00
|
|
|
LIST_OF(struct option) *ptr = (LIST_OF(struct option) *)mem_alloc(sizeof(*ptr));
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
if (ptr) init_list(*ptr);
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/** Some default pre-autocreated options. Doh. */
|
2005-09-15 09:58:31 -04:00
|
|
|
static inline void
|
|
|
|
register_autocreated_options(void)
|
|
|
|
{
|
|
|
|
/* TODO: Use table-driven initialization. --jonas */
|
2009-04-26 10:16:00 -04:00
|
|
|
get_opt_int("terminal.linux.type", NULL) = TERM_LINUX;
|
|
|
|
get_opt_int("terminal.linux.colors", NULL) = COLOR_MODE_16;
|
2007-08-28 12:41:18 -04:00
|
|
|
get_opt_bool("terminal.linux.m11_hack", NULL) = 1;
|
2009-04-26 10:16:00 -04:00
|
|
|
get_opt_int("terminal.vt100.type", NULL) = TERM_VT100;
|
|
|
|
get_opt_int("terminal.vt110.type", NULL) = TERM_VT100;
|
|
|
|
get_opt_int("terminal.xterm.type", NULL) = TERM_VT100;
|
2007-08-28 12:41:18 -04:00
|
|
|
get_opt_bool("terminal.xterm.underline", NULL) = 1;
|
2009-04-26 10:16:00 -04:00
|
|
|
get_opt_int("terminal.xterm-color.type", NULL) = TERM_VT100;
|
2007-08-28 12:41:18 -04:00
|
|
|
get_opt_int("terminal.xterm-color.colors", NULL) = COLOR_MODE_16;
|
|
|
|
get_opt_bool("terminal.xterm-color.underline", NULL) = 1;
|
2005-09-15 09:58:31 -04:00
|
|
|
#ifdef CONFIG_88_COLORS
|
2009-04-26 10:16:00 -04:00
|
|
|
get_opt_int("terminal.xterm-88color.type", NULL) = TERM_VT100;
|
2007-08-28 12:41:18 -04:00
|
|
|
get_opt_int("terminal.xterm-88color.colors", NULL) = COLOR_MODE_88;
|
|
|
|
get_opt_bool("terminal.xterm-88color.underline", NULL) = 1;
|
2005-09-15 09:58:31 -04:00
|
|
|
#endif
|
2009-01-01 14:59:57 -05:00
|
|
|
get_opt_int("terminal.rxvt-unicode.type", NULL) = 1;
|
|
|
|
#ifdef CONFIG_88_COLORS
|
|
|
|
get_opt_int("terminal.rxvt-unicode.colors", NULL) = COLOR_MODE_88;
|
|
|
|
#else
|
|
|
|
get_opt_int("terminal.rxvt-unicode.colors", NULL) = COLOR_MODE_16;
|
|
|
|
#endif
|
|
|
|
get_opt_bool("terminal.rxvt-unicode.italic", NULL) = 1;
|
|
|
|
get_opt_bool("terminal.rxvt-unicode.underline", NULL) = 1;
|
2005-09-15 09:58:31 -04:00
|
|
|
#ifdef CONFIG_256_COLORS
|
2009-04-26 10:16:00 -04:00
|
|
|
get_opt_int("terminal.xterm-256color.type", NULL) = TERM_VT100;
|
2007-08-28 12:41:18 -04:00
|
|
|
get_opt_int("terminal.xterm-256color.colors", NULL) = COLOR_MODE_256;
|
|
|
|
get_opt_bool("terminal.xterm-256color.underline", NULL) = 1;
|
2009-03-27 14:44:46 -04:00
|
|
|
get_opt_int("terminal.fbterm.type", NULL) = TERM_FBTERM;
|
|
|
|
get_opt_int("terminal.fbterm.colors", NULL) = COLOR_MODE_256;
|
|
|
|
get_opt_bool("terminal.fbterm.underline", NULL) = 0;
|
2005-09-15 09:58:31 -04:00
|
|
|
#endif
|
2020-07-05 03:21:24 -04:00
|
|
|
get_opt_int("terminal.st-256color.type", NULL) = TERM_VT100;
|
2020-07-05 06:21:22 -04:00
|
|
|
get_opt_bool("terminal.st-256color.latin1_title", NULL) = 0;
|
2020-07-05 03:21:24 -04:00
|
|
|
#ifdef CONFIG_TRUE_COLOR
|
|
|
|
get_opt_int("terminal.st-256color.colors", NULL) = COLOR_MODE_TRUE_COLOR;
|
|
|
|
#elif defined(CONFIG_256_COLORS)
|
|
|
|
get_opt_int("terminal.st-256color.colors", NULL) = COLOR_MODE_256;
|
|
|
|
#elif defined(CONFIG_88_COLORS)
|
|
|
|
get_opt_int("terminal.st-256color.colors", NULL) = COLOR_MODE_88;
|
|
|
|
#else
|
|
|
|
get_opt_int("terminal.st-256color.colors", NULL) = COLOR_MODE_16;
|
|
|
|
#endif
|
|
|
|
get_opt_bool("terminal.st-256color.italic", NULL) = 1;
|
|
|
|
get_opt_bool("terminal.st-256color.underline", NULL) = 1;
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
bug 764: Initialize the right member of union option_value
INIT_OPTION used to initialize union option_value at compile time by
casting the default value to LIST_OF(struct option) *, which is the
type of the first member. On sparc64 and other big-endian systems
where sizeof(int) < sizeof(struct list_head *), this tended to leave
option->value.number as zero, thus messing up OPT_INT and OPT_BOOL
at least. OPT_LONG however tended to work right.
This would be easy to fix with C99 designated initializers,
but doc/hacking.txt says ELinks must be kept C89 compatible.
Another solution would be to make register_options() read the
value from option->value.tree (the first member), cast it back
to the right type, and write it to the appropriate member;
but that would still require somewhat dubious conversions
between integers, data pointers, and function pointers.
So here's a rather more invasive solution. Add struct option_init,
which is somewhat similar to struct option but has non-overlapping
members for different types of values, to ensure nothing is lost
in compile-time conversions. Move unsigned char *path from struct
option_info to struct option_init, and replace struct option_info
with a union that contains struct option_init and struct option.
Now, this union can be initialized with no portability problems,
and register_options() then moves the values from struct option_init
to their final places in struct option.
In my x86 ELinks build with plenty of options configured in, this
change bloated the text section by 340 bytes but compressed the data
section by 2784 bytes, presumably because union option_info is a
pointer smaller than struct option_info was.
(cherry picked from elinks-0.12 commit e5f6592ee20780a61f70feeb1f9e17631b9c5835)
Conflicts:
src/protocol/fsp/fsp.c: All options had been removed in 0.13.GIT.
src/protocol/smb/smb2.c: Ditto.
2009-08-15 15:39:07 -04:00
|
|
|
extern union option_info cmdline_options_info[];
|
2016-04-20 15:23:55 -04:00
|
|
|
|
|
|
|
#include "config/options.inc"
|
|
|
|
|
|
|
|
static int
|
|
|
|
change_hook_cache(struct session *ses, struct option *current, struct option *changed)
|
|
|
|
{
|
|
|
|
shrink_memory(0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
change_hook_connection(struct session *ses, struct option *current, struct option *changed)
|
|
|
|
{
|
|
|
|
register_check_queue();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
change_hook_html(struct session *ses, struct option *current, struct option *changed)
|
|
|
|
{
|
|
|
|
foreach (ses, sessions) ses->tab->resize = 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
change_hook_insert_mode(struct session *ses, struct option *current, struct option *changed)
|
|
|
|
{
|
|
|
|
update_status();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
change_hook_active_link(struct session *ses, struct option *current, struct option *changed)
|
|
|
|
{
|
|
|
|
update_cached_document_options(ses);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
change_hook_terminal(struct session *ses, struct option *current, struct option *changed)
|
|
|
|
{
|
|
|
|
cls_redraw_all_terminals();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
change_hook_ui(struct session *ses, struct option *current, struct option *changed)
|
|
|
|
{
|
|
|
|
update_status();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-03-21 09:11:39 -04:00
|
|
|
static int
|
|
|
|
change_hook_ui_colors(struct session *ses, struct option *current, struct option *changed)
|
|
|
|
{
|
|
|
|
unsigned int node_number = changed->root->node_number;
|
|
|
|
|
|
|
|
if (node_number) {
|
|
|
|
reset_bfu_node_number(node_number);
|
|
|
|
cls_redraw_all_terminals();
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-12-20 06:15:11 -05:00
|
|
|
static int
|
|
|
|
change_hook_ui_double_esc(struct session *ses, struct option *current, struct option *changed)
|
|
|
|
{
|
|
|
|
ui_double_esc = changed->value.number;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-01-02 09:17:53 -05:00
|
|
|
static int
|
|
|
|
change_hook_ui_mouse_disable(struct session *ses, struct option *current, struct option *changed)
|
|
|
|
{
|
|
|
|
char *lock_filename = straconcat(empty_string_or_(elinks_home), "mouse.lock", (char *)NULL);
|
|
|
|
|
|
|
|
if (lock_filename) {
|
|
|
|
if (changed->value.number) {
|
|
|
|
creat(lock_filename, 0600);
|
|
|
|
} else {
|
|
|
|
unlink(lock_filename);
|
|
|
|
}
|
|
|
|
mem_free(lock_filename);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-20 15:23:55 -04:00
|
|
|
/** Make option templates visible or invisible in the option manager.
|
|
|
|
* This is called once on startup, and then each time the value of the
|
|
|
|
* "config.show_template" option is changed.
|
|
|
|
*
|
|
|
|
* @param tree
|
|
|
|
* The option tree whose children should be affected.
|
|
|
|
*
|
|
|
|
* @param show
|
|
|
|
* A set of bits:
|
|
|
|
* - The 0x01 bit means templates should be made visible.
|
|
|
|
* If the bit is clear, templates become invisible instead.
|
|
|
|
* - The 0x02 bit means @a tree is itself part of a template,
|
|
|
|
* and so all of its children should be affected, regardless
|
|
|
|
* of whether they are templates of their own.
|
|
|
|
*
|
|
|
|
* Deleted options are never visible.
|
|
|
|
*
|
|
|
|
* @relates option */
|
|
|
|
static void
|
|
|
|
update_visibility(LIST_OF(struct option) *tree, int show)
|
|
|
|
{
|
|
|
|
struct option *opt;
|
|
|
|
|
|
|
|
foreach (opt, *tree) {
|
|
|
|
if (opt->flags & OPT_DELETED) continue;
|
|
|
|
|
|
|
|
if (!strcmp(opt->name, "_template_")) {
|
|
|
|
if (opt->box_item)
|
|
|
|
opt->box_item->visible = (show & 1);
|
|
|
|
|
|
|
|
if (opt->type == OPT_TREE)
|
|
|
|
update_visibility(opt->value.tree, show | 2);
|
|
|
|
} else {
|
|
|
|
if (opt->box_item && (show & 2))
|
|
|
|
opt->box_item->visible = (show & 1);
|
|
|
|
|
|
|
|
if (opt->type == OPT_TREE)
|
|
|
|
update_visibility(opt->value.tree, show);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
change_hook_stemplate(struct session *ses, struct option *current, struct option *changed)
|
|
|
|
{
|
|
|
|
update_visibility(config_options->value.tree, changed->value.number);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
change_hook_language(struct session *ses, struct option *current, struct option *changed)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_NLS
|
|
|
|
set_language(changed->value.number);
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct change_hook_info change_hooks[] = {
|
|
|
|
{ "config.show_template", change_hook_stemplate },
|
|
|
|
{ "connection", change_hook_connection },
|
|
|
|
{ "document.browse", change_hook_html },
|
|
|
|
{ "document.browse.forms.insert_mode",
|
|
|
|
change_hook_insert_mode },
|
|
|
|
{ "document.browse.links.active_link",
|
|
|
|
change_hook_active_link },
|
|
|
|
{ "document.cache", change_hook_cache },
|
|
|
|
{ "document.codepage", change_hook_html },
|
|
|
|
{ "document.colors", change_hook_html },
|
|
|
|
{ "document.html", change_hook_html },
|
|
|
|
{ "document.plain", change_hook_html },
|
|
|
|
{ "terminal", change_hook_terminal },
|
2022-03-21 09:11:39 -04:00
|
|
|
{ "ui.colors", change_hook_ui_colors },
|
2021-12-20 06:15:11 -05:00
|
|
|
{ "ui.double_esc", change_hook_ui_double_esc },
|
2016-04-20 15:23:55 -04:00
|
|
|
{ "ui.language", change_hook_language },
|
2022-01-02 09:17:53 -05:00
|
|
|
{ "ui.mouse_disable", change_hook_ui_mouse_disable },
|
2016-04-20 15:23:55 -04:00
|
|
|
{ "ui", change_hook_ui },
|
|
|
|
{ NULL, NULL },
|
|
|
|
};
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
void
|
|
|
|
init_options(void)
|
|
|
|
{
|
|
|
|
cmdline_options = add_opt_tree_tree(&options_root, "", "",
|
2022-01-15 14:10:37 -05:00
|
|
|
"cmdline", OPT_ZERO, "");
|
2005-09-15 09:58:31 -04:00
|
|
|
register_options(cmdline_options_info, cmdline_options);
|
|
|
|
|
|
|
|
config_options = add_opt_tree_tree(&options_root, "", "",
|
|
|
|
"config", OPT_SORT, "");
|
|
|
|
config_options->flags |= OPT_LISTBOX;
|
|
|
|
config_options->box_item = &option_browser.root;
|
|
|
|
register_options(config_options_info, config_options);
|
|
|
|
|
|
|
|
register_autocreated_options();
|
|
|
|
register_change_hooks(change_hooks);
|
2021-12-20 06:15:11 -05:00
|
|
|
|
|
|
|
ui_double_esc = get_opt_bool("ui.double_esc", NULL);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/*! @relates option */
|
2005-09-15 09:58:31 -04:00
|
|
|
static void
|
2007-07-26 15:39:08 -04:00
|
|
|
free_options_tree(LIST_OF(struct option) *tree, int recursive)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
while (!list_empty(*tree))
|
2022-01-25 11:26:39 -05:00
|
|
|
delete_option_do((struct option *)tree->next, recursive);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
done_options(void)
|
|
|
|
{
|
2007-08-30 16:29:40 -04:00
|
|
|
done_domain_trees();
|
2005-09-15 09:58:31 -04:00
|
|
|
unregister_options(config_options_info, config_options);
|
|
|
|
unregister_options(cmdline_options_info, cmdline_options);
|
|
|
|
config_options->box_item = NULL;
|
|
|
|
free_options_tree(&options_root_tree, 0);
|
|
|
|
}
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/*! @relates change_hook_info */
|
2005-09-15 09:58:31 -04:00
|
|
|
void
|
2007-01-27 12:00:47 -05:00
|
|
|
register_change_hooks(const struct change_hook_info *change_hooks)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; change_hooks[i].name; i++) {
|
|
|
|
struct option *option = get_opt_rec(config_options,
|
|
|
|
change_hooks[i].name);
|
|
|
|
|
|
|
|
assert(option);
|
|
|
|
option->change_hook = change_hooks[i].change_hook;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/** Set or clear the ::OPT_MUST_SAVE flag in all descendants of @a tree.
|
2012-11-18 12:48:43 -05:00
|
|
|
*
|
|
|
|
* @param tree
|
|
|
|
* The option tree in which this function recursively sets or clears
|
|
|
|
* the flag.
|
2009-08-15 21:08:59 -04:00
|
|
|
*
|
|
|
|
* @param set_all
|
|
|
|
* If true, set ::OPT_MUST_SAVE in all options of the tree.
|
|
|
|
* If false, set it only in touched or deleted options, and clear in others.
|
|
|
|
*
|
|
|
|
* @relates option */
|
2005-09-15 09:58:31 -04:00
|
|
|
void
|
2008-02-03 06:07:43 -05:00
|
|
|
prepare_mustsave_flags(LIST_OF(struct option) *tree, int set_all)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
struct option *option;
|
|
|
|
|
|
|
|
foreach (option, *tree) {
|
2008-02-03 06:07:43 -05:00
|
|
|
/* XXX: OPT_LANGUAGE shouldn't have any bussiness
|
|
|
|
* here, but we're just weird in that area. */
|
|
|
|
if (set_all
|
|
|
|
|| (option->flags & (OPT_TOUCHED | OPT_DELETED))
|
|
|
|
|| option->type == OPT_LANGUAGE)
|
|
|
|
option->flags |= OPT_MUST_SAVE;
|
|
|
|
else
|
|
|
|
option->flags &= ~OPT_MUST_SAVE;
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
if (option->type == OPT_TREE)
|
2008-02-03 06:07:43 -05:00
|
|
|
prepare_mustsave_flags(option->value.tree, set_all);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/** Clear the ::OPT_TOUCHED flag in all descendants of @a tree.
|
|
|
|
* @relates option */
|
2005-09-15 09:58:31 -04:00
|
|
|
void
|
2008-02-03 12:00:48 -05:00
|
|
|
untouch_options(LIST_OF(struct option) *tree)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
struct option *option;
|
|
|
|
|
|
|
|
foreach (option, *tree) {
|
2008-02-03 12:00:48 -05:00
|
|
|
option->flags &= ~OPT_TOUCHED;
|
|
|
|
|
|
|
|
if (option->type == OPT_TREE)
|
|
|
|
untouch_options(option->value.tree);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/*! @relates option */
|
2005-09-15 09:58:31 -04:00
|
|
|
static int
|
2007-07-26 15:39:08 -04:00
|
|
|
check_nonempty_tree(LIST_OF(struct option) *options)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
struct option *opt;
|
|
|
|
|
|
|
|
foreach (opt, *options) {
|
|
|
|
if (opt->type == OPT_TREE) {
|
|
|
|
if (check_nonempty_tree(opt->value.tree))
|
|
|
|
return 1;
|
2008-02-03 06:07:43 -05:00
|
|
|
} else if (opt->flags & OPT_MUST_SAVE) {
|
2005-09-15 09:58:31 -04:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/*! @relates option */
|
2005-09-15 09:58:31 -04:00
|
|
|
void
|
2019-04-21 06:27:40 -04:00
|
|
|
smart_config_string(struct string *str, int print_comment, int i18n,
|
2007-07-26 15:39:08 -04:00
|
|
|
LIST_OF(struct option) *options,
|
2021-01-02 10:20:27 -05:00
|
|
|
char *path, int depth,
|
2019-04-21 06:27:40 -04:00
|
|
|
void (*fn)(struct string *, struct option *,
|
2021-01-02 10:20:27 -05:00
|
|
|
char *, int, int, int, int))
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
struct option *option;
|
|
|
|
|
|
|
|
foreach (option, *options) {
|
|
|
|
int do_print_comment = 1;
|
|
|
|
|
|
|
|
if (option->flags & OPT_HIDDEN ||
|
|
|
|
option->type == OPT_ALIAS ||
|
|
|
|
!strcmp(option->name, "_template_"))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Is there anything to be printed anyway? */
|
|
|
|
if (option->type == OPT_TREE
|
2008-02-03 06:07:43 -05:00
|
|
|
? !check_nonempty_tree(option->value.tree)
|
|
|
|
: !(option->flags & OPT_MUST_SAVE))
|
2005-09-15 09:58:31 -04:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/* We won't pop out the description when we're in autocreate
|
|
|
|
* category and not template. It'd be boring flood of
|
|
|
|
* repetitive comments otherwise ;). */
|
|
|
|
|
|
|
|
/* This print_comment parameter is weird. If it is negative, it
|
|
|
|
* means that we shouldn't print comments at all. If it is 1,
|
|
|
|
* we shouldn't print comment UNLESS the option is _template_
|
|
|
|
* or not-an-autocreating-tree (it is set for the first-level
|
|
|
|
* autocreation tree). When it is 2, we can print out comments
|
|
|
|
* normally. */
|
|
|
|
/* It is still broken somehow, as it didn't work for terminal.*
|
|
|
|
* (the first autocreated level) by the time I wrote this. Good
|
|
|
|
* summer job for bored mad hackers with spare boolean mental
|
|
|
|
* power. I have better things to think about, personally.
|
|
|
|
* Maybe we should just mark autocreated options somehow ;). */
|
|
|
|
if (!print_comment || (print_comment == 1
|
|
|
|
&& (strcmp(option->name, "_template_")
|
|
|
|
&& (option->flags & OPT_AUTOCREATE
|
|
|
|
&& option->type == OPT_TREE))))
|
|
|
|
do_print_comment = 0;
|
|
|
|
|
|
|
|
/* Pop out the comment */
|
|
|
|
|
|
|
|
/* For config file, we ignore do_print_comment everywhere
|
|
|
|
* except 1, but sometimes we want to skip the option totally.
|
|
|
|
*/
|
|
|
|
fn(str, option, path, depth,
|
|
|
|
option->type == OPT_TREE ? print_comment
|
|
|
|
: do_print_comment,
|
|
|
|
0, i18n);
|
|
|
|
|
|
|
|
fn(str, option, path, depth, do_print_comment, 1, i18n);
|
|
|
|
|
|
|
|
/* And the option itself */
|
|
|
|
|
2022-05-09 12:53:36 -04:00
|
|
|
if (option_types[option->type].write2) {
|
2005-09-15 09:58:31 -04:00
|
|
|
fn(str, option, path, depth,
|
|
|
|
do_print_comment, 2, i18n);
|
|
|
|
|
|
|
|
} else if (option->type == OPT_TREE) {
|
2019-04-21 06:27:40 -04:00
|
|
|
struct string newpath;
|
2005-09-15 09:58:31 -04:00
|
|
|
int pc = print_comment;
|
|
|
|
|
|
|
|
if (!init_string(&newpath)) continue; /* OK? */
|
|
|
|
|
|
|
|
if (pc == 2 && option->flags & OPT_AUTOCREATE)
|
|
|
|
pc = 1;
|
|
|
|
else if (pc == 1 && strcmp(option->name, "_template_"))
|
|
|
|
pc = 0;
|
|
|
|
|
|
|
|
fn(str, option, path, depth, /*pc*/1, 3, i18n);
|
|
|
|
|
|
|
|
if (path) {
|
|
|
|
add_to_string(&newpath, path);
|
|
|
|
add_char_to_string(&newpath, '.');
|
|
|
|
}
|
|
|
|
add_to_string(&newpath, option->name);
|
|
|
|
smart_config_string(str, pc, i18n, option->value.tree,
|
|
|
|
newpath.source, depth + 1, fn);
|
|
|
|
done_string(&newpath);
|
|
|
|
|
|
|
|
fn(str, option, path, depth, /*pc*/1, 3, i18n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
update_options_visibility(void)
|
|
|
|
{
|
|
|
|
update_visibility(config_options->value.tree,
|
2007-08-28 12:41:18 -04:00
|
|
|
get_opt_bool("config.show_template", NULL));
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
toggle_option(struct session *ses, struct option *option)
|
|
|
|
{
|
|
|
|
long number = option->value.number + 1;
|
|
|
|
|
|
|
|
assert(option->type == OPT_BOOL || option->type == OPT_INT);
|
|
|
|
assert(option->max);
|
|
|
|
|
|
|
|
option->value.number = (number <= option->max) ? number : option->min;
|
2007-01-27 12:52:49 -05:00
|
|
|
option_changed(ses, option);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
call_change_hooks(struct session *ses, struct option *current, struct option *option)
|
|
|
|
{
|
|
|
|
/* This boolean thing can look a little weird - it
|
|
|
|
* basically says that we should proceed when there's
|
|
|
|
* no change_hook or there's one and its return value
|
|
|
|
* was zero. */
|
|
|
|
while (current && (!current->change_hook ||
|
|
|
|
!current->change_hook(ses, current, option))) {
|
|
|
|
if (!current->root)
|
|
|
|
break;
|
|
|
|
|
|
|
|
current = current->root;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-10-17 17:20:53 -04:00
|
|
|
void
|
2007-01-27 12:52:49 -05:00
|
|
|
option_changed(struct session *ses, struct option *option)
|
2005-10-17 17:20:53 -04:00
|
|
|
{
|
|
|
|
option->flags |= OPT_TOUCHED;
|
|
|
|
/* Notify everyone out there! */
|
2007-01-27 12:52:49 -05:00
|
|
|
call_change_hooks(ses, option, option);
|
2005-10-17 17:20:53 -04:00
|
|
|
}
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/*! @relates option_resolver */
|
2005-09-15 09:58:31 -04:00
|
|
|
int
|
|
|
|
commit_option_values(struct option_resolver *resolvers,
|
|
|
|
struct option *root, union option_value *values, int size)
|
|
|
|
{
|
|
|
|
int touched = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
assert(resolvers && root && values && size);
|
|
|
|
|
|
|
|
for (i = 0; i < size; i++) {
|
2022-02-04 12:19:15 -05:00
|
|
|
const char *name = resolvers[i].name;
|
2005-09-15 09:58:31 -04:00
|
|
|
struct option *option = get_opt_rec(root, name);
|
|
|
|
int id = resolvers[i].id;
|
|
|
|
|
2007-12-21 17:49:38 -05:00
|
|
|
assertm(option, "Bad option '%s' in options resolver", name);
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
if (memcmp(&option->value, &values[id], sizeof(union option_value))) {
|
|
|
|
option->value = values[id];
|
|
|
|
option->flags |= OPT_TOUCHED;
|
|
|
|
/* Speed hack: Directly call the change-hook for each
|
|
|
|
* option in resolvers and later call call_change_hooks
|
|
|
|
* on the root; if we were to call call_change_hooks
|
|
|
|
* on each option in resolvers, we would end up calling
|
|
|
|
* the change-hooks of root, its parents, its
|
|
|
|
* grandparents, and so on for each option in resolvers
|
|
|
|
* because call_change_hooks is recursive. -- Miciah */
|
|
|
|
if (option->change_hook)
|
|
|
|
option->change_hook(NULL, option, NULL);
|
|
|
|
touched++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* See above 'Speed hack' comment. */
|
|
|
|
call_change_hooks(NULL, root, NULL);
|
|
|
|
|
|
|
|
return touched;
|
|
|
|
}
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/*! @relates option_resolver */
|
2005-09-15 09:58:31 -04:00
|
|
|
void
|
|
|
|
checkout_option_values(struct option_resolver *resolvers,
|
|
|
|
struct option *root,
|
|
|
|
union option_value *values, int size)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < size; i++) {
|
2022-02-04 12:19:15 -05:00
|
|
|
const char *name = resolvers[i].name;
|
2005-09-15 09:58:31 -04:00
|
|
|
struct option *option = get_opt_rec(root, name);
|
|
|
|
int id = resolvers[i].id;
|
|
|
|
|
2007-12-21 17:49:38 -05:00
|
|
|
assertm(option, "Bad option '%s' in options resolver", name);
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
values[id] = option->value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**********************************************************************
|
|
|
|
Options values
|
|
|
|
**********************************************************************/
|
|
|
|
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/*! @relates option_info */
|
2005-09-15 09:58:31 -04:00
|
|
|
void
|
bug 764: Initialize the right member of union option_value
INIT_OPTION used to initialize union option_value at compile time by
casting the default value to LIST_OF(struct option) *, which is the
type of the first member. On sparc64 and other big-endian systems
where sizeof(int) < sizeof(struct list_head *), this tended to leave
option->value.number as zero, thus messing up OPT_INT and OPT_BOOL
at least. OPT_LONG however tended to work right.
This would be easy to fix with C99 designated initializers,
but doc/hacking.txt says ELinks must be kept C89 compatible.
Another solution would be to make register_options() read the
value from option->value.tree (the first member), cast it back
to the right type, and write it to the appropriate member;
but that would still require somewhat dubious conversions
between integers, data pointers, and function pointers.
So here's a rather more invasive solution. Add struct option_init,
which is somewhat similar to struct option but has non-overlapping
members for different types of values, to ensure nothing is lost
in compile-time conversions. Move unsigned char *path from struct
option_info to struct option_init, and replace struct option_info
with a union that contains struct option_init and struct option.
Now, this union can be initialized with no portability problems,
and register_options() then moves the values from struct option_init
to their final places in struct option.
In my x86 ELinks build with plenty of options configured in, this
change bloated the text section by 340 bytes but compressed the data
section by 2784 bytes, presumably because union option_info is a
pointer smaller than struct option_info was.
(cherry picked from elinks-0.12 commit e5f6592ee20780a61f70feeb1f9e17631b9c5835)
Conflicts:
src/protocol/fsp/fsp.c: All options had been removed in 0.13.GIT.
src/protocol/smb/smb2.c: Ditto.
2009-08-15 15:39:07 -04:00
|
|
|
register_options(union option_info info[], struct option *tree)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
int i;
|
2009-08-21 17:04:50 -04:00
|
|
|
static const struct option zero = INIT_OPTION(
|
2022-01-24 13:11:06 -05:00
|
|
|
NULL, OPT_ZERO, OPT_BOOL, 0, 0, 0, NULL, NULL);
|
2009-08-21 17:04:50 -04:00
|
|
|
|
|
|
|
/* To let unregister_options() correctly find the end of the
|
|
|
|
* info[] array, this loop must convert every element from
|
|
|
|
* struct option_init to struct option. That is, even if
|
|
|
|
* there is not enough memory to fully initialize some option,
|
|
|
|
* the loop must continue. */
|
bug 764: Initialize the right member of union option_value
INIT_OPTION used to initialize union option_value at compile time by
casting the default value to LIST_OF(struct option) *, which is the
type of the first member. On sparc64 and other big-endian systems
where sizeof(int) < sizeof(struct list_head *), this tended to leave
option->value.number as zero, thus messing up OPT_INT and OPT_BOOL
at least. OPT_LONG however tended to work right.
This would be easy to fix with C99 designated initializers,
but doc/hacking.txt says ELinks must be kept C89 compatible.
Another solution would be to make register_options() read the
value from option->value.tree (the first member), cast it back
to the right type, and write it to the appropriate member;
but that would still require somewhat dubious conversions
between integers, data pointers, and function pointers.
So here's a rather more invasive solution. Add struct option_init,
which is somewhat similar to struct option but has non-overlapping
members for different types of values, to ensure nothing is lost
in compile-time conversions. Move unsigned char *path from struct
option_info to struct option_init, and replace struct option_info
with a union that contains struct option_init and struct option.
Now, this union can be initialized with no portability problems,
and register_options() then moves the values from struct option_init
to their final places in struct option.
In my x86 ELinks build with plenty of options configured in, this
change bloated the text section by 340 bytes but compressed the data
section by 2784 bytes, presumably because union option_info is a
pointer smaller than struct option_info was.
(cherry picked from elinks-0.12 commit e5f6592ee20780a61f70feeb1f9e17631b9c5835)
Conflicts:
src/protocol/fsp/fsp.c: All options had been removed in 0.13.GIT.
src/protocol/smb/smb2.c: Ditto.
2009-08-15 15:39:07 -04:00
|
|
|
for (i = 0; info[i].init.path; i++) {
|
2009-08-21 17:04:50 -04:00
|
|
|
/* Copy the value aside before it is overwritten
|
|
|
|
* with the other member of the union. */
|
bug 764: Initialize the right member of union option_value
INIT_OPTION used to initialize union option_value at compile time by
casting the default value to LIST_OF(struct option) *, which is the
type of the first member. On sparc64 and other big-endian systems
where sizeof(int) < sizeof(struct list_head *), this tended to leave
option->value.number as zero, thus messing up OPT_INT and OPT_BOOL
at least. OPT_LONG however tended to work right.
This would be easy to fix with C99 designated initializers,
but doc/hacking.txt says ELinks must be kept C89 compatible.
Another solution would be to make register_options() read the
value from option->value.tree (the first member), cast it back
to the right type, and write it to the appropriate member;
but that would still require somewhat dubious conversions
between integers, data pointers, and function pointers.
So here's a rather more invasive solution. Add struct option_init,
which is somewhat similar to struct option but has non-overlapping
members for different types of values, to ensure nothing is lost
in compile-time conversions. Move unsigned char *path from struct
option_info to struct option_init, and replace struct option_info
with a union that contains struct option_init and struct option.
Now, this union can be initialized with no portability problems,
and register_options() then moves the values from struct option_init
to their final places in struct option.
In my x86 ELinks build with plenty of options configured in, this
change bloated the text section by 340 bytes but compressed the data
section by 2784 bytes, presumably because union option_info is a
pointer smaller than struct option_info was.
(cherry picked from elinks-0.12 commit e5f6592ee20780a61f70feeb1f9e17631b9c5835)
Conflicts:
src/protocol/fsp/fsp.c: All options had been removed in 0.13.GIT.
src/protocol/smb/smb2.c: Ditto.
2009-08-15 15:39:07 -04:00
|
|
|
const struct option_init init = info[i].init;
|
2009-08-21 17:04:50 -04:00
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
struct option *option = &info[i].option;
|
2021-01-02 10:20:27 -05:00
|
|
|
char *string;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
bug 764: Initialize the right member of union option_value
INIT_OPTION used to initialize union option_value at compile time by
casting the default value to LIST_OF(struct option) *, which is the
type of the first member. On sparc64 and other big-endian systems
where sizeof(int) < sizeof(struct list_head *), this tended to leave
option->value.number as zero, thus messing up OPT_INT and OPT_BOOL
at least. OPT_LONG however tended to work right.
This would be easy to fix with C99 designated initializers,
but doc/hacking.txt says ELinks must be kept C89 compatible.
Another solution would be to make register_options() read the
value from option->value.tree (the first member), cast it back
to the right type, and write it to the appropriate member;
but that would still require somewhat dubious conversions
between integers, data pointers, and function pointers.
So here's a rather more invasive solution. Add struct option_init,
which is somewhat similar to struct option but has non-overlapping
members for different types of values, to ensure nothing is lost
in compile-time conversions. Move unsigned char *path from struct
option_info to struct option_init, and replace struct option_info
with a union that contains struct option_init and struct option.
Now, this union can be initialized with no portability problems,
and register_options() then moves the values from struct option_init
to their final places in struct option.
In my x86 ELinks build with plenty of options configured in, this
change bloated the text section by 340 bytes but compressed the data
section by 2784 bytes, presumably because union option_info is a
pointer smaller than struct option_info was.
(cherry picked from elinks-0.12 commit e5f6592ee20780a61f70feeb1f9e17631b9c5835)
Conflicts:
src/protocol/fsp/fsp.c: All options had been removed in 0.13.GIT.
src/protocol/smb/smb2.c: Ditto.
2009-08-15 15:39:07 -04:00
|
|
|
*option = zero;
|
|
|
|
option->name = init.name;
|
|
|
|
option->capt = init.capt;
|
|
|
|
option->desc = init.desc;
|
|
|
|
option->flags = init.flags;
|
|
|
|
option->type = init.type;
|
|
|
|
option->min = init.min;
|
|
|
|
option->max = init.max;
|
|
|
|
/* init.value_long, init.value_dataptr, or init.value_funcptr
|
|
|
|
* is handled below as appropriate for each type. */
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
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);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (option->type) {
|
|
|
|
case OPT_TREE:
|
|
|
|
option->value.tree = init_options_tree();
|
|
|
|
if (!option->value.tree) {
|
|
|
|
delete_option(option);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case OPT_STRING:
|
2022-01-16 13:09:27 -05:00
|
|
|
string = (char *)mem_alloc(MAX_STR_LEN);
|
2005-09-15 09:58:31 -04:00
|
|
|
if (!string) {
|
|
|
|
delete_option(option);
|
|
|
|
continue;
|
|
|
|
}
|
2022-01-25 11:26:39 -05:00
|
|
|
safe_strncpy(string, (const char *)init.value_dataptr, MAX_STR_LEN);
|
2005-09-15 09:58:31 -04:00
|
|
|
option->value.string = string;
|
|
|
|
break;
|
|
|
|
case OPT_COLOR:
|
2022-01-25 11:26:39 -05:00
|
|
|
string = (char *)init.value_dataptr;
|
2005-09-15 09:58:31 -04:00
|
|
|
assert(string);
|
|
|
|
decode_color(string, strlen(string),
|
|
|
|
&option->value.color);
|
|
|
|
break;
|
|
|
|
case OPT_CODEPAGE:
|
2022-01-25 11:26:39 -05:00
|
|
|
string = (char *)init.value_dataptr;
|
2005-09-15 09:58:31 -04:00
|
|
|
assert(string);
|
|
|
|
option->value.number = get_cp_index(string);
|
|
|
|
break;
|
|
|
|
case OPT_BOOL:
|
|
|
|
case OPT_INT:
|
bug 764: Initialize the right member of union option_value
INIT_OPTION used to initialize union option_value at compile time by
casting the default value to LIST_OF(struct option) *, which is the
type of the first member. On sparc64 and other big-endian systems
where sizeof(int) < sizeof(struct list_head *), this tended to leave
option->value.number as zero, thus messing up OPT_INT and OPT_BOOL
at least. OPT_LONG however tended to work right.
This would be easy to fix with C99 designated initializers,
but doc/hacking.txt says ELinks must be kept C89 compatible.
Another solution would be to make register_options() read the
value from option->value.tree (the first member), cast it back
to the right type, and write it to the appropriate member;
but that would still require somewhat dubious conversions
between integers, data pointers, and function pointers.
So here's a rather more invasive solution. Add struct option_init,
which is somewhat similar to struct option but has non-overlapping
members for different types of values, to ensure nothing is lost
in compile-time conversions. Move unsigned char *path from struct
option_info to struct option_init, and replace struct option_info
with a union that contains struct option_init and struct option.
Now, this union can be initialized with no portability problems,
and register_options() then moves the values from struct option_init
to their final places in struct option.
In my x86 ELinks build with plenty of options configured in, this
change bloated the text section by 340 bytes but compressed the data
section by 2784 bytes, presumably because union option_info is a
pointer smaller than struct option_info was.
(cherry picked from elinks-0.12 commit e5f6592ee20780a61f70feeb1f9e17631b9c5835)
Conflicts:
src/protocol/fsp/fsp.c: All options had been removed in 0.13.GIT.
src/protocol/smb/smb2.c: Ditto.
2009-08-15 15:39:07 -04:00
|
|
|
option->value.number = init.value_long;
|
|
|
|
break;
|
2005-09-15 09:58:31 -04:00
|
|
|
case OPT_LONG:
|
bug 764: Initialize the right member of union option_value
INIT_OPTION used to initialize union option_value at compile time by
casting the default value to LIST_OF(struct option) *, which is the
type of the first member. On sparc64 and other big-endian systems
where sizeof(int) < sizeof(struct list_head *), this tended to leave
option->value.number as zero, thus messing up OPT_INT and OPT_BOOL
at least. OPT_LONG however tended to work right.
This would be easy to fix with C99 designated initializers,
but doc/hacking.txt says ELinks must be kept C89 compatible.
Another solution would be to make register_options() read the
value from option->value.tree (the first member), cast it back
to the right type, and write it to the appropriate member;
but that would still require somewhat dubious conversions
between integers, data pointers, and function pointers.
So here's a rather more invasive solution. Add struct option_init,
which is somewhat similar to struct option but has non-overlapping
members for different types of values, to ensure nothing is lost
in compile-time conversions. Move unsigned char *path from struct
option_info to struct option_init, and replace struct option_info
with a union that contains struct option_init and struct option.
Now, this union can be initialized with no portability problems,
and register_options() then moves the values from struct option_init
to their final places in struct option.
In my x86 ELinks build with plenty of options configured in, this
change bloated the text section by 340 bytes but compressed the data
section by 2784 bytes, presumably because union option_info is a
pointer smaller than struct option_info was.
(cherry picked from elinks-0.12 commit e5f6592ee20780a61f70feeb1f9e17631b9c5835)
Conflicts:
src/protocol/fsp/fsp.c: All options had been removed in 0.13.GIT.
src/protocol/smb/smb2.c: Ditto.
2009-08-15 15:39:07 -04:00
|
|
|
option->value.big_number = init.value_long;
|
|
|
|
break;
|
2005-09-15 09:58:31 -04:00
|
|
|
case OPT_LANGUAGE:
|
bug 764: Initialize the right member of union option_value
INIT_OPTION used to initialize union option_value at compile time by
casting the default value to LIST_OF(struct option) *, which is the
type of the first member. On sparc64 and other big-endian systems
where sizeof(int) < sizeof(struct list_head *), this tended to leave
option->value.number as zero, thus messing up OPT_INT and OPT_BOOL
at least. OPT_LONG however tended to work right.
This would be easy to fix with C99 designated initializers,
but doc/hacking.txt says ELinks must be kept C89 compatible.
Another solution would be to make register_options() read the
value from option->value.tree (the first member), cast it back
to the right type, and write it to the appropriate member;
but that would still require somewhat dubious conversions
between integers, data pointers, and function pointers.
So here's a rather more invasive solution. Add struct option_init,
which is somewhat similar to struct option but has non-overlapping
members for different types of values, to ensure nothing is lost
in compile-time conversions. Move unsigned char *path from struct
option_info to struct option_init, and replace struct option_info
with a union that contains struct option_init and struct option.
Now, this union can be initialized with no portability problems,
and register_options() then moves the values from struct option_init
to their final places in struct option.
In my x86 ELinks build with plenty of options configured in, this
change bloated the text section by 340 bytes but compressed the data
section by 2784 bytes, presumably because union option_info is a
pointer smaller than struct option_info was.
(cherry picked from elinks-0.12 commit e5f6592ee20780a61f70feeb1f9e17631b9c5835)
Conflicts:
src/protocol/fsp/fsp.c: All options had been removed in 0.13.GIT.
src/protocol/smb/smb2.c: Ditto.
2009-08-15 15:39:07 -04:00
|
|
|
/* INIT_OPT_LANGUAGE has no def parameter */
|
|
|
|
option->value.number = 0;
|
|
|
|
break;
|
2005-09-15 09:58:31 -04:00
|
|
|
case OPT_COMMAND:
|
bug 764: Initialize the right member of union option_value
INIT_OPTION used to initialize union option_value at compile time by
casting the default value to LIST_OF(struct option) *, which is the
type of the first member. On sparc64 and other big-endian systems
where sizeof(int) < sizeof(struct list_head *), this tended to leave
option->value.number as zero, thus messing up OPT_INT and OPT_BOOL
at least. OPT_LONG however tended to work right.
This would be easy to fix with C99 designated initializers,
but doc/hacking.txt says ELinks must be kept C89 compatible.
Another solution would be to make register_options() read the
value from option->value.tree (the first member), cast it back
to the right type, and write it to the appropriate member;
but that would still require somewhat dubious conversions
between integers, data pointers, and function pointers.
So here's a rather more invasive solution. Add struct option_init,
which is somewhat similar to struct option but has non-overlapping
members for different types of values, to ensure nothing is lost
in compile-time conversions. Move unsigned char *path from struct
option_info to struct option_init, and replace struct option_info
with a union that contains struct option_init and struct option.
Now, this union can be initialized with no portability problems,
and register_options() then moves the values from struct option_init
to their final places in struct option.
In my x86 ELinks build with plenty of options configured in, this
change bloated the text section by 340 bytes but compressed the data
section by 2784 bytes, presumably because union option_info is a
pointer smaller than struct option_info was.
(cherry picked from elinks-0.12 commit e5f6592ee20780a61f70feeb1f9e17631b9c5835)
Conflicts:
src/protocol/fsp/fsp.c: All options had been removed in 0.13.GIT.
src/protocol/smb/smb2.c: Ditto.
2009-08-15 15:39:07 -04:00
|
|
|
option->value.command = init.value_funcptr;
|
|
|
|
break;
|
2005-09-15 09:58:31 -04:00
|
|
|
case OPT_ALIAS:
|
2022-01-25 11:26:39 -05:00
|
|
|
option->value.string = (char *)init.value_dataptr;
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
bug 764: Initialize the right member of union option_value
INIT_OPTION used to initialize union option_value at compile time by
casting the default value to LIST_OF(struct option) *, which is the
type of the first member. On sparc64 and other big-endian systems
where sizeof(int) < sizeof(struct list_head *), this tended to leave
option->value.number as zero, thus messing up OPT_INT and OPT_BOOL
at least. OPT_LONG however tended to work right.
This would be easy to fix with C99 designated initializers,
but doc/hacking.txt says ELinks must be kept C89 compatible.
Another solution would be to make register_options() read the
value from option->value.tree (the first member), cast it back
to the right type, and write it to the appropriate member;
but that would still require somewhat dubious conversions
between integers, data pointers, and function pointers.
So here's a rather more invasive solution. Add struct option_init,
which is somewhat similar to struct option but has non-overlapping
members for different types of values, to ensure nothing is lost
in compile-time conversions. Move unsigned char *path from struct
option_info to struct option_init, and replace struct option_info
with a union that contains struct option_init and struct option.
Now, this union can be initialized with no portability problems,
and register_options() then moves the values from struct option_init
to their final places in struct option.
In my x86 ELinks build with plenty of options configured in, this
change bloated the text section by 340 bytes but compressed the data
section by 2784 bytes, presumably because union option_info is a
pointer smaller than struct option_info was.
(cherry picked from elinks-0.12 commit e5f6592ee20780a61f70feeb1f9e17631b9c5835)
Conflicts:
src/protocol/fsp/fsp.c: All options had been removed in 0.13.GIT.
src/protocol/smb/smb2.c: Ditto.
2009-08-15 15:39:07 -04:00
|
|
|
add_opt_rec(tree, init.path, option);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
2009-08-21 17:04:50 -04:00
|
|
|
|
|
|
|
/* Convert the sentinel at the end of the array, too. */
|
|
|
|
info[i].option = zero;
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
2009-08-15 21:08:59 -04:00
|
|
|
/*! @relates option_info */
|
2005-09-15 09:58:31 -04:00
|
|
|
void
|
bug 764: Initialize the right member of union option_value
INIT_OPTION used to initialize union option_value at compile time by
casting the default value to LIST_OF(struct option) *, which is the
type of the first member. On sparc64 and other big-endian systems
where sizeof(int) < sizeof(struct list_head *), this tended to leave
option->value.number as zero, thus messing up OPT_INT and OPT_BOOL
at least. OPT_LONG however tended to work right.
This would be easy to fix with C99 designated initializers,
but doc/hacking.txt says ELinks must be kept C89 compatible.
Another solution would be to make register_options() read the
value from option->value.tree (the first member), cast it back
to the right type, and write it to the appropriate member;
but that would still require somewhat dubious conversions
between integers, data pointers, and function pointers.
So here's a rather more invasive solution. Add struct option_init,
which is somewhat similar to struct option but has non-overlapping
members for different types of values, to ensure nothing is lost
in compile-time conversions. Move unsigned char *path from struct
option_info to struct option_init, and replace struct option_info
with a union that contains struct option_init and struct option.
Now, this union can be initialized with no portability problems,
and register_options() then moves the values from struct option_init
to their final places in struct option.
In my x86 ELinks build with plenty of options configured in, this
change bloated the text section by 340 bytes but compressed the data
section by 2784 bytes, presumably because union option_info is a
pointer smaller than struct option_info was.
(cherry picked from elinks-0.12 commit e5f6592ee20780a61f70feeb1f9e17631b9c5835)
Conflicts:
src/protocol/fsp/fsp.c: All options had been removed in 0.13.GIT.
src/protocol/smb/smb2.c: Ditto.
2009-08-15 15:39:07 -04:00
|
|
|
unregister_options(union option_info info[], struct option *tree)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
/* We need to remove the options in inverse order to the order how we
|
|
|
|
* added them. */
|
|
|
|
|
bug 764: Initialize the right member of union option_value
INIT_OPTION used to initialize union option_value at compile time by
casting the default value to LIST_OF(struct option) *, which is the
type of the first member. On sparc64 and other big-endian systems
where sizeof(int) < sizeof(struct list_head *), this tended to leave
option->value.number as zero, thus messing up OPT_INT and OPT_BOOL
at least. OPT_LONG however tended to work right.
This would be easy to fix with C99 designated initializers,
but doc/hacking.txt says ELinks must be kept C89 compatible.
Another solution would be to make register_options() read the
value from option->value.tree (the first member), cast it back
to the right type, and write it to the appropriate member;
but that would still require somewhat dubious conversions
between integers, data pointers, and function pointers.
So here's a rather more invasive solution. Add struct option_init,
which is somewhat similar to struct option but has non-overlapping
members for different types of values, to ensure nothing is lost
in compile-time conversions. Move unsigned char *path from struct
option_info to struct option_init, and replace struct option_info
with a union that contains struct option_init and struct option.
Now, this union can be initialized with no portability problems,
and register_options() then moves the values from struct option_init
to their final places in struct option.
In my x86 ELinks build with plenty of options configured in, this
change bloated the text section by 340 bytes but compressed the data
section by 2784 bytes, presumably because union option_info is a
pointer smaller than struct option_info was.
(cherry picked from elinks-0.12 commit e5f6592ee20780a61f70feeb1f9e17631b9c5835)
Conflicts:
src/protocol/fsp/fsp.c: All options had been removed in 0.13.GIT.
src/protocol/smb/smb2.c: Ditto.
2009-08-15 15:39:07 -04:00
|
|
|
while (info[i].option.name) i++;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
for (i--; i >= 0; i--)
|
|
|
|
delete_option_do(&info[i].option, 0);
|
|
|
|
}
|