OSDN Git Service

In libobjc/:
[pf3gnuchains/gcc-fork.git] / gcc / opts-common.c
index af8e44c..8299edd 100644 (file)
@@ -1,5 +1,5 @@
 /* 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.
 
@@ -22,10 +22,13 @@ along with GCC; see the file COPYING3.  If not see
 #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
@@ -72,8 +75,8 @@ find_opt (const char *input, int lang_mask)
     }
 
   /* 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
@@ -93,7 +96,7 @@ find_opt (const char *input, int lang_mask)
 
          /* 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;
        }
 
@@ -103,10 +106,413 @@ find_opt (const char *input, int lang_mask)
     }
   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.  */
 
@@ -121,7 +527,7 @@ cancel_option (int opt_idx, int next_opt_idx, int 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;
 }
 
@@ -132,6 +538,10 @@ prune_options (int *argcp, char ***argvp)
 {
   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;
@@ -144,7 +554,7 @@ prune_options (int *argcp, char ***argvp)
       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] == '-')
        {
@@ -163,7 +573,7 @@ prune_options (int *argcp, char ***argvp)
          free (dup);
        }
 
-      if (opt_index == cl_options_count)
+      if (opt_index == OPT_SPECIAL_unknown)
        {
 cont:
          options [i] = 0;
@@ -225,6 +635,9 @@ keep:
     {
       *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
     {
@@ -234,3 +647,211 @@ done:
 
   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);
+}