1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-06-15 23:35:34 +00: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 e5f6592ee2)

Conflicts:
	src/protocol/fsp/fsp.c: All options had been removed in 0.13.GIT.
	src/protocol/smb/smb2.c: Ditto.
This commit is contained in:
Kalle Olavi Niemitalo 2009-08-15 22:39:07 +03:00 committed by Kalle Olavi Niemitalo
parent 7e8c95d934
commit 8b00e1ef70
27 changed files with 167 additions and 53 deletions

3
NEWS
View File

@ -110,6 +110,9 @@ ELinks 0.12pre5.GIT now:
To be released as 0.12pre6 or 0.12rc1.
* major bug 764: Correctly initialize options on big-endian 64-bit
systems.
Bugs that should be removed from NEWS before the 0.12.0 release:
* critical bug 943: Don't let user JavaScripts call any methods of

View File

@ -73,7 +73,7 @@ enum led_option {
LEDS_OPTIONS,
};
static struct option_info led_options[] = {
static union option_info led_options[] = {
INIT_OPT_TREE("ui", N_("Clock"),
"clock", 0, N_("Digital clock in the status bar.")),

View File

@ -44,7 +44,7 @@ static struct bookmark *bm_snapshot_last_folder;
/* Life functions */
static struct option_info bookmark_options_info[] = {
static union option_info bookmark_options_info[] = {
INIT_OPT_TREE("", N_("Bookmarks"),
"bookmarks", 0,
N_("Bookmark options.")),

View File

@ -734,7 +734,7 @@ printconfigdump_cmd(struct option *option, unsigned char ***argv, int *argc)
/* Keep options in alphabetical order. */
struct option_info cmdline_options_info[] = {
union option_info cmdline_options_info[] = {
/* [gettext_accelerator_context(IGNORE)] */
INIT_OPT_BOOL("", N_("Restrict to anonymous mode"),
"anonymous", 0, 0,

View File

@ -779,8 +779,8 @@ register_autocreated_options(void)
#endif
}
static struct option_info config_options_info[];
extern struct option_info cmdline_options_info[];
static union option_info config_options_info[];
extern union option_info cmdline_options_info[];
static const struct change_hook_info change_hooks[];
void
@ -1188,14 +1188,28 @@ checkout_option_values(struct option_resolver *resolvers,
#include "config/options.inc"
void
register_options(struct option_info info[], struct option *tree)
register_options(union option_info info[], struct option *tree)
{
int i;
for (i = 0; info[i].path; i++) {
for (i = 0; info[i].init.path; i++) {
static const struct option zero = INIT_OPTION(
NULL, 0, 0, 0, 0, 0, NULL, NULL);
const struct option_init init = info[i].init;
struct option *option = &info[i].option;
unsigned char *string;
*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. */
debug_check_option_syntax(option);
if (option->type != OPT_ALIAS
@ -1222,42 +1236,52 @@ register_options(struct option_info info[], struct option *tree)
delete_option(option);
continue;
}
safe_strncpy(string, option->value.string, MAX_STR_LEN);
safe_strncpy(string, init.value_dataptr, MAX_STR_LEN);
option->value.string = string;
break;
case OPT_COLOR:
string = option->value.string;
string = init.value_dataptr;
assert(string);
decode_color(string, strlen(string),
&option->value.color);
break;
case OPT_CODEPAGE:
string = option->value.string;
string = init.value_dataptr;
assert(string);
option->value.number = get_cp_index(string);
break;
case OPT_BOOL:
case OPT_INT:
option->value.number = init.value_long;
break;
case OPT_LONG:
option->value.big_number = init.value_long;
break;
case OPT_LANGUAGE:
/* INIT_OPT_LANGUAGE has no def parameter */
option->value.number = 0;
break;
case OPT_COMMAND:
option->value.command = init.value_funcptr;
break;
case OPT_ALIAS:
option->value.string = init.value_dataptr;
break;
}
add_opt_rec(tree, info[i].path, option);
add_opt_rec(tree, init.path, option);
}
}
void
unregister_options(struct option_info info[], struct option *tree)
unregister_options(union option_info info[], struct option *tree)
{
int i = 0;
/* We need to remove the options in inverse order to the order how we
* added them. */
while (info[i].path) i++;
while (info[i].option.name) i++;
for (i--; i >= 0; i--)
delete_option_do(&info[i].option, 0);

View File

@ -99,6 +99,9 @@ struct listbox_item; /* bfu/listbox.h */
struct option; /* defined later in this file */
struct session; /* session/session.h */
typedef unsigned char *option_command_fn_T(struct option *,
unsigned char ***, int *);
union option_value {
/* XXX: Keep first to make @options_root initialization possible. */
/* The OPT_TREE list_head is allocated. */
@ -114,7 +117,7 @@ union option_value {
color_T color;
/* The OPT_COMMAND value */
unsigned char *(*command)(struct option *, unsigned char ***, int *);
option_command_fn_T *command;
/* The OPT_STRING string is allocated and has length MAX_STR_LEN.
* The OPT_ALIAS string is NOT allocated, has variable length
@ -159,6 +162,9 @@ struct option {
struct listbox_item *box_item;
};
/** An initializer for struct option. This is quite rare:
* most places should instead initialize struct option_init,
* with ::INIT_OPT_INT or a similar macro. */
#define INIT_OPTION(name, flags, type, min, max, value, desc, capt) \
{ NULL_LIST_HEAD, INIT_OBJECT("option"), name, flags, type, min, max, { (LIST_OF(struct option) *) (value) }, desc, capt }
@ -326,49 +332,130 @@ do { \
/* Builtin options */
struct option_info {
struct option option;
/** How to initialize and register struct option. register_options()
* moves the values from this to struct option. This initialization
* must be deferred to run time because C89 provides no portable way
* to initialize the correct member of union option_value at compile
* time. */
struct option_init {
/** The name of the option tree where the option should be
* registered. option.root is computed from this. */
unsigned char *path;
/** The name of the option. This goes to option.name. */
unsigned char *name;
/** The caption shown in the option manager. This goes to
* option.capt. */
unsigned char *capt;
/** The long description shown when the user edits the option,
* or NULL if not available. This goes to option.desc. */
unsigned char *desc;
/** Flags for the option. These go to option.flags. */
enum option_flags flags;
/** Type of the option. This goes to option.type. */
enum option_type type;
/** Minimum and maximum value of the option. These go to
* option.min and option.max. For some option types, @c max
* is the maximum length or fixed length instead. */
long min, max;
/** @name Default value of the option
* The value of an option can be an integer, a data pointer,
* or a function pointer. This structure has a separate
* member for each of those types, to avoid compile-time casts
* that could lose some bits of the value. Although this
* scheme looks a bit bloaty, struct option_init remains
* smaller than struct option and so does not require any
* extra space in union option_info. @{ */
/** The default value of the option, if the #type is ::OPT_BOOL,
* ::OPT_INT, or ::OPT_LONG. Zero otherwise. This goes to
* option_value.number or option_value.big_number. */
long value_long;
/** The default value of the option, if the #type is ::OPT_STRING,
* ::OPT_CODEPAGE, ::OPT_COLOR, or ::OPT_ALIAS. NULL otherwise.
* This goes to option_value.string, or after some parsing to
* option_value.color or option_value.number. */
void *value_dataptr;
/** The constant value of the option, if the #type is ::OPT_COMMAND.
* NULL otherwise. This goes to option_value.command. */
option_command_fn_T *value_funcptr;
/** @} */
};
extern void register_options(struct option_info info[], struct option *tree);
extern void unregister_options(struct option_info info[], struct option *tree);
/** Instructions for registering an option, and storage for the option
* itself. */
union option_info {
/** How to initialize and register #option. This must be the
* first member of the union, to let C89 compilers initialize
* it. */
struct option_init init;
/** register_options() constructs the option here, based on
* the instructions in #init. By doing so, it of course
* overwrites #init. Thus, only @c option can be used
* afterwards. */
struct option option;
};
extern void register_options(union option_info info[], struct option *tree);
extern void unregister_options(union option_info info[], struct option *tree);
#define NULL_OPTION_INFO \
{ INIT_OPTION(NULL, 0, 0, 0, 0, NULL, NULL, NULL), NULL }
{{ NULL, NULL, NULL, NULL, 0, \
0, 0, 0, 0, NULL, NULL }}
#define INIT_OPT_BOOL(path, capt, name, flags, def, desc) \
{ INIT_OPTION(name, flags, OPT_BOOL, 0, 1, def, DESC(desc), capt), path }
{{ path, name, capt, DESC(desc), flags, \
OPT_BOOL, 0, 1, def, NULL, NULL }}
#define INIT_OPT_INT(path, capt, name, flags, min, max, def, desc) \
{ INIT_OPTION(name, flags, OPT_INT, min, max, def, DESC(desc), capt), path }
{{ path, name, capt, DESC(desc), flags, \
OPT_INT, min, max, def, NULL, NULL }}
#define INIT_OPT_LONG(path, capt, name, flags, min, max, def, desc) \
{ INIT_OPTION(name, flags, OPT_LONG, min, max, def, DESC(desc), capt), path }
{{ path, name, capt, DESC(desc), flags, \
OPT_LONG, min, max, def, NULL, NULL }}
#define INIT_OPT_STRING(path, capt, name, flags, def, desc) \
{ INIT_OPTION(name, flags, OPT_STRING, 0, MAX_STR_LEN, def, DESC(desc), capt), path }
{{ path, name, capt, DESC(desc), flags, \
OPT_STRING, 0, MAX_STR_LEN, 0, def, NULL }}
#define INIT_OPT_CODEPAGE(path, capt, name, flags, def, desc) \
{ INIT_OPTION(name, flags, OPT_CODEPAGE, 0, 0, def, DESC(desc), capt), path }
{{ path, name, capt, DESC(desc), flags, \
OPT_CODEPAGE, 0, 0, 0, def, NULL }}
#define INIT_OPT_COLOR(path, capt, name, flags, def, desc) \
{ INIT_OPTION(name, flags, OPT_COLOR, 0, 0, def, DESC(desc), capt), path }
{{ path, name, capt, DESC(desc), flags, \
OPT_COLOR, 0, 0, 0, def, NULL }}
#define INIT_OPT_LANGUAGE(path, capt, name, flags, desc) \
{ INIT_OPTION(name, flags, OPT_LANGUAGE, 0, 0, 0, DESC(desc), capt), path }
{{ path, name, capt, DESC(desc), flags, \
OPT_LANGUAGE, 0, 0, 0, NULL, NULL }}
#define INIT_OPT_COMMAND(path, capt, name, flags, cmd, desc) \
{ INIT_OPTION(name, flags, OPT_COMMAND, 0, 0, cmd, DESC(desc), capt), path }
{{ path, name, capt, DESC(desc), flags, \
OPT_COMMAND, 0, 0, 0, NULL, cmd }}
#define INIT_OPT_CMDALIAS(path, capt, name, flags, def, desc) \
{ INIT_OPTION(name, flags, OPT_ALIAS, 0, sizeof(def) - 1, def, DESC(desc), capt), path }
{{ path, name, capt, DESC(desc), flags, \
OPT_ALIAS, 0, sizeof(def) - 1, 0, def, NULL }}
#define INIT_OPT_ALIAS(path, name, flags, def) \
{ INIT_OPTION(name, flags, OPT_ALIAS, 0, sizeof(def) - 1, def, NULL, NULL), path }
{{ path, name, NULL, NULL, flags, \
OPT_ALIAS, 0, sizeof(def) - 1, 0, def, NULL }}
#define INIT_OPT_TREE(path, capt, name, flags, desc) \
{ INIT_OPTION(name, flags, OPT_TREE, 0, 0, NULL, DESC(desc), capt), path }
{{ path, name, capt, DESC(desc), flags, \
OPT_TREE, 0, 0, 0, NULL, NULL }}
/* TODO: We need to do *something* with this ;). */

View File

@ -14,7 +14,7 @@
* 4. When adding an alias put a comment about the date the alias was added.
* 5. Love thy option system! :) */
static struct option_info config_options_info[] = {
static union option_info config_options_info[] = {
/* [gettext_accelerator_context(IGNORE)] */
INIT_OPT_TREE("", N_("Configuration system"),
"config", 0,

View File

@ -84,7 +84,7 @@ enum cookies_option {
COOKIES_OPTIONS,
};
static struct option_info cookies_options[] = {
static union option_info cookies_options[] = {
INIT_OPT_TREE("", N_("Cookies"),
"cookies", 0,
N_("Cookies options.")),

View File

@ -26,7 +26,7 @@
#include "viewer/text/draw.h"
struct option_info css_options_info[] = {
union option_info css_options_info[] = {
INIT_OPT_TREE("document", N_("Cascading Style Sheets"),
"css", OPT_SORT,
N_("Options concerning how to use CSS for styling "

View File

@ -36,7 +36,7 @@
* messing with your menubar/statusbar visibility, disallow changing the
* statusbar content etc. --pasky */
static struct option_info ecmascript_options[] = {
static union option_info ecmascript_options[] = {
INIT_OPT_TREE("", N_("ECMAScript"),
"ecmascript", 0,
N_("ECMAScript options.")),

View File

@ -32,7 +32,7 @@
* TODO: Password manager GUI (here?) (in dialogs.c, of course --pasky). */
static struct option_info forms_history_options[] = {
static union option_info forms_history_options[] = {
INIT_OPT_BOOL("document.browse.forms", N_("Show form history dialog"),
"show_formhist", 0, 0,
N_("Ask if a login form should be saved to file or not. "

View File

@ -56,7 +56,7 @@ enum global_history_options {
GLOBHIST_OPTIONS,
};
static struct option_info global_history_options[] = {
static union option_info global_history_options[] = {
INIT_OPT_TREE("document.history", N_("Global history"),
"global", 0,
N_("Global history options.")),

View File

@ -13,7 +13,7 @@ struct module {
/* The options that should be registered for this module.
* The table should end with NULL_OPTION_INFO. */
struct option_info *options;
union option_info *options;
/* The event hooks that should be registered for this module.
* The table should end with NULL_EVENT_HOOK_INFO. */

View File

@ -21,7 +21,7 @@
#include "util/string.h"
static struct option_info default_mime_options[] = {
static union option_info default_mime_options[] = {
INIT_OPT_TREE("mime", N_("MIME type associations"),
"type", OPT_AUTOCREATE,
N_("Handler <-> MIME type association. The first sub-tree is "

View File

@ -85,7 +85,7 @@ enum mailcap_option {
MAILCAP_OPTIONS
};
static struct option_info mailcap_options[] = {
static union option_info mailcap_options[] = {
INIT_OPT_TREE("mime", N_("Mailcap"),
"mailcap", 0,
N_("Options for mailcap support.")),

View File

@ -42,7 +42,7 @@ enum mimetypes_option {
};
/* Keep options in alphabetical order. */
static struct option_info mimetypes_options[] = {
static union option_info mimetypes_options[] = {
INIT_OPT_TREE("mime", N_("Mimetypes files"),
"mimetypes", 0,
N_("Options for the support of mime.types files. These files "

View File

@ -34,7 +34,7 @@ enum mime_options {
MIME_OPTIONS,
};
static struct option_info mime_options[] = {
static union option_info mime_options[] = {
INIT_OPT_TREE("", N_("MIME"),
"mime", OPT_SORT,
N_("MIME-related options (handlers of various MIME types).")),

View File

@ -107,7 +107,7 @@ done_openssl(struct module *module)
/* There is no function that undoes SSL_get_ex_new_index. */
}
static struct option_info openssl_options[] = {
static union option_info openssl_options[] = {
INIT_OPT_BOOL("connection.ssl", N_("Verify certificates"),
"cert_verify", 0, 0,
N_("Verify the peer's SSL certificate. Note that this "
@ -215,7 +215,7 @@ done_gnutls(struct module *module)
gnutls_global_deinit();
}
static struct option_info gnutls_options[] = {
static union option_info gnutls_options[] = {
INIT_OPT_BOOL("connection.ssl", N_("Verify certificates"),
"cert_verify", 0, 0,
N_("Verify the peer's SSL certificate. If you enable "
@ -254,7 +254,7 @@ static struct module gnutls_module = struct_module(
#endif /* USE_OPENSSL or CONFIG_GNUTLS */
static struct option_info ssl_options[] = {
static union option_info ssl_options[] = {
INIT_OPT_TREE("connection", N_("SSL"),
"ssl", OPT_SORT,
N_("SSL options.")),

View File

@ -22,7 +22,7 @@
/* Hey, anything is possible, I am about to drink a carrot! */
static struct option_info bittorrent_protocol_options[] = {
static union option_info bittorrent_protocol_options[] = {
INIT_OPT_TREE("protocol", N_("BitTorrent"),
"bittorrent", 0,
N_("BitTorrent specific options.")),

View File

@ -36,7 +36,7 @@
#include "util/env.h"
#include "util/string.h"
static struct option_info cgi_options[] = {
static union option_info cgi_options[] = {
INIT_OPT_TREE("protocol.file", N_("Local CGI"),
"cgi", 0,
N_("Local CGI specific options.")),

View File

@ -38,7 +38,7 @@
#include "util/string.h"
static struct option_info file_options[] = {
static union option_info file_options[] = {
INIT_OPT_TREE("protocol", N_("Local files"),
"file", 0,
N_("Options specific to local browsing.")),

View File

@ -52,7 +52,7 @@
#include "util/string.h"
struct option_info ftp_options[] = {
union option_info ftp_options[] = {
INIT_OPT_TREE("protocol", N_("FTP"),
"ftp", 0,
N_("FTP specific options.")),

View File

@ -68,7 +68,7 @@ static struct auth_entry proxy_auth;
static unsigned char *accept_charset = NULL;
static struct option_info http_options[] = {
static union option_info http_options[] = {
INIT_OPT_TREE("protocol", N_("HTTP"),
"http", 0,
N_("HTTP-specific options.")),

View File

@ -28,7 +28,7 @@ enum nntp_protocol_option {
NNTP_PROTOCOL_OPTIONS,
};
static struct option_info nntp_protocol_options[] = {
static union option_info nntp_protocol_options[] = {
INIT_OPT_TREE("protocol", N_("NNTP"),
"nntp", 0,
N_("NNTP and news specific options.")),

View File

@ -271,7 +271,7 @@ get_protocol_external_handler(struct terminal *term, struct uri *uri)
}
static struct option_info protocol_options[] = {
static union option_info protocol_options[] = {
INIT_OPT_TREE("", N_("Protocols"),
"protocol", OPT_SORT,
N_("Protocol specific options.")),

View File

@ -40,7 +40,7 @@ enum uri_rewrite_option {
URI_REWRITE_OPTIONS,
};
static struct option_info uri_rewrite_options[] = {
static union option_info uri_rewrite_options[] = {
INIT_OPT_TREE("protocol", N_("URI rewriting"),
"rewrite", OPT_SORT,
N_("Rules for rewriting URIs entered in the goto dialog. "

View File

@ -30,7 +30,7 @@
#include "util/string.h"
static struct option_info user_protocol_options[] = {
static union option_info user_protocol_options[] = {
INIT_OPT_TREE("protocol", N_("User protocols"),
"user", OPT_AUTOCREATE,
N_("User protocols. Options in this tree specify external "