// -*- C++ -*-
//
// This file was generated by CLI, a command line interface
// compiler for C++.
//

// Begin prologue.
//
//
// End prologue.

#include <libbuild2/common-options.hxx>

#include <map>
#include <set>
#include <string>
#include <vector>
#include <utility>
#include <ostream>
#include <sstream>
#include <cstring>
#include <fstream>

namespace build2
{
  namespace build
  {
    namespace cli
    {
      // unknown_option
      //
      unknown_option::
      ~unknown_option () noexcept
      {
      }

      void unknown_option::
      print (::std::ostream& os) const
      {
        os << "unknown option '" << option ().c_str () << "'";
      }

      const char* unknown_option::
      what () const noexcept
      {
        return "unknown option";
      }

      // unknown_argument
      //
      unknown_argument::
      ~unknown_argument () noexcept
      {
      }

      void unknown_argument::
      print (::std::ostream& os) const
      {
        os << "unknown argument '" << argument ().c_str () << "'";
      }

      const char* unknown_argument::
      what () const noexcept
      {
        return "unknown argument";
      }

      // missing_value
      //
      missing_value::
      ~missing_value () noexcept
      {
      }

      void missing_value::
      print (::std::ostream& os) const
      {
        os << "missing value for option '" << option ().c_str () << "'";
      }

      const char* missing_value::
      what () const noexcept
      {
        return "missing option value";
      }

      // invalid_value
      //
      invalid_value::
      ~invalid_value () noexcept
      {
      }

      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 noexcept
      {
        return "invalid option value";
      }

      // eos_reached
      //
      void eos_reached::
      print (::std::ostream& os) const
      {
        os << what ();
      }

      const char* eos_reached::
      what () const noexcept
      {
        return "end of argument stream reached";
      }

      // file_io_failure
      //
      file_io_failure::
      ~file_io_failure () noexcept
      {
      }

      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 noexcept
      {
        return "unable to open file or read failure";
      }

      // unmatched_quote
      //
      unmatched_quote::
      ~unmatched_quote () noexcept
      {
      }

      void unmatched_quote::
      print (::std::ostream& os) const
      {
        os << "unmatched quote in argument '" << argument ().c_str () << "'";
      }

      const char* unmatched_quote::
      what () const noexcept
      {
        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_;
      }

      // vector_scanner
      //
      bool vector_scanner::
      more ()
      {
        return i_ < v_.size ();
      }

      const char* vector_scanner::
      peek ()
      {
        if (i_ < v_.size ())
          return v_[i_].c_str ();
        else
          throw eos_reached ();
      }

      const char* vector_scanner::
      next ()
      {
        if (i_ < v_.size ())
          return v_[i_++].c_str ();
        else
          throw eos_reached ();
      }

      void vector_scanner::
      skip ()
      {
        if (i_ < v_.size ())
          ++i_;
        else
          throw eos_reached ();
      }

      std::size_t vector_scanner::
      position ()
      {
        return start_position_ + i_;
      }

      // 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 K, typename V, typename C>
      struct parser<std::multimap<K, V, C> >
      {
        static void
        parse (std::multimap<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.insert (typename std::multimap<K, V, C>::value_type (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 build2
{
}

// Begin epilogue.
//
//
// End epilogue.

