diff --git a/NEWS b/NEWS index 1b27fcda0..76ced334a 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,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 989acb601..34b66f474 100644 --- a/src/bfu/leds.c +++ b/src/bfu/leds.c @@ -70,7 +70,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 bf8db05d3..f0903c3b7 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 2c9956a36..ba53a6804 100644 --- a/src/config/cmdline.c +++ b/src/config/cmdline.c @@ -732,7 +732,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 5bf518140..1b4dd349e 100644 --- a/src/config/options.c +++ b/src/config/options.c @@ -706,8 +706,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 @@ -1110,14 +1110,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 @@ -1144,42 +1158,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 2baea8cd1..eaa08f465 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 } @@ -314,49 +320,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 163f7b948..9f64204d1 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 011f853c5..ad5c9fb81 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 d361597e3..6cb40d95e 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 1d2bb368e..d9a6a883a 100644 --- a/src/ecmascript/ecmascript.c +++ b/src/ecmascript/ecmascript.c @@ -37,7 +37,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 b305a7c72..edf3be284 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 ecb7c901d..6b87aa333 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 3b56e40bd..891bb7ad5 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 f2ea4500d..f13aeac67 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 afbb9d12d..12bc40f84 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 6e3e67530..28c9b295c 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 7f0d45410..352080761 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 8f3e7e517..c008121dc 100644 --- a/src/network/ssl/ssl.c +++ b/src/network/ssl/ssl.c @@ -68,7 +68,7 @@ done_openssl(struct module *module) if (context) SSL_CTX_free(context); } -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 " @@ -157,7 +157,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 " @@ -196,7 +196,7 @@ static struct module gnutls_module = struct_module( #endif /* CONFIG_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 902eb61f0..b21266ab3 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 feef28723..c22e23803 100644 --- a/src/protocol/file/cgi.c +++ b/src/protocol/file/cgi.c @@ -35,7 +35,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 a2aba3f18..46a3eac25 100644 --- a/src/protocol/file/file.c +++ b/src/protocol/file/file.c @@ -36,7 +36,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/fsp/fsp.c b/src/protocol/fsp/fsp.c index 4479f5ba4..46c2b64b6 100644 --- a/src/protocol/fsp/fsp.c +++ b/src/protocol/fsp/fsp.c @@ -41,7 +41,7 @@ #include "util/string.h" -struct option_info fsp_options[] = { +union option_info fsp_options[] = { INIT_OPT_TREE("protocol", N_("FSP"), "fsp", 0, N_("FSP specific options.")), diff --git a/src/protocol/ftp/ftp.c b/src/protocol/ftp/ftp.c index 04c994bff..10c9e2833 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 e857f8e92..18cc641a3 100644 --- a/src/protocol/http/http.c +++ b/src/protocol/http/http.c @@ -91,7 +91,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 45016bc5d..db05ec2f5 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 02743c322..b017a2f67 100644 --- a/src/protocol/protocol.c +++ b/src/protocol/protocol.c @@ -269,7 +269,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 42ef5aee2..b33739675 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/smb/smb2.c b/src/protocol/smb/smb2.c index 30452705e..44460b391 100644 --- a/src/protocol/smb/smb2.c +++ b/src/protocol/smb/smb2.c @@ -39,7 +39,7 @@ /* These options are not used. */ #if 0 -struct option_info smb_options[] = { +union option_info smb_options[] = { INIT_OPT_TREE("protocol", N_("SMB"), "smb", 0, N_("SAMBA specific options.")), diff --git a/src/protocol/user.c b/src/protocol/user.c index 8c6f9782d..e6cedb522 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 "