// A version of the getopt library for use with wide character strings. // // This is simply the gnu getopt library, but converted for use with wchar_t instead of char. This // is not usually useful since the argv array is always defined to be of type char**, but in fish, // all internal commands use wide characters and hence this library is useful. // // If you want to use this version of getopt in your program, download the fish sourcecode, // available at the fish homepage. Extract the sourcode, copy // wgetopt.c and wgetopt.h into your program directory, include wgetopt.h in your program, and use // all the regular getopt functions, prefixing every function, global variable and structure with a // 'w', and use only wide character strings. There are no other functional changes in this version // of getopt besides using wide character strings. // // For examples of how to use wgetopt, see the fish builtin functions, many of which are defined in // builtin.c. // Getopt for GNU. // // NOTE: getopt is now part of the C library, so if you don't know what "Keep this file name-space // clean" means, talk to roland@gnu.ai.mit.edu before changing it! // // Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94 // Free Software Foundation, Inc. // // This file is part of the GNU C Library. Its master source is NOT part of the C library, however. // The master source lives in /gd/gnu/lib. // // The GNU C Library is free software; you can redistribute it and/or modify it under the terms of // the GNU Library General Public License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // The GNU C Library 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 Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License along with the GNU C // Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 675 Mass // Ave, Cambridge, MA 02139, USA. #include "config.h" // IWYU pragma: keep #include #include #include // This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves // differently for the user, since it allows the user to intersperse the options with the other // arguments. // // As `getopt' works, it permutes the elements of ARGV so that, when it is done, all the options // precede everything else. Thus all application programs are extended to handle flexible argument // order. // // GNU application programs can use a third alternative mode in which they can distinguish the // relative order of options and other arguments. #include "common.h" #include "fallback.h" // IWYU pragma: keep #include "wgetopt.h" #include "wutil.h" // IWYU pragma: keep // Exchange two adjacent subsequences of ARGV. One subsequence is elements // [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The // other is elements [last_nonopt,woptind), which contains all the options processed since those // non-options were skipped. // // `first_nonopt' and `last_nonopt' are relocated so that they describe the new indices of the // non-options in ARGV after they are moved. void wgetopter_t::exchange(wchar_t **argv) { int bottom = first_nonopt; int middle = last_nonopt; int top = woptind; wchar_t *tem; // Exchange the shorter segment with the far end of the longer segment. That puts the shorter // segment into the right place. It leaves the longer segment in the right place overall, but it // consists of two parts that need to be swapped next. while (top > middle && middle > bottom) { if (top - middle > middle - bottom) { // Bottom segment is the short one. int len = middle - bottom; int i; // Swap it with the top part of the top segment. for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[top - (middle - bottom) + i]; argv[top - (middle - bottom) + i] = tem; } // Exclude the moved bottom segment from further swapping. top -= len; } else { // Top segment is the short one. int len = top - middle; int i; // Swap it with the bottom part of the bottom segment. for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[middle + i]; argv[middle + i] = tem; } // Exclude the moved top segment from further swapping. bottom += len; } } // Update records for the slots the non-options now occupy. first_nonopt += (woptind - last_nonopt); last_nonopt = woptind; } // Initialize the internal data when the first call is made. void wgetopter_t::_wgetopt_initialize(const wchar_t *optstring) { // Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the // sequence of previously skipped non-option ARGV-elements is empty. first_nonopt = last_nonopt = woptind = 1; nextchar = NULL; // Determine how to handle the ordering of options and nonoptions. if (optstring[0] == '-') { ordering = RETURN_IN_ORDER; ++optstring; } else if (optstring[0] == '+') { ordering = REQUIRE_ORDER; ++optstring; } else { ordering = PERMUTE; } if (optstring[0] == ':') { missing_arg_return_colon = true; ++optstring; } shortopts = optstring; initialized = true; } // Advance to the next ARGV-element. int wgetopter_t::_advance_to_next_argv( //!OCLINT(high cyclomatic complexity) int argc, wchar_t **argv, const struct woption *longopts) { if (ordering == PERMUTE) { // If we have just processed some options following some non-options, exchange them so // that the options come first. if (first_nonopt != last_nonopt && last_nonopt != woptind) { exchange(argv); } else if (last_nonopt != woptind) { first_nonopt = woptind; } // Skip any additional non-options and extend the range of non-options previously // skipped. while (woptind < argc && (argv[woptind][0] != '-' || argv[woptind][1] == '\0')) { woptind++; } last_nonopt = woptind; } // The special ARGV-element `--' means premature end of options. Skip it like a null option, // then exchange with previous non-options as if it were an option, then skip everything // else like a non-option. if (woptind != argc && !std::wcscmp(argv[woptind], L"--")) { woptind++; if (first_nonopt != last_nonopt && last_nonopt != woptind) { exchange(argv); } else if (first_nonopt == last_nonopt) { first_nonopt = woptind; } last_nonopt = argc; woptind = argc; } // If we have done all the ARGV-elements, stop the scan and back over any non-options that // we skipped and permuted. if (woptind == argc) { // Set the next-arg-index to point at the non-options that we previously skipped, so the // caller will digest them. if (first_nonopt != last_nonopt) woptind = first_nonopt; return EOF; } // If we have come to a non-option and did not permute it, either stop the scan or describe // it to the caller and pass it by. if ((argv[woptind][0] != '-' || argv[woptind][1] == '\0')) { if (ordering == REQUIRE_ORDER) return EOF; woptarg = argv[woptind++]; return 1; } // We have found another option-ARGV-element. Skip the initial punctuation. nextchar = (argv[woptind] + 1 + (longopts != NULL && argv[woptind][1] == '-')); return 0; } // Check for a matching short opt. int wgetopter_t::_handle_short_opt(int argc, wchar_t **argv) { // Look at and handle the next short option-character. wchar_t c = *nextchar++; const wchar_t *temp = std::wcschr(shortopts, c); // Increment `woptind' when we start to process its last character. if (*nextchar == '\0') ++woptind; if (temp == NULL || c == ':') { if (wopterr) { std::fwprintf(stderr, _(L"%ls: Invalid option -- %lc\n"), argv[0], (wint_t)c); } woptopt = c; if (*nextchar != '\0') woptind++; return '?'; } if (temp[1] != ':') { return c; } if (temp[2] == ':') { // This is an option that accepts an argument optionally. if (*nextchar != '\0') { woptarg = nextchar; woptind++; } else { woptarg = NULL; } nextchar = NULL; } else { // This is an option that requires an argument. if (*nextchar != '\0') { woptarg = nextchar; // If we end this ARGV-element by taking the rest as an arg, we must advance to // the next element now. woptind++; } else if (woptind == argc) { if (wopterr) { // 1003.2 specifies the format of this message. std::fwprintf(stderr, _(L"%ls: Option requires an argument -- %lc\n"), argv[0], (wint_t)c); } woptopt = c; c = missing_arg_return_colon ? ':' : '?'; } else { // We already incremented `woptind' once; increment it again when taking next // ARGV-elt as argument. woptarg = argv[woptind++]; } nextchar = NULL; } return c; } void wgetopter_t::_update_long_opt(int argc, wchar_t **argv, const struct woption *pfound, wchar_t *nameend, int *longind, int option_index, int *retval) { woptind++; if (*nameend) { // Don't test has_arg with >, because some C compilers don't allow it to be used on // enums. if (pfound->has_arg) woptarg = nameend + 1; else { if (wopterr) { if (argv[woptind - 1][1] == '-') // --option std::fwprintf(stderr, _(L"%ls: Option '--%ls' doesn't allow an argument\n"), argv[0], pfound->name); else // +option or -option std::fwprintf(stderr, _(L"%ls: Option '%lc%ls' doesn't allow an argument\n"), argv[0], argv[woptind - 1][0], pfound->name); } nextchar += std::wcslen(nextchar); *retval = '?'; return; } } else if (pfound->has_arg == 1) { if (woptind < argc) woptarg = argv[woptind++]; else { if (wopterr) std::fwprintf(stderr, _(L"%ls: Option '%ls' requires an argument\n"), argv[0], argv[woptind - 1]); nextchar += std::wcslen(nextchar); *retval = missing_arg_return_colon ? ':' : '?'; return; } } nextchar += std::wcslen(nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; *retval = 0; } else { *retval = pfound->val; } } // Find a matching long opt. const struct woption *wgetopter_t::_find_matching_long_opt(const struct woption *longopts, wchar_t *nameend, int *exact, int *ambig, int *indfound) { const struct woption *pfound = NULL; int option_index = 0; // Test all long options for either exact match or abbreviated matches. for (const struct woption *p = longopts; p->name; p++, option_index++) { if (!std::wcsncmp(p->name, nextchar, nameend - nextchar)) { if ((unsigned int)(nameend - nextchar) == (unsigned int)wcslen(p->name)) { // Exact match found. pfound = p; *indfound = option_index; *exact = 1; break; } else if (pfound == NULL) { // First nonexact match found. pfound = p; *indfound = option_index; } else // Second or later nonexact match found. *ambig = 1; } } return pfound; } // Check for a matching long opt. bool wgetopter_t::_handle_long_opt(int argc, wchar_t **argv, const struct woption *longopts, int *longind, int long_only, int *retval) { int exact = 0; int ambig = 0; int indfound = 0; wchar_t *nameend; for (nameend = nextchar; *nameend && *nameend != '='; nameend++) ; //!OCLINT(empty body) const struct woption *pfound = _find_matching_long_opt(longopts, nameend, &exact, &ambig, &indfound); if (ambig && !exact) { if (wopterr) { std::fwprintf(stderr, _(L"%ls: Option '%ls' is ambiguous\n"), argv[0], argv[woptind]); } nextchar += std::wcslen(nextchar); woptind++; *retval = '?'; return true; } if (pfound) { _update_long_opt(argc, argv, pfound, nameend, longind, indfound, retval); return true; } // Can't find it as a long option. If this is not getopt_long_only, or the option starts // with '--' or is not a valid short option, then it's an error. Otherwise interpret it as a // short option. if (!long_only || argv[woptind][1] == '-' || std::wcschr(shortopts, *nextchar) == NULL) { if (wopterr) { if (argv[woptind][1] == '-') // --option std::fwprintf(stderr, _(L"%ls: Unrecognized option '--%ls'\n"), argv[0], nextchar); else // +option or -option std::fwprintf(stderr, _(L"%ls: Unrecognized option '%lc%ls'\n"), argv[0], argv[woptind][0], nextchar); } nextchar = (wchar_t *)L""; woptind++; *retval = '?'; return true; } return false; } // Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. // // If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option // element. The characters of this element (aside from the initial '-') are option characters. If // `getopt' is called repeatedly, it returns successively each of the option characters from each of // the option elements. // // If `getopt' finds another option character, it returns that character, updating `woptind' and // `nextchar' so that the next call to `getopt' can resume the scan with the following option // character or ARGV-element. // // If there are no more option characters, `getopt' returns `EOF'. Then `woptind' is the index in // ARGV of the first ARGV-element that is not an option. (The ARGV-elements have been permuted so // that those that are not options now come last.) // // OPTSTRING is a string containing the legitimate option characters. If an option character is seen // that is not listed in OPTSTRING, return '?' after printing an error message. If you set // `wopterr' to zero, the error message is suppressed but we still return '?'. // // If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text // in the same ARGV-element, or the text of the following ARGV-element, is returned in `optarg'. // Two colons mean an option that wants an optional arg; if there is text in the current // ARGV-element, it is returned in `w.woptarg', otherwise `w.woptarg' is set to zero. // // If OPTSTRING starts with `-' or `+', it requests different methods of handling the non-option // ARGV-elements. See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. // // Long-named options begin with `--' instead of `-'. Their names may be abbreviated as long as the // abbreviation is unique or is an exact match for some defined option. If they have an argument, // it follows the option name in the same ARGV-element, separated from the option name by a `=', or // else the in next ARGV-element. When `getopt' finds a long-named option, it returns 0 if that // option's `flag' field is nonzero, the value of the option's `val' field if the `flag' field is // zero. // // LONGOPTS is a vector of `struct option' terminated by an element containing a name which is zero. // // LONGIND returns the index in LONGOPT of the long-named option found. It is only valid when a // long-named option has been found by the most recent call. // // If LONG_ONLY is nonzero, '-' as well as '--' can introduce long-named options. int wgetopter_t::_wgetopt_internal(int argc, wchar_t **argv, const wchar_t *optstring, const struct woption *longopts, int *longind, int long_only) { if (!initialized) _wgetopt_initialize(optstring); woptarg = NULL; if (nextchar == NULL || *nextchar == '\0') { int retval = _advance_to_next_argv(argc, argv, longopts); if (retval != 0) return retval; } // Decode the current option-ARGV-element. // Check whether the ARGV-element is a long option. // // If long_only and the ARGV-element has the form "-f", where f is a valid short option, don't // consider it an abbreviated form of a long option that starts with f. Otherwise there would // be no way to give the -f short option. // // On the other hand, if there's a long option "fubar" and the ARGV-element is "-fu", do // consider that an abbreviation of the long option, just like "--fu", and not "-f" with arg // "u". // // This distinction seems to be the most useful approach. if (longopts != NULL && (argv[woptind][1] == '-' || (long_only && (argv[woptind][2] || !std::wcschr(shortopts, argv[woptind][1]))))) { int retval; if (_handle_long_opt(argc, argv, longopts, longind, long_only, &retval)) return retval; } return _handle_short_opt(argc, argv); } int wgetopter_t::wgetopt_long(int argc, wchar_t **argv, const wchar_t *options, const struct woption *long_options, int *opt_index) { return _wgetopt_internal(argc, argv, options, long_options, opt_index, 0); } #if 0 // This function should never be used by fish. We keep the signature just in case we find a // need to use it in the future. int wgetopter_t::wgetopt_long_only(int argc, wchar_t **argv, const wchar_t *options, const struct woption *long_options, int *opt_index) { return _wgetopt_internal(argc, argv, options, long_options, opt_index, 1); } #endif