From b5778fbc764cb72dcc2ec2fa72209b969f266d69 Mon Sep 17 00:00:00 2001
From: Karen Arutyunov <karen@codesynthesis.com>
Date: Mon, 12 Sep 2022 15:37:40 +0300
Subject: Use ad hoc recipe to compile options.cli file

---
 build/root.build                                   |   10 +-
 manifest                                           |    1 +
 odb/mysql/buildfile                                |  104 +-
 .../pregenerated/odb/mysql/details/options.cxx     | 1075 ++++++++++++++++++++
 .../pregenerated/odb/mysql/details/options.hxx     |  570 +++++++++++
 .../pregenerated/odb/mysql/details/options.ixx     |  384 +++++++
 repositories.manifest                              |    4 +
 7 files changed, 2110 insertions(+), 38 deletions(-)
 create mode 100644 odb/mysql/details/pregenerated/odb/mysql/details/options.cxx
 create mode 100644 odb/mysql/details/pregenerated/odb/mysql/details/options.hxx
 create mode 100644 odb/mysql/details/pregenerated/odb/mysql/details/options.ixx

diff --git a/build/root.build b/build/root.build
index 49d1911..c98d520 100644
--- a/build/root.build
+++ b/build/root.build
@@ -1,6 +1,8 @@
 # file      : build/root.build
 # license   : GNU GPL v2; see accompanying LICENSE file
 
+config [bool] config.libodb_mysql.develop ?= false
+
 # Configure which database client library to use for build2 versions greater
 # than 0.12.0 and always use MySQL client library otherwise (due to the lack
 # of the project configuration variables support).
@@ -41,11 +43,3 @@ if ($cxx.target.system == 'win32-msvc')
 
 if ($cxx.class == 'msvc')
   cxx.coptions += /wd4251 /wd4275 /wd4800
-
-# Load the cli module but only if it's available. This way a distribution
-# that includes pre-generated files can be built without installing cli.
-# This is also the reason why we need to explicitly spell out individual
-# source file prerequisites instead of using the cli.cxx{} group (it won't
-# be there unless the module is configured).
-#
-using? cli
diff --git a/manifest b/manifest
index 99bb006..3ac63d8 100644
--- a/manifest
+++ b/manifest
@@ -22,3 +22,4 @@ depends: * bpkg >= 0.15.0-
 #depends: libmysqlclient >= 5.0.3 | libmariadb ^10.2.2
 depends: libmysqlclient >= 5.0.3
 depends: libodb [2.5.0-b.24.1 2.5.0-b.25)
+depends: * cli ^1.2.0- ? ($config.libodb_mysql.develop)
diff --git a/odb/mysql/buildfile b/odb/mysql/buildfile
index 2796103..65631cb 100644
--- a/odb/mysql/buildfile
+++ b/odb/mysql/buildfile
@@ -1,15 +1,18 @@
 # file      : odb/mysql/buildfile
 # license   : GNU GPL v2; see accompanying LICENSE file
 
+define cli: file
+cli{*}: extension = cli
+
 import int_libs = libodb%lib{odb}
 
 import int_libs += ($client_lib == 'mysql'            \
                     ? libmysqlclient%lib{mysqlclient} \
                     : libmariadb%lib{mariadb})
 
-lib{odb-mysql}: {hxx ixx txx cxx}{* -version-build2} {hxx}{version-build2}   \
-        details/{hxx ixx txx cxx}{* -options} details/{hxx ixx cxx}{options} \
- details/build2/{h}{*}                                                       \
+lib{odb-mysql}: {hxx ixx txx cxx}{* -version-build2} {hxx}{version-build2} \
+        details/{hxx ixx txx cxx}{* -options}                              \
+ details/build2/{h}{*}                                                     \
                 $int_libs
 
 # Include the generated version header into the distribution (so that we don't
@@ -56,36 +59,77 @@ if $version.pre_release
 else
   lib{odb-mysql}: bin.lib.version = @"-$version.major.$version.minor"
 
-# Generated options parser.
+develop = $config.libodb_mysql.develop
+
+## Consumption build ($develop == false).
+#
+
+# Use pregenerated versions in the consumption build.
+#
+lib{odb-mysql}: details/pregenerated/{hxx ixx cxx}{**}: include = (!$develop)
+
+if! $develop
+  cxx.poptions =+ "-I($src_base/details/pregenerated)" # Note: must come first.
+
+# Don't install pregenerated headers since they are only used internally in
+# the database implementation (also below).
+#
+details/pregenerated/{hxx ixx}{*}: install = false
+
+# Distribute pregenerated versions only in the consumption build.
+#
+details/pregenerated/{hxx ixx cxx}{*}: dist = (!$develop)
+
 #
-details/
+##
+
+
+## Development build ($develop == true).
+#
+
+lib{odb-mysql}: details/{hxx ixx cxx}{options}: include = $develop
+
+if $develop
+  import! [metadata] cli = cli%exe{cli}
+
+# In the development build distribute regenerated {hxx ixx cxx}{options},
+# remapping their locations to the paths of the pregenerated versions (which
+# are only distributed in the consumption build; see above). This way we make
+# sure that the distributed files are always up-to-date.
+#
+<details/{hxx ixx cxx}{options}>: details/cli{options} $cli
 {
-  if $cli.configured
-  {
-    cli.cxx{options}: cli{options}
-
-    cli.options += --include-with-brackets --include-prefix odb/mysql/details \
---guard-prefix LIBODB_MYSQL_DETAILS --generate-file-scanner \
---cli-namespace odb::mysql::details::cli --long-usage --generate-specifier \
---no-combined-flags
-
-    # Include generated cli files into the distribution and don't remove them
-    # when cleaning in src (so that clean results in a state identical to
-    # distributed). But don't install their headers since they are only used
-    # internally in the database implementation.
-    #
-    cli.cxx{*}:
-    {
-      dist    = true
-      clean   = ($src_root != $out_root)
-      install = false
-    }
-  }
-  else
-    # No install for the pre-generated case.
-    #
-    hxx{options}@./ ixx{options}@./: install = false
+  install = false
+  dist    = ($develop ? details/pregenerated/odb/mysql/details/ : false)
+
+  # Symlink the generated code in src for convenience of development.
+  #
+  backlink = true
 }
+%
+if $develop
+{{
+  options = --include-with-brackets --include-prefix odb/mysql/details  \
+            --guard-prefix LIBODB_MYSQL_DETAILS --generate-file-scanner \
+            --cli-namespace odb::mysql::details::cli --long-usage       \
+            --generate-specifier --no-combined-flags
+
+  $cli $options -o $out_base/details/ $path($<[0])
+
+  # If the result differs from the pregenerated version, copy it over.
+  #
+  d = [dir_path] $src_base/details/pregenerated/odb/mysql/details/
+
+  if diff $d/options.hxx $path($>[0]) >- && \
+     diff $d/options.ixx $path($>[1]) >- && \
+     diff $d/options.cxx $path($>[2]) >-
+    exit
+  end
+
+  cp $path($>[0]) $d/options.hxx
+  cp $path($>[1]) $d/options.ixx
+  cp $path($>[2]) $d/options.cxx
+}}
 
 # Install into the odb/mysql/ subdirectory of, say, /usr/include/
 # recreating subdirectories.
diff --git a/odb/mysql/details/pregenerated/odb/mysql/details/options.cxx b/odb/mysql/details/pregenerated/odb/mysql/details/options.cxx
new file mode 100644
index 0000000..207d6b6
--- /dev/null
+++ b/odb/mysql/details/pregenerated/odb/mysql/details/options.cxx
@@ -0,0 +1,1075 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <odb/mysql/details/options.hxx>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <utility>
+#include <ostream>
+#include <sstream>
+#include <cstring>
+#include <fstream>
+
+namespace odb
+{
+  namespace mysql
+  {
+    namespace details
+    {
+      namespace cli
+      {
+        // unknown_option
+        //
+        unknown_option::
+        ~unknown_option () throw ()
+        {
+        }
+
+        void unknown_option::
+        print (::std::ostream& os) const
+        {
+          os << "unknown option '" << option ().c_str () << "'";
+        }
+
+        const char* unknown_option::
+        what () const throw ()
+        {
+          return "unknown option";
+        }
+
+        // unknown_argument
+        //
+        unknown_argument::
+        ~unknown_argument () throw ()
+        {
+        }
+
+        void unknown_argument::
+        print (::std::ostream& os) const
+        {
+          os << "unknown argument '" << argument ().c_str () << "'";
+        }
+
+        const char* unknown_argument::
+        what () const throw ()
+        {
+          return "unknown argument";
+        }
+
+        // missing_value
+        //
+        missing_value::
+        ~missing_value () throw ()
+        {
+        }
+
+        void missing_value::
+        print (::std::ostream& os) const
+        {
+          os << "missing value for option '" << option ().c_str () << "'";
+        }
+
+        const char* missing_value::
+        what () const throw ()
+        {
+          return "missing option value";
+        }
+
+        // invalid_value
+        //
+        invalid_value::
+        ~invalid_value () throw ()
+        {
+        }
+
+        void invalid_value::
+        print (::std::ostream& os) const
+        {
+          os << "invalid value '" << value ().c_str () << "' for option '"
+             << option ().c_str () << "'";
+
+          if (!message ().empty ())
+            os << ": " << message ().c_str ();
+        }
+
+        const char* invalid_value::
+        what () const throw ()
+        {
+          return "invalid option value";
+        }
+
+        // eos_reached
+        //
+        void eos_reached::
+        print (::std::ostream& os) const
+        {
+          os << what ();
+        }
+
+        const char* eos_reached::
+        what () const throw ()
+        {
+          return "end of argument stream reached";
+        }
+
+        // file_io_failure
+        //
+        file_io_failure::
+        ~file_io_failure () throw ()
+        {
+        }
+
+        void file_io_failure::
+        print (::std::ostream& os) const
+        {
+          os << "unable to open file '" << file ().c_str () << "' or read failure";
+        }
+
+        const char* file_io_failure::
+        what () const throw ()
+        {
+          return "unable to open file or read failure";
+        }
+
+        // unmatched_quote
+        //
+        unmatched_quote::
+        ~unmatched_quote () throw ()
+        {
+        }
+
+        void unmatched_quote::
+        print (::std::ostream& os) const
+        {
+          os << "unmatched quote in argument '" << argument ().c_str () << "'";
+        }
+
+        const char* unmatched_quote::
+        what () const throw ()
+        {
+          return "unmatched quote";
+        }
+
+        // scanner
+        //
+        scanner::
+        ~scanner ()
+        {
+        }
+
+        // argv_scanner
+        //
+        bool argv_scanner::
+        more ()
+        {
+          return i_ < argc_;
+        }
+
+        const char* argv_scanner::
+        peek ()
+        {
+          if (i_ < argc_)
+            return argv_[i_];
+          else
+            throw eos_reached ();
+        }
+
+        const char* argv_scanner::
+        next ()
+        {
+          if (i_ < argc_)
+          {
+            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_;
+
+            ++start_position_;
+            return r;
+          }
+          else
+            throw eos_reached ();
+        }
+
+        void argv_scanner::
+        skip ()
+        {
+          if (i_ < argc_)
+          {
+            ++i_;
+            ++start_position_;
+          }
+          else
+            throw eos_reached ();
+        }
+
+        std::size_t argv_scanner::
+        position ()
+        {
+          return start_position_;
+        }
+
+        // argv_file_scanner
+        //
+        int argv_file_scanner::zero_argc_ = 0;
+        std::string argv_file_scanner::empty_string_;
+
+        bool argv_file_scanner::
+        more ()
+        {
+          if (!args_.empty ())
+            return true;
+
+          while (base::more ())
+          {
+            // See if the next argument is the file option.
+            //
+            const char* a (base::peek ());
+            const option_info* oi = 0;
+            const char* ov = 0;
+
+            if (!skip_)
+            {
+              if ((oi = find (a)) != 0)
+              {
+                base::next ();
+
+                if (!base::more ())
+                  throw missing_value (a);
+
+                ov = base::next ();
+              }
+              else if (std::strncmp (a, "-", 1) == 0)
+              {
+                if ((ov = std::strchr (a, '=')) != 0)
+                {
+                  std::string o (a, 0, ov - a);
+                  if ((oi = find (o.c_str ())) != 0)
+                  {
+                    base::next ();
+                    ++ov;
+                  }
+                }
+              }
+            }
+
+            if (oi != 0)
+            {
+              if (oi->search_func != 0)
+              {
+                std::string f (oi->search_func (ov, oi->arg));
+
+                if (!f.empty ())
+                  load (f);
+              }
+              else
+                load (ov);
+
+              if (!args_.empty ())
+                return true;
+            }
+            else
+            {
+              if (!skip_)
+                skip_ = (std::strcmp (a, "--") == 0);
+
+              return true;
+            }
+          }
+
+          return false;
+        }
+
+        const char* argv_file_scanner::
+        peek ()
+        {
+          if (!more ())
+            throw eos_reached ();
+
+          return args_.empty () ? base::peek () : args_.front ().value.c_str ();
+        }
+
+        const std::string& argv_file_scanner::
+        peek_file ()
+        {
+          if (!more ())
+            throw eos_reached ();
+
+          return args_.empty () ? empty_string_ : *args_.front ().file;
+        }
+
+        std::size_t argv_file_scanner::
+        peek_line ()
+        {
+          if (!more ())
+            throw eos_reached ();
+
+          return args_.empty () ? 0 : args_.front ().line;
+        }
+
+        const char* argv_file_scanner::
+        next ()
+        {
+          if (!more ())
+            throw eos_reached ();
+
+          if (args_.empty ())
+            return base::next ();
+          else
+          {
+            hold_[i_ == 0 ? ++i_ : --i_].swap (args_.front ().value);
+            args_.pop_front ();
+            ++start_position_;
+            return hold_[i_].c_str ();
+          }
+        }
+
+        void argv_file_scanner::
+        skip ()
+        {
+          if (!more ())
+            throw eos_reached ();
+
+          if (args_.empty ())
+            return base::skip ();
+          else
+          {
+            args_.pop_front ();
+            ++start_position_;
+          }
+        }
+
+        const argv_file_scanner::option_info* argv_file_scanner::
+        find (const char* a) const
+        {
+          for (std::size_t i (0); i < options_count_; ++i)
+            if (std::strcmp (a, options_[i].option) == 0)
+              return &options_[i];
+
+          return 0;
+        }
+
+        std::size_t argv_file_scanner::
+        position ()
+        {
+          return start_position_;
+        }
+
+        void argv_file_scanner::
+        load (const std::string& file)
+        {
+          using namespace std;
+
+          ifstream is (file.c_str ());
+
+          if (!is.is_open ())
+            throw file_io_failure (file);
+
+          files_.push_back (file);
+
+          arg a;
+          a.file = &*files_.rbegin ();
+
+          for (a.line = 1; !is.eof (); ++a.line)
+          {
+            string line;
+            getline (is, line);
+
+            if (is.fail () && !is.eof ())
+              throw file_io_failure (file);
+
+            string::size_type n (line.size ());
+
+            // Trim the line from leading and trailing whitespaces.
+            //
+            if (n != 0)
+            {
+              const char* f (line.c_str ());
+              const char* l (f + n);
+
+              const char* of (f);
+              while (f < l && (*f == ' ' || *f == '\t' || *f == '\r'))
+                ++f;
+
+              --l;
+
+              const char* ol (l);
+              while (l > f && (*l == ' ' || *l == '\t' || *l == '\r'))
+                --l;
+
+              if (f != of || l != ol)
+                line = f <= l ? string (f, l - f + 1) : string ();
+            }
+
+            // Ignore empty lines, those that start with #.
+            //
+            if (line.empty () || line[0] == '#')
+              continue;
+
+            string::size_type p (string::npos);
+            if (line.compare (0, 1, "-") == 0)
+            {
+              p = line.find (' ');
+
+              string::size_type q (line.find ('='));
+              if (q != string::npos && q < p)
+                p = q;
+            }
+
+            string s1;
+            if (p != string::npos)
+            {
+              s1.assign (line, 0, p);
+
+              // Skip leading whitespaces in the argument.
+              //
+              if (line[p] == '=')
+                ++p;
+              else
+              {
+                n = line.size ();
+                for (++p; p < n; ++p)
+                {
+                  char c (line[p]);
+                  if (c != ' ' && c != '\t' && c != '\r')
+                    break;
+                }
+              }
+            }
+            else if (!skip_)
+              skip_ = (line == "--");
+
+            string s2 (line, p != string::npos ? p : 0);
+
+            // If the string (which is an option value or argument) is
+            // wrapped in quotes, remove them.
+            //
+            n = s2.size ();
+            char cf (s2[0]), cl (s2[n - 1]);
+
+            if (cf == '"' || cf == '\'' || cl == '"' || cl == '\'')
+            {
+              if (n == 1 || cf != cl)
+                throw unmatched_quote (s2);
+
+              s2 = string (s2, 1, n - 2);
+            }
+
+            if (!s1.empty ())
+            {
+              // See if this is another file option.
+              //
+              const option_info* oi;
+              if (!skip_ && (oi = find (s1.c_str ())))
+              {
+                if (s2.empty ())
+                  throw missing_value (oi->option);
+
+                if (oi->search_func != 0)
+                {
+                  string f (oi->search_func (s2.c_str (), oi->arg));
+                  if (!f.empty ())
+                    load (f);
+                }
+                else
+                {
+                  // If the path of the file being parsed is not simple and the
+                  // path of the file that needs to be loaded is relative, then
+                  // complete the latter using the former as a base.
+                  //
+#ifndef _WIN32
+                  string::size_type p (file.find_last_of ('/'));
+                  bool c (p != string::npos && s2[0] != '/');
+#else
+                  string::size_type p (file.find_last_of ("/\\"));
+                  bool c (p != string::npos && s2[1] != ':');
+#endif
+                  if (c)
+                    s2.insert (0, file, 0, p + 1);
+
+                  load (s2);
+                }
+
+                continue;
+              }
+
+              a.value = s1;
+              args_.push_back (a);
+            }
+
+            a.value = s2;
+            args_.push_back (a);
+          }
+        }
+
+        template <typename X>
+        struct parser
+        {
+          static void
+          parse (X& x, bool& xs, scanner& s)
+          {
+            using namespace std;
+
+            const char* o (s.next ());
+            if (s.more ())
+            {
+              string v (s.next ());
+              istringstream is (v);
+              if (!(is >> x && is.peek () == istringstream::traits_type::eof ()))
+                throw invalid_value (o, v);
+            }
+            else
+              throw missing_value (o);
+
+            xs = true;
+          }
+        };
+
+        template <>
+        struct parser<bool>
+        {
+          static void
+          parse (bool& x, bool& xs, scanner& s)
+          {
+            const char* o (s.next ());
+
+            if (s.more ())
+            {
+              const char* v (s.next ());
+
+              if (std::strcmp (v, "1")    == 0 ||
+                  std::strcmp (v, "true") == 0 ||
+                  std::strcmp (v, "TRUE") == 0 ||
+                  std::strcmp (v, "True") == 0)
+                x = true;
+              else if (std::strcmp (v, "0")     == 0 ||
+                       std::strcmp (v, "false") == 0 ||
+                       std::strcmp (v, "FALSE") == 0 ||
+                       std::strcmp (v, "False") == 0)
+                x = false;
+              else
+                throw invalid_value (o, v);
+            }
+            else
+              throw missing_value (o);
+
+            xs = true;
+          }
+        };
+
+        template <>
+        struct parser<std::string>
+        {
+          static void
+          parse (std::string& x, bool& xs, scanner& s)
+          {
+            const char* o (s.next ());
+
+            if (s.more ())
+              x = s.next ();
+            else
+              throw missing_value (o);
+
+            xs = true;
+          }
+        };
+
+        template <typename X>
+        struct parser<std::pair<X, std::size_t> >
+        {
+          static void
+          parse (std::pair<X, std::size_t>& x, bool& xs, scanner& s)
+          {
+            x.second = s.position ();
+            parser<X>::parse (x.first, xs, s);
+          }
+        };
+
+        template <typename X>
+        struct parser<std::vector<X> >
+        {
+          static void
+          parse (std::vector<X>& c, bool& xs, scanner& s)
+          {
+            X x;
+            bool dummy;
+            parser<X>::parse (x, dummy, s);
+            c.push_back (x);
+            xs = true;
+          }
+        };
+
+        template <typename X, typename C>
+        struct parser<std::set<X, C> >
+        {
+          static void
+          parse (std::set<X, C>& c, bool& xs, scanner& s)
+          {
+            X x;
+            bool dummy;
+            parser<X>::parse (x, dummy, s);
+            c.insert (x);
+            xs = true;
+          }
+        };
+
+        template <typename K, typename V, typename C>
+        struct parser<std::map<K, V, C> >
+        {
+          static void
+          parse (std::map<K, V, C>& m, bool& xs, scanner& s)
+          {
+            const char* o (s.next ());
+
+            if (s.more ())
+            {
+              std::size_t pos (s.position ());
+              std::string ov (s.next ());
+              std::string::size_type p = ov.find ('=');
+
+              K k = K ();
+              V v = V ();
+              std::string kstr (ov, 0, p);
+              std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+              int ac (2);
+              char* av[] =
+              {
+                const_cast<char*> (o),
+                0
+              };
+
+              bool dummy;
+              if (!kstr.empty ())
+              {
+                av[1] = const_cast<char*> (kstr.c_str ());
+                argv_scanner s (0, ac, av, false, pos);
+                parser<K>::parse (k, dummy, s);
+              }
+
+              if (!vstr.empty ())
+              {
+                av[1] = const_cast<char*> (vstr.c_str ());
+                argv_scanner s (0, ac, av, false, pos);
+                parser<V>::parse (v, dummy, s);
+              }
+
+              m[k] = v;
+            }
+            else
+              throw missing_value (o);
+
+            xs = true;
+          }
+        };
+
+        template <typename X, typename T, T X::*M>
+        void
+        thunk (X& x, scanner& s)
+        {
+          parser<T>::parse (x.*M, s);
+        }
+
+        template <typename X, bool X::*M>
+        void
+        thunk (X& x, scanner& s)
+        {
+          s.next ();
+          x.*M = true;
+        }
+
+        template <typename X, typename T, T X::*M, bool X::*S>
+        void
+        thunk (X& x, scanner& s)
+        {
+          parser<T>::parse (x.*M, x.*S, s);
+        }
+      }
+    }
+  }
+}
+
+#include <map>
+
+namespace odb
+{
+  namespace mysql
+  {
+    namespace details
+    {
+      // options
+      //
+
+      options::
+      options ()
+      : user_ (),
+        user_specified_ (false),
+        password_ (),
+        password_specified_ (false),
+        database_ (),
+        database_specified_ (false),
+        host_ (),
+        host_specified_ (false),
+        port_ (0),
+        port_specified_ (false),
+        socket_ (),
+        socket_specified_ (false),
+        options_file_ (),
+        options_file_specified_ (false)
+      {
+      }
+
+      options::
+      options (int& argc,
+               char** argv,
+               bool erase,
+               ::odb::mysql::details::cli::unknown_mode opt,
+               ::odb::mysql::details::cli::unknown_mode arg)
+      : user_ (),
+        user_specified_ (false),
+        password_ (),
+        password_specified_ (false),
+        database_ (),
+        database_specified_ (false),
+        host_ (),
+        host_specified_ (false),
+        port_ (0),
+        port_specified_ (false),
+        socket_ (),
+        socket_specified_ (false),
+        options_file_ (),
+        options_file_specified_ (false)
+      {
+        ::odb::mysql::details::cli::argv_scanner s (argc, argv, erase);
+        _parse (s, opt, arg);
+      }
+
+      options::
+      options (int start,
+               int& argc,
+               char** argv,
+               bool erase,
+               ::odb::mysql::details::cli::unknown_mode opt,
+               ::odb::mysql::details::cli::unknown_mode arg)
+      : user_ (),
+        user_specified_ (false),
+        password_ (),
+        password_specified_ (false),
+        database_ (),
+        database_specified_ (false),
+        host_ (),
+        host_specified_ (false),
+        port_ (0),
+        port_specified_ (false),
+        socket_ (),
+        socket_specified_ (false),
+        options_file_ (),
+        options_file_specified_ (false)
+      {
+        ::odb::mysql::details::cli::argv_scanner s (start, argc, argv, erase);
+        _parse (s, opt, arg);
+      }
+
+      options::
+      options (int& argc,
+               char** argv,
+               int& end,
+               bool erase,
+               ::odb::mysql::details::cli::unknown_mode opt,
+               ::odb::mysql::details::cli::unknown_mode arg)
+      : user_ (),
+        user_specified_ (false),
+        password_ (),
+        password_specified_ (false),
+        database_ (),
+        database_specified_ (false),
+        host_ (),
+        host_specified_ (false),
+        port_ (0),
+        port_specified_ (false),
+        socket_ (),
+        socket_specified_ (false),
+        options_file_ (),
+        options_file_specified_ (false)
+      {
+        ::odb::mysql::details::cli::argv_scanner s (argc, argv, erase);
+        _parse (s, opt, arg);
+        end = s.end ();
+      }
+
+      options::
+      options (int start,
+               int& argc,
+               char** argv,
+               int& end,
+               bool erase,
+               ::odb::mysql::details::cli::unknown_mode opt,
+               ::odb::mysql::details::cli::unknown_mode arg)
+      : user_ (),
+        user_specified_ (false),
+        password_ (),
+        password_specified_ (false),
+        database_ (),
+        database_specified_ (false),
+        host_ (),
+        host_specified_ (false),
+        port_ (0),
+        port_specified_ (false),
+        socket_ (),
+        socket_specified_ (false),
+        options_file_ (),
+        options_file_specified_ (false)
+      {
+        ::odb::mysql::details::cli::argv_scanner s (start, argc, argv, erase);
+        _parse (s, opt, arg);
+        end = s.end ();
+      }
+
+      options::
+      options (::odb::mysql::details::cli::scanner& s,
+               ::odb::mysql::details::cli::unknown_mode opt,
+               ::odb::mysql::details::cli::unknown_mode arg)
+      : user_ (),
+        user_specified_ (false),
+        password_ (),
+        password_specified_ (false),
+        database_ (),
+        database_specified_ (false),
+        host_ (),
+        host_specified_ (false),
+        port_ (0),
+        port_specified_ (false),
+        socket_ (),
+        socket_specified_ (false),
+        options_file_ (),
+        options_file_specified_ (false)
+      {
+        _parse (s, opt, arg);
+      }
+
+      ::odb::mysql::details::cli::usage_para options::
+      print_usage (::std::ostream& os, ::odb::mysql::details::cli::usage_para p)
+      {
+        CLI_POTENTIALLY_UNUSED (os);
+
+        if (p != ::odb::mysql::details::cli::usage_para::none)
+          os << ::std::endl;
+
+        os << "--user <name>         MySQL database user." << ::std::endl;
+
+        os << std::endl
+           << "--password <str>      MySQL database password" << ::std::endl;
+
+        os << std::endl
+           << "--database <name>     MySQL database name." << ::std::endl;
+
+        os << std::endl
+           << "--host <addr>         MySQL database host name or address (localhost by" << ::std::endl
+           << "                      default)." << ::std::endl;
+
+        os << std::endl
+           << "--port <integer>      MySQL database port number." << ::std::endl;
+
+        os << std::endl
+           << "--socket <name>       MySQL database socket name." << ::std::endl;
+
+        os << std::endl
+           << "--options-file <file> Read additional options from <file>. Each option should" << ::std::endl
+           << "                      appear on a separate line optionally followed by space or" << ::std::endl
+           << "                      equal sign (=) and an option value. Empty lines and lines" << ::std::endl
+           << "                      starting with # are ignored." << ::std::endl;
+
+        p = ::odb::mysql::details::cli::usage_para::option;
+
+        return p;
+      }
+
+      typedef
+      std::map<std::string, void (*) (options&, ::odb::mysql::details::cli::scanner&)>
+      _cli_options_map;
+
+      static _cli_options_map _cli_options_map_;
+
+      struct _cli_options_map_init
+      {
+        _cli_options_map_init ()
+        {
+          _cli_options_map_["--user"] =
+          &::odb::mysql::details::cli::thunk< options, std::string, &options::user_,
+            &options::user_specified_ >;
+          _cli_options_map_["--password"] =
+          &::odb::mysql::details::cli::thunk< options, std::string, &options::password_,
+            &options::password_specified_ >;
+          _cli_options_map_["--database"] =
+          &::odb::mysql::details::cli::thunk< options, std::string, &options::database_,
+            &options::database_specified_ >;
+          _cli_options_map_["--host"] =
+          &::odb::mysql::details::cli::thunk< options, std::string, &options::host_,
+            &options::host_specified_ >;
+          _cli_options_map_["--port"] =
+          &::odb::mysql::details::cli::thunk< options, unsigned int, &options::port_,
+            &options::port_specified_ >;
+          _cli_options_map_["--socket"] =
+          &::odb::mysql::details::cli::thunk< options, std::string, &options::socket_,
+            &options::socket_specified_ >;
+          _cli_options_map_["--options-file"] =
+          &::odb::mysql::details::cli::thunk< options, std::string, &options::options_file_,
+            &options::options_file_specified_ >;
+        }
+      };
+
+      static _cli_options_map_init _cli_options_map_init_;
+
+      bool options::
+      _parse (const char* o, ::odb::mysql::details::cli::scanner& s)
+      {
+        _cli_options_map::const_iterator i (_cli_options_map_.find (o));
+
+        if (i != _cli_options_map_.end ())
+        {
+          (*(i->second)) (*this, s);
+          return true;
+        }
+
+        return false;
+      }
+
+      bool options::
+      _parse (::odb::mysql::details::cli::scanner& s,
+              ::odb::mysql::details::cli::unknown_mode opt_mode,
+              ::odb::mysql::details::cli::unknown_mode arg_mode)
+      {
+        bool r = false;
+        bool opt = true;
+
+        while (s.more ())
+        {
+          const char* o = s.peek ();
+
+          if (std::strcmp (o, "--") == 0)
+          {
+            opt = false;
+            s.skip ();
+            r = true;
+            continue;
+          }
+
+          if (opt)
+          {
+            if (_parse (o, s))
+            {
+              r = true;
+              continue;
+            }
+
+            if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0')
+            {
+              // Handle combined option values.
+              //
+              std::string co;
+              if (const char* v = std::strchr (o, '='))
+              {
+                co.assign (o, 0, v - o);
+                ++v;
+
+                int ac (2);
+                char* av[] =
+                {
+                  const_cast<char*> (co.c_str ()),
+                  const_cast<char*> (v)
+                };
+
+                ::odb::mysql::details::cli::argv_scanner ns (0, ac, av);
+
+                if (_parse (co.c_str (), ns))
+                {
+                  // Parsed the option but not its value?
+                  //
+                  if (ns.end () != 2)
+                    throw ::odb::mysql::details::cli::invalid_value (co, v);
+
+                  s.next ();
+                  r = true;
+                  continue;
+                }
+                else
+                {
+                  // Set the unknown option and fall through.
+                  //
+                  o = co.c_str ();
+                }
+              }
+
+              switch (opt_mode)
+              {
+                case ::odb::mysql::details::cli::unknown_mode::skip:
+                {
+                  s.skip ();
+                  r = true;
+                  continue;
+                }
+                case ::odb::mysql::details::cli::unknown_mode::stop:
+                {
+                  break;
+                }
+                case ::odb::mysql::details::cli::unknown_mode::fail:
+                {
+                  throw ::odb::mysql::details::cli::unknown_option (o);
+                }
+              }
+
+              break;
+            }
+          }
+
+          switch (arg_mode)
+          {
+            case ::odb::mysql::details::cli::unknown_mode::skip:
+            {
+              s.skip ();
+              r = true;
+              continue;
+            }
+            case ::odb::mysql::details::cli::unknown_mode::stop:
+            {
+              break;
+            }
+            case ::odb::mysql::details::cli::unknown_mode::fail:
+            {
+              throw ::odb::mysql::details::cli::unknown_argument (o);
+            }
+          }
+
+          break;
+        }
+
+        return r;
+      }
+    }
+  }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
diff --git a/odb/mysql/details/pregenerated/odb/mysql/details/options.hxx b/odb/mysql/details/pregenerated/odb/mysql/details/options.hxx
new file mode 100644
index 0000000..daa7d52
--- /dev/null
+++ b/odb/mysql/details/pregenerated/odb/mysql/details/options.hxx
@@ -0,0 +1,570 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+#ifndef LIBODB_MYSQL_DETAILS_OPTIONS_HXX
+#define LIBODB_MYSQL_DETAILS_OPTIONS_HXX
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <list>
+#include <deque>
+#include <iosfwd>
+#include <string>
+#include <cstddef>
+#include <exception>
+
+#ifndef CLI_POTENTIALLY_UNUSED
+#  if defined(_MSC_VER) || defined(__xlC__)
+#    define CLI_POTENTIALLY_UNUSED(x) (void*)&x
+#  else
+#    define CLI_POTENTIALLY_UNUSED(x) (void)x
+#  endif
+#endif
+
+namespace odb
+{
+  namespace mysql
+  {
+    namespace details
+    {
+      namespace cli
+      {
+        class usage_para
+        {
+          public:
+          enum value
+          {
+            none,
+            text,
+            option
+          };
+
+          usage_para (value);
+
+          operator value () const 
+          {
+            return v_;
+          }
+
+          private:
+          value v_;
+        };
+
+        class unknown_mode
+        {
+          public:
+          enum value
+          {
+            skip,
+            stop,
+            fail
+          };
+
+          unknown_mode (value);
+
+          operator value () const 
+          {
+            return v_;
+          }
+
+          private:
+          value v_;
+        };
+
+        // Exceptions.
+        //
+
+        class exception: public std::exception
+        {
+          public:
+          virtual void
+          print (::std::ostream&) const = 0;
+        };
+
+        ::std::ostream&
+        operator<< (::std::ostream&, const exception&);
+
+        class unknown_option: public exception
+        {
+          public:
+          virtual
+          ~unknown_option () throw ();
+
+          unknown_option (const std::string& option);
+
+          const std::string&
+          option () const;
+
+          virtual void
+          print (::std::ostream&) const;
+
+          virtual const char*
+          what () const throw ();
+
+          private:
+          std::string option_;
+        };
+
+        class unknown_argument: public exception
+        {
+          public:
+          virtual
+          ~unknown_argument () throw ();
+
+          unknown_argument (const std::string& argument);
+
+          const std::string&
+          argument () const;
+
+          virtual void
+          print (::std::ostream&) const;
+
+          virtual const char*
+          what () const throw ();
+
+          private:
+          std::string argument_;
+        };
+
+        class missing_value: public exception
+        {
+          public:
+          virtual
+          ~missing_value () throw ();
+
+          missing_value (const std::string& option);
+
+          const std::string&
+          option () const;
+
+          virtual void
+          print (::std::ostream&) const;
+
+          virtual const char*
+          what () const throw ();
+
+          private:
+          std::string option_;
+        };
+
+        class invalid_value: public exception
+        {
+          public:
+          virtual
+          ~invalid_value () throw ();
+
+          invalid_value (const std::string& option,
+                         const std::string& value,
+                         const std::string& message = std::string ());
+
+          const std::string&
+          option () const;
+
+          const std::string&
+          value () const;
+
+          const std::string&
+          message () const;
+
+          virtual void
+          print (::std::ostream&) const;
+
+          virtual const char*
+          what () const throw ();
+
+          private:
+          std::string option_;
+          std::string value_;
+          std::string message_;
+        };
+
+        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:
+          virtual
+          ~file_io_failure () throw ();
+
+          file_io_failure (const std::string& file);
+
+          const std::string&
+          file () const;
+
+          virtual void
+          print (::std::ostream&) const;
+
+          virtual const char*
+          what () const throw ();
+
+          private:
+          std::string file_;
+        };
+
+        class unmatched_quote: public exception
+        {
+          public:
+          virtual
+          ~unmatched_quote () throw ();
+
+          unmatched_quote (const std::string& argument);
+
+          const std::string&
+          argument () const;
+
+          virtual void
+          print (::std::ostream&) const;
+
+          virtual const char*
+          what () const throw ();
+
+          private:
+          std::string argument_;
+        };
+
+        // Command line argument scanner interface.
+        //
+        // The values returned by next() are guaranteed to be valid
+        // for the two previous arguments up until a call to a third
+        // peek() or next().
+        //
+        // The position() function returns a monotonically-increasing
+        // number which, if stored, can later be used to determine the
+        // relative position of the argument returned by the following
+        // call to next(). Note that if multiple scanners are used to
+        // extract arguments from multiple sources, then the end
+        // position of the previous scanner should be used as the
+        // start position of the next.
+        //
+        class scanner
+        {
+          public:
+          virtual
+          ~scanner ();
+
+          virtual bool
+          more () = 0;
+
+          virtual const char*
+          peek () = 0;
+
+          virtual const char*
+          next () = 0;
+
+          virtual void
+          skip () = 0;
+
+          virtual std::size_t
+          position () = 0;
+        };
+
+        class argv_scanner: public scanner
+        {
+          public:
+          argv_scanner (int& argc,
+                        char** argv,
+                        bool erase = false,
+                        std::size_t start_position = 0);
+
+          argv_scanner (int start,
+                        int& argc,
+                        char** argv,
+                        bool erase = false,
+                        std::size_t start_position = 0);
+
+          int
+          end () const;
+
+          virtual bool
+          more ();
+
+          virtual const char*
+          peek ();
+
+          virtual const char*
+          next ();
+
+          virtual void
+          skip ();
+
+          virtual std::size_t
+          position ();
+
+          protected:
+          std::size_t start_position_;
+          int i_;
+          int& argc_;
+          char** argv_;
+          bool erase_;
+        };
+
+        class argv_file_scanner: public argv_scanner
+        {
+          public:
+          argv_file_scanner (int& argc,
+                             char** argv,
+                             const std::string& option,
+                             bool erase = false,
+                             std::size_t start_position = 0);
+
+          argv_file_scanner (int start,
+                             int& argc,
+                             char** argv,
+                             const std::string& option,
+                             bool erase = false,
+                             std::size_t start_position = 0);
+
+          argv_file_scanner (const std::string& file,
+                             const std::string& option,
+                             std::size_t start_position = 0);
+
+          struct option_info
+          {
+            // If search_func is not NULL, it is called, with the arg
+            // value as the second argument, to locate the options file.
+            // If it returns an empty string, then the file is ignored.
+            //
+            const char* option;
+            std::string (*search_func) (const char*, void* arg);
+            void* arg;
+          };
+
+          argv_file_scanner (int& argc,
+                              char** argv,
+                              const option_info* options,
+                              std::size_t options_count,
+                              bool erase = false,
+                              std::size_t start_position = 0);
+
+          argv_file_scanner (int start,
+                             int& argc,
+                             char** argv,
+                             const option_info* options,
+                             std::size_t options_count,
+                             bool erase = false,
+                             std::size_t start_position = 0);
+
+          argv_file_scanner (const std::string& file,
+                             const option_info* options = 0,
+                             std::size_t options_count = 0,
+                             std::size_t start_position = 0);
+
+          virtual bool
+          more ();
+
+          virtual const char*
+          peek ();
+
+          virtual const char*
+          next ();
+
+          virtual void
+          skip ();
+
+          virtual std::size_t
+          position ();
+
+          // Return the file path if the peeked at argument came from a file and
+          // the empty string otherwise. The reference is guaranteed to be valid
+          // till the end of the scanner lifetime.
+          //
+          const std::string&
+          peek_file ();
+
+          // Return the 1-based line number if the peeked at argument came from
+          // a file and zero otherwise.
+          //
+          std::size_t
+          peek_line ();
+
+          private:
+          const option_info*
+          find (const char*) const;
+
+          void
+          load (const std::string& file);
+
+          typedef argv_scanner base;
+
+          const std::string option_;
+          option_info option_info_;
+          const option_info* options_;
+          std::size_t options_count_;
+
+          struct arg
+          {
+            std::string value;
+            const std::string* file;
+            std::size_t line;
+          };
+
+          std::deque<arg> args_;
+          std::list<std::string> files_;
+
+          // Circular buffer of two arguments.
+          //
+          std::string hold_[2];
+          std::size_t i_;
+
+          bool skip_;
+
+          static int zero_argc_;
+          static std::string empty_string_;
+        };
+
+        template <typename X>
+        struct parser;
+      }
+    }
+  }
+}
+
+#include <string>
+
+namespace odb
+{
+  namespace mysql
+  {
+    namespace details
+    {
+      class options
+      {
+        public:
+        options ();
+
+        options (int& argc,
+                 char** argv,
+                 bool erase = false,
+                 ::odb::mysql::details::cli::unknown_mode option = ::odb::mysql::details::cli::unknown_mode::fail,
+                 ::odb::mysql::details::cli::unknown_mode argument = ::odb::mysql::details::cli::unknown_mode::stop);
+
+        options (int start,
+                 int& argc,
+                 char** argv,
+                 bool erase = false,
+                 ::odb::mysql::details::cli::unknown_mode option = ::odb::mysql::details::cli::unknown_mode::fail,
+                 ::odb::mysql::details::cli::unknown_mode argument = ::odb::mysql::details::cli::unknown_mode::stop);
+
+        options (int& argc,
+                 char** argv,
+                 int& end,
+                 bool erase = false,
+                 ::odb::mysql::details::cli::unknown_mode option = ::odb::mysql::details::cli::unknown_mode::fail,
+                 ::odb::mysql::details::cli::unknown_mode argument = ::odb::mysql::details::cli::unknown_mode::stop);
+
+        options (int start,
+                 int& argc,
+                 char** argv,
+                 int& end,
+                 bool erase = false,
+                 ::odb::mysql::details::cli::unknown_mode option = ::odb::mysql::details::cli::unknown_mode::fail,
+                 ::odb::mysql::details::cli::unknown_mode argument = ::odb::mysql::details::cli::unknown_mode::stop);
+
+        options (::odb::mysql::details::cli::scanner&,
+                 ::odb::mysql::details::cli::unknown_mode option = ::odb::mysql::details::cli::unknown_mode::fail,
+                 ::odb::mysql::details::cli::unknown_mode argument = ::odb::mysql::details::cli::unknown_mode::stop);
+
+        // Option accessors.
+        //
+        const std::string&
+        user () const;
+
+        bool
+        user_specified () const;
+
+        const std::string&
+        password () const;
+
+        bool
+        password_specified () const;
+
+        const std::string&
+        database () const;
+
+        bool
+        database_specified () const;
+
+        const std::string&
+        host () const;
+
+        bool
+        host_specified () const;
+
+        const unsigned int&
+        port () const;
+
+        bool
+        port_specified () const;
+
+        const std::string&
+        socket () const;
+
+        bool
+        socket_specified () const;
+
+        const std::string&
+        options_file () const;
+
+        bool
+        options_file_specified () const;
+
+        // Print usage information.
+        //
+        static ::odb::mysql::details::cli::usage_para
+        print_usage (::std::ostream&,
+                     ::odb::mysql::details::cli::usage_para = ::odb::mysql::details::cli::usage_para::none);
+
+        // Implementation details.
+        //
+        protected:
+        bool
+        _parse (const char*, ::odb::mysql::details::cli::scanner&);
+
+        private:
+        bool
+        _parse (::odb::mysql::details::cli::scanner&,
+                ::odb::mysql::details::cli::unknown_mode option,
+                ::odb::mysql::details::cli::unknown_mode argument);
+
+        public:
+        std::string user_;
+        bool user_specified_;
+        std::string password_;
+        bool password_specified_;
+        std::string database_;
+        bool database_specified_;
+        std::string host_;
+        bool host_specified_;
+        unsigned int port_;
+        bool port_specified_;
+        std::string socket_;
+        bool socket_specified_;
+        std::string options_file_;
+        bool options_file_specified_;
+      };
+    }
+  }
+}
+
+#include <odb/mysql/details/options.ixx>
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
+#endif // LIBODB_MYSQL_DETAILS_OPTIONS_HXX
diff --git a/odb/mysql/details/pregenerated/odb/mysql/details/options.ixx b/odb/mysql/details/pregenerated/odb/mysql/details/options.ixx
new file mode 100644
index 0000000..66bc5df
--- /dev/null
+++ b/odb/mysql/details/pregenerated/odb/mysql/details/options.ixx
@@ -0,0 +1,384 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <cassert>
+
+namespace odb
+{
+  namespace mysql
+  {
+    namespace details
+    {
+      namespace cli
+      {
+        // usage_para
+        //
+        inline usage_para::
+        usage_para (value v)
+        : v_ (v)
+        {
+        }
+
+        // unknown_mode
+        //
+        inline unknown_mode::
+        unknown_mode (value v)
+        : v_ (v)
+        {
+        }
+
+        // exception
+        //
+        inline ::std::ostream&
+        operator<< (::std::ostream& os, const exception& e)
+        {
+          e.print (os);
+          return os;
+        }
+
+        // unknown_option
+        //
+        inline unknown_option::
+        unknown_option (const std::string& option)
+        : option_ (option)
+        {
+        }
+
+        inline const std::string& unknown_option::
+        option () const
+        {
+          return option_;
+        }
+
+        // unknown_argument
+        //
+        inline unknown_argument::
+        unknown_argument (const std::string& argument)
+        : argument_ (argument)
+        {
+        }
+
+        inline const std::string& unknown_argument::
+        argument () const
+        {
+          return argument_;
+        }
+
+        // missing_value
+        //
+        inline missing_value::
+        missing_value (const std::string& option)
+        : option_ (option)
+        {
+        }
+
+        inline const std::string& missing_value::
+        option () const
+        {
+          return option_;
+        }
+
+        // invalid_value
+        //
+        inline invalid_value::
+        invalid_value (const std::string& option,
+                       const std::string& value,
+                       const std::string& message)
+        : option_ (option),
+          value_ (value),
+          message_ (message)
+        {
+        }
+
+        inline const std::string& invalid_value::
+        option () const
+        {
+          return option_;
+        }
+
+        inline const std::string& invalid_value::
+        value () const
+        {
+          return value_;
+        }
+
+        inline const std::string& invalid_value::
+        message () const
+        {
+          return message_;
+        }
+
+        // file_io_failure
+        //
+        inline file_io_failure::
+        file_io_failure (const std::string& file)
+        : file_ (file)
+        {
+        }
+
+        inline const std::string& file_io_failure::
+        file () const
+        {
+          return file_;
+        }
+
+        // unmatched_quote
+        //
+        inline unmatched_quote::
+        unmatched_quote (const std::string& argument)
+        : argument_ (argument)
+        {
+        }
+
+        inline const std::string& unmatched_quote::
+        argument () const
+        {
+          return argument_;
+        }
+
+        // argv_scanner
+        //
+        inline argv_scanner::
+        argv_scanner (int& argc,
+                      char** argv,
+                      bool erase,
+                      std::size_t sp)
+        : start_position_ (sp + 1),
+          i_ (1),
+          argc_ (argc),
+          argv_ (argv),
+          erase_ (erase)
+        {
+        }
+
+        inline argv_scanner::
+        argv_scanner (int start,
+                      int& argc,
+                      char** argv,
+                      bool erase,
+                      std::size_t sp)
+        : start_position_ (sp + static_cast<std::size_t> (start)),
+          i_ (start),
+          argc_ (argc),
+          argv_ (argv),
+          erase_ (erase)
+        {
+        }
+
+        inline int argv_scanner::
+        end () const
+        {
+          return i_;
+        }
+
+        // argv_file_scanner
+        //
+        inline argv_file_scanner::
+        argv_file_scanner (int& argc,
+                           char** argv,
+                           const std::string& option,
+                           bool erase,
+                           std::size_t sp)
+        : argv_scanner (argc, argv, erase, sp),
+          option_ (option),
+          options_ (&option_info_),
+          options_count_ (1),
+          i_ (1),
+          skip_ (false)
+        {
+          option_info_.option = option_.c_str ();
+          option_info_.search_func = 0;
+        }
+
+        inline argv_file_scanner::
+        argv_file_scanner (int start,
+                           int& argc,
+                           char** argv,
+                           const std::string& option,
+                           bool erase,
+                           std::size_t sp)
+        : argv_scanner (start, argc, argv, erase, sp),
+          option_ (option),
+          options_ (&option_info_),
+          options_count_ (1),
+          i_ (1),
+          skip_ (false)
+        {
+          option_info_.option = option_.c_str ();
+          option_info_.search_func = 0;
+        }
+
+        inline argv_file_scanner::
+        argv_file_scanner (const std::string& file,
+                           const std::string& option,
+                           std::size_t sp)
+        : argv_scanner (0, zero_argc_, 0, sp),
+          option_ (option),
+          options_ (&option_info_),
+          options_count_ (1),
+          i_ (1),
+          skip_ (false)
+        {
+          option_info_.option = option_.c_str ();
+          option_info_.search_func = 0;
+
+          load (file);
+        }
+
+        inline argv_file_scanner::
+        argv_file_scanner (int& argc,
+                           char** argv,
+                           const option_info* options,
+                           std::size_t options_count,
+                           bool erase,
+                           std::size_t sp)
+        : argv_scanner (argc, argv, erase, sp),
+          options_ (options),
+          options_count_ (options_count),
+          i_ (1),
+          skip_ (false)
+        {
+        }
+
+        inline argv_file_scanner::
+        argv_file_scanner (int start,
+                           int& argc,
+                           char** argv,
+                           const option_info* options,
+                           std::size_t options_count,
+                           bool erase,
+                           std::size_t sp)
+        : argv_scanner (start, argc, argv, erase, sp),
+          options_ (options),
+          options_count_ (options_count),
+          i_ (1),
+          skip_ (false)
+        {
+        }
+
+        inline argv_file_scanner::
+        argv_file_scanner (const std::string& file,
+                           const option_info* options,
+                           std::size_t options_count,
+                           std::size_t sp)
+        : argv_scanner (0, zero_argc_, 0, sp),
+          options_ (options),
+          options_count_ (options_count),
+          i_ (1),
+          skip_ (false)
+        {
+          load (file);
+        }
+      }
+    }
+  }
+}
+
+namespace odb
+{
+  namespace mysql
+  {
+    namespace details
+    {
+      // options
+      //
+
+      inline const std::string& options::
+      user () const
+      {
+        return this->user_;
+      }
+
+      inline bool options::
+      user_specified () const
+      {
+        return this->user_specified_;
+      }
+
+      inline const std::string& options::
+      password () const
+      {
+        return this->password_;
+      }
+
+      inline bool options::
+      password_specified () const
+      {
+        return this->password_specified_;
+      }
+
+      inline const std::string& options::
+      database () const
+      {
+        return this->database_;
+      }
+
+      inline bool options::
+      database_specified () const
+      {
+        return this->database_specified_;
+      }
+
+      inline const std::string& options::
+      host () const
+      {
+        return this->host_;
+      }
+
+      inline bool options::
+      host_specified () const
+      {
+        return this->host_specified_;
+      }
+
+      inline const unsigned int& options::
+      port () const
+      {
+        return this->port_;
+      }
+
+      inline bool options::
+      port_specified () const
+      {
+        return this->port_specified_;
+      }
+
+      inline const std::string& options::
+      socket () const
+      {
+        return this->socket_;
+      }
+
+      inline bool options::
+      socket_specified () const
+      {
+        return this->socket_specified_;
+      }
+
+      inline const std::string& options::
+      options_file () const
+      {
+        return this->options_file_;
+      }
+
+      inline bool options::
+      options_file_specified () const
+      {
+        return this->options_file_specified_;
+      }
+    }
+  }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
diff --git a/repositories.manifest b/repositories.manifest
index 6082b74..efd374c 100644
--- a/repositories.manifest
+++ b/repositories.manifest
@@ -8,3 +8,7 @@ location: https://git.build2.org/packaging/mysql/mysql.git##HEAD
 :
 role: prerequisite
 location: ../libodb.git##HEAD
+
+:
+role: prerequisite
+location: https://git.codesynthesis.com/cli/cli.git##HEAD
-- 
cgit v1.1