From 4c2de881de86cbb0d5d0abb0fadfb996048849a6 Mon Sep 17 00:00:00 2001 From: Marc Alff Date: Wed, 25 Aug 2010 18:59:28 -0600 Subject: Bug#55873 short startup options do not work in 5.5 Before this fix, the server did not recognize 'short' (as in -a) options but only 'long' (as in --ansi) options in the startup command line, due to earlier changes in 5.5 introduced for the performance schema. The root cause is that handle_options() did not honor the my_getopt_skip_unknown flag when parsing 'short' options. The fix changes handle_options(), so that my_getopt_skip_unknown is honored in all cases. Note that there are limitations to this, see the added doxygen documentation in handle_options(). The current usage of handle_options() by the server to parse early performance schema options fits within the limitations. This has been enforced by an assert for PARSE_EARLY options, for safety. --- mysys/my_getopt.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 93 insertions(+), 7 deletions(-) (limited to 'mysys/my_getopt.c') diff --git a/mysys/my_getopt.c b/mysys/my_getopt.c index 1c5bf0b6b1c..5689e0bafb5 100644 --- a/mysys/my_getopt.c +++ b/mysys/my_getopt.c @@ -98,6 +98,49 @@ void my_getopt_register_get_addr(my_getopt_value func_addr) matches with one of the options in struct 'my_option'. Check that option was given an argument if it requires one Call the optional 'get_one_option()' function once for each option. + + Note that handle_options() can be invoked multiple times to + parse a command line in several steps. + In this case, use the global flag @c my_getopt_skip_unknown to indicate + that options unknown in the current step should be preserved in the + command line for later parsing in subsequent steps. + + For 'long' options (--a_long_option), @c my_getopt_skip_unknown is + fully supported. Command line parameters such as: + - "--a_long_option" + - "--a_long_option=value" + - "--a_long_option value" + will be preserved as is when the option is not known. + + For 'short' options (-S), support for @c my_getopt_skip_unknown + comes with some limitation, because several short options + can also be specified together in the same command line argument, + as in "-XYZ". + + The first use case supported is: all short options are declared. + handle_options() will be able to interpret "-XYZ" as one of: + - an unknown X option + - "-X -Y -Z", three short options with no arguments + - "-X -YZ", where Y is a short option with argument Z + - "-XYZ", where X is a short option with argument YZ + based on the full short options specifications. + + The second use case supported is: no short option is declared. + handle_options() will reject "-XYZ" as unknown, to be parsed later. + + The use case that is explicitly not supported is to provide + only a partial list of short options to handle_options(). + This function can not be expected to extract some option Y + in the middle of the string "-XYZ" in these conditions, + without knowing if X will be declared an option later. + + Note that this limitation only impacts parsing of several + short options from the same command line argument, + as in "mysqld -anW5". + When each short option is properly separated out in the command line + argument, for example in "mysqld -a -n -w5", the code would actually + work even with partial options specs given at each stage. + @param [in, out] argc command line options (count) @param [in, out] argv command line options (values) @param [in] longopts descriptor of all valid options @@ -464,14 +507,57 @@ int handle_options(int *argc, char ***argv, } if (!opt_found) { - if (my_getopt_print_errors) - my_getopt_error_reporter(ERROR_LEVEL, - "%s: unknown option '-%c'", - my_progname, *optend); - return EXIT_UNKNOWN_OPTION; + if (my_getopt_skip_unknown) + { + /* + We are currently parsing a single argv[] argument + of the form "-XYZ", and parsing is done in multiple phases. + One or the argument found is not an option. + */ + if (optend == cur_arg) + { + /* + The first argument, "-X", is not an option + In this case, the entire argument "-XYZ" is rejected + from this phase, and preserved as is for later parsing. + */ + (*argv)[argvpos++]= *pos; + } + else + { + /* + We are in the middle of an "-XYZ" string already, + "-X" has already been parsed, and "Y" (pointed by optend) + is not an option. + Hack the string "-XYZ" to make a "-YZ" substring in it, + and push that to the next parsing phase. + */ + DBUG_ASSERT(optend > *pos); + DBUG_ASSERT(optend > cur_arg); + DBUG_ASSERT(optend <= *pos + strlen(*pos)); + DBUG_ASSERT(*optend); + optend--; + optend[0]= '-'; /* replace 'X' by '-' */ + (*argv)[argvpos++]= optend; + } + /* + Do not continue to parse at the current "-XYZ" argument, + skip to the next argv[] argument instead. + */ + optend= (char*) " "; + } + else + { + if (my_getopt_print_errors) + my_getopt_error_reporter(ERROR_LEVEL, + "%s: unknown option '-%c'", + my_progname, *optend); + return EXIT_UNKNOWN_OPTION; + } } } - (*argc)--; /* option handled (short), decrease argument count */ + if (opt_found) + (*argc)--; /* option handled (short), decrease argument count */ continue; } if ((error= setval(optp, value, argument, set_maximum_value))) @@ -479,7 +565,7 @@ int handle_options(int *argc, char ***argv, if (get_one_option && get_one_option(optp->id, optp, argument)) return EXIT_UNSPECIFIED_ERROR; - (*argc)--; /* option handled (short or long), decrease argument count */ + (*argc)--; /* option handled (long), decrease argument count */ } else /* non-option found */ (*argv)[argvpos++]= cur_arg; -- cgit v1.2.1