void Args::ParseAliasOptions ( Options &options, CommandReturnObject &result, OptionArgVector *option_arg_vector ) { StreamString sstr; int i; struct option *long_options = options.GetLongOptions(); if (long_options == NULL) { result.AppendError ("invalid long options"); result.SetStatus (eReturnStatusFailed); return; } for (i = 0; long_options[i].name != NULL; ++i) { if (long_options[i].flag == NULL) { sstr << (char) long_options[i].val; switch (long_options[i].has_arg) { default: case no_argument: break; case required_argument: sstr << ":"; break; case optional_argument: sstr << "::"; break; } } } #ifdef __GLIBC__ optind = 0; #else optreset = 1; optind = 1; #endif int val; while (1) { int long_options_index = -1; val = ::getopt_long (GetArgumentCount(), GetArgumentVector(), sstr.GetData(), long_options, &long_options_index); if (val == -1) break; if (val == '?') { result.AppendError ("unknown or ambiguous option"); result.SetStatus (eReturnStatusFailed); break; } if (val == 0) continue; ((Options *) &options)->OptionSeen (val); // Look up the long option index if (long_options_index == -1) { for (int j = 0; long_options[j].name || long_options[j].has_arg || long_options[j].flag || long_options[j].val; ++j) { if (long_options[j].val == val) { long_options_index = j; break; } } } // See if the option takes an argument, and see if one was supplied. if (long_options_index >= 0) { StreamString option_str; option_str.Printf ("-%c", (char) val); switch (long_options[long_options_index].has_arg) { case no_argument: option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), "<no-argument>")); break; case required_argument: if (optarg != NULL) { option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), std::string (optarg))); result.SetStatus (eReturnStatusSuccessFinishNoResult); } else { result.AppendErrorWithFormat ("Option '%s' is missing argument specifier.\n", option_str.GetData()); result.SetStatus (eReturnStatusFailed); } break; case optional_argument: if (optarg != NULL) { option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), std::string (optarg))); result.SetStatus (eReturnStatusSuccessFinishNoResult); } else { option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), "<no-argument>")); result.SetStatus (eReturnStatusSuccessFinishNoResult); } break; default: result.AppendErrorWithFormat ("error with options table; invalid value in has_arg field for option '%c'.\n", (char) val); result.SetStatus (eReturnStatusFailed); break; } } else { result.AppendErrorWithFormat ("Invalid option with value '%c'.\n", (char) val); result.SetStatus (eReturnStatusFailed); } if (!result.Succeeded()) break; } }
void Args::ParseArgsForCompletion ( Options &options, OptionElementVector &option_element_vector, uint32_t cursor_index ) { StreamString sstr; struct option *long_options = options.GetLongOptions(); option_element_vector.clear(); if (long_options == NULL) { return; } // Leading : tells getopt to return a : for a missing option argument AND // to suppress error messages. sstr << ":"; for (int i = 0; long_options[i].name != NULL; ++i) { if (long_options[i].flag == NULL) { sstr << (char) long_options[i].val; switch (long_options[i].has_arg) { default: case no_argument: break; case required_argument: sstr << ":"; break; case optional_argument: sstr << "::"; break; } } } #ifdef __GLIBC__ optind = 0; #else optreset = 1; optind = 1; #endif opterr = 0; int val; const OptionDefinition *opt_defs = options.GetDefinitions(); // Fooey... getopt_long permutes the GetArgumentVector to move the options to the front. // So we have to build another Arg and pass that to getopt_long so it doesn't // change the one we have. std::vector<const char *> dummy_vec (GetArgumentVector(), GetArgumentVector() + GetArgumentCount() + 1); bool failed_once = false; uint32_t dash_dash_pos = -1; while (1) { bool missing_argument = false; int parse_start = optind; int long_options_index = -1; val = ::getopt_long (dummy_vec.size() - 1, (char *const *) dummy_vec.data(), sstr.GetData(), long_options, &long_options_index); if (val == -1) { // When we're completing a "--" which is the last option on line, if (failed_once) break; failed_once = true; // If this is a bare "--" we mark it as such so we can complete it successfully later. // Handling the "--" is a little tricky, since that may mean end of options or arguments, or the // user might want to complete options by long name. I make this work by checking whether the // cursor is in the "--" argument, and if so I assume we're completing the long option, otherwise // I let it pass to getopt_long which will terminate the option parsing. // Note, in either case we continue parsing the line so we can figure out what other options // were passed. This will be useful when we come to restricting completions based on what other // options we've seen on the line. if (optind < dummy_vec.size() - 1 && (strcmp (dummy_vec[optind-1], "--") == 0)) { dash_dash_pos = optind - 1; if (optind - 1 == cursor_index) { option_element_vector.push_back (OptionArgElement (OptionArgElement::eBareDoubleDash, optind - 1, OptionArgElement::eBareDoubleDash)); continue; } else break; } else break; } else if (val == '?') { option_element_vector.push_back (OptionArgElement (OptionArgElement::eUnrecognizedArg, optind - 1, OptionArgElement::eUnrecognizedArg)); continue; } else if (val == 0) { continue; } else if (val == ':') { // This is a missing argument. val = optopt; missing_argument = true; } ((Options *) &options)->OptionSeen (val); // Look up the long option index if (long_options_index == -1) { for (int j = 0; long_options[j].name || long_options[j].has_arg || long_options[j].flag || long_options[j].val; ++j) { if (long_options[j].val == val) { long_options_index = j; break; } } } // See if the option takes an argument, and see if one was supplied. if (long_options_index >= 0) { int opt_defs_index = -1; for (int i = 0; ; i++) { if (opt_defs[i].short_option == 0) break; else if (opt_defs[i].short_option == val) { opt_defs_index = i; break; } } switch (long_options[long_options_index].has_arg) { case no_argument: option_element_vector.push_back (OptionArgElement (opt_defs_index, parse_start, 0)); break; case required_argument: if (optarg != NULL) { int arg_index; if (missing_argument) arg_index = -1; else arg_index = optind - 1; option_element_vector.push_back (OptionArgElement (opt_defs_index, optind - 2, arg_index)); } else { option_element_vector.push_back (OptionArgElement (opt_defs_index, optind - 1, -1)); } break; case optional_argument: if (optarg != NULL) { option_element_vector.push_back (OptionArgElement (opt_defs_index, optind - 2, optind - 1)); } else { option_element_vector.push_back (OptionArgElement (opt_defs_index, optind - 2, optind - 1)); } break; default: // The options table is messed up. Here we'll just continue option_element_vector.push_back (OptionArgElement (OptionArgElement::eUnrecognizedArg, optind - 1, OptionArgElement::eUnrecognizedArg)); break; } } else { option_element_vector.push_back (OptionArgElement (OptionArgElement::eUnrecognizedArg, optind - 1, OptionArgElement::eUnrecognizedArg)); } } // Finally we have to handle the case where the cursor index points at a single "-". We want to mark that in // the option_element_vector, but only if it is not after the "--". But it turns out that getopt_long just ignores // an isolated "-". So we have to look it up by hand here. We only care if it is AT the cursor position. if ((dash_dash_pos == -1 || cursor_index < dash_dash_pos) && strcmp (GetArgumentAtIndex(cursor_index), "-") == 0) { option_element_vector.push_back (OptionArgElement (OptionArgElement::eBareDash, cursor_index, OptionArgElement::eBareDash)); } }
Error Args::ParseOptions (Options &options) { StreamString sstr; Error error; struct option *long_options = options.GetLongOptions(); if (long_options == NULL) { error.SetErrorStringWithFormat("Invalid long options.\n"); return error; } for (int i=0; long_options[i].name != NULL; ++i) { if (long_options[i].flag == NULL) { sstr << (char)long_options[i].val; switch (long_options[i].has_arg) { default: case no_argument: break; case required_argument: sstr << ':'; break; case optional_argument: sstr << "::"; break; } } } #ifdef __GLIBC__ optind = 0; #else optreset = 1; optind = 1; #endif int val; while (1) { int long_options_index = -1; val = ::getopt_long(GetArgumentCount(), GetArgumentVector(), sstr.GetData(), long_options, &long_options_index); if (val == -1) break; // Did we get an error? if (val == '?') { error.SetErrorStringWithFormat("Unknown or ambiguous option.\n"); break; } // The option auto-set itself if (val == 0) continue; ((Options *) &options)->OptionSeen (val); // Lookup the long option index if (long_options_index == -1) { for (int i=0; long_options[i].name || long_options[i].has_arg || long_options[i].flag || long_options[i].val; ++i) { if (long_options[i].val == val) { long_options_index = i; break; } } } // Call the callback with the option if (long_options_index >= 0) { error = options.SetOptionValue(long_options_index, long_options[long_options_index].has_arg == no_argument ? NULL : optarg); } else { error.SetErrorStringWithFormat("Invalid option with value '%i'.\n", val); } if (error.Fail()) break; } // Update our ARGV now that get options has consumed all the options m_argv.erase(m_argv.begin(), m_argv.begin() + optind); UpdateArgsAfterOptionParsing (); return error; }