diff --git a/NEWS b/NEWS index 52a106fc..2219d5ee 100644 --- a/NEWS +++ b/NEWS @@ -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 diff --git a/src/bfu/leds.c b/src/bfu/leds.c index aeac9450..5cb76779 100644 --- a/src/bfu/leds.c +++ b/src/bfu/leds.c @@ -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.")), diff --git a/src/bookmarks/bookmarks.c b/src/bookmarks/bookmarks.c index dcc6f96b..3876fe21 100644 --- a/src/bookmarks/bookmarks.c +++ b/src/bookmarks/bookmarks.c @@ -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.")), diff --git a/src/config/cmdline.c b/src/config/cmdline.c index 3891b767..a1711465 100644 --- a/src/config/cmdline.c +++ b/src/config/cmdline.c @@ -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, diff --git a/src/config/options.c b/src/config/options.c index dcc0ef97..f4fe044d 100644 --- a/src/config/options.c +++ b/src/config/options.c @@ -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); diff --git a/src/config/options.h b/src/config/options.h index ba7fd1ac..bd9954bd 100644 --- a/src/config/options.h +++ b/src/config/options.h @@ -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 ;). */ diff --git a/src/config/options.inc b/src/config/options.inc index a267abff..221107fb 100644 --- a/src/config/options.inc +++ b/src/config/options.inc @@ -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, diff --git a/src/cookies/cookies.c b/src/cookies/cookies.c index bc02d16d..ad8db5c3 100644 --- a/src/cookies/cookies.c +++ b/src/cookies/cookies.c @@ -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.")), diff --git a/src/document/css/css.c b/src/document/css/css.c index 87229de2..ba6f22e2 100644 --- a/src/document/css/css.c +++ b/src/document/css/css.c @@ -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 " diff --git a/src/ecmascript/ecmascript.c b/src/ecmascript/ecmascript.c index 3df8e981..e493b6f4 100644 --- a/src/ecmascript/ecmascript.c +++ b/src/ecmascript/ecmascript.c @@ -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.")), diff --git a/src/formhist/formhist.c b/src/formhist/formhist.c index b305a7c7..edf3be28 100644 --- a/src/formhist/formhist.c +++ b/src/formhist/formhist.c @@ -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. " diff --git a/src/globhist/globhist.c b/src/globhist/globhist.c index ecb7c901..6b87aa33 100644 --- a/src/globhist/globhist.c +++ b/src/globhist/globhist.c @@ -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.")), diff --git a/src/main/module.h b/src/main/module.h index 3b56e40b..891bb7ad 100644 --- a/src/main/module.h +++ b/src/main/module.h @@ -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. */ diff --git a/src/mime/backend/default.c b/src/mime/backend/default.c index 2f21dd9c..09578d76 100644 --- a/src/mime/backend/default.c +++ b/src/mime/backend/default.c @@ -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 " diff --git a/src/mime/backend/mailcap.c b/src/mime/backend/mailcap.c index eb186e09..9f92a9a4 100644 --- a/src/mime/backend/mailcap.c +++ b/src/mime/backend/mailcap.c @@ -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.")), diff --git a/src/mime/backend/mimetypes.c b/src/mime/backend/mimetypes.c index 6e3e6753..28c9b295 100644 --- a/src/mime/backend/mimetypes.c +++ b/src/mime/backend/mimetypes.c @@ -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 " diff --git a/src/mime/mime.c b/src/mime/mime.c index db47294e..1c8268cc 100644 --- a/src/mime/mime.c +++ b/src/mime/mime.c @@ -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).")), diff --git a/src/network/ssl/ssl.c b/src/network/ssl/ssl.c index 79f11f20..59a69577 100644 --- a/src/network/ssl/ssl.c +++ b/src/network/ssl/ssl.c @@ -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.")), diff --git a/src/protocol/bittorrent/bittorrent.c b/src/protocol/bittorrent/bittorrent.c index 3287e233..60dfca37 100644 --- a/src/protocol/bittorrent/bittorrent.c +++ b/src/protocol/bittorrent/bittorrent.c @@ -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.")), diff --git a/src/protocol/file/cgi.c b/src/protocol/file/cgi.c index 9d330684..e4ea70ac 100644 --- a/src/protocol/file/cgi.c +++ b/src/protocol/file/cgi.c @@ -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.")), diff --git a/src/protocol/file/file.c b/src/protocol/file/file.c index f7ff57b3..0adaeff1 100644 --- a/src/protocol/file/file.c +++ b/src/protocol/file/file.c @@ -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.")), diff --git a/src/protocol/ftp/ftp.c b/src/protocol/ftp/ftp.c index c0573756..dd7dd105 100644 --- a/src/protocol/ftp/ftp.c +++ b/src/protocol/ftp/ftp.c @@ -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.")), diff --git a/src/protocol/http/http.c b/src/protocol/http/http.c index 98053c04..f03e9c85 100644 --- a/src/protocol/http/http.c +++ b/src/protocol/http/http.c @@ -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.")), diff --git a/src/protocol/nntp/nntp.c b/src/protocol/nntp/nntp.c index 45016bc5..db05ec2f 100644 --- a/src/protocol/nntp/nntp.c +++ b/src/protocol/nntp/nntp.c @@ -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.")), diff --git a/src/protocol/protocol.c b/src/protocol/protocol.c index 285c0f74..aecfd098 100644 --- a/src/protocol/protocol.c +++ b/src/protocol/protocol.c @@ -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.")), diff --git a/src/protocol/rewrite/rewrite.c b/src/protocol/rewrite/rewrite.c index e01da74e..4ed81830 100644 --- a/src/protocol/rewrite/rewrite.c +++ b/src/protocol/rewrite/rewrite.c @@ -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. " diff --git a/src/protocol/user.c b/src/protocol/user.c index 8c6f9782..e6cedb52 100644 --- a/src/protocol/user.c +++ b/src/protocol/user.c @@ -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 "