+/* 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;
+ }
+}
+
+/* Structure describing mappings from options on the command line to
+ options to look up with find_opt. */
+struct option_map
+{
+ /* Prefix of the option on the command line. */
+ const char *opt0;
+ /* If two argv elements are considered to be merged into one option,
+ prefix for the second element, otherwise NULL. */
+ const char *opt1;
+ /* The new prefix to map to. */
+ const char *new_prefix;
+ /* Whether at least one character is needed following opt1 or opt0
+ for this mapping to be used. (--optimize= is valid for -O, but
+ --warn- is not valid for -W.) */
+ bool another_char_needed;
+ /* Whether the original option is a negated form of the option
+ resulting from this map. */
+ bool negated;
+};
+static const struct option_map option_map[] =
+ {
+ { "-Wno-", NULL, "-W", false, true },
+ { "-fno-", NULL, "-f", false, true },
+ { "-mno-", NULL, "-m", false, true },
+ { "--debug=", NULL, "-g", false, false },
+ { "--machine-", NULL, "-m", true, false },
+ { "--machine-no-", NULL, "-m", false, true },
+ { "--machine=", NULL, "-m", false, false },
+ { "--machine=no-", NULL, "-m", false, true },
+ { "--machine", "", "-m", false, false },
+ { "--machine", "no-", "-m", false, true },
+ { "--optimize=", NULL, "-O", false, false },
+ { "--std=", NULL, "-std=", false, false },
+ { "--std", "", "-std=", false, false },
+ { "--warn-", NULL, "-W", true, false },
+ { "--warn-no-", NULL, "-W", false, true },
+ { "--", NULL, "-f", true, false },
+ { "--no-", NULL, "-f", false, true }
+ };
+
+/* 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 *arg = 0;
+ int value = 1;
+ unsigned int result = 1, i, extra_args, separate_args = 0;
+ int adjust_len = 0;
+ 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;
+
+ extra_args = 0;
+
+ opt_index = find_opt (argv[0] + 1, lang_mask);
+ i = 0;
+ while (opt_index == OPT_SPECIAL_unknown
+ && i < ARRAY_SIZE (option_map))
+ {
+ const char *opt0 = option_map[i].opt0;
+ const char *opt1 = option_map[i].opt1;
+ const char *new_prefix = option_map[i].new_prefix;
+ bool another_char_needed = option_map[i].another_char_needed;
+ size_t opt0_len = strlen (opt0);
+ size_t opt1_len = (opt1 == NULL ? 0 : strlen (opt1));
+ size_t optn_len = (opt1 == NULL ? opt0_len : opt1_len);
+ size_t new_prefix_len = strlen (new_prefix);
+
+ extra_args = (opt1 == NULL ? 0 : 1);
+ value = !option_map[i].negated;
+
+ if (strncmp (argv[0], opt0, opt0_len) == 0
+ && (opt1 == NULL
+ || (argv[1] != NULL && strncmp (argv[1], opt1, opt1_len) == 0))
+ && (!another_char_needed
+ || argv[extra_args][optn_len] != 0))
+ {
+ size_t arglen = strlen (argv[extra_args]);
+ char *dup;
+
+ adjust_len = (int) optn_len - (int) new_prefix_len;
+ dup = XNEWVEC (char, arglen + 1 - adjust_len);
+ memcpy (dup, new_prefix, new_prefix_len);
+ memcpy (dup + new_prefix_len, argv[extra_args] + optn_len,
+ arglen - optn_len + 1);
+ opt_index = find_opt (dup + 1, lang_mask);
+ free (dup);
+ }
+ i++;
+ }
+
+ if (opt_index == OPT_SPECIAL_unknown)
+ {
+ arg = argv[0];
+ extra_args = 0;
+ value = 1;
+ 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;
+ }
+
+ result = extra_args + 1;
+ 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, and, if
+ so, how many such arguments. */
+ separate_arg_flag = ((option->flags & CL_SEPARATE)
+ && !((option->flags & CL_NO_DRIVER_ARG)
+ && (lang_mask & CL_DRIVER)));
+ separate_args = (separate_arg_flag
+ ? ((option->flags & CL_SEPARATE_NARGS_MASK)
+ >> CL_SEPARATE_NARGS_SHIFT) + 1
+ : 0);
+ 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[extra_args] + cl_options[opt_index].opt_len + 1 + adjust_len;
+
+ if (*arg == '\0' && !(option->flags & CL_MISSING_OK))
+ {
+ if (separate_arg_flag)
+ {
+ arg = argv[extra_args + 1];
+ result = extra_args + 2;
+ if (arg == NULL)
+ result = extra_args + 1;
+ else
+ have_separate_arg = true;
+ }
+ else
+ /* Missing argument. */
+ arg = NULL;
+ }
+ }
+ else if (separate_arg_flag)
+ {
+ arg = argv[extra_args + 1];
+ for (i = 0; i < separate_args; i++)
+ if (argv[extra_args + 1 + i] == NULL)
+ {
+ errors |= CL_ERR_MISSING_ARG;
+ break;
+ }
+ result = extra_args + 1 + i;
+ if (arg != NULL)
+ 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
+ || (new_option->flags & CL_SEPARATE_ALIAS));
+
+ 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 (separate_args > 1 || (option->flags & CL_SEPARATE_NARGS_MASK))
+ gcc_assert (separate_args
+ == ((option->flags & CL_SEPARATE_NARGS_MASK)
+ >> CL_SEPARATE_NARGS_SHIFT) + 1);
+
+ if (!(errors & CL_ERR_MISSING_ARG))
+ {
+ if (separate_arg_flag || joined_arg_flag)
+ {
+ if ((option->flags & CL_MISSING_OK) && arg == NULL)
+ arg = "";
+ 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:
+ 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)
+ gcc_assert (result == 1);
+
+ 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);
+ if (separate_args > 1)
+ {
+ for (i = 0; i < separate_args; i++)
+ {
+ if (argv[extra_args + 1 + i] == NULL)
+ break;
+ else
+ decoded->canonical_option[1 + i] = argv[extra_args + 1 + i];
+ }
+ gcc_assert (result == 1 + i);
+ decoded->canonical_option_num_elements = result;
+ }
+ }
+ 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;
+ bool argv_copied = false;
+
+ 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++;
+ }
+
+ if (argv_copied)
+ free (argv);
+ *decoded_options = opt_array;
+ *decoded_options_count = num_decoded_options;
+ prune_options (decoded_options, decoded_options_count);
+}
+