summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
Diffstat (limited to 'cli')
-rw-r--r--cli/options.cli18
-rw-r--r--cli/options.cxx80
-rw-r--r--cli/runtime-source.cxx134
3 files changed, 140 insertions, 92 deletions
diff --git a/cli/options.cli b/cli/options.cli
index 8781bc6..9af36cd 100644
--- a/cli/options.cli
+++ b/cli/options.cli
@@ -633,15 +633,15 @@ class options
std::string --options-file
{
"<file>",
- "Read additional options from <file> with each option appearing on a
- separate line optionally followed by space and an option value. Empty
- lines and lines starting with \cb{#} are ignored. Option values can
- be enclosed in double (\cb{\"}) or single (\cb{'}) quotes to preserve
- leading and trailing whitespaces as well as to specify empty values.
- If the value itself contains trailing or leading quotes, enclose it
- with an extra pair of quotes, for example \cb{'\"x\"'}. Non-leading
- and non-trailing quotes are interpreted as being part of the option
- value.
+ "Read additional options from <file>. Each option should appear on a
+ separate line optionally followed by space or equal sign (\cb{=}) and an
+ option value. Empty lines and lines starting with \cb{#} are ignored.
+ Option values can be enclosed in double (\cb{\"}) or single (\cb{'})
+ quotes to preserve leading and trailing whitespaces as well as to specify
+ empty values. If the value itself contains trailing or leading quotes,
+ enclose it with an extra pair of quotes, for example \cb{'\"x\"'}.
+ Non-leading and non-trailing quotes are interpreted as being part of the
+ option value.
The semantics of providing options in a file is equivalent to providing
the same set of options in the same order on the command line at the
diff --git a/cli/options.cxx b/cli/options.cxx
index 809b588..bb102e0 100644
--- a/cli/options.cxx
+++ b/cli/options.cxx
@@ -372,45 +372,59 @@ namespace cli
if (line.empty () || line[0] == '#')
continue;
- string::size_type p (line.find (' '));
-
- if (p == string::npos)
+ string::size_type p (string::npos);
+ if (line.compare (0, 1, "-") == 0)
{
- if (!skip_)
- skip_ = (line == "--");
+ p = line.find (' ');
- args_.push_back (line);
+ string::size_type q (line.find ('='));
+ if (q != string::npos && q < p)
+ p = q;
}
- else
+
+ string s1;
+ if (p != string::npos)
{
- string s1 (line, 0, p);
+ s1.assign (line, 0, p);
// Skip leading whitespaces in the argument.
//
- n = line.size ();
- for (++p; p < n; ++p)
+ if (line[p] == '=')
+ ++p;
+ else
{
- char c (line[p]);
-
- if (c != ' ' && c != '\t' && c != '\r')
- break;
+ n = line.size ();
+ for (++p; p < n; ++p)
+ {
+ char c (line[p]);
+ if (c != ' ' && c != '\t' && c != '\r')
+ break;
+ }
}
+ }
+ else if (!skip_)
+ skip_ = (line == "--");
- string s2 (line, p);
+ string s2 (line, p != string::npos ? p : 0);
- // If the string is wrapped in quotes, remove them.
- //
- n = s2.size ();
- char cf (s2[0]), cl (s2[n - 1]);
+ // If the string (which is an option value or argument) is
+ // wrapped in quotes, remove them.
+ //
+ n = s2.size ();
+ char cf (s2[0]), cl (s2[n - 1]);
- if (cf == '"' || cf == '\'' || cl == '"' || cl == '\'')
- {
- if (n == 1 || cf != cl)
- throw unmatched_quote (s2);
+ if (cf == '"' || cf == '\'' || cl == '"' || cl == '\'')
+ {
+ if (n == 1 || cf != cl)
+ throw unmatched_quote (s2);
- s2 = string (s2, 1, n - 2);
- }
+ s2 = string (s2, 1, n - 2);
+ }
+ if (!s1.empty ())
+ {
+ // See if this is another file option.
+ //
const option_info* oi;
if (!skip_ && (oi = find (s1.c_str ())))
{
@@ -420,19 +434,19 @@ namespace cli
if (oi->search_func != 0)
{
std::string f (oi->search_func (s2.c_str (), oi->arg));
-
if (!f.empty ())
load (f);
}
else
load (s2);
+
+ continue;
}
- else
- {
- args_.push_back (s1);
- args_.push_back (s2);
- }
+
+ args_.push_back (s1);
}
+
+ args_.push_back (s2);
}
}
@@ -1659,9 +1673,7 @@ print_usage (::std::ostream& os, ::cli::usage_para p)
<< " the list of names that should not be used as" << ::std::endl
<< " identifiers." << ::std::endl;
- os << "--options-file <file> Read additional options from <file> with each" << ::std::endl
- << " option appearing on a separate line optionally" << ::std::endl
- << " followed by space and an option value." << ::std::endl;
+ os << "--options-file <file> Read additional options from <file>." << ::std::endl;
p = ::cli::usage_para::option;
diff --git a/cli/runtime-source.cxx b/cli/runtime-source.cxx
index f9248d9..778bed4 100644
--- a/cli/runtime-source.cxx
+++ b/cli/runtime-source.cxx
@@ -359,7 +359,8 @@ generate_runtime_source (context& ctx, bool complete)
bool sep (!ctx.opt_sep.empty ());
const string& pfx (ctx.opt_prefix);
- bool comb_values (!pfx.empty () && !ctx.options.no_combined_values ());
+ size_t pfx_n (pfx.size ());
+ bool comb_values (pfx_n != 0 && !ctx.options.no_combined_values ());
os << "// argv_file_scanner" << endl
<< "//" << endl
@@ -398,10 +399,8 @@ generate_runtime_source (context& ctx, bool complete)
//
if (comb_values)
{
- size_t n (pfx.size ());
-
os << "else if (std::strncmp (a, \"" << pfx << "\", " <<
- n << ") == 0)" // It looks like an option.
+ pfx_n << ") == 0)" // It looks like an option.
<< "{"
<< "if ((ov = std::strchr (a, '=')) != 0)" // Has '='.
<< "{"
@@ -537,67 +536,104 @@ generate_runtime_source (context& ctx, bool complete)
<< "if (line.empty () || line[0] == '#')" << endl
<< "continue;"
<< endl
- << "string::size_type p (line.find (' '));"
- << endl
- << "if (p == string::npos)"
- << "{";
- if (sep)
- os << "if (!skip_)" << endl
- << "skip_ = (line == \"" << ctx.opt_sep << "\");"
- << endl;
- os << "args_.push_back (line);"
- << "}"
- << "else"
+ << "string::size_type p (string::npos);";
+
+ // If we have the option prefix, then only consider lines that start
+ // with that as options.
+ //
+ if (pfx_n != 0)
+ os << "if (line.compare (0, " << pfx_n << ", \"" << pfx << "\") == 0)"
+ << "{";
+
+ os << "p = line.find (' ');";
+
+ // Handle the combined option/value (--foo=bar). This is a bit tricky
+ // since the equal sign can be part of the value (--foo bar=baz).
+ //
+ // Note that we cannot just pass it along as combined because of
+ // quoting (--foo="'bar baz'").
+ //
+ if (comb_values)
+ {
+ os << endl
+ << "string::size_type q (line.find ('='));"
+ << "if (q != string::npos && q < p)" << endl
+ << "p = q;";
+ }
+
+ if (pfx_n != 0)
+ os << "}";
+ else
+ os << endl;
+
+ os << "string s1;"
+ << "if (p != string::npos)"
<< "{"
- << "string s1 (line, 0, p);"
+ << "s1.assign (line, 0, p);"
<< endl
<< "// Skip leading whitespaces in the argument." << endl
- << "//" << endl
- << "n = line.size ();"
- << "for (++p; p < n; ++p)"
- << "{"
- << "char c (line[p]);"
- << endl
- << "if (c != ' ' && c != '\\t' && c != '\\r')" << endl
- << "break;"
- << "}"
- << "string s2 (line, p);"
+ << "//" << endl;
+ if (comb_values)
+ os << "if (line[p] == '=')" << endl // Keep whitespaces after '='.
+ << "++p;"
+ << "else"
+ << "{";
+ os << "n = line.size ();"
+ << "for (++p; p < n; ++p)"
+ << "{"
+ << "char c (line[p]);"
+ << "if (c != ' ' && c != '\\t' && c != '\\r')" << endl
+ << "break;"
+ << "}";
+ if (comb_values)
+ os << "}";
+ os << "}";
+ if (sep)
+ os << "else if (!skip_)" << endl
+ << "skip_ = (line == \"" << ctx.opt_sep << "\");"
+ << endl;
+
+ os << "string s2 (line, p != string::npos ? p : 0);"
<< endl
- << "// If the string is wrapped in quotes, remove them." << endl
+ << "// If the string (which is an option value or argument) is" << endl
+ << "// wrapped in quotes, remove them." << endl
<< "//" << endl
<< "n = s2.size ();"
<< "char cf (s2[0]), cl (s2[n - 1]);"
<< endl
<< "if (cf == '\"' || cf == '\\'' || cl == '\"' || cl == '\\'')"
<< "{"
- << "if (n == 1 || cf != cl)" << endl
- << "throw unmatched_quote (s2);"
+ << "if (n == 1 || cf != cl)" << endl
+ << "throw unmatched_quote (s2);"
<< endl
- << "s2 = string (s2, 1, n - 2);"
- << "}"
- << "const option_info* oi;"
- << "if (" << (sep ? "!skip_ && " : "") <<
- "(oi = find (s1.c_str ())))" << endl
+ << "s2 = string (s2, 1, n - 2);"
+ << "}";
+
+ os << "if (!s1.empty ())"
<< "{"
- << "if (s2.empty ())" << endl
- << "throw missing_value (oi->option);"
+ << "// See if this is another file option." << endl
+ << "//" << endl
+ << "const option_info* oi;"
+ << "if (" << (sep ? "!skip_ && " : "") <<
+ "(oi = find (s1.c_str ())))" << endl
+ << "{"
+ << "if (s2.empty ())" << endl
+ << "throw missing_value (oi->option);"
<< endl
- << "if (oi->search_func != 0)"
- << "{"
- << "std::string f (oi->search_func (s2.c_str (), oi->arg));"
+ << "if (oi->search_func != 0)"
+ << "{"
+ << "std::string f (oi->search_func (s2.c_str (), oi->arg));"
+ << "if (!f.empty ())" << endl
+ << "load (f);"
+ << "}"
+ << "else" << endl
+ << "load (s2);"
<< endl
- << "if (!f.empty ())" << endl
- << "load (f);"
- << "}"
- << "else" << endl
- << "load (s2);"
+ << "continue;"
+ << "}"
+ << "args_.push_back (s1);"
<< "}"
- << "else"
- << "{"
- << "args_.push_back (s1);"
<< "args_.push_back (s2);"
- << "}"
- << "}"
<< "}" // while
<< "}";
}