summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS25
-rw-r--r--cli/header.cxx17
-rw-r--r--cli/options.cli11
-rw-r--r--cli/options.cxx84
-rw-r--r--cli/options.hxx30
-rw-r--r--cli/options.ixx14
-rw-r--r--cli/runtime-header.cxx82
-rw-r--r--cli/runtime-inline.cxx63
-rw-r--r--cli/runtime-source.cxx228
-rw-r--r--cli/source.cxx49
-rw-r--r--doc/cli.15
-rw-r--r--doc/cli.xhtml5
-rw-r--r--doc/guide/index.xhtml148
-rw-r--r--examples/README4
-rw-r--r--examples/examples-8.0.sln6
-rw-r--r--examples/examples-9.0.sln6
-rw-r--r--examples/file/README38
-rw-r--r--examples/file/driver.cxx36
-rw-r--r--examples/file/file-8.0.vcproj237
-rw-r--r--examples/file/file-9.0.vcproj233
-rw-r--r--examples/file/makefile74
-rw-r--r--examples/file/options.cli7
-rw-r--r--examples/file/test.ops7
-rw-r--r--examples/makefile2
-rw-r--r--tests/ctor/driver.cxx69
-rw-r--r--tests/ctor/makefile73
-rw-r--r--tests/ctor/test.cli9
-rw-r--r--tests/erase/driver.cxx35
-rw-r--r--tests/erase/makefile73
-rw-r--r--tests/erase/test.cli10
-rw-r--r--tests/file/base.ops2
-rw-r--r--tests/file/driver.cxx29
-rw-r--r--tests/file/empty.ops3
-rw-r--r--tests/file/makefile96
-rw-r--r--tests/file/test-000.ops6
-rw-r--r--tests/file/test-000.std18
-rw-r--r--tests/file/test-001.ops3
-rw-r--r--tests/file/test-001.std6
-rw-r--r--tests/file/test-002.ops7
-rw-r--r--tests/file/test-002.std17
-rw-r--r--tests/file/test-003.std7
-rw-r--r--tests/file/test.cli8
-rw-r--r--tests/makefile2
43 files changed, 1813 insertions, 71 deletions
diff --git a/NEWS b/NEWS
index 0d5f8f8..789c8bb 100644
--- a/NEWS
+++ b/NEWS
@@ -3,9 +3,8 @@ Version 1.1.0
* Support for option documentation. Option documentation is used to print
the usage information as well as to generate the program documentation in
the HTML and man page formats. For details, see Sections 2.5, "Adding
- Documentation" and 3.3, "Option Documentation" in the CLI Language Getting
- Started Guide. New CLI compiler command line options related to this
- feature:
+ Documentation" and 3.3, "Option Documentation" in the Getting Started
+ Guide. New CLI compiler command line options related to this feature:
--suppress-usage
--long-usage
@@ -25,8 +24,24 @@ Version 1.1.0
The CLI compiler usage, HTML documentation, and man page are auto-generated
using this feature.
- * New option, --generate-modifier, triggers generation of the option value
- modifiers in addition to the accessors.
+ * New option, --generate-modifier, triggers the generation of the option
+ value modifiers in addition to the accessors.
+
+ * Support for erasing the parsed elements from the argc/argv array. See
+ Section 3.1, "Option Class Definition" in the Getting Started Guide for
+ more information.
+
+ * New scanner interface. Starting with this version, the option class has
+ a new constructor which accepts an abstract scanner interface. See Section
+ 3.1, "Option Class Definition" in the Getting Started Guide for more
+ information.
+
+ * New option, --generate-file-scanner, triggers the generation of the
+ argv_file_scanner scanner implementation which provides support for
+ reading command line arguments from the argv array as well as files
+ specified with command line options. For more information see Section
+ 3.1, "Option Class Definition" in the Getting Started Guide as well as
+ the 'file' example.
Version 1.0.0
diff --git a/cli/header.cxx b/cli/header.cxx
index 0cc9ede..012933d 100644
--- a/cli/header.cxx
+++ b/cli/header.cxx
@@ -73,30 +73,39 @@ namespace
//
string um ("::cli::unknown_mode");
- os << name << " (int argc," << endl
+ os << name << " (int& argc," << endl
<< "char** argv," << endl
+ << "bool erase = false," << endl
<< um << " option = " << um << "::fail," << endl
<< um << " argument = " << um << "::stop);"
<< endl;
os << name << " (int start," << endl
- << "int argc," << endl
+ << "int& argc," << endl
<< "char** argv," << endl
+ << "bool erase = false," << endl
<< um << " option = " << um << "::fail," << endl
<< um << " argument = " << um << "::stop);"
<< endl;
- os << name << " (int argc," << endl
+ os << name << " (int& argc," << endl
<< "char** argv," << endl
<< "int& end," << endl
+ << "bool erase = false," << endl
<< um << " option = " << um << "::fail," << endl
<< um << " argument = " << um << "::stop);"
<< endl;
os << name << " (int start," << endl
- << "int argc," << endl
+ << "int& argc," << endl
<< "char** argv," << endl
<< "int& end," << endl
+ << "bool erase = false," << endl
+ << um << " option = " << um << "::fail," << endl
+ << um << " argument = " << um << "::stop);"
+ << endl;
+
+ os << name << " (::cli::scanner&," << endl
<< um << " option = " << um << "::fail," << endl
<< um << " argument = " << um << "::stop);"
<< endl;
diff --git a/cli/options.cli b/cli/options.cli
index b9df64f..afd0f98 100644
--- a/cli/options.cli
+++ b/cli/options.cli
@@ -3,8 +3,8 @@
// copyright : Copyright (c) 2009 Code Synthesis Tools CC
// license : MIT; see accompanying LICENSE file
-// NOTE: Make sure you have a working CLI compiler around
-// before modifying this file.
+// NOTE: Make sure you have a working CLI compiler around before
+// modifying this file.
//
include <map>;
@@ -28,6 +28,13 @@ class options
"Generate option value modifiers in addition to accessors."
};
+ bool --generate-file-scanner
+ {
+ "Generate the \c{argv_file_scanner} implementation. This scanner is
+ capable of reading command line arguments from the \c{argv} array as
+ well as files specified with command line options."
+ };
+
bool --suppress-inline
{
"Generate all functions non-inline. By default simple functions are
diff --git a/cli/options.cxx b/cli/options.cxx
index 399069e..6d4ee87 100644
--- a/cli/options.cxx
+++ b/cli/options.cxx
@@ -132,7 +132,22 @@ namespace cli
next ()
{
if (i_ < argc_)
- return argv_[i_++];
+ {
+ const char* r (argv_[i_]);
+
+ if (erase_)
+ {
+ for (int i (i_ + 1); i < argc_; ++i)
+ argv_[i - 1] = argv_[i];
+
+ --argc_;
+ argv_[argc_] = 0;
+ }
+ else
+ ++i_;
+
+ return r;
+ }
else
throw eos_reached ();
}
@@ -141,7 +156,7 @@ namespace cli
skip ()
{
if (i_ < argc_)
- i_++;
+ ++i_;
else
throw eos_reached ();
}
@@ -289,14 +304,16 @@ namespace cli
//
options::
-options (int argc,
+options (int& argc,
char** argv,
+ bool erase,
::cli::unknown_mode opt,
::cli::unknown_mode arg)
: help_ (),
version_ (),
output_dir_ (),
generate_modifier_ (),
+ generate_file_scanner_ (),
suppress_inline_ (),
suppress_usage_ (),
long_usage_ (),
@@ -322,20 +339,22 @@ options (int argc,
guard_prefix_ (),
reserved_name_ ()
{
- ::cli::argv_scanner s (argc, argv);
+ ::cli::argv_scanner s (argc, argv, erase);
_parse (s, opt, arg);
}
options::
options (int start,
- int argc,
+ int& argc,
char** argv,
+ bool erase,
::cli::unknown_mode opt,
::cli::unknown_mode arg)
: help_ (),
version_ (),
output_dir_ (),
generate_modifier_ (),
+ generate_file_scanner_ (),
suppress_inline_ (),
suppress_usage_ (),
long_usage_ (),
@@ -361,20 +380,22 @@ options (int start,
guard_prefix_ (),
reserved_name_ ()
{
- ::cli::argv_scanner s (start, argc, argv);
+ ::cli::argv_scanner s (start, argc, argv, erase);
_parse (s, opt, arg);
}
options::
-options (int argc,
+options (int& argc,
char** argv,
int& end,
+ bool erase,
::cli::unknown_mode opt,
::cli::unknown_mode arg)
: help_ (),
version_ (),
output_dir_ (),
generate_modifier_ (),
+ generate_file_scanner_ (),
suppress_inline_ (),
suppress_usage_ (),
long_usage_ (),
@@ -400,22 +421,24 @@ options (int argc,
guard_prefix_ (),
reserved_name_ ()
{
- ::cli::argv_scanner s (argc, argv);
+ ::cli::argv_scanner s (argc, argv, erase);
_parse (s, opt, arg);
end = s.end ();
}
options::
options (int start,
- int argc,
+ int& argc,
char** argv,
int& end,
+ bool erase,
::cli::unknown_mode opt,
::cli::unknown_mode arg)
: help_ (),
version_ (),
output_dir_ (),
generate_modifier_ (),
+ generate_file_scanner_ (),
suppress_inline_ (),
suppress_usage_ (),
long_usage_ (),
@@ -441,11 +464,48 @@ options (int start,
guard_prefix_ (),
reserved_name_ ()
{
- ::cli::argv_scanner s (start, argc, argv);
+ ::cli::argv_scanner s (start, argc, argv, erase);
_parse (s, opt, arg);
end = s.end ();
}
+options::
+options (::cli::scanner& s,
+ ::cli::unknown_mode opt,
+ ::cli::unknown_mode arg)
+: help_ (),
+ version_ (),
+ output_dir_ (),
+ generate_modifier_ (),
+ generate_file_scanner_ (),
+ suppress_inline_ (),
+ suppress_usage_ (),
+ long_usage_ (),
+ option_length_ (0),
+ generate_cxx_ (),
+ generate_man_ (),
+ generate_html_ (),
+ man_prologue_ (),
+ man_epilogue_ (),
+ html_prologue_ (),
+ html_epilogue_ (),
+ class__ (),
+ stdout_ (),
+ hxx_suffix_ (".hxx"),
+ ixx_suffix_ (".ixx"),
+ cxx_suffix_ (".cxx"),
+ man_suffix_ (".1"),
+ html_suffix_ (".html"),
+ option_prefix_ ("-"),
+ option_separator_ ("--"),
+ include_with_brackets_ (),
+ include_prefix_ (),
+ guard_prefix_ (),
+ reserved_name_ ()
+{
+ _parse (s, opt, arg);
+}
+
void options::
print_usage (::std::ostream& os)
{
@@ -459,6 +519,8 @@ print_usage (::std::ostream& os)
os << "--generate-modifier Generate option value modifiers in addition to" << ::std::endl
<< " accessors." << ::std::endl;
+ os << "--generate-file-scanner Generate the 'argv_file_scanner' implementation." << ::std::endl;
+
os << "--suppress-inline Generate all functions non-inline." << ::std::endl;
os << "--suppress-usage Suppress the generation of the usage printing code." << ::std::endl;
@@ -547,6 +609,8 @@ struct _cli_options_map_init
&::cli::thunk< options, std::string, &options::output_dir_ >;
_cli_options_map_["--generate-modifier"] =
&::cli::thunk< options, bool, &options::generate_modifier_ >;
+ _cli_options_map_["--generate-file-scanner"] =
+ &::cli::thunk< options, bool, &options::generate_file_scanner_ >;
_cli_options_map_["--suppress-inline"] =
&::cli::thunk< options, bool, &options::suppress_inline_ >;
_cli_options_map_["--suppress-usage"] =
diff --git a/cli/options.hxx b/cli/options.hxx
index 2be8e91..54143b9 100644
--- a/cli/options.hxx
+++ b/cli/options.hxx
@@ -166,8 +166,8 @@ namespace cli
class argv_scanner: public scanner
{
public:
- argv_scanner (int argc, char** argv);
- argv_scanner (int start, int argc, char** argv);
+ argv_scanner (int& argc, char** argv, bool erase = false);
+ argv_scanner (int start, int& argc, char** argv, bool erase = false);
int
end () const;
@@ -184,9 +184,11 @@ namespace cli
virtual void
skip ();
- private:int i_;
- int argc_;
+ private:
+ int i_;
+ int& argc_;
char** argv_;
+ bool erase_;
};
}
@@ -202,27 +204,35 @@ class options
{
public:
- options (int argc,
+ options (int& argc,
char** argv,
+ bool erase = false,
::cli::unknown_mode option = ::cli::unknown_mode::fail,
::cli::unknown_mode argument = ::cli::unknown_mode::stop);
options (int start,
- int argc,
+ int& argc,
char** argv,
+ bool erase = false,
::cli::unknown_mode option = ::cli::unknown_mode::fail,
::cli::unknown_mode argument = ::cli::unknown_mode::stop);
- options (int argc,
+ options (int& argc,
char** argv,
int& end,
+ bool erase = false,
::cli::unknown_mode option = ::cli::unknown_mode::fail,
::cli::unknown_mode argument = ::cli::unknown_mode::stop);
options (int start,
- int argc,
+ int& argc,
char** argv,
int& end,
+ bool erase = false,
+ ::cli::unknown_mode option = ::cli::unknown_mode::fail,
+ ::cli::unknown_mode argument = ::cli::unknown_mode::stop);
+
+ options (::cli::scanner&,
::cli::unknown_mode option = ::cli::unknown_mode::fail,
::cli::unknown_mode argument = ::cli::unknown_mode::stop);
@@ -242,6 +252,9 @@ class options
generate_modifier () const;
const bool&
+ generate_file_scanner () const;
+
+ const bool&
suppress_inline () const;
const bool&
@@ -329,6 +342,7 @@ class options
bool version_;
std::string output_dir_;
bool generate_modifier_;
+ bool generate_file_scanner_;
bool suppress_inline_;
bool suppress_usage_;
bool long_usage_;
diff --git a/cli/options.ixx b/cli/options.ixx
index 6fcc80f..74d7b15 100644
--- a/cli/options.ixx
+++ b/cli/options.ixx
@@ -87,14 +87,14 @@ namespace cli
// argv_scanner
//
inline argv_scanner::
- argv_scanner (int argc, char** argv)
- : i_ (1), argc_ (argc), argv_ (argv)
+ argv_scanner (int& argc, char** argv, bool erase)
+ : i_ (1), argc_ (argc), argv_ (argv), erase_ (erase)
{
}
inline argv_scanner::
- argv_scanner (int start, int argc, char** argv)
- : i_ (start), argc_ (argc), argv_ (argv)
+ argv_scanner (int start, int& argc, char** argv, bool erase)
+ : i_ (start), argc_ (argc), argv_ (argv), erase_ (erase)
{
}
@@ -133,6 +133,12 @@ generate_modifier () const
}
inline const bool& options::
+generate_file_scanner () const
+{
+ return this->generate_file_scanner_;
+}
+
+inline const bool& options::
suppress_inline () const
{
return this->suppress_inline_;
diff --git a/cli/runtime-header.cxx b/cli/runtime-header.cxx
index cf3ebd3..5da33c0 100644
--- a/cli/runtime-header.cxx
+++ b/cli/runtime-header.cxx
@@ -12,6 +12,9 @@ generate_runtime_header (context& ctx)
{
ostream& os (ctx.os);
+ if (ctx.options.generate_file_scanner ())
+ os << "#include <deque>" << endl;
+
os << "#include <iosfwd>" << endl
<< "#include <string>" << endl
<< "#include <exception>" << endl
@@ -155,6 +158,30 @@ generate_runtime_header (context& ctx)
<< "what () const throw ();"
<< "};";
+ if (ctx.options.generate_file_scanner ())
+ {
+ os << "class file_io_failure: public exception"
+ << "{"
+ << "public:" << endl
+ << "virtual" << endl
+ << "~file_io_failure () throw ();"
+ << endl
+ << "file_io_failure (const std::string& file);"
+ << endl
+ << "const std::string&" << endl
+ << "file () const;"
+ << endl
+ << "virtual void" << endl
+ << "print (std::ostream&) const;"
+ << endl
+ << "virtual const char*" << endl
+ << "what () const throw ();"
+ << endl
+ << "private:" << endl
+ << "std::string file_;"
+ << "};";
+ }
+
// scanner
//
os << "class scanner"
@@ -181,8 +208,8 @@ generate_runtime_header (context& ctx)
os << "class argv_scanner: public scanner"
<< "{"
<< "public:" << endl
- << "argv_scanner (int argc, char** argv);"
- << "argv_scanner (int start, int argc, char** argv);"
+ << "argv_scanner (int& argc, char** argv, bool erase = false);"
+ << "argv_scanner (int start, int& argc, char** argv, bool erase = false);"
<< endl
<< "int" << endl
<< "end () const;"
@@ -199,11 +226,58 @@ generate_runtime_header (context& ctx)
<< "virtual void" << endl
<< "skip ();"
<< endl
- << "private:"
+ << "private:" << endl
<< "int i_;"
- << "int argc_;"
+ << "int& argc_;"
<< "char** argv_;"
+ << "bool erase_;"
<< "};";
+ // argv_file_scanner
+ //
+ if (ctx.options.generate_file_scanner ())
+ {
+ os << "class argv_file_scanner: public argv_scanner"
+ << "{"
+ << "public:" << endl
+ << "argv_file_scanner (int& argc," << endl
+ << "char** argv," << endl
+ << "const std::string& file_option," << endl
+ << "bool erase = false);"
+ << endl
+ << "argv_file_scanner (int start," << endl
+ << "int& argc," << endl
+ << "char** argv," << endl
+ << "const std::string& file_option," << endl
+ << "bool erase = false);"
+ << endl
+ << "virtual bool" << endl
+ << "more ();"
+ << endl
+ << "virtual const char*" << endl
+ << "peek ();"
+ << endl
+ << "virtual const char*" << endl
+ << "next ();"
+ << endl
+ << "virtual void" << endl
+ << "skip ();"
+ << endl
+ << "private:" << endl
+ << "void" << endl
+ << "load (const char* file);"
+ << endl
+ << "typedef argv_scanner base;"
+ << endl
+ << "const std::string option_;"
+ << "std::string hold_;"
+ << "std::deque<std::string> args_;";
+
+ if (!ctx.opt_sep.empty ())
+ os << "bool skip_;";
+
+ os << "};";
+ }
+
os << "}"; // namespace cli
}
diff --git a/cli/runtime-inline.cxx b/cli/runtime-inline.cxx
index d4ce3ec..c99739c 100644
--- a/cli/runtime-inline.cxx
+++ b/cli/runtime-inline.cxx
@@ -103,18 +103,36 @@ generate_runtime_inline (context& ctx)
<< "return value_;"
<< "}";
+ if (ctx.options.generate_file_scanner ())
+ {
+ os << "// file_io_failure" << endl
+ << "//" << endl
+
+ << inl << "file_io_failure::" << endl
+ << "file_io_failure (const std::string& file)" << endl
+ << ": file_ (file)"
+ << "{"
+ << "}"
+
+ << inl << "const std::string& file_io_failure::" << endl
+ << "file () const"
+ << "{"
+ << "return file_;"
+ << "}";
+ }
+
os << "// argv_scanner" << endl
<< "//" << endl;
os << inl << "argv_scanner::" << endl
- << "argv_scanner (int argc, char** argv)" << endl
- << ": i_ (1), argc_ (argc), argv_ (argv)"
+ << "argv_scanner (int& argc, char** argv, bool erase)" << endl
+ << ": i_ (1), argc_ (argc), argv_ (argv), erase_ (erase)"
<< "{"
<< "}";
os << inl << "argv_scanner::" << endl
- << "argv_scanner (int start, int argc, char** argv)" << endl
- << ": i_ (start), argc_ (argc), argv_ (argv)"
+ << "argv_scanner (int start, int& argc, char** argv, bool erase)" << endl
+ << ": i_ (start), argc_ (argc), argv_ (argv), erase_ (erase)"
<< "{"
<< "}";
@@ -124,5 +142,42 @@ generate_runtime_inline (context& ctx)
<< "return i_;"
<< "}";
+ // argv_file_scanner
+ //
+ if (ctx.options.generate_file_scanner ())
+ {
+ bool sep (!ctx.opt_sep.empty ());
+
+ os << "// argv_file_scanner" << endl
+ << "//" << endl;
+
+ os << inl << "argv_file_scanner::" << endl
+ << "argv_file_scanner (int& argc," << endl
+ << "char** argv," << endl
+ << "const std::string& option," << endl
+ << "bool erase)" << endl
+ << ": argv_scanner (argc, argv, erase)," << endl
+ << " option_ (option)";
+ if (sep)
+ os << "," << endl
+ << " skip_ (false)";
+ os << "{"
+ << "}";
+
+ os << inl << "argv_file_scanner::" << endl
+ << "argv_file_scanner (int start," << endl
+ << "int& argc," << endl
+ << "char** argv," << endl
+ << "const std::string& option," << endl
+ << "bool erase)" << endl
+ << ": argv_scanner (start, argc, argv, erase)," << endl
+ << " option_ (option)";
+ if (sep)
+ os << "," << endl
+ << " skip_ (false)";
+ os << "{"
+ << "}";
+ }
+
os << "}"; // namespace cli
}
diff --git a/cli/runtime-source.cxx b/cli/runtime-source.cxx
index 3003b6b..323b604 100644
--- a/cli/runtime-source.cxx
+++ b/cli/runtime-source.cxx
@@ -17,8 +17,13 @@ generate_runtime_source (context& ctx)
<< "#include <string>" << endl
<< "#include <vector>" << endl
<< "#include <ostream>" << endl
- << "#include <sstream>" << endl
- << endl;
+ << "#include <sstream>" << endl;
+
+ if (ctx.options.generate_file_scanner ())
+ os << "#include <cstring>" << endl
+ << "#include <fstream>" << endl;
+
+ os << endl;
os << "namespace cli"
<< "{";
@@ -31,11 +36,13 @@ generate_runtime_source (context& ctx)
<< "~unknown_option () throw ()"
<< "{"
<< "}"
+
<< "void unknown_option::" << endl
<< "print (std::ostream& os) const"
<< "{"
<< "os << \"unknown option '\" << option () << \"'\";"
<< "}"
+
<< "const char* unknown_option::" << endl
<< "what () const throw ()"
<< "{"
@@ -50,11 +57,13 @@ generate_runtime_source (context& ctx)
<< "~unknown_argument () throw ()"
<< "{"
<< "}"
+
<< "void unknown_argument::" << endl
<< "print (std::ostream& os) const"
<< "{"
<< "os << \"unknown argument '\" << argument () << \"'\";"
<< "}"
+
<< "const char* unknown_argument::" << endl
<< "what () const throw ()"
<< "{"
@@ -69,11 +78,13 @@ generate_runtime_source (context& ctx)
<< "~missing_value () throw ()"
<< "{"
<< "}"
+
<< "void missing_value::" << endl
<< "print (std::ostream& os) const"
<< "{"
<< "os << \"missing value for option '\" << option () << \"'\";"
<< "}"
+
<< "const char* missing_value::" << endl
<< "what () const throw ()"
<< "{"
@@ -88,12 +99,14 @@ generate_runtime_source (context& ctx)
<< "~invalid_value () throw ()"
<< "{"
<< "}"
+
<< "void invalid_value::" << endl
<< "print (std::ostream& os) const"
<< "{"
<< "os << \"invalid value '\" << value () << \"' for option '\"" << endl
<< " << option () << \"'\";"
<< "}"
+
<< "const char* invalid_value::" << endl
<< "what () const throw ()"
<< "{"
@@ -109,12 +122,37 @@ generate_runtime_source (context& ctx)
<< "{"
<< "os << what ();"
<< "}"
+
<< "const char* eos_reached::" << endl
<< "what () const throw ()"
<< "{"
<< "return \"end of argument stream reached\";"
<< "}";
+ // file_io_failure
+ //
+ if (ctx.options.generate_file_scanner ())
+ {
+ os << "// file_io_failure" << endl
+ << "//" << endl
+ << "file_io_failure::" << endl
+ << "~file_io_failure () throw ()"
+ << "{"
+ << "}"
+
+ << "void file_io_failure::" << endl
+ << "print (std::ostream& os) const"
+ << "{"
+ << "os << \"unable to open file '\" << file () << \"' or read failure\";"
+ << "}"
+
+ << "const char* file_io_failure::" << endl
+ << "what () const throw ()"
+ << "{"
+ << "return \"unable to open file or read failure\";"
+ << "}";
+ }
+
// scanner
//
os << "// scanner" << endl
@@ -147,8 +185,23 @@ generate_runtime_source (context& ctx)
<< "const char* argv_scanner::" << endl
<< "next ()"
<< "{"
- << "if (i_ < argc_)" << endl
- << "return argv_[i_++];"
+ << "if (i_ < argc_)"
+ << "{"
+ << "const char* r (argv_[i_]);"
+ << endl
+ << "if (erase_)"
+ << "{"
+ << "for (int i (i_ + 1); i < argc_; ++i)" << endl
+ << "argv_[i - 1] = argv_[i];"
+ << endl
+ << "--argc_;"
+ << "argv_[argc_] = 0;"
+ << "}"
+ << "else" << endl
+ << "++i_;"
+ << endl
+ << "return r;"
+ << "}"
<< "else" << endl
<< "throw eos_reached ();"
<< "}"
@@ -157,11 +210,176 @@ generate_runtime_source (context& ctx)
<< "skip ()"
<< "{"
<< "if (i_ < argc_)" << endl
- << "i_++;"
+ << "++i_;"
<< "else" << endl
<< "throw eos_reached ();"
<< "}";
+ // argv_file_scanner
+ //
+ if (ctx.options.generate_file_scanner ())
+ {
+ bool sep (!ctx.opt_sep.empty ());
+
+ os << "// argv_file_scanner" << endl
+ << "//" << endl
+
+ << "bool argv_file_scanner::" << endl
+ << "more ()"
+ << "{"
+ << "if (!args_.empty ())" << endl
+ << "return true;"
+ << endl
+ << "while (base::more ())"
+ << "{"
+ << "// See if the next argument is the file option." << endl
+ << "//" << endl
+ << "const char* a (base::peek ());"
+ << endl
+ << "if (" << (sep ? "!skip_ && " : "") << "a == option_)"
+ << "{"
+ << "base::next ();"
+ << endl
+ << "if (!base::more ())" << endl
+ << "throw missing_value (option_);"
+ << endl
+ << "load (base::next ());"
+ << endl
+ << "if (!args_.empty ())" << endl
+ << "return true;"
+ << "}"
+ << "else"
+ << "{";
+ if (sep)
+ os << "if (!skip_)" << endl
+ << "skip_ = (std::strcmp (a, \"" << ctx.opt_sep << "\") == 0);"
+ << endl;
+ os << "return true;"
+ << "}"
+ << "}" // while
+ << "return false;"
+ << "}"
+
+ << "const char* argv_file_scanner::" << endl
+ << "peek ()"
+ << "{"
+ << "if (!more ())" << endl
+ << "throw eos_reached ();"
+ << endl
+ << "return args_.empty () ? base::peek () : args_.front ().c_str ();"
+ << "}"
+
+ << "const char* argv_file_scanner::" << endl
+ << "next ()"
+ << "{"
+ << "if (!more ())" << endl
+ << "throw eos_reached ();"
+ << endl
+ << "if (args_.empty ())" << endl
+ << "return base::next ();"
+ << "else"
+ << "{"
+ << "hold_.swap (args_.front ());"
+ << "args_.pop_front ();"
+ << "return hold_.c_str ();"
+ << "}"
+ << "}"
+
+ << "void argv_file_scanner::" << endl
+ << "skip ()"
+ << "{"
+ << "if (!more ())" << endl
+ << "throw eos_reached ();"
+ << endl
+ << "if (args_.empty ())" << endl
+ << "return base::skip ();"
+ << "else" << endl
+ << "args_.pop_front ();"
+ << "}"
+
+ << "void argv_file_scanner::" << endl
+ << "load (const char* file)"
+ << "{"
+ << "using namespace std;"
+ << endl
+ << "ifstream is (file);"
+ << endl
+ << "if (!is.is_open ())" << endl
+ << "throw file_io_failure (file);"
+ << endl
+ << "while (!is.eof ())"
+ << "{"
+ << "string line;"
+ << "getline (is, line);"
+ << endl
+ << "if (is.fail () && !is.eof ())" << endl
+ << "throw file_io_failure (file);"
+ << endl
+ << "string::size_type n (line.size ());"
+ << endl
+ << "// Trim the line from leading and trailing whitespaces." << endl
+ << "//" << endl
+ << "if (n != 0)"
+ << "{"
+ << "const char* f (line.c_str ());"
+ << "const char* l (f + n);"
+ << endl
+ << "const char* of (f);"
+ << "while (f < l && (*f == ' ' || *f == '\\t' || *f == '\\r'))" << endl
+ << "++f;"
+ << endl
+ << "--l;"
+ << endl
+ << "const char* ol (l);"
+ << "while (l > f && (*l == ' ' || *l == '\\t' || *l == '\\r'))" << endl
+ << "--l;"
+ << endl
+ << "if (f != of || l != ol)" << endl
+ << "line = f <= l ? string (f, l - f + 1) : string ();"
+ << "}"
+ << "// Ignore empty lines, those that start with #." << endl
+ << "//" << endl
+ << "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 s1 (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 (" << (sep ? "!skip_ && " : "") << "s1 == option_)" << endl
+ << "load (s2.c_str ());"
+ << "else"
+ << "{"
+ << "args_.push_back (s1);"
+ << "args_.push_back (s2);"
+ << "}"
+ << "}"
+ << "}" // while
+ << "}";
+ }
+
// parser class template & its specializations
//
os << "template <typename X>" << endl
diff --git a/cli/source.cxx b/cli/source.cxx
index 5f0b199..1c0b890 100644
--- a/cli/source.cxx
+++ b/cli/source.cxx
@@ -24,7 +24,11 @@ namespace
os << "," << endl
<< " ";
else
+ {
+ os << endl
+ << ": ";
comma_ = true;
+ }
os << emember (o);
@@ -356,75 +360,88 @@ namespace
string um ("::cli::unknown_mode");
os << name << "::" << endl
- << name << " (int argc," << endl
+ << name << " (int& argc," << endl
<< "char** argv," << endl
+ << "bool erase," << endl
<< um << " opt," << endl
- << um << " arg)" << endl
- << ": ";
+ << um << " arg)";
{
option_init init (*this);
traversal::names names_init (init);
names (c, names_init);
}
os << "{"
- << "::cli::argv_scanner s (argc, argv);"
+ << "::cli::argv_scanner s (argc, argv, erase);"
<< "_parse (s, opt, arg);"
<< "}";
os << name << "::" << endl
<< name << " (int start," << endl
- << "int argc," << endl
+ << "int& argc," << endl
<< "char** argv," << endl
+ << "bool erase," << endl
<< um << " opt," << endl
- << um << " arg)" << endl
- << ": ";
+ << um << " arg)";
{
option_init init (*this);
traversal::names names_init (init);
names (c, names_init);
}
os << "{"
- << "::cli::argv_scanner s (start, argc, argv);"
+ << "::cli::argv_scanner s (start, argc, argv, erase);"
<< "_parse (s, opt, arg);"
<< "}";
os << name << "::" << endl
- << name << " (int argc," << endl
+ << name << " (int& argc," << endl
<< "char** argv," << endl
<< "int& end," << endl
+ << "bool erase," << endl
<< um << " opt," << endl
- << um << " arg)" << endl
- << ": ";
+ << um << " arg)";
{
option_init init (*this);
traversal::names names_init (init);
names (c, names_init);
}
os << "{"
- << "::cli::argv_scanner s (argc, argv);"
+ << "::cli::argv_scanner s (argc, argv, erase);"
<< "_parse (s, opt, arg);"
<< "end = s.end ();"
<< "}";
os << name << "::" << endl
<< name << " (int start," << endl
- << "int argc," << endl
+ << "int& argc," << endl
<< "char** argv," << endl
<< "int& end," << endl
+ << "bool erase," << endl
<< um << " opt," << endl
- << um << " arg)" << endl
- << ": ";
+ << um << " arg)";
{
option_init init (*this);
traversal::names names_init (init);
names (c, names_init);
}
os << "{"
- << "::cli::argv_scanner s (start, argc, argv);"
+ << "::cli::argv_scanner s (start, argc, argv, erase);"
<< "_parse (s, opt, arg);"
<< "end = s.end ();"
<< "}";
+ os << name << "::" << endl
+ << name << " (::cli::scanner& s," << endl
+ << um << " opt," << endl
+ << um << " arg)";
+ {
+ option_init init (*this);
+ traversal::names names_init (init);
+ names (c, names_init);
+ }
+ os << "{"
+ << "_parse (s, opt, arg);"
+ << "}";
+
// usage
//
if (usage)
diff --git a/doc/cli.1 b/doc/cli.1
index 7c89ace..0a2021f 100644
--- a/doc/cli.1
+++ b/doc/cli.1
@@ -73,6 +73,11 @@ Write the generated files to \fIdir\fP instead of the current directory\.
.IP "\fB--generate-modifier\fP"
Generate option value modifiers in addition to accessors\.
+.IP "\fB--generate-file-scanner\fP"
+Generate the argv_file_scanner implementation\. This scanner is capable of
+reading command line arguments from the argv array as well as files
+specified with command line options\.
+
.IP "\fB--suppress-inline\fP"
Generate all functions non-inline\. By default simple functions are made
inline\. This option suppresses creation of the inline file\.
diff --git a/doc/cli.xhtml b/doc/cli.xhtml
index e431f97..3389c77 100644
--- a/doc/cli.xhtml
+++ b/doc/cli.xhtml
@@ -96,6 +96,11 @@
<dt><code><b>--generate-modifier</b></code></dt>
<dd>Generate option value modifiers in addition to accessors.</dd>
+ <dt><code><b>--generate-file-scanner</b></code></dt>
+ <dd>Generate the <code>argv_file_scanner</code> implementation. This scanner is
+ capable of reading command line arguments from the <code>argv</code> array
+ as well as files specified with command line options.</dd>
+
<dt><code><b>--suppress-inline</b></code></dt>
<dd>Generate all functions non-inline. By default simple functions are made
inline. This option suppresses creation of the inline file.</dd>
diff --git a/doc/guide/index.xhtml b/doc/guide/index.xhtml
index 89f56e7..0b43209 100644
--- a/doc/guide/index.xhtml
+++ b/doc/guide/index.xhtml
@@ -561,27 +561,35 @@ class options
class options
{
public:
- options (int argc,
+ options (int&amp; argc,
char** argv,
+ bool erase = false,
cli::unknown_mode opt_mode = cli::unknown_mode::fail,
cli::unknown_mode arg_mode = cli::unknown_mode::stop);
options (int start,
- int argc,
+ int&amp; argc,
char** argv,
+ bool erase = false,
cli::unknown_mode opt_mode = cli::unknown_mode::fail,
cli::unknown_mode arg_mode = cli::unknown_mode::stop);
- options (int argc,
+ options (int&amp; argc,
char** argv,
int&amp; end,
+ bool erase = false,
cli::unknown_mode opt_mode = cli::unknown_mode::fail,
cli::unknown_mode arg_mode = cli::unknown_mode::stop);
options (int start,
- int argc,
+ int&amp; argc,
char** argv,
int&amp; end,
+ bool erase = false,
+ cli::unknown_mode opt_mode = cli::unknown_mode::fail,
+ cli::unknown_mode arg_mode = cli::unknown_mode::stop);
+
+ options (cli::scanner&amp;,
cli::unknown_mode opt_mode = cli::unknown_mode::fail,
cli::unknown_mode arg_mode = cli::unknown_mode::stop);
@@ -621,7 +629,10 @@ public:
from position 1, skipping the executable name in <code>argv[0]</code>.
The <code>end</code> argument is used to return the position in
the arguments array where the parsing of options stopped. This is the
- position of the first program argument, if any.</p>
+ position of the first program argument, if any. If the <code>erase</code>
+ argument is <code>true</code>, then the recognized options and their
+ values are removed from the <code>argv</code> array and the
+ <code>argc</code> count is updated accordingly.</p>
<p>The <code>opt_mode</code> and <code>arg_mode</code> arguments
specify the parser behavior when it encounters an unknown option
@@ -657,8 +668,96 @@ namespace cli
exception (described blow) on encountering an unknown option or argument,
respectively.</p>
- <p>The parsing constructor (those with the <code>argc/argv</code> arguments)
- can throw the following exceptions: <code>cli::unknown_option</code>,
+ <p>Instead of the <code>argc/argv</code> arguments, the last overloaded
+ constructor accepts the <code>cli::scanner</code> object. It is part
+ of the generated CLI runtime support code and has the following
+ abstract interface:</p>
+
+ <pre>
+namespace cli
+{
+ class scanner
+ {
+ public:
+ virtual bool
+ more () = 0;
+
+ virtual const char*
+ peek () = 0;
+
+ virtual const char*
+ next () = 0;
+
+ virtual void
+ skip () = 0;
+ };
+}
+ </pre>
+
+ <p>The CLI runtime also provides two implementations of this interface:
+ <code>cli::argv_scanner</code> and <code>cli::argv_file_scanner</code>.
+ The first implementation is a simple scanner for the <code>argv</code>
+ array (it is used internally by all the other constructors) and has the
+ following interface:</p>
+
+ <pre>
+namespace cli
+{
+ class argv_scanner
+ {
+ public:
+ argv_scanner (int&amp; argc, char** argv, bool erase = false);
+ argv_scanner (int start, int&amp; argc, char** argv, bool erase = false);
+
+ int
+ end () const;
+
+ ...
+ };
+}
+ </pre>
+
+ <p>The <code>cli::argv_file_scanner</code> implementation provides
+ support for reading command line arguments from the <code>argv</code>
+ array as well as files specified with command line options. It is
+ generated only if explicitly requested with the
+ <code>--generate-file-scanner</code> CLI compiler option and has
+ the following interface:</p>
+
+ <pre>
+namespace cli
+{
+ class argv_file_scanner
+ {
+ public:
+ argv_file_scanner (int&amp; argc,
+ char** argv,
+ const std::string&amp; file_option,
+ bool erase = false);
+
+ argv_file_scanner (int start,
+ int&amp; argc,
+ char** argv,
+ const std::string&amp; file_option,
+ bool erase = false);
+ ...
+ };
+}
+ </pre>
+
+ <p>The <code>file_option</code> argument is used to pass the option name
+ that should be recognized as specifying the file containing additional
+ options. Such a file contains a set of options, each appearing on a
+ separate line optionally followed by space and an argument. Empty lines
+ and lines starting with <code>#</code> are ignored. 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 point where the
+ options file is specified, except that shell escaping and quoting is not
+ required. Multiple files can be specified by including several file
+ options on the command line or inside other files.</p>
+
+ <p>The parsing constructor (those with the <code>argc/argv</code> or
+ <code>cli::scanner</code> arguments) can throw the following exceptions: <code>cli::unknown_option</code>,
<code>cli::unknown_argument</code>, <code>cli::missing_value</code>, and
<code>cli::invalid_value</code>. The first two exceptions are thrown
on encountering unknown options and arguments, respectively, as
@@ -667,6 +766,16 @@ namespace cli
thrown when an option value is invalid, for example, a non-integer value
is specified for an option of type <code>int</code>.</p>
+ <p>Furthermore, all scanners (and thus the parsing constructors that
+ call them) can throw the <code>cli::eos_reached</code> exception
+ which indicates that one of the <code>peek()</code>, <code>next()</code>,
+ or <code>skip()</code> functions were called while <code>more()</code>
+ returns <code>false</code>. Catching this exception normally indicates an
+ error in an option parser implementation. The <code>argv_file_scanner</code>
+ class can also throw the <code>cli::file_io_failure</code> exception
+ which indicates that a file could not be opened or there was a reading
+ error.</p>
+
<p>All CLI exceptions are derived from the common <code>cli::exception</code>
class which implements the polymorphic <code>std::ostream</code> insertion.
For example, if you catch the <code>cli::unknown_option</code>
@@ -759,6 +868,31 @@ namespace cli
virtual const char*
what () const throw ();
};
+
+ class eos_reached: public exception
+ {
+ public:
+ virtual void
+ print (std::ostream&amp;) const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class file_io_failure: public exception
+ {
+ public:
+ file_io_failure (const std::string&amp; file);
+
+ const std::string&amp;
+ file () const;
+
+ virtual void
+ print (std::ostream&amp;) const;
+
+ virtual const char*
+ what () const throw ();
+ };
}
</pre>
diff --git a/examples/README b/examples/README
index d333f11..84ef156 100644
--- a/examples/README
+++ b/examples/README
@@ -9,3 +9,7 @@ hello
features
Shows how to use various features of the CLI language.
+
+file
+ Shows how to allow the users of your application to supply options in
+ files in addition to the command line.
diff --git a/examples/examples-8.0.sln b/examples/examples-8.0.sln
index 8b7e077..aded850 100644
--- a/examples/examples-8.0.sln
+++ b/examples/examples-8.0.sln
@@ -5,6 +5,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hello", "hello\hello-8.0.vc
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "features", "features\features-8.0.vcproj", "{FA7FD071-4FF6-4EB3-96E0-95366AB2CA6F}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "file", "file\file-8.0.vcproj", "{ECCD3577-1DB2-4F38-9ED7-757433B8D66F}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@@ -19,6 +21,10 @@ Global
{FA7FD071-4FF6-4EB3-96E0-95366AB2CA6F}.Debug|Win32.Build.0 = Debug|Win32
{FA7FD071-4FF6-4EB3-96E0-95366AB2CA6F}.Release|Win32.ActiveCfg = Release|Win32
{FA7FD071-4FF6-4EB3-96E0-95366AB2CA6F}.Release|Win32.Build.0 = Release|Win32
+ {ECCD3577-1DB2-4F38-9ED7-757433B8D66F}.Debug|Win32.ActiveCfg = Debug|Win32
+ {ECCD3577-1DB2-4F38-9ED7-757433B8D66F}.Debug|Win32.Build.0 = Debug|Win32
+ {ECCD3577-1DB2-4F38-9ED7-757433B8D66F}.Release|Win32.ActiveCfg = Release|Win32
+ {ECCD3577-1DB2-4F38-9ED7-757433B8D66F}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/examples/examples-9.0.sln b/examples/examples-9.0.sln
index 8a2d2b5..d0c1be5 100644
--- a/examples/examples-9.0.sln
+++ b/examples/examples-9.0.sln
@@ -5,6 +5,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hello", "hello\hello-9.0.vc
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "features", "features\features-9.0.vcproj", "{A2A8DB0D-FA16-4F85-9775-8ADBDD06AE90}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "file", "file\file-9.0.vcproj", "{ECCD3577-1DB2-4F38-9ED7-757433B8D66F}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@@ -19,6 +21,10 @@ Global
{A2A8DB0D-FA16-4F85-9775-8ADBDD06AE90}.Debug|Win32.Build.0 = Debug|Win32
{A2A8DB0D-FA16-4F85-9775-8ADBDD06AE90}.Release|Win32.ActiveCfg = Release|Win32
{A2A8DB0D-FA16-4F85-9775-8ADBDD06AE90}.Release|Win32.Build.0 = Release|Win32
+ {ECCD3577-1DB2-4F38-9ED7-757433B8D66F}.Debug|Win32.ActiveCfg = Debug|Win32
+ {ECCD3577-1DB2-4F38-9ED7-757433B8D66F}.Debug|Win32.Build.0 = Debug|Win32
+ {ECCD3577-1DB2-4F38-9ED7-757433B8D66F}.Release|Win32.ActiveCfg = Release|Win32
+ {ECCD3577-1DB2-4F38-9ED7-757433B8D66F}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/examples/file/README b/examples/file/README
new file mode 100644
index 0000000..289fc64
--- /dev/null
+++ b/examples/file/README
@@ -0,0 +1,38 @@
+This example shows how to allow the users of your application to supply
+options in files in addition to the command line.
+
+The example consists of the following files:
+
+options.cli
+ Command line interface description in the CLI language.
+
+options.hxx
+options.ixx
+options.cxx
+ Command line interface implementation in C++. These files are generated
+ by the CLI compiler from options.cli using the following command line:
+
+ cli --generate-file-scanner hello.cli
+
+ We use the --generate-file-scanner CLI compiler option to include the
+ argv_file_scanner scanner implementation which provides support for
+ reading options from files in addition to the command line.
+
+driver.cxx
+ Driver for the example. It first creates the argv_file_scanner object
+ and indicates that the values for the --options-file option should be
+ recognized as files containing additional options. It then passes this
+ scanner object to the option class which parses the command line. The
+ driver then prints the option values.
+
+test.ops
+ Sample options file.
+
+To run this example you can try the following command line:
+
+$ ./driver --verbose 2 --val 1 --options-file test.ops --val 4
+
+The output will be:
+
+verbosity: 5
+values: 1 2 3 4
diff --git a/examples/file/driver.cxx b/examples/file/driver.cxx
new file mode 100644
index 0000000..df7a67c
--- /dev/null
+++ b/examples/file/driver.cxx
@@ -0,0 +1,36 @@
+// file : examples/file/driver.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#include <iostream>
+#include <iterator>
+#include <algorithm>
+
+#include "options.hxx"
+
+using namespace std;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ cli::argv_file_scanner s (argc, argv, "--options-file");
+ options o (s);
+
+ cout << "verbosity: " << o.verbose () << endl
+ << "values: ";
+
+ copy (o.val ().begin (),
+ o.val ().end (),
+ ostream_iterator<int> (cout, " "));
+
+ cerr << endl;
+ }
+ catch (const cli::exception& e)
+ {
+ cerr << e << endl;
+ return 1;
+ }
+}
diff --git a/examples/file/file-8.0.vcproj b/examples/file/file-8.0.vcproj
new file mode 100644
index 0000000..0b1000b
--- /dev/null
+++ b/examples/file/file-8.0.vcproj
@@ -0,0 +1,237 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="file"
+ ProjectGUID="{ECCD3577-1DB2-4F38-9ED7-757433B8D66F}"
+ RootNamespace="file"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{53E343D0-A5BB-4C2A-A5B1-7C54774D5071}"
+ >
+ <File
+ RelativePath=".\driver.cxx"
+ >
+ </File>
+ <File
+ RelativePath=".\options.cxx"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{5409EE27-2176-4719-B660-DBD88D351153}"
+ >
+ <File
+ RelativePath=".\options.hxx"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="CLI Files"
+ Filter="*.cli"
+ >
+ <File
+ RelativePath=".\options.cli"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ Description="cli options.cli"
+ CommandLine="..\..\cli\cli.exe --generate-file-scanner options.cli"
+ Outputs="options.hxx;options.ixx;options.cxx"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ Description="cli options.cli"
+ CommandLine="..\..\cli\cli.exe --generate-file-scanner options.cli"
+ Outputs="options.hxx;options.ixx;options.cxx"
+ />
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Inline Files"
+ Filter="*.ixx"
+ >
+ <File
+ RelativePath=".\options.ixx"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/examples/file/file-9.0.vcproj b/examples/file/file-9.0.vcproj
new file mode 100644
index 0000000..c2a78e5
--- /dev/null
+++ b/examples/file/file-9.0.vcproj
@@ -0,0 +1,233 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="file"
+ ProjectGUID="{ECCD3577-1DB2-4F38-9ED7-757433B8D66F}"
+ RootNamespace="file"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{53E343D0-A5BB-4C2A-A5B1-7C54774D5071}"
+ >
+ <File
+ RelativePath=".\driver.cxx"
+ >
+ </File>
+ <File
+ RelativePath=".\options.cxx"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{5409EE27-2176-4719-B660-DBD88D351153}"
+ >
+ <File
+ RelativePath=".\options.hxx"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Inline Files"
+ Filter="*.ixx"
+ >
+ <File
+ RelativePath=".\options.ixx"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="CLI Files"
+ Filter="*.cli"
+ >
+ <File
+ RelativePath=".\options.cli"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ Description="cli options.cli"
+ CommandLine="..\..\cli\cli.exe --generate-file-scanner options.cli"
+ Outputs="options.hxx;options.ixx;options.cxx"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ Description="cli options.cli"
+ CommandLine="..\..\cli\cli.exe --generate-file-scanner options.cli"
+ Outputs="options.hxx;options.ixx;options.cxx"
+ />
+ </FileConfiguration>
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/examples/file/makefile b/examples/file/makefile
new file mode 100644
index 0000000..04c45a7
--- /dev/null
+++ b/examples/file/makefile
@@ -0,0 +1,74 @@
+# file : examples/file/makefile
+# author : Boris Kolpackov <boris@codesynthesis.com>
+# copyright : Copyright (c) 2009 Code Synthesis Tools CC
+# license : MIT; see accompanying LICENSE file
+
+include $(dir $(lastword $(MAKEFILE_LIST)))../../build/bootstrap.make
+
+cli := options.cli
+cxx := driver.cxx
+
+obj := $(addprefix $(out_base)/,$(cxx:.cxx=.o) $(cli:.cli=.o))
+dep := $(obj:.o=.o.d)
+
+driver := $(out_base)/driver
+install := $(out_base)/.install
+clean := $(out_base)/.clean
+
+# Build.
+#
+$(driver): $(obj)
+
+$(obj) $(dep): cpp_options := -I$(out_base)
+
+genf := $(cli:.cli=.hxx) $(cli:.cli=.ixx) $(cli:.cli=.cxx)
+gen := $(addprefix $(out_base)/,$(genf))
+
+$(gen): cli := $(out_root)/cli/cli
+$(gen): cli_options := --generate-file-scanner
+$(gen): $(out_root)/cli/cli
+
+$(call include-dep,$(dep),$(obj),$(gen))
+
+# Convenience alias for default target.
+#
+$(out_base)/: $(driver)
+
+# Install
+#
+$(install): path := $(subst $(src_root)/,,$(src_base))
+$(install):
+ $(call install-data,$(src_base)/driver.cxx,$(install_doc_dir)/cli/$(path)/driver.cxx)
+ $(call install-data,$(src_base)/options.cli,$(install_doc_dir)/cli/$(path)/options.cli)
+ $(call install-data,$(src_base)/README,$(install_doc_dir)/cli/$(path)/README)
+ $(call install-data,$(src_base)/test.ops,$(install_doc_dir)/cli/$(path)/test.ops)
+
+# Clean.
+#
+$(clean): $(driver).o.clean \
+ $(addsuffix .cxx.clean,$(obj)) \
+ $(addsuffix .cxx.clean,$(dep)) \
+ $(addprefix $(out_base)/,$(cli:.cli=.cxx.cli.clean))
+
+# Generated .gitignore.
+#
+ifeq ($(out_base),$(src_base))
+$(gen): | $(out_base)/.gitignore
+$(driver): | $(out_base)/.gitignore
+
+$(out_base)/.gitignore: files := driver $(genf)
+$(clean): $(out_base)/.gitignore.clean
+
+$(call include,$(bld_root)/git/gitignore.make)
+endif
+
+# How to.
+#
+$(call include,$(bld_root)/cxx/o-e.make)
+$(call include,$(bld_root)/cxx/cxx-o.make)
+$(call include,$(bld_root)/cxx/cxx-d.make)
+$(call include,$(scf_root)/cli/cli-cxx.make)
+
+# Dependencies.
+#
+$(call import,$(src_root)/cli/makefile)
diff --git a/examples/file/options.cli b/examples/file/options.cli
new file mode 100644
index 0000000..3e6db5a
--- /dev/null
+++ b/examples/file/options.cli
@@ -0,0 +1,7 @@
+include <vector>;
+
+class options
+{
+ int --verbose;
+ std::vector<int> --val;
+};
diff --git a/examples/file/test.ops b/examples/file/test.ops
new file mode 100644
index 0000000..47097fb
--- /dev/null
+++ b/examples/file/test.ops
@@ -0,0 +1,7 @@
+# Sample options file. Empty lines and lines starting with '#' are
+# ignored.
+#
+--verbose 5
+
+--val 2
+--val 3
diff --git a/examples/makefile b/examples/makefile
index 135b9bb..9b1de12 100644
--- a/examples/makefile
+++ b/examples/makefile
@@ -5,7 +5,7 @@
include $(dir $(lastword $(MAKEFILE_LIST)))../build/bootstrap.make
-examples := hello features
+examples := hello features file
default := $(out_base)/
install := $(out_base)/.install
diff --git a/tests/ctor/driver.cxx b/tests/ctor/driver.cxx
new file mode 100644
index 0000000..c923ec1
--- /dev/null
+++ b/tests/ctor/driver.cxx
@@ -0,0 +1,69 @@
+// file : tests/ctor/driver.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#include "test.hxx"
+
+int
+main (int argc, char* argv[])
+{
+ // Test that we can call all the c-tors unambiguously.
+ //
+ {
+ options o1 (argc, argv);
+ options o2 (argc, argv,
+ cli::unknown_mode::fail);
+ options o3 (argc, argv,
+ cli::unknown_mode::fail,
+ cli::unknown_mode::stop);
+ options o4 (argc, argv, true,
+ cli::unknown_mode::fail,
+ cli::unknown_mode::stop);
+ }
+
+ {
+ options o1 (1, argc, argv);
+ options o2 (1, argc, argv,
+ cli::unknown_mode::fail);
+ options o3 (1, argc, argv,
+ cli::unknown_mode::fail,
+ cli::unknown_mode::stop);
+ options o4 (1, argc, argv, true,
+ cli::unknown_mode::fail,
+ cli::unknown_mode::stop);
+ }
+
+ {
+ int end;
+ options o1 (argc, argv, end);
+ options o2 (argc, argv, end,
+ cli::unknown_mode::fail);
+ options o3 (argc, argv, end,
+ cli::unknown_mode::fail,
+ cli::unknown_mode::stop);
+ options o4 (argc, argv, end, true,
+ cli::unknown_mode::fail,
+ cli::unknown_mode::stop);
+ }
+
+ {
+ int end;
+ options o1 (1, argc, argv, end);
+ options o2 (1, argc, argv, end,
+ cli::unknown_mode::fail);
+ options o3 (1, argc, argv, end,
+ cli::unknown_mode::fail,
+ cli::unknown_mode::stop);
+ options o4 (1, argc, argv, end, true,
+ cli::unknown_mode::fail,
+ cli::unknown_mode::stop);
+ }
+
+ {
+ cli::argv_scanner s (argc, argv);
+ options o1 (s);
+ options o2 (s, cli::unknown_mode::fail);
+ options o3 (s, cli::unknown_mode::fail, cli::unknown_mode::stop);
+ }
+}
diff --git a/tests/ctor/makefile b/tests/ctor/makefile
new file mode 100644
index 0000000..93d832a
--- /dev/null
+++ b/tests/ctor/makefile
@@ -0,0 +1,73 @@
+# file : tests/ctor/makefile
+# author : Boris Kolpackov <boris@codesynthesis.com>
+# copyright : Copyright (c) 2009 Code Synthesis Tools CC
+# license : MIT; see accompanying LICENSE file
+
+include $(dir $(lastword $(MAKEFILE_LIST)))../../build/bootstrap.make
+
+cxx_tun := driver.cxx
+cli_tun := test.cli
+
+#
+#
+cxx_obj := $(addprefix $(out_base)/,$(cxx_tun:.cxx=.o) $(cli_tun:.cli=.o))
+cxx_od := $(cxx_obj:.o=.o.d)
+
+driver := $(out_base)/driver
+test := $(out_base)/.test
+clean := $(out_base)/.clean
+
+# Build.
+#
+$(driver): $(cxx_obj)
+$(cxx_obj) $(cxx_od): cpp_options := -I$(out_base)
+
+genf := $(cli_tun:.cli=.hxx) $(cli_tun:.cli=.ixx) $(cli_tun:.cli=.cxx)
+gen := $(addprefix $(out_base)/,$(genf))
+
+$(gen): $(out_root)/cli/cli
+$(gen): cli := $(out_root)/cli/cli
+$(gen): cli_options :=
+
+$(call include-dep,$(cxx_od),$(cxx_obj),$(gen))
+
+# Alias for default target.
+#
+$(out_base)/: $(driver)
+
+# Test.
+#
+$(test): driver := $(driver)
+$(test): $(driver)
+ $(call message,test $$1,$$1,$(driver))
+
+# Clean.
+#
+$(clean): \
+ $(driver).o.clean \
+ $(addsuffix .cxx.clean,$(cxx_obj)) \
+ $(addsuffix .cxx.clean,$(cxx_od)) \
+ $(addprefix $(out_base)/,$(cli_tun:.cli=.cxx.cli.clean))
+
+# Generated .gitignore.
+#
+ifeq ($(out_base),$(src_base))
+$(driver): | $(out_base)/.gitignore
+
+$(out_base)/.gitignore: files := driver $(genf)
+$(clean): $(out_base)/.gitignore.clean
+
+$(call include,$(bld_root)/git/gitignore.make)
+endif
+
+# How to.
+#
+$(call include,$(bld_root)/cxx/o-e.make)
+$(call include,$(bld_root)/cxx/cxx-o.make)
+$(call include,$(bld_root)/cxx/cxx-d.make)
+$(call include,$(scf_root)/cli/cli-cxx.make)
+
+# Dependencies.
+#
+$(call import,$(src_root)/cli/makefile)
+
diff --git a/tests/ctor/test.cli b/tests/ctor/test.cli
new file mode 100644
index 0000000..f85ad98
--- /dev/null
+++ b/tests/ctor/test.cli
@@ -0,0 +1,9 @@
+// file : tests/ctor/test.cli
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+class options
+{
+ bool --help;
+};
diff --git a/tests/erase/driver.cxx b/tests/erase/driver.cxx
new file mode 100644
index 0000000..ddb6b8d
--- /dev/null
+++ b/tests/erase/driver.cxx
@@ -0,0 +1,35 @@
+// file : tests/erase/driver.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+// Test argument erasing.
+//
+
+#include <string>
+#include <cassert>
+
+#include "test.hxx"
+
+using namespace std;
+
+int
+main (int argc, char* argv[])
+{
+ options o (argc, argv, true,
+ cli::unknown_mode::skip,
+ cli::unknown_mode::skip);
+
+ assert (o.a ());
+ assert (o.b () == 123);
+
+ // We should have 'foo bar --arg -- -b 234'.
+ //
+ assert (argc == 7);
+ assert (argv[1] == string ("foo"));
+ assert (argv[2] == string ("bar"));
+ assert (argv[3] == string ("--arg"));
+ assert (argv[4] == string ("--"));
+ assert (argv[5] == string ("-b"));
+ assert (argv[6] == string ("234"));
+}
diff --git a/tests/erase/makefile b/tests/erase/makefile
new file mode 100644
index 0000000..5a8f49a
--- /dev/null
+++ b/tests/erase/makefile
@@ -0,0 +1,73 @@
+# file : tests/erase/makefile
+# author : Boris Kolpackov <boris@codesynthesis.com>
+# copyright : Copyright (c) 2009 Code Synthesis Tools CC
+# license : MIT; see accompanying LICENSE file
+
+include $(dir $(lastword $(MAKEFILE_LIST)))../../build/bootstrap.make
+
+cxx_tun := driver.cxx
+cli_tun := test.cli
+
+#
+#
+cxx_obj := $(addprefix $(out_base)/,$(cxx_tun:.cxx=.o) $(cli_tun:.cli=.o))
+cxx_od := $(cxx_obj:.o=.o.d)
+
+driver := $(out_base)/driver
+test := $(out_base)/.test
+clean := $(out_base)/.clean
+
+# Build.
+#
+$(driver): $(cxx_obj)
+$(cxx_obj) $(cxx_od): cpp_options := -I$(out_base)
+
+genf := $(cli_tun:.cli=.hxx) $(cli_tun:.cli=.ixx) $(cli_tun:.cli=.cxx)
+gen := $(addprefix $(out_base)/,$(genf))
+
+$(gen): $(out_root)/cli/cli
+$(gen): cli := $(out_root)/cli/cli
+$(gen): cli_options :=
+
+$(call include-dep,$(cxx_od),$(cxx_obj),$(gen))
+
+# Alias for default target.
+#
+$(out_base)/: $(driver)
+
+# Test.
+#
+$(test): driver := $(driver)
+$(test): $(driver)
+ $(call message,test $$1,$$1 foo -a bar -b 123 --arg -- -b 234,$(driver))
+
+# Clean.
+#
+$(clean): \
+ $(driver).o.clean \
+ $(addsuffix .cxx.clean,$(cxx_obj)) \
+ $(addsuffix .cxx.clean,$(cxx_od)) \
+ $(addprefix $(out_base)/,$(cli_tun:.cli=.cxx.cli.clean))
+
+# Generated .gitignore.
+#
+ifeq ($(out_base),$(src_base))
+$(driver): | $(out_base)/.gitignore
+
+$(out_base)/.gitignore: files := driver $(genf)
+$(clean): $(out_base)/.gitignore.clean
+
+$(call include,$(bld_root)/git/gitignore.make)
+endif
+
+# How to.
+#
+$(call include,$(bld_root)/cxx/o-e.make)
+$(call include,$(bld_root)/cxx/cxx-o.make)
+$(call include,$(bld_root)/cxx/cxx-d.make)
+$(call include,$(scf_root)/cli/cli-cxx.make)
+
+# Dependencies.
+#
+$(call import,$(src_root)/cli/makefile)
+
diff --git a/tests/erase/test.cli b/tests/erase/test.cli
new file mode 100644
index 0000000..57820c0
--- /dev/null
+++ b/tests/erase/test.cli
@@ -0,0 +1,10 @@
+// file : tests/erase/test.cli
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+class options
+{
+ bool -a;
+ int -b;
+};
diff --git a/tests/file/base.ops b/tests/file/base.ops
new file mode 100644
index 0000000..45d7696
--- /dev/null
+++ b/tests/file/base.ops
@@ -0,0 +1,2 @@
+-a 21
+-b 21
diff --git a/tests/file/driver.cxx b/tests/file/driver.cxx
new file mode 100644
index 0000000..e0aec6e
--- /dev/null
+++ b/tests/file/driver.cxx
@@ -0,0 +1,29 @@
+// file : tests/file/driver.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+// Test argv_file_scanner.
+//
+
+#include <iostream>
+
+#include "test.hxx"
+
+using namespace std;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ cli::argv_file_scanner s (argc, argv, "--file");
+
+ while (s.more ())
+ cout << s.next () << endl;
+ }
+ catch (const cli::exception& e)
+ {
+ cout << e << endl;
+ }
+}
diff --git a/tests/file/empty.ops b/tests/file/empty.ops
new file mode 100644
index 0000000..ed080fb
--- /dev/null
+++ b/tests/file/empty.ops
@@ -0,0 +1,3 @@
+# Empty options file.
+#
+
diff --git a/tests/file/makefile b/tests/file/makefile
new file mode 100644
index 0000000..56e95c6
--- /dev/null
+++ b/tests/file/makefile
@@ -0,0 +1,96 @@
+# file : tests/file/makefile
+# author : Boris Kolpackov <boris@codesynthesis.com>
+# copyright : Copyright (c) 2009 Code Synthesis Tools CC
+# license : MIT; see accompanying LICENSE file
+
+include $(dir $(lastword $(MAKEFILE_LIST)))../../build/bootstrap.make
+
+cxx_tun := driver.cxx
+cli_tun := test.cli
+
+tests := 000 001 002 003
+
+#
+#
+cxx_obj := $(addprefix $(out_base)/,$(cxx_tun:.cxx=.o) $(cli_tun:.cli=.o))
+cxx_od := $(cxx_obj:.o=.o.d)
+
+driver := $(out_base)/driver
+test := $(out_base)/.test
+clean := $(out_base)/.clean
+
+# Build.
+#
+$(driver): $(cxx_obj)
+$(cxx_obj) $(cxx_od): cpp_options := -I$(out_base)
+
+genf := $(cli_tun:.cli=.hxx) $(cli_tun:.cli=.ixx) $(cli_tun:.cli=.cxx)
+gen := $(addprefix $(out_base)/,$(genf))
+
+$(gen): $(out_root)/cli/cli
+$(gen): cli := $(out_root)/cli/cli
+$(gen): cli_options := --generate-file-scanner
+
+$(call include-dep,$(cxx_od),$(cxx_obj),$(gen))
+
+# Alias for default target.
+#
+$(out_base)/: $(driver)
+
+# Test.
+#
+test_targets := $(addprefix $(out_base)/.test-,$(tests))
+
+$(test): $(test_targets)
+$(test_targets): driver := $(driver)
+
+.PHONY: $(out_base)/.test-%
+
+$(out_base)/.test-000: $(driver) $(src_base)/test-000.ops
+ $(call message,test $(out_base)/000,$(driver) -a 1 \
+--file $(src_base)/empty.ops -b 1 --file $(src_base)/base.ops \
+--file $(src_base)/test-000.ops b | diff -u $(src_base)/test-000.std -)
+
+$(out_base)/.test-001: $(driver) $(src_base)/test-001.ops
+ $(call message,test $(out_base)/001,$(driver) -a 1 -- --file \
+$(src_base)/test-001.ops b | diff -u $(src_base)/test-001.std -)
+
+$(out_base)/.test-002: $(driver) $(src_base)/test-002.ops
+ $(call message,test $(out_base)/002,$(driver) -a 1 --file \
+$(src_base)/test-002.ops --file $(src_base)/empty.ops b | \
+diff -u $(src_base)/test-002.std -)
+
+$(out_base)/.test-003: $(driver)
+ $(call message,test $(out_base)/003,$(driver) -a 1 --file \
+$(src_base)/base.ops --file test-003.ops b | diff -u $(src_base)/test-003.std -)
+
+# Clean.
+#
+$(clean): \
+ $(driver).o.clean \
+ $(addsuffix .cxx.clean,$(cxx_obj)) \
+ $(addsuffix .cxx.clean,$(cxx_od)) \
+ $(addprefix $(out_base)/,$(cli_tun:.cli=.cxx.cli.clean))
+
+# Generated .gitignore.
+#
+ifeq ($(out_base),$(src_base))
+$(driver): | $(out_base)/.gitignore
+
+$(out_base)/.gitignore: files := driver $(genf)
+$(clean): $(out_base)/.gitignore.clean
+
+$(call include,$(bld_root)/git/gitignore.make)
+endif
+
+# How to.
+#
+$(call include,$(bld_root)/cxx/o-e.make)
+$(call include,$(bld_root)/cxx/cxx-o.make)
+$(call include,$(bld_root)/cxx/cxx-d.make)
+$(call include,$(scf_root)/cli/cli-cxx.make)
+
+# Dependencies.
+#
+$(call import,$(src_root)/cli/makefile)
+
diff --git a/tests/file/test-000.ops b/tests/file/test-000.ops
new file mode 100644
index 0000000..c0462e2
--- /dev/null
+++ b/tests/file/test-000.ops
@@ -0,0 +1,6 @@
+-a 11
+-b 11
+ -a 12
+
+ -b 12
+a
diff --git a/tests/file/test-000.std b/tests/file/test-000.std
new file mode 100644
index 0000000..392b83d
--- /dev/null
+++ b/tests/file/test-000.std
@@ -0,0 +1,18 @@
+-a
+1
+-b
+1
+-a
+21
+-b
+21
+-a
+11
+-b
+11
+-a
+12
+-b
+12
+a
+b
diff --git a/tests/file/test-001.ops b/tests/file/test-001.ops
new file mode 100644
index 0000000..ed080fb
--- /dev/null
+++ b/tests/file/test-001.ops
@@ -0,0 +1,3 @@
+# Empty options file.
+#
+
diff --git a/tests/file/test-001.std b/tests/file/test-001.std
new file mode 100644
index 0000000..36fabf3
--- /dev/null
+++ b/tests/file/test-001.std
@@ -0,0 +1,6 @@
+-a
+1
+--
+--file
+/home/boris/work/cli/cli/tests/file/test-001.ops
+b
diff --git a/tests/file/test-002.ops b/tests/file/test-002.ops
new file mode 100644
index 0000000..5c728a0
--- /dev/null
+++ b/tests/file/test-002.ops
@@ -0,0 +1,7 @@
+-a 11
+-b 11
+--
+--file base.ops
+-a 12
+-b 12
+a
diff --git a/tests/file/test-002.std b/tests/file/test-002.std
new file mode 100644
index 0000000..0336afe
--- /dev/null
+++ b/tests/file/test-002.std
@@ -0,0 +1,17 @@
+-a
+1
+-a
+11
+-b
+11
+--
+--file
+base.ops
+-a
+12
+-b
+12
+a
+--file
+/home/boris/work/cli/cli/tests/file/empty.ops
+b
diff --git a/tests/file/test-003.std b/tests/file/test-003.std
new file mode 100644
index 0000000..6e9a6ae
--- /dev/null
+++ b/tests/file/test-003.std
@@ -0,0 +1,7 @@
+-a
+1
+-a
+21
+-b
+21
+unable to open file 'test-003.ops' or read failure
diff --git a/tests/file/test.cli b/tests/file/test.cli
new file mode 100644
index 0000000..4bd40d0
--- /dev/null
+++ b/tests/file/test.cli
@@ -0,0 +1,8 @@
+// file : tests/file/test.cli
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+class options
+{
+};
diff --git a/tests/makefile b/tests/makefile
index 422b93b..5eef36e 100644
--- a/tests/makefile
+++ b/tests/makefile
@@ -5,7 +5,7 @@
include $(dir $(lastword $(MAKEFILE_LIST)))../build/bootstrap.make
-tests := lexer parser
+tests := ctor erase file lexer parser
default := $(out_base)/
test := $(out_base)/.test