/* Command line option handling.
- Copyright (C) 2006, 2007 Free Software Foundation, Inc.
+ Copyright (C) 2006, 2007, 2008, 2010 Free Software Foundation, Inc.
This file is part of GCC.
#include "intl.h"
#include "coretypes.h"
#include "opts.h"
+#include "options.h"
+#include "diagnostic.h"
+#include "tm.h" /* For SWITCH_TAKES_ARG and WORD_SWITCH_TAKES_ARG. */
/* Perform a binary search to find which option the command-line INPUT
- matches. Returns its index in the option array, and N_OPTS
- (cl_options_count) on failure.
+ matches. Returns its index in the option array, and
+ OPT_SPECIAL_unknown on failure.
This routine is quite subtle. A normal binary search is not good
enough because some options can be suffixed with an argument, and
}
/* This is the switch that is the best match but for a different
- front end, or cl_options_count if there is no match at all. */
- match_wrong_lang = cl_options_count;
+ front end, or OPT_SPECIAL_unknown if there is no match at all. */
+ match_wrong_lang = OPT_SPECIAL_unknown;
/* Backtrace the chain of possible matches, returning the longest
one, if any, that fits best. With current GCC switches, this
/* If we haven't remembered a prior match, remember this
one. Any prior match is necessarily better. */
- if (match_wrong_lang == cl_options_count)
+ if (match_wrong_lang == OPT_SPECIAL_unknown)
match_wrong_lang = mn;
}
}
while (mn != cl_options_count);
- /* Return the best wrong match, or cl_options_count if none. */
+ /* Return the best wrong match, or OPT_SPECIAL_unknown if none. */
return match_wrong_lang;
}
+/* If ARG is a non-negative integer made up solely of digits, return its
+ value, otherwise return -1. */
+
+int
+integral_argument (const char *arg)
+{
+ const char *p = arg;
+
+ while (*p && ISDIGIT (*p))
+ p++;
+
+ if (*p == '\0')
+ return atoi (arg);
+
+ return -1;
+}
+
+/* Return whether OPTION is OK for the language given by
+ LANG_MASK. */
+static bool
+option_ok_for_language (const struct cl_option *option,
+ unsigned int lang_mask)
+{
+ if (!(option->flags & lang_mask))
+ return false;
+ else if ((option->flags & CL_TARGET)
+ && (option->flags & (CL_LANG_ALL | CL_DRIVER))
+ && !(option->flags & (lang_mask & ~CL_COMMON & ~CL_TARGET)))
+ /* Complain for target flag language mismatches if any languages
+ are specified. */
+ return false;
+ return true;
+}
+
+
+/* Fill in the canonical option part of *DECODED with an option
+ described by OPT_INDEX, ARG and VALUE. */
+
+static void
+generate_canonical_option (size_t opt_index, const char *arg, int value,
+ struct cl_decoded_option *decoded)
+{
+ const struct cl_option *option = &cl_options[opt_index];
+ const char *opt_text = option->opt_text;
+
+ if (value == 0
+ && !(option->flags & CL_REJECT_NEGATIVE)
+ && (opt_text[1] == 'W' || opt_text[1] == 'f' || opt_text[1] == 'm'))
+ {
+ char *t = XNEWVEC (char, option->opt_len + 5);
+ t[0] = '-';
+ t[1] = opt_text[1];
+ t[2] = 'n';
+ t[3] = 'o';
+ t[4] = '-';
+ memcpy (t + 5, opt_text + 2, option->opt_len);
+ opt_text = t;
+ }
+
+ decoded->canonical_option[2] = NULL;
+ decoded->canonical_option[3] = NULL;
+
+ if (arg)
+ {
+ if ((option->flags & CL_SEPARATE)
+ && !(option->flags & CL_SEPARATE_ALIAS))
+ {
+ decoded->canonical_option[0] = opt_text;
+ decoded->canonical_option[1] = arg;
+ decoded->canonical_option_num_elements = 2;
+ }
+ else
+ {
+ gcc_assert (option->flags & CL_JOINED);
+ decoded->canonical_option[0] = concat (opt_text, arg, NULL);
+ decoded->canonical_option[1] = NULL;
+ decoded->canonical_option_num_elements = 1;
+ }
+ }
+ else
+ {
+ decoded->canonical_option[0] = opt_text;
+ decoded->canonical_option[1] = NULL;
+ decoded->canonical_option_num_elements = 1;
+ }
+}
+
+/* Decode the switch beginning at ARGV for the language indicated by
+ LANG_MASK (including CL_COMMON and CL_TARGET if applicable), into
+ the structure *DECODED. Returns the number of switches
+ consumed. */
+
+static unsigned int
+decode_cmdline_option (const char **argv, unsigned int lang_mask,
+ struct cl_decoded_option *decoded)
+{
+ size_t opt_index;
+ const char *opt, *arg = 0;
+ char *dup = 0;
+ int value = 1;
+ unsigned int result = 1, i;
+ size_t total_len;
+ char *p;
+ const struct cl_option *option;
+ int errors = 0;
+ const char *warn_message = NULL;
+ bool separate_arg_flag;
+ bool joined_arg_flag;
+ bool have_separate_arg = false;
+
+ opt = argv[0];
+
+ opt_index = find_opt (opt + 1, lang_mask);
+ if (opt_index == OPT_SPECIAL_unknown
+ && (opt[1] == 'W' || opt[1] == 'f' || opt[1] == 'm')
+ && opt[2] == 'n' && opt[3] == 'o' && opt[4] == '-')
+ {
+ /* Drop the "no-" from negative switches. */
+ size_t len = strlen (opt) - 3;
+
+ dup = XNEWVEC (char, len + 1);
+ dup[0] = '-';
+ dup[1] = opt[1];
+ memcpy (dup + 2, opt + 5, len - 2 + 1);
+ opt = dup;
+ value = 0;
+ opt_index = find_opt (opt + 1, lang_mask);
+ }
+
+ if (opt_index == OPT_SPECIAL_unknown)
+ {
+ arg = argv[0];
+ goto done;
+ }
+
+ option = &cl_options[opt_index];
+
+ /* Reject negative form of switches that don't take negatives as
+ unrecognized. */
+ if (!value && (option->flags & CL_REJECT_NEGATIVE))
+ {
+ opt_index = OPT_SPECIAL_unknown;
+ errors |= CL_ERR_NEGATIVE;
+ arg = argv[0];
+ goto done;
+ }
+
+ warn_message = option->warn_message;
+
+ /* Check to see if the option is disabled for this configuration. */
+ if (option->flags & CL_DISABLED)
+ errors |= CL_ERR_DISABLED;
+
+ /* Determine whether there may be a separate argument based on
+ whether this option is being processed for the driver. */
+ separate_arg_flag = ((option->flags & CL_SEPARATE)
+ && !((option->flags & CL_NO_DRIVER_ARG)
+ && (lang_mask & CL_DRIVER)));
+ joined_arg_flag = (option->flags & CL_JOINED) != 0;
+
+ /* Sort out any argument the switch takes. */
+ if (joined_arg_flag)
+ {
+ /* Have arg point to the original switch. This is because
+ some code, such as disable_builtin_function, expects its
+ argument to be persistent until the program exits. */
+ arg = argv[0] + cl_options[opt_index].opt_len + 1;
+ if (!value)
+ arg += strlen ("no-");
+
+ if (*arg == '\0' && !(option->flags & CL_MISSING_OK))
+ {
+ if (separate_arg_flag)
+ {
+ arg = argv[1];
+ result = 2;
+ if (arg == NULL)
+ result = 1;
+ else
+ have_separate_arg = true;
+ }
+ else
+ /* Missing argument. */
+ arg = NULL;
+ }
+ }
+ else if (separate_arg_flag)
+ {
+ arg = argv[1];
+ result = 2;
+ if (arg == NULL)
+ result = 1;
+ else
+ have_separate_arg = true;
+ }
+
+ if (arg == NULL && (separate_arg_flag || joined_arg_flag))
+ errors |= CL_ERR_MISSING_ARG;
+
+ /* Is this option an alias (or an ignored option, marked as an alias
+ of OPT_SPECIAL_ignore)? */
+ if (option->alias_target != N_OPTS
+ && (!(option->flags & CL_SEPARATE_ALIAS) || have_separate_arg))
+ {
+ size_t new_opt_index = option->alias_target;
+
+ if (new_opt_index == OPT_SPECIAL_ignore)
+ {
+ gcc_assert (option->alias_arg == NULL);
+ gcc_assert (option->neg_alias_arg == NULL);
+ opt_index = new_opt_index;
+ arg = NULL;
+ value = 1;
+ }
+ else
+ {
+ const struct cl_option *new_option = &cl_options[new_opt_index];
+
+ /* The new option must not be an alias itself. */
+ gcc_assert (new_option->alias_target == N_OPTS);
+
+ if (option->neg_alias_arg)
+ {
+ gcc_assert (option->alias_arg != NULL);
+ gcc_assert (arg == NULL);
+ if (value)
+ arg = option->alias_arg;
+ else
+ arg = option->neg_alias_arg;
+ value = 1;
+ }
+ else if (option->alias_arg)
+ {
+ gcc_assert (value == 1);
+ gcc_assert (arg == NULL);
+ arg = option->alias_arg;
+ }
+
+ opt_index = new_opt_index;
+ option = new_option;
+
+ if (value == 0)
+ gcc_assert (!(option->flags & CL_REJECT_NEGATIVE));
+
+ /* Recompute what arguments are allowed. */
+ separate_arg_flag = ((option->flags & CL_SEPARATE)
+ && !((option->flags & CL_NO_DRIVER_ARG)
+ && (lang_mask & CL_DRIVER)));
+ joined_arg_flag = (option->flags & CL_JOINED) != 0;
+
+ if (!(errors & CL_ERR_MISSING_ARG))
+ {
+ if (separate_arg_flag || joined_arg_flag)
+ gcc_assert (arg != NULL);
+ else
+ gcc_assert (arg == NULL);
+ }
+
+ /* Recheck for warnings and disabled options. */
+ if (option->warn_message)
+ {
+ gcc_assert (warn_message == NULL);
+ warn_message = option->warn_message;
+ }
+ if (option->flags & CL_DISABLED)
+ errors |= CL_ERR_DISABLED;
+ }
+ }
+
+ /* Check if this is a switch for a different front end. */
+ if (!option_ok_for_language (option, lang_mask))
+ errors |= CL_ERR_WRONG_LANG;
+
+ /* If the switch takes an integer, convert it. */
+ if (arg && (option->flags & CL_UINTEGER))
+ {
+ value = integral_argument (arg);
+ if (value == -1)
+ errors |= CL_ERR_UINT_ARG;
+ }
+
+ done:
+ if (dup)
+ free (dup);
+ decoded->opt_index = opt_index;
+ decoded->arg = arg;
+ decoded->value = value;
+ decoded->errors = errors;
+ decoded->warn_message = warn_message;
+
+ if (opt_index == OPT_SPECIAL_unknown)
+ {
+ /* Skip the correct number of arguments for options handled
+ through specs. */
+ const char *popt = argv[0] + 1;
+ int c = *popt;
+
+ gcc_assert (result == 1);
+ if (SWITCH_TAKES_ARG (c) > (popt[1] != 0))
+ result += SWITCH_TAKES_ARG (c) - (popt[1] != 0);
+ else if (WORD_SWITCH_TAKES_ARG (popt))
+ result += WORD_SWITCH_TAKES_ARG (popt);
+ if (result > 1)
+ for (i = 1; i < result; i++)
+ if (argv[i] == NULL)
+ {
+ result = i;
+ break;
+ }
+ }
+
+ gcc_assert (result >= 1 && result <= ARRAY_SIZE (decoded->canonical_option));
+ decoded->canonical_option_num_elements = result;
+ total_len = 0;
+ for (i = 0; i < ARRAY_SIZE (decoded->canonical_option); i++)
+ {
+ if (i < result)
+ {
+ if (opt_index == OPT_SPECIAL_unknown)
+ decoded->canonical_option[i] = argv[i];
+ else
+ decoded->canonical_option[i] = NULL;
+ total_len += strlen (argv[i]) + 1;
+ }
+ else
+ decoded->canonical_option[i] = NULL;
+ }
+ if (opt_index != OPT_SPECIAL_unknown && opt_index != OPT_SPECIAL_ignore)
+ generate_canonical_option (opt_index, arg, value, decoded);
+ decoded->orig_option_with_args_text = p = XNEWVEC (char, total_len);
+ for (i = 0; i < result; i++)
+ {
+ size_t len = strlen (argv[i]);
+
+ memcpy (p, argv[i], len);
+ p += len;
+ if (i == result - 1)
+ *p++ = 0;
+ else
+ *p++ = ' ';
+ }
+
+ return result;
+}
+
+/* Decode command-line options (ARGC and ARGV being the arguments of
+ main) into an array, setting *DECODED_OPTIONS to a pointer to that
+ array and *DECODED_OPTIONS_COUNT to the number of entries in the
+ array. The first entry in the array is always one for the program
+ name (OPT_SPECIAL_program_name). LANG_MASK indicates the language
+ flags applicable for decoding (including CL_COMMON and CL_TARGET if
+ those options should be considered applicable). Do not produce any
+ diagnostics or set state outside of these variables. */
+
+void
+decode_cmdline_options_to_array (unsigned int argc, const char **argv,
+ unsigned int lang_mask,
+ struct cl_decoded_option **decoded_options,
+ unsigned int *decoded_options_count)
+{
+ unsigned int n, i;
+ struct cl_decoded_option *opt_array;
+ unsigned int num_decoded_options;
+
+ opt_array = XNEWVEC (struct cl_decoded_option, argc);
+
+ opt_array[0].opt_index = OPT_SPECIAL_program_name;
+ opt_array[0].warn_message = NULL;
+ opt_array[0].arg = argv[0];
+ opt_array[0].orig_option_with_args_text = argv[0];
+ opt_array[0].canonical_option_num_elements = 1;
+ opt_array[0].canonical_option[0] = argv[0];
+ opt_array[0].canonical_option[1] = NULL;
+ opt_array[0].canonical_option[2] = NULL;
+ opt_array[0].canonical_option[3] = NULL;
+ opt_array[0].value = 1;
+ opt_array[0].errors = 0;
+ num_decoded_options = 1;
+
+ for (i = 1; i < argc; i += n)
+ {
+ const char *opt = argv[i];
+
+ /* Interpret "-" or a non-switch as a file name. */
+ if (opt[0] != '-' || opt[1] == '\0')
+ {
+ generate_option_input_file (opt, &opt_array[num_decoded_options]);
+ num_decoded_options++;
+ n = 1;
+ continue;
+ }
+
+ n = decode_cmdline_option (argv + i, lang_mask,
+ &opt_array[num_decoded_options]);
+ num_decoded_options++;
+ }
+
+ opt_array = XRESIZEVEC (struct cl_decoded_option, opt_array,
+ num_decoded_options);
+ *decoded_options = opt_array;
+ *decoded_options_count = num_decoded_options;
+}
+
/* Return true if NEXT_OPT_IDX cancels OPT_IDX. Return false if the
next one is the same as ORIG_NEXT_OPT_IDX. */
if (cl_options [next_opt_idx].neg_index != orig_next_opt_idx)
return cancel_option (opt_idx, cl_options [next_opt_idx].neg_index,
orig_next_opt_idx);
-
+
return false;
}
{
int argc = *argcp;
int *options = XNEWVEC (int, argc);
+ /* We will only return this replacement argv if we remove at least
+ one argument, so it does not need to be size (argc + 1) to
+ make room for the terminating NULL because we will always have
+ freed up at least one slot when we end up using it at all. */
char **argv = XNEWVEC (char *, argc);
int i, arg_count, need_prune = 0;
const struct cl_option *option;
const char *opt = (*argvp) [i];
opt_index = find_opt (opt + 1, -1);
- if (opt_index == cl_options_count
+ if (opt_index == OPT_SPECIAL_unknown
&& (opt[1] == 'W' || opt[1] == 'f' || opt[1] == 'm')
&& opt[2] == 'n' && opt[3] == 'o' && opt[4] == '-')
{
free (dup);
}
- if (opt_index == cl_options_count)
+ if (opt_index == OPT_SPECIAL_unknown)
{
cont:
options [i] = 0;
{
*argcp = arg_count;
*argvp = argv;
+ /* Add NULL-termination. Guaranteed not to overflow because
+ arg_count here can only be less than argc. */
+ argv[arg_count] = 0;
}
else
{
free (options);
}
+
+/* Handle option DECODED for the language indicated by LANG_MASK,
+ using the handlers in HANDLERS. KIND is the diagnostic_t if this
+ is a diagnostics option, DK_UNSPECIFIED otherwise. Returns false
+ if the switch was invalid. */
+
+bool
+handle_option (const struct cl_decoded_option *decoded,
+ unsigned int lang_mask, int kind,
+ const struct cl_option_handlers *handlers)
+{
+ size_t opt_index = decoded->opt_index;
+ const char *arg = decoded->arg;
+ int value = decoded->value;
+ const struct cl_option *option = &cl_options[opt_index];
+ size_t i;
+
+ if (option->flag_var)
+ set_option (opt_index, value, arg, kind);
+
+ for (i = 0; i < handlers->num_handlers; i++)
+ if (option->flags & handlers->handlers[i].mask)
+ {
+ if (!handlers->handlers[i].handler (decoded,
+ lang_mask, kind, handlers))
+ return false;
+ else
+ handlers->post_handling_callback (decoded,
+ handlers->handlers[i].mask);
+ }
+
+ return true;
+}
+
+/* Like handle_option, but OPT_INDEX, ARG and VALUE describe the
+ option instead of DECODED. This is used for callbacks when one
+ option implies another instead of an option being decoded from the
+ command line. */
+
+bool
+handle_generated_option (size_t opt_index, const char *arg, int value,
+ unsigned int lang_mask, int kind,
+ const struct cl_option_handlers *handlers)
+{
+ struct cl_decoded_option decoded;
+
+ generate_option (opt_index, arg, value, lang_mask, &decoded);
+ return handle_option (&decoded, lang_mask, kind, handlers);
+}
+
+/* Fill in *DECODED with an option described by OPT_INDEX, ARG and
+ VALUE for a front end using LANG_MASK. This is used when the
+ compiler generates options internally. */
+
+void
+generate_option (size_t opt_index, const char *arg, int value,
+ unsigned int lang_mask, struct cl_decoded_option *decoded)
+{
+ const struct cl_option *option = &cl_options[opt_index];
+
+ decoded->opt_index = opt_index;
+ decoded->warn_message = NULL;
+ decoded->arg = arg;
+ decoded->value = value;
+ decoded->errors = (option_ok_for_language (option, lang_mask)
+ ? 0
+ : CL_ERR_WRONG_LANG);
+
+ generate_canonical_option (opt_index, arg, value, decoded);
+ switch (decoded->canonical_option_num_elements)
+ {
+ case 1:
+ decoded->orig_option_with_args_text = decoded->canonical_option[0];
+ break;
+
+ case 2:
+ decoded->orig_option_with_args_text
+ = concat (decoded->canonical_option[0], " ",
+ decoded->canonical_option[1], NULL);
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Fill in *DECODED with an option for input file FILE. */
+
+void
+generate_option_input_file (const char *file,
+ struct cl_decoded_option *decoded)
+{
+ decoded->opt_index = OPT_SPECIAL_input_file;
+ decoded->warn_message = NULL;
+ decoded->arg = file;
+ decoded->orig_option_with_args_text = file;
+ decoded->canonical_option_num_elements = 1;
+ decoded->canonical_option[0] = file;
+ decoded->canonical_option[1] = NULL;
+ decoded->canonical_option[2] = NULL;
+ decoded->canonical_option[3] = NULL;
+ decoded->value = 1;
+ decoded->errors = 0;
+}
+
+/* Handle the switch DECODED for the language indicated by LANG_MASK,
+ using the handlers in *HANDLERS. */
+
+void
+read_cmdline_option (struct cl_decoded_option *decoded,
+ unsigned int lang_mask,
+ const struct cl_option_handlers *handlers)
+{
+ const struct cl_option *option;
+ const char *opt = decoded->orig_option_with_args_text;
+
+ if (decoded->warn_message)
+ warning (0, decoded->warn_message, opt);
+
+ if (decoded->opt_index == OPT_SPECIAL_unknown)
+ {
+ if (handlers->unknown_option_callback (decoded))
+ error ("unrecognized command line option %qs", decoded->arg);
+ return;
+ }
+
+ if (decoded->opt_index == OPT_SPECIAL_ignore)
+ return;
+
+ option = &cl_options[decoded->opt_index];
+
+ if (decoded->errors & CL_ERR_DISABLED)
+ {
+ error ("command line option %qs"
+ " is not supported by this configuration", opt);
+ return;
+ }
+
+ if (decoded->errors & CL_ERR_WRONG_LANG)
+ {
+ handlers->wrong_lang_callback (decoded, lang_mask);
+ return;
+ }
+
+ if (decoded->errors & CL_ERR_MISSING_ARG)
+ {
+ if (option->missing_argument_error)
+ error (option->missing_argument_error, opt);
+ else
+ error ("missing argument to %qs", opt);
+ return;
+ }
+
+ if (decoded->errors & CL_ERR_UINT_ARG)
+ {
+ error ("argument to %qs should be a non-negative integer",
+ option->opt_text);
+ return;
+ }
+
+ gcc_assert (!decoded->errors);
+
+ if (!handle_option (decoded, lang_mask, DK_UNSPECIFIED, handlers))
+ error ("unrecognized command line option %qs", opt);
+}
+
+/* Set any variable for option OPT_INDEX according to VALUE and ARG,
+ diagnostic kind KIND. */
+
+void
+set_option (int opt_index, int value, const char *arg, int kind)
+{
+ const struct cl_option *option = &cl_options[opt_index];
+
+ if (!option->flag_var)
+ return;
+
+ switch (option->var_type)
+ {
+ case CLVC_BOOLEAN:
+ *(int *) option->flag_var = value;
+ break;
+
+ case CLVC_EQUAL:
+ *(int *) option->flag_var = (value
+ ? option->var_value
+ : !option->var_value);
+ break;
+
+ case CLVC_BIT_CLEAR:
+ case CLVC_BIT_SET:
+ if ((value != 0) == (option->var_type == CLVC_BIT_SET))
+ *(int *) option->flag_var |= option->var_value;
+ else
+ *(int *) option->flag_var &= ~option->var_value;
+ if (option->flag_var == &target_flags)
+ target_flags_explicit |= option->var_value;
+ break;
+
+ case CLVC_STRING:
+ *(const char **) option->flag_var = arg;
+ break;
+ }
+
+ if ((diagnostic_t) kind != DK_UNSPECIFIED)
+ diagnostic_classify_diagnostic (global_dc, opt_index, (diagnostic_t) kind,
+ UNKNOWN_LOCATION);
+}