From f8edfd22cb45b554a573d2722900196758e9e958 Mon Sep 17 00:00:00 2001
From: Boris Kolpackov
Date: Thu, 10 Dec 2009 09:47:29 +0200
Subject: Scanner-based parsing with support for element erasing
Also implement argv_file_scanner which provides support for reading command
line arguments from the argv array as well as files specified with command
line options. New examples: file. New tests: ctor, erase, file.
---
NEWS | 25 ++++-
cli/header.cxx | 17 ++-
cli/options.cli | 11 +-
cli/options.cxx | 84 +++++++++++++--
cli/options.hxx | 30 ++++--
cli/options.ixx | 14 ++-
cli/runtime-header.cxx | 82 ++++++++++++++-
cli/runtime-inline.cxx | 63 ++++++++++-
cli/runtime-source.cxx | 228 +++++++++++++++++++++++++++++++++++++++-
cli/source.cxx | 49 ++++++---
doc/cli.1 | 5 +
doc/cli.xhtml | 5 +
doc/guide/index.xhtml | 148 ++++++++++++++++++++++++--
examples/README | 4 +
examples/examples-8.0.sln | 6 ++
examples/examples-9.0.sln | 6 ++
examples/file/README | 38 +++++++
examples/file/driver.cxx | 36 +++++++
examples/file/file-8.0.vcproj | 237 ++++++++++++++++++++++++++++++++++++++++++
examples/file/file-9.0.vcproj | 233 +++++++++++++++++++++++++++++++++++++++++
examples/file/makefile | 74 +++++++++++++
examples/file/options.cli | 7 ++
examples/file/test.ops | 7 ++
examples/makefile | 2 +-
tests/ctor/driver.cxx | 69 ++++++++++++
tests/ctor/makefile | 73 +++++++++++++
tests/ctor/test.cli | 9 ++
tests/erase/driver.cxx | 35 +++++++
tests/erase/makefile | 73 +++++++++++++
tests/erase/test.cli | 10 ++
tests/file/base.ops | 2 +
tests/file/driver.cxx | 29 ++++++
tests/file/empty.ops | 3 +
tests/file/makefile | 96 +++++++++++++++++
tests/file/test-000.ops | 6 ++
tests/file/test-000.std | 18 ++++
tests/file/test-001.ops | 3 +
tests/file/test-001.std | 6 ++
tests/file/test-002.ops | 7 ++
tests/file/test-002.std | 17 +++
tests/file/test-003.std | 7 ++
tests/file/test.cli | 8 ++
tests/makefile | 2 +-
43 files changed, 1813 insertions(+), 71 deletions(-)
create mode 100644 examples/file/README
create mode 100644 examples/file/driver.cxx
create mode 100644 examples/file/file-8.0.vcproj
create mode 100644 examples/file/file-9.0.vcproj
create mode 100644 examples/file/makefile
create mode 100644 examples/file/options.cli
create mode 100644 examples/file/test.ops
create mode 100644 tests/ctor/driver.cxx
create mode 100644 tests/ctor/makefile
create mode 100644 tests/ctor/test.cli
create mode 100644 tests/erase/driver.cxx
create mode 100644 tests/erase/makefile
create mode 100644 tests/erase/test.cli
create mode 100644 tests/file/base.ops
create mode 100644 tests/file/driver.cxx
create mode 100644 tests/file/empty.ops
create mode 100644 tests/file/makefile
create mode 100644 tests/file/test-000.ops
create mode 100644 tests/file/test-000.std
create mode 100644 tests/file/test-001.ops
create mode 100644 tests/file/test-001.std
create mode 100644 tests/file/test-002.ops
create mode 100644 tests/file/test-002.std
create mode 100644 tests/file/test-003.std
create mode 100644 tests/file/test.cli
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
+ position of the first program argument, if any. If the erase
+ argument is true
, then the recognized options and their
+ values are removed from the argv
array and the
+ argc
count is updated accordingly.
The opt_mode
and arg_mode
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.
- The parsing constructor (those with the argc/argv
arguments)
- can throw the following exceptions: cli::unknown_option
,
+
Instead of the argc/argv
arguments, the last overloaded
+ constructor accepts the cli::scanner
object. It is part
+ of the generated CLI runtime support code and has the following
+ abstract interface:
+
+
+namespace cli
+{
+ class scanner
+ {
+ public:
+ virtual bool
+ more () = 0;
+
+ virtual const char*
+ peek () = 0;
+
+ virtual const char*
+ next () = 0;
+
+ virtual void
+ skip () = 0;
+ };
+}
+
+
+ The CLI runtime also provides two implementations of this interface:
+ cli::argv_scanner
and cli::argv_file_scanner
.
+ The first implementation is a simple scanner for the argv
+ array (it is used internally by all the other constructors) and has the
+ following interface:
+
+
+namespace cli
+{
+ class argv_scanner
+ {
+ public:
+ argv_scanner (int& argc, char** argv, bool erase = false);
+ argv_scanner (int start, int& argc, char** argv, bool erase = false);
+
+ int
+ end () const;
+
+ ...
+ };
+}
+
+
+ The cli::argv_file_scanner
implementation provides
+ support for reading command line arguments from the argv
+ array as well as files specified with command line options. It is
+ generated only if explicitly requested with the
+ --generate-file-scanner
CLI compiler option and has
+ the following interface:
+
+
+namespace cli
+{
+ class argv_file_scanner
+ {
+ public:
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& file_option,
+ bool erase = false);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& file_option,
+ bool erase = false);
+ ...
+ };
+}
+
+
+ The file_option
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 #
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.
+
+ The parsing constructor (those with the argc/argv
or
+ cli::scanner
arguments) can throw the following exceptions: cli::unknown_option
,
cli::unknown_argument
, cli::missing_value
, and
cli::invalid_value
. 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 int
.
+ Furthermore, all scanners (and thus the parsing constructors that
+ call them) can throw the cli::eos_reached
exception
+ which indicates that one of the peek()
, next()
,
+ or skip()
functions were called while more()
+ returns false
. Catching this exception normally indicates an
+ error in an option parser implementation. The argv_file_scanner
+ class can also throw the cli::file_io_failure
exception
+ which indicates that a file could not be opened or there was a reading
+ error.
+
All CLI exceptions are derived from the common cli::exception
class which implements the polymorphic std::ostream
insertion.
For example, if you catch the cli::unknown_option
@@ -759,6 +868,31 @@ namespace cli
virtual const char*
what () const throw ();
};
+
+ class eos_reached: public exception
+ {
+ public:
+ virtual void
+ print (std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class file_io_failure: public exception
+ {
+ public:
+ file_io_failure (const std::string& file);
+
+ const std::string&
+ file () const;
+
+ virtual void
+ print (std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+ };
}
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
+// copyright : Copyright (c) 2009 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#include
+#include
+#include
+
+#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 (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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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
+# 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 ;
+
+class options
+{
+ int --verbose;
+ std::vector --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
+// 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
+# 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
+// 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
+// copyright : Copyright (c) 2009 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+// Test argument erasing.
+//
+
+#include
+#include
+
+#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
+# 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
+// 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
+// copyright : Copyright (c) 2009 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+// Test argv_file_scanner.
+//
+
+#include
+
+#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
+# 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
+// 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
--
cgit v1.1