Skip to content

Split up the two parts of cmdlinet::parse into separate functions #5462

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 75 additions & 68 deletions src/util/cmdline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,100 +155,56 @@ bool cmdlinet::parse(int argc, const char **argv, const char *optstring)
{
clear();

while(optstring[0]!=0)
parse_optstring(optstring);
return parse_arguments(argc, argv);
}

cmdlinet::option_namest cmdlinet::option_names() const
{
return option_namest{*this};
}
void cmdlinet::parse_optstring(const char *optstring)
{
while(optstring[0] != 0)
{
optiont option;

DATA_INVARIANT(
optstring[0] != ':', "cmdlinet::parse: Invalid option string\n");

if(optstring[0]=='(')
if(optstring[0] == '(')
{
option.islong=true;
option.optchar=0;
option.isset=false;
option.islong = true;
option.optchar = 0;
option.isset = false;
option.optstring.clear();

for(optstring++; optstring[0]!=')' && optstring[0]!=0; optstring++)
option.optstring+=optstring[0];
for(optstring++; optstring[0] != ')' && optstring[0] != 0; optstring++)
option.optstring += optstring[0];

if(optstring[0]==')')
if(optstring[0] == ')')
optstring++;
}
else
{
option.islong=false;
option.optchar=optstring[0];
option.islong = false;
option.optchar = optstring[0];
option.optstring.clear();
option.isset=false;
option.isset = false;

optstring++;
}

if(optstring[0]==':')
if(optstring[0] == ':')
{
option.hasval=true;
option.hasval = true;
optstring++;
}
else
option.hasval=false;
option.hasval = false;

options.push_back(option);
}

for(int i=1; i<argc; i++)
{
if(argv[i][0]!='-')
args.push_back(argv[i]);
else
{
optionalt<std::size_t> optnr;

if(argv[i][1]!=0 && argv[i][2]==0)
optnr=getoptnr(argv[i][1]); // single-letter option -X
else if(argv[i][1]=='-')
optnr=getoptnr(argv[i]+2); // multi-letter option with --XXX
else
{
// Multi-letter option -XXX, or single-letter with argument -Xval
// We first try single-letter.
optnr=getoptnr(argv[i][1]);

if(!optnr.has_value()) // try multi-letter
optnr=getoptnr(argv[i]+1);
}

if(!optnr.has_value())
{
unknown_arg=argv[i];
return true;
}

options[*optnr].isset=true;

if(options[*optnr].hasval)
{
if(argv[i][2]==0 || options[*optnr].islong)
{
i++;
if(i==argc)
return true;
if(argv[i][0]=='-' && argv[i][1]!=0)
return true;
options[*optnr].values.push_back(argv[i]);
}
else
options[*optnr].values.push_back(argv[i]+2);
}
}
}

return false;
}

cmdlinet::option_namest cmdlinet::option_names() const
{
return option_namest{*this};
}

std::vector<std::string>
Expand Down Expand Up @@ -311,6 +267,57 @@ cmdlinet::get_argument_suggestions(const std::string &unknown_argument)
return final_suggestions;
}

bool cmdlinet::parse_arguments(int argc, const char **argv)
{
for(int i = 1; i < argc; i++)
{
if(argv[i][0] != '-')
args.push_back(argv[i]);
else
{
optionalt<std::size_t> optnr;

if(argv[i][1] != 0 && argv[i][2] == 0)
optnr = getoptnr(argv[i][1]); // single-letter option -X
else if(argv[i][1] == '-')
optnr = getoptnr(argv[i] + 2); // multi-letter option with --XXX
else
{
// Multi-letter option -XXX, or single-letter with argument -Xval
// We first try single-letter.
optnr = getoptnr(argv[i][1]);

if(!optnr.has_value()) // try multi-letter
optnr = getoptnr(argv[i] + 1);
}

if(!optnr.has_value())
{
unknown_arg = argv[i];
return true;
}

options[*optnr].isset = true;

if(options[*optnr].hasval)
{
if(argv[i][2] == 0 || options[*optnr].islong)
{
i++;
if(i == argc)
return true;
if(argv[i][0] == '-' && argv[i][1] != 0)
return true;
options[*optnr].values.push_back(argv[i]);
}
else
options[*optnr].values.push_back(argv[i] + 2);
}
}
}
return false;
}

cmdlinet::option_namest::option_names_iteratort::option_names_iteratort(
const cmdlinet *command_line,
std::size_t index)
Expand Down
64 changes: 64 additions & 0 deletions src/util/cmdline.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,58 @@ Author: Daniel Kroening, [email protected]
class cmdlinet
{
public:
/// Parses a commandline according to a specification given in \p optstring.
/// \param argc How many arguments there are.
/// \param argv An array of C strings.
/// The 0th element is assumed to be the name of the command as
/// it was invoked (e.g. /usr/bin/cmake) and is ignored. It is
/// further assumed the array holds \p argc+1 elements with the C
/// string at index argc being a terminating null pointer.
/// This argument is parsed based on \p optstring.
/// \param optstring A specification of allowed command line options.
/// This is a C string container any number of single
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⛏️ container -> containing

/// characters other than '(', ')' or ':' signifying a
/// "short" option consisting of just that character, or
/// names consisting of any characters other than ')'
/// surrounded by a matching pair of '(' and ')' signifying a
/// "long" option with the name being the string between '('
/// and ')', both of which can be optionally followed by a
/// single ':' indicating that the option takes a argument,
/// if not present it does not. arguments must be in the
/// next array element in \p argv , except for short options
/// whose argument may also be concatenated directly on them.
///
/// Option names in \p argv must start with either '-' or "--",
/// no distinction between long and short options is made
/// here, although it is customary to use only one '-' for
/// short options and "--" for long options.
///
/// All options are optional, if some are required it is up
/// to the user to check that they are present.
///
/// Examples:
///
/// argc = 4
/// argv = `{"name", "-V", "--name", "CProver", nullptr}`
/// opstring = `"V(version)(name):"`
///
/// here the argument to "name" would be "CProver", and
/// "V" is a short option passed without arguments.
///
/// argc = 3
/// argv = `{"other-name", "-fFilename", "--trace", nullptr}`
/// optstring = `"f:(trace)(some-other-option):G"`
///
/// here the argument to option "f" would be "Filename",
/// "trace" is a long option with no argument, and
/// "some-other-option" and "G" are both allowed options that
/// don’t appear on the commandline (with and without
/// argument respectively).
///
/// \return true if there was an error while parsing argv, false otherwise. If
/// this failed due to an unknown option name being in argv, the
/// public variable cmdlinet::unknown_arg will be non-empty and
/// contain the name of that option.
virtual bool parse(int argc, const char **argv, const char *optstring);

std::string get_value(char option) const;
Expand Down Expand Up @@ -115,6 +167,18 @@ class cmdlinet
{}
};

/// Parses an optstring and writes the result to cmdlinet::options.
/// It is considered a logic error to pass an invalid option string here.
/// \see cmdlinet::parse(int,const char**,const char*)
/// for details on the format of the optstring
void parse_optstring(const char *optstring);

/// Parses a commandline according to a previously parsed optstring and
/// writes the result to cmdlinet::options.
/// \see cmdlinet::parse(int,const char**,const char*)
/// for details the meaning of argc and argv
bool parse_arguments(int argc, const char **argv);

std::vector<optiont> options;

optionalt<std::size_t> getoptnr(char option) const;
Expand Down