From 51dc9396d8bd8f0971a59f4e3aa11e8315c394cd Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 2 Jun 2010 18:48:40 +0200 Subject: Add support for quoting in option file scanner --- cli/runtime-header.cxx | 21 +++++++++++++++++++++ cli/runtime-inline.cxx | 21 +++++++++++++++++++-- cli/runtime-source.cxx | 36 ++++++++++++++++++++++++++++++++++-- doc/guide/index.xhtml | 38 +++++++++++++++++++++++++++++++------- tests/file/makefile | 18 +++++++++++++++++- tests/file/test-004.ops | 6 ++++++ tests/file/test-004.std | 12 ++++++++++++ tests/file/test-005.ops | 1 + tests/file/test-005.std | 1 + tests/file/test-006.ops | 1 + tests/file/test-006.std | 1 + tests/file/test-007.ops | 1 + tests/file/test-007.std | 1 + 13 files changed, 146 insertions(+), 12 deletions(-) create mode 100644 tests/file/test-004.ops create mode 100644 tests/file/test-004.std create mode 100644 tests/file/test-005.ops create mode 100644 tests/file/test-005.std create mode 100644 tests/file/test-006.ops create mode 100644 tests/file/test-006.std create mode 100644 tests/file/test-007.ops create mode 100644 tests/file/test-007.std diff --git a/cli/runtime-header.cxx b/cli/runtime-header.cxx index f25d64c..5a2905c 100644 --- a/cli/runtime-header.cxx +++ b/cli/runtime-header.cxx @@ -180,6 +180,27 @@ generate_runtime_header (context& ctx) << "private:" << endl << "std::string file_;" << "};"; + + os << "class unmatched_quote: public exception" + << "{" + << "public:" << endl + << "virtual" << endl + << "~unmatched_quote () throw ();" + << endl + << "unmatched_quote (const std::string& argument);" + << endl + << "const std::string&" << endl + << "argument () const;" + << endl + << "virtual void" << endl + << "print (std::ostream&) const;" + << endl + << "virtual const char*" << endl + << "what () const throw ();" + << endl + << "private:" << endl + << "std::string argument_;" + << "};"; } // scanner diff --git a/cli/runtime-inline.cxx b/cli/runtime-inline.cxx index cc54b5e..da2decf 100644 --- a/cli/runtime-inline.cxx +++ b/cli/runtime-inline.cxx @@ -115,10 +115,10 @@ generate_runtime_inline (context& ctx) << "return value_;" << "}"; - // file_io_failure - // if (ctx.options.generate_file_scanner ()) { + // file_io_failure + // os << "// file_io_failure" << endl << "//" << endl @@ -133,6 +133,23 @@ generate_runtime_inline (context& ctx) << "{" << "return file_;" << "}"; + + // unmatched_option + // + os << "// unmatched_quote" << endl + << "//" << endl + + << inl << "unmatched_quote::" << endl + << "unmatched_quote (const std::string& argument)" << endl + << ": argument_ (argument)" + << "{" + << "}" + + << inl << "const std::string& unmatched_quote::" << endl + << "argument () const" + << "{" + << "return argument_;" + << "}"; } // argv_scanner diff --git a/cli/runtime-source.cxx b/cli/runtime-source.cxx index fea0347..348d7b7 100644 --- a/cli/runtime-source.cxx +++ b/cli/runtime-source.cxx @@ -129,10 +129,10 @@ generate_runtime_source (context& ctx) << "return \"end of argument stream reached\";" << "}"; - // file_io_failure - // if (ctx.options.generate_file_scanner ()) { + // file_io_failure + // os << "// file_io_failure" << endl << "//" << endl << "file_io_failure::" << endl @@ -151,6 +151,27 @@ generate_runtime_source (context& ctx) << "{" << "return \"unable to open file or read failure\";" << "}"; + + // unmatched_argument + // + os << "// unmatched_quote" << endl + << "//" << endl + << "unmatched_quote::" << endl + << "~unmatched_quote () throw ()" + << "{" + << "}" + + << "void unmatched_quote::" << endl + << "print (std::ostream& os) const" + << "{" + << "os << \"unmatched quote in argument '\" << argument () << \"'\";" + << "}" + + << "const char* unmatched_quote::" << endl + << "what () const throw ()" + << "{" + << "return \"unmatched quote\";" + << "}"; } // scanner @@ -368,6 +389,17 @@ generate_runtime_source (context& ctx) << "}" << "string s2 (line, p);" << endl + << "// If the string is wrapped in quotes, remove them." << endl + << "//" << endl + << "n = s2.size ();" + << endl + << "if (s2[0] == '\"' || s2[n - 1] == '\"')" + << "{" + << "if (n == 1 || s2[0] != s2[n - 1])" << endl + << "throw unmatched_quote (s2);" + << endl + << "s2 = string (s2, 1, n - 2);" + << "}" << "if (" << (sep ? "!skip_ && " : "") << "s1 == option_)" << endl << "load (s2.c_str ());" << "else" diff --git a/doc/guide/index.xhtml b/doc/guide/index.xhtml index 2ae7f1e..098fcd7 100644 --- a/doc/guide/index.xhtml +++ b/doc/guide/index.xhtml @@ -749,12 +749,19 @@ namespace cli 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 option value. 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.

+ and lines starting with # are ignored. Option values can + be enclosed in double quotes (" ") to preserve leading + and trailing whitespaces as well as to specify empty values. If the + value itself contains trailing or leading double quote, enclose + it into an extra pair of double quotes, for example ""x"". + Non-leading and non-trailing quotes are interpreted as being part of + the option value.

+ +

The semantics of providing options in a file is equivalent to providing + the same set of options in the same order on the command line at the + 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, @@ -774,7 +781,9 @@ namespace cli 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.

+ error as well as the cli::unmatched_quote exception + which indicates that an unmatched leading or trailing quote was + found in an option value.

All CLI exceptions are derived from the common cli::exception class which implements the polymorphic std::ostream insertion. @@ -893,6 +902,21 @@ namespace cli virtual const char* what () const throw (); }; + + class unmatched_quote: public exception + { + public: + unmatched_quote (const std::string& argument); + + const std::string& + argument () const; + + virtual void + print (std::ostream&) const; + + virtual const char* + what () const throw (); + }; } diff --git a/tests/file/makefile b/tests/file/makefile index 011582a..2029d83 100644 --- a/tests/file/makefile +++ b/tests/file/makefile @@ -8,7 +8,7 @@ include $(dir $(lastword $(MAKEFILE_LIST)))../../build/bootstrap.make cxx_tun := driver.cxx cli_tun := test.cli -tests := 000 001 002 003 +tests := 000 001 002 003 004 005 006 007 # # @@ -63,6 +63,22 @@ $(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 -) +$(out_base)/.test-004: $(driver) $(src_base)/test-004.ops + $(call message,test $(out_base)/004,$(driver) --file \ +$(src_base)/test-004.ops | diff -u $(src_base)/test-004.std -) + +$(out_base)/.test-005: $(driver) $(src_base)/test-005.ops + $(call message,test $(out_base)/005,$(driver) --file \ +$(src_base)/test-005.ops | diff -u $(src_base)/test-005.std -) + +$(out_base)/.test-006: $(driver) $(src_base)/test-006.ops + $(call message,test $(out_base)/006,$(driver) --file \ +$(src_base)/test-006.ops | diff -u $(src_base)/test-006.std -) + +$(out_base)/.test-007: $(driver) $(src_base)/test-007.ops + $(call message,test $(out_base)/007,$(driver) --file \ +$(src_base)/test-007.ops | diff -u $(src_base)/test-007.std -) + # Clean. # $(clean): \ diff --git a/tests/file/test-004.ops b/tests/file/test-004.ops new file mode 100644 index 0000000..3246233 --- /dev/null +++ b/tests/file/test-004.ops @@ -0,0 +1,6 @@ +-a a"b"c +-a "abc" +-a "a"b" +-a "" +-a " abc " +-a " " diff --git a/tests/file/test-004.std b/tests/file/test-004.std new file mode 100644 index 0000000..01114a0 --- /dev/null +++ b/tests/file/test-004.std @@ -0,0 +1,12 @@ +-a +a"b"c +-a +abc +-a +a"b +-a + +-a + abc +-a + diff --git a/tests/file/test-005.ops b/tests/file/test-005.ops new file mode 100644 index 0000000..c9acf33 --- /dev/null +++ b/tests/file/test-005.ops @@ -0,0 +1 @@ +-a " diff --git a/tests/file/test-005.std b/tests/file/test-005.std new file mode 100644 index 0000000..52b6e89 --- /dev/null +++ b/tests/file/test-005.std @@ -0,0 +1 @@ +unmatched quote in argument '"' diff --git a/tests/file/test-006.ops b/tests/file/test-006.ops new file mode 100644 index 0000000..454366d --- /dev/null +++ b/tests/file/test-006.ops @@ -0,0 +1 @@ +-a "abc diff --git a/tests/file/test-006.std b/tests/file/test-006.std new file mode 100644 index 0000000..903f7a7 --- /dev/null +++ b/tests/file/test-006.std @@ -0,0 +1 @@ +unmatched quote in argument '"abc' diff --git a/tests/file/test-007.ops b/tests/file/test-007.ops new file mode 100644 index 0000000..8012af9 --- /dev/null +++ b/tests/file/test-007.ops @@ -0,0 +1 @@ +-a abc" diff --git a/tests/file/test-007.std b/tests/file/test-007.std new file mode 100644 index 0000000..fff8f85 --- /dev/null +++ b/tests/file/test-007.std @@ -0,0 +1 @@ +unmatched quote in argument 'abc"' -- cgit v1.1