| /* Command line option handling. |
| Copyright (C) 2006, 2007, 2008 Free Software Foundation, Inc. |
| |
| 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 3, 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 COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "intl.h" |
| #include "coretypes.h" |
| #include "opts.h" |
| |
| /* 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. |
| |
| This routine is quite subtle. A normal binary search is not good |
| enough because some options can be suffixed with an argument, and |
| multiple sub-matches can occur, e.g. input of "-pedantic" matching |
| the initial substring of "-pedantic-errors". |
| |
| A more complicated example is -gstabs. It should match "-g" with |
| an argument of "stabs". Suppose, however, that the number and list |
| of switches are such that the binary search tests "-gen-decls" |
| before having tested "-g". This doesn't match, and as "-gen-decls" |
| is less than "-gstabs", it will become the lower bound of the |
| binary search range, and "-g" will never be seen. To resolve this |
| issue, 'optc-gen.awk' makes "-gen-decls" point, via the back_chain member, |
| to "-g" so that failed searches that end between "-gen-decls" and |
| the lexicographically subsequent switch know to go back and see if |
| "-g" causes a match (which it does in this example). |
| |
| This search is done in such a way that the longest match for the |
| front end in question wins. If there is no match for the current |
| front end, the longest match for a different front end is returned |
| (or N_OPTS if none) and the caller emits an error message. */ |
| size_t |
| find_opt (const char *input, int lang_mask) |
| { |
| size_t mn, mx, md, opt_len; |
| size_t match_wrong_lang; |
| int comp; |
| |
| mn = 0; |
| mx = cl_options_count; |
| |
| /* Find mn such this lexicographical inequality holds: |
| cl_options[mn] <= input < cl_options[mn + 1]. */ |
| while (mx - mn > 1) |
| { |
| md = (mn + mx) / 2; |
| opt_len = cl_options[md].opt_len; |
| comp = strncmp (input, cl_options[md].opt_text + 1, opt_len); |
| |
| if (comp < 0) |
| mx = md; |
| else |
| mn = md; |
| } |
| |
| /* 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; |
| |
| /* Backtrace the chain of possible matches, returning the longest |
| one, if any, that fits best. With current GCC switches, this |
| loop executes at most twice. */ |
| do |
| { |
| const struct cl_option *opt = &cl_options[mn]; |
| |
| /* Is the input either an exact match or a prefix that takes a |
| joined argument? */ |
| if (!strncmp (input, opt->opt_text + 1, opt->opt_len) |
| && (input[opt->opt_len] == '\0' || (opt->flags & CL_JOINED))) |
| { |
| /* If language is OK, return it. */ |
| if (opt->flags & lang_mask) |
| return mn; |
| |
| /* If we haven't remembered a prior match, remember this |
| one. Any prior match is necessarily better. */ |
| if (match_wrong_lang == cl_options_count) |
| match_wrong_lang = mn; |
| } |
| |
| /* Try the next possibility. This is cl_options_count if there |
| are no more. */ |
| mn = opt->back_chain; |
| } |
| while (mn != cl_options_count); |
| |
| /* Return the best wrong match, or cl_options_count if none. */ |
| return match_wrong_lang; |
| } |
| |
| /* Return true if NEXT_OPT_IDX cancels OPT_IDX. Return false if the |
| next one is the same as ORIG_NEXT_OPT_IDX. */ |
| |
| static bool |
| cancel_option (int opt_idx, int next_opt_idx, int orig_next_opt_idx) |
| { |
| /* An option can be canceled by the same option or an option with |
| Negative. */ |
| if (cl_options [next_opt_idx].neg_index == opt_idx) |
| return true; |
| |
| 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; |
| } |
| |
| /* Filter out options canceled by the ones after them. */ |
| |
| void |
| prune_options (int *argcp, char ***argvp) |
| { |
| int argc = *argcp; |
| int *options = XNEWVEC (int, argc); |
| char **argv = XNEWVEC (char *, argc); |
| int i, arg_count, need_prune = 0; |
| const struct cl_option *option; |
| size_t opt_index; |
| |
| /* Scan all arguments. */ |
| for (i = 1; i < argc; i++) |
| { |
| int value = 1; |
| const char *opt = (*argvp) [i]; |
| |
| opt_index = find_opt (opt + 1, -1); |
| if (opt_index == cl_options_count |
| && (opt[1] == 'W' || opt[1] == 'f' || opt[1] == 'm') |
| && opt[2] == 'n' && opt[3] == 'o' && opt[4] == '-') |
| { |
| char *dup; |
| |
| /* 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, -1); |
| free (dup); |
| } |
| |
| if (opt_index == cl_options_count) |
| { |
| cont: |
| options [i] = 0; |
| continue; |
| } |
| |
| option = &cl_options[opt_index]; |
| if (option->neg_index < 0) |
| goto cont; |
| |
| /* Skip joined switches. */ |
| if ((option->flags & CL_JOINED)) |
| goto cont; |
| |
| /* Reject negative form of switches that don't take negatives as |
| unrecognized. */ |
| if (!value && (option->flags & CL_REJECT_NEGATIVE)) |
| goto cont; |
| |
| options [i] = (int) opt_index; |
| need_prune |= options [i]; |
| } |
| |
| if (!need_prune) |
| goto done; |
| |
| /* Remove arguments which are negated by others after them. */ |
| argv [0] = (*argvp) [0]; |
| arg_count = 1; |
| for (i = 1; i < argc; i++) |
| { |
| int j, opt_idx; |
| |
| opt_idx = options [i]; |
| if (opt_idx) |
| { |
| int next_opt_idx; |
| for (j = i + 1; j < argc; j++) |
| { |
| next_opt_idx = options [j]; |
| if (next_opt_idx |
| && cancel_option (opt_idx, next_opt_idx, |
| next_opt_idx)) |
| break; |
| } |
| } |
| else |
| goto keep; |
| |
| if (j == argc) |
| { |
| keep: |
| argv [arg_count] = (*argvp) [i]; |
| arg_count++; |
| } |
| } |
| |
| if (arg_count != argc) |
| { |
| *argcp = arg_count; |
| *argvp = argv; |
| } |
| else |
| { |
| done: |
| free (argv); |
| } |
| |
| free (options); |
| } |