/* Command line option handling. Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Neil Booth. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #include "system.h" #include "intl.h" #include "opts.h" #include "options.h" /* True if we should exit after parsing options. */ bool exit_after_options; /* Treat warnings as errors. -Werror. */ bool warnings_are_errors; /* Don't suppress warnings from system headers. -Wsystem-headers. */ bool warn_system_headers; /* Columns of --help display. */ static unsigned int columns = 80; /* What to print when a switch has no documentation. */ static const char undocumented_msg[] = N_("This switch lacks documentation"); /* Input file names. */ const char **in_fnames; unsigned num_in_fnames; static int common_handle_option (size_t scode, const char *arg, int value); static unsigned int handle_option (const char **argv, unsigned int lang_mask); static char *write_langs (unsigned int lang_mask); static void complain_wrong_lang (const char *, const struct cl_option *, unsigned int lang_mask); static void handle_options (unsigned int, const char **, unsigned int); static void wrap_help (const char *help, const char *item, unsigned int); static void print_help (void); static void print_filtered_help (unsigned int); static unsigned int print_switch (const char *text, unsigned int indent); /* If ARG is a non-negative integer made up solely of digits, return its value, otherwise return -1. */ static int integral_argument (const char *arg) { const char *p = arg; while (*p && ISDIGIT (*p)) p++; if (*p == '\0') return atoi (arg); return -1; } /* Return a malloced slash-separated list of languages in MASK. */ static char * write_langs (unsigned int mask) { unsigned int n = 0, len = 0; const char *lang_name; char *result; for (n = 0; (lang_name = lang_names[n]) != 0; n++) if (mask & (1U << n)) len += strlen (lang_name) + 1; result = XNEWVEC (char, len); len = 0; for (n = 0; (lang_name = lang_names[n]) != 0; n++) if (mask & (1U << n)) { if (len) result[len++] = '/'; strcpy (result + len, lang_name); len += strlen (lang_name); } result[len] = 0; return result; } /* Complain that switch OPT_INDEX does not apply to this front end. */ static void complain_wrong_lang (const char *text, const struct cl_option *option, unsigned int lang_mask) { char *ok_langs, *bad_lang; ok_langs = write_langs (option->flags); bad_lang = write_langs (lang_mask); /* Eventually this should become a hard error IMO. */ warning (0, "command line option \"%s\" is valid for %s but not for %s", text, ok_langs, bad_lang); free (ok_langs); free (bad_lang); } /* Handle the switch beginning at ARGV for the language indicated by LANG_MASK. Returns the number of switches consumed. */ static unsigned int handle_option (const char **argv, unsigned int lang_mask) { size_t opt_index; const char *opt, *arg = 0; char *dup = 0; int value = 1; unsigned int result = 0; const struct cl_option *option; opt = argv[0]; opt_index = find_opt (opt + 1, lang_mask | CL_COMMON | CL_TARGET); if (opt_index == cl_options_count && (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 | CL_COMMON | CL_TARGET); } if (opt_index == cl_options_count) 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)) goto done; /* We've recognized this switch. */ result = 1; /* Check to see if the option is disabled for this configuration. */ if (option->flags & CL_DISABLED) { error ("command line option \"%s\"" " is not supported by this configuration", opt); goto done; } /* Sort out any argument the switch takes. */ if (option->flags & CL_JOINED) { /* 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 (option->flags & CL_SEPARATE) { arg = argv[1]; result = 2; } else /* Missing argument. */ arg = NULL; } } else if (option->flags & CL_SEPARATE) { arg = argv[1]; result = 2; } /* Now we've swallowed any potential argument, complain if this is a switch for a different front end. */ if (!(option->flags & (lang_mask | CL_COMMON | CL_TARGET))) { complain_wrong_lang (argv[0], option, lang_mask); goto done; } if (arg == NULL && (option->flags & (CL_JOINED | CL_SEPARATE))) { if (!lang_hooks.missing_argument (opt, opt_index)) error ("missing argument to \"%s\"", opt); goto done; } /* If the switch takes an integer, convert it. */ if (arg && (option->flags & CL_UINTEGER)) { value = integral_argument (arg); if (value == -1) { error ("argument to \"%s\" should be a non-negative integer", option->opt_text); goto done; } } if (option->flag_var) 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 (option->flags & lang_mask) if (lang_hooks.handle_option (opt_index, arg, value) == 0) result = 0; if (result && (option->flags & CL_COMMON)) if (common_handle_option (opt_index, arg, value) == 0) result = 0; ////if (result && (option->flags & CL_TARGET)) //// if (!targetm.handle_option (opt_index, arg, value)) //// result = 0; done: if (dup) free (dup); return result; } /* Handle FILENAME from the command line. */ static void add_input_filename (const char *filename) { num_in_fnames++; in_fnames = xrealloc (in_fnames, num_in_fnames * sizeof (in_fnames[0])); in_fnames[num_in_fnames - 1] = filename; } /* Decode and handle the vector of command line options. LANG_MASK contains has a single bit set representing the current language. */ static void handle_options (unsigned int argc, const char **argv, unsigned int lang_mask) { unsigned int n, i; 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') { if (main_input_filename == NULL) main_input_filename = opt; add_input_filename (opt); n = 1; continue; } n = handle_option (argv + i, lang_mask); if (!n) { n = 1; error ("unrecognized command line option \"%s\"", opt); } } } /* Parse command line options and set default flag values. Do minimal options processing. */ void decode_options (unsigned int argc, const char **argv) { unsigned int lang_mask; /* Perform language-specific options initialization. */ lang_mask = lang_hooks.init_options (argc, argv); /* Scan to see what optimization level has been specified. That will determine the default value of many flags. */ handle_options (argc, argv, lang_mask); } /* Handle target- and language-independent options. Return zero to generate an "unknown option" message. Only options that need extra handling need to be listed here; if you simply want VALUE assigned to a variable, it happens automatically. */ static int common_handle_option (size_t scode, const char *arg, int value ATTRIBUTE_UNUSED) { enum opt_code code = (enum opt_code) scode; switch (code) { default: abort (); case OPT__help: print_help (); exit_after_options = true; break; case OPT__version: print_version (stderr, ""); exit_after_options = true; break; case OPT_Werror: warnings_are_errors = value; break; case OPT_Wsystem_headers: warn_system_headers = value; break; case OPT_d: decode_d_option (arg); break; case OPT_pedantic_errors: flag_pedantic_errors = 1; break; case OPT_w: inhibit_warnings = true; break; } return 1; } /* Output --help text. */ static void print_help (void) { size_t i; const char *p; GET_ENVIRONMENT (p, "COLUMNS"); if (p) { int value = atoi (p); if (value > 0) columns = value; } puts (_("The following options are language-independent:\n")); print_filtered_help (CL_COMMON); for (i = 0; lang_names[i]; i++) { printf (_("The %s front end recognizes the following options:\n\n"), lang_names[i]); print_filtered_help (1U << i); } } /* Print help for a specific front-end, etc. */ static void print_filtered_help (unsigned int flag) { unsigned int i, len, filter, indent = 0; bool duplicates = false; const char *help, *opt, *tab; static char *printed; if (flag == CL_COMMON || flag == CL_TARGET) { filter = flag; if (!printed) printed = xmalloc (cl_options_count); memset (printed, 0, cl_options_count); } else { /* Don't print COMMON options twice. */ filter = flag | CL_COMMON; for (i = 0; i < cl_options_count; i++) { if ((cl_options[i].flags & filter) != flag) continue; /* Skip help for internal switches. */ if (cl_options[i].flags & CL_UNDOCUMENTED) continue; /* Skip switches that have already been printed, mark them to be listed later. */ if (printed[i]) { duplicates = true; indent = print_switch (cl_options[i].opt_text, indent); } } if (duplicates) { putchar ('\n'); putchar ('\n'); } } for (i = 0; i < cl_options_count; i++) { if ((cl_options[i].flags & filter) != flag) continue; /* Skip help for internal switches. */ if (cl_options[i].flags & CL_UNDOCUMENTED) continue; /* Skip switches that have already been printed. */ if (printed[i]) continue; printed[i] = true; help = cl_options[i].help; if (!help) help = undocumented_msg; /* Get the translation. */ help = _(help); tab = strchr (help, '\t'); if (tab) { len = tab - help; opt = help; help = tab + 1; } else { opt = cl_options[i].opt_text; len = strlen (opt); } wrap_help (help, opt, len); } putchar ('\n'); } /* Output ITEM, of length ITEM_WIDTH, in the left column, followed by word-wrapped HELP in a second column. */ static unsigned int print_switch (const char *text, unsigned int indent) { unsigned int len = strlen (text) + 1; /* trailing comma */ if (indent) { putchar (','); if (indent + len > columns) { putchar ('\n'); putchar (' '); indent = 1; } } else putchar (' '); putchar (' '); fputs (text, stdout); return indent + len + 1; } /* Output ITEM, of length ITEM_WIDTH, in the left column, followed by word-wrapped HELP in a second column. */ static void wrap_help (const char *help, const char *item, unsigned int item_width) { unsigned int col_width = 27; unsigned int remaining, room, len; remaining = strlen (help); do { room = columns - 3 - MAX (col_width, item_width); if (room > columns) room = 0; len = remaining; if (room < len) { unsigned int i; for (i = 0; help[i]; i++) { if (i >= room && len != remaining) break; if (help[i] == ' ') len = i; else if ((help[i] == '-' || help[i] == '/') && help[i + 1] != ' ' && i > 0 && ISALPHA (help[i - 1])) len = i + 1; } } printf( " %-*.*s %.*s\n", col_width, item_width, item, len, help); item_width = 0; while (help[len] == ' ') len++; help += len; remaining -= len; } while (remaining); } /* Return 1 if OPTION is enabled, 0 if it is disabled, or -1 if it isn't a simple on-off switch. */ int option_enabled (int opt_idx) { const struct cl_option *option = &(cl_options[opt_idx]); if (option->flag_var) switch (option->var_type) { case CLVC_BOOLEAN: return *(int *) option->flag_var != 0; case CLVC_EQUAL: return *(int *) option->flag_var == option->var_value; case CLVC_BIT_CLEAR: return (*(int *) option->flag_var & option->var_value) == 0; case CLVC_BIT_SET: return (*(int *) option->flag_var & option->var_value) != 0; case CLVC_STRING: break; } return -1; } /* Fill STATE with the current state of option OPTION. Return true if there is some state to store. */ bool get_option_state (int option, struct cl_option_state *state) { if (cl_options[option].flag_var == 0) return false; switch (cl_options[option].var_type) { case CLVC_BOOLEAN: case CLVC_EQUAL: state->data = cl_options[option].flag_var; state->size = sizeof (int); break; case CLVC_BIT_CLEAR: case CLVC_BIT_SET: state->ch = option_enabled (option); state->data = &state->ch; state->size = 1; break; case CLVC_STRING: state->data = *(const char **) cl_options[option].flag_var; if (state->data == 0) state->data = ""; state->size = strlen (state->data) + 1; break; } return true; }