www.pudn.com > cppcc.rar > cmdline_parser.cc


/*
 *  File:       cucu.c
 *              $Id: cmdline_parser.cc,v 1.3 2002/04/29 09:34:10 alec Exp $
 *
 *  Author:     Alec Panoviciu (alecu@email.com)
 *
 *  Comments:
 *
 *  Revision history:
 *
 *  $Log: cmdline_parser.cc,v $
 *  Revision 1.3  2002/04/29 09:34:10  alec
 *  scanner ptree building compiles
 *
 */

/* 
  Copyright (C) 2002 Alexandru Panoviciu (alecu@email.com)

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 */


#include 

#include "cmdline_parser.hh"

CmdLineParser::CmdLineAtom::operator string () const
{
  string s;
  string args;

  switch (tag)
  {
  case k_string: args = " "; break;
  case k_bool: args = " [=yes|no|true|false]"; break;
  case k_int: args = " "; break;
  case k_float: args = " "; break;
  default: ASSERT(0, "bad cmd line atom tag");
  }
  
  if ((sOpt != "") && (lOpt != "")) s = "-" + sOpt + " | --" + lOpt;
  else
    if (sOpt != "") s = "-" + sOpt;
    else s = "--" + lOpt;

  s += args;
  
  if (multi)
    if (optional) s = "(" + s + ")*";
    else s = "(" + s + ")+";
  else
    if (optional) s = "[" + s + "]";
    else if ((sOpt != "") && (lOpt != "")) s = "(" + s + ")";

  return s;
}

#define COLS 80
#define TS 30
ostream& CmdLineParser::CmdLineAtom::dumpDescription (ostream &os) const
{
  string syn = (string) *this;
  int c = syn.length(); //column number (we wrap at 80);
  int i = 0; //index into the description string
  
  os << syn; c %= COLS;
  
  int aux = c + 5;
  while (c++ < max(TS, aux)) os << ' '; // :)

  for (int i = 0; i < description.length(); )
  {
    int wb = min(description.find(' ', i + 1), description.length());
    if (c + wb - i > COLS)
    {
      os << endl;
      for (c = 0; c++ < TS; os << ' ');
    }
    c += wb - i;
    while (i < wb) os << description[i++];
  }
  return os;
}


int CmdLineParser::parse (int argc, char *argv[])
  throw (CmdLineParseException)
{

  //first set the invocation property with the command line string list
  for (int i = 0; i < argc; i++)
    pr["invocation"] << argv[i];

  for (int i = 0; i < argc; i++)
  {
    string s = argv[i];
    
    if (s[0] != '-') return i;
    if (s == "--") return i + 1;
    
    bool isLong = s[1] == '-'; //long form ?
    int c = isLong ? 2 : 1;
    string opt;
    int n;
    while ((s[c] != '=') && (c < s.length())) opt += s[c++];
    
    for (n = 0; n < desc.size(); n++)
    {
      if ((isLong && (opt == desc[n].lOpt)) ||
          (!isLong && (opt == desc[n].sOpt)))
      {
        switch (desc[n].tag) {
        case CmdLineAtom::k_string:
          {
            string val;
            if (c + 1 < s.length()) val.assign(s, c + 1, s.length() - c - 1);
            else if (i + 1 < argc) val = argv[++i];
            else throw CmdLineParseException("string argument expected after option \"" + opt + "\"");
            if (desc[n].multi) pr[desc[n].prop] << val;
            else pr[desc[n].prop] = val;
          }
          break;
        case CmdLineAtom::k_bool:
          {
            string val;
            bool bval;
            
            if (c + 1 < s.length()) val.assign(s, c + 1, s.length() - c - 1);
            else if ((i + 1 < argc) && (argv[i + 1][0] != '-')) val = argv[++i];
            else val = "yes";

            if ((val == "yes") || (val == "true") ||
                (val == "YES") || (val == "TRUE")) bval = true;
            else if ((val == "no") || (val == "false") ||
                (val == "NO") || (val == "FALSE")) bval = false;
            else throw CmdLineParseException("value of boolean option \"" + opt + "\" must be one of yes/no/false/true");
            if (desc[n].multi) pr[desc[n].prop] << bval;
            else pr[desc[n].prop] = bval;
          }
          break;
        case CmdLineAtom::k_int:
          {
            string val;
            int ival;
            
            if (c + 1 < s.length()) val.assign(s, c + 1, s.length() - c - 1);
            else if (i + 1 < argc) val = argv[++i];
            else throw CmdLineParseException("integer value expected after option \"" + opt + "\"");

            if (!(istringstream(val) >> ival))
              throw CmdLineParseException("bad integer argument for option \"" + opt + "\"");
            if (desc[n].multi) pr[desc[n].prop] << ival;
            else pr[desc[n].prop] = ival;
          }
          break;
        case CmdLineAtom::k_float:
          {
            string val;
            float fval;
            
            if (c + 1 < s.length()) val.assign(s, c + 1, s.length() - c - 1);
            else if (i + 1 < argc) val = argv[++i];
            else throw CmdLineParseException("integer value expected after option \"" + opt + "\"");

            if (!(istringstream(val) >> fval))
              throw CmdLineParseException("bad floating point argument for option \"" + opt + "\"");
            if (desc[n].multi) pr[desc[n].prop] << fval;
            else pr[desc[n].prop] = fval;
          }
          break;
        }
        break;
      }
    }
    
    if (n == desc.size())
      throw CmdLineParseException("unknown option \"" + opt + "\"");
  }
}


void CmdLineParser::printUsage (ostream &os)
{
  os << "Usage: " << endl << progName << ' ';
  for (int i = 0; i < desc.size(); i++)
    os << (string) desc[i] << ' ';

  os << endl << endl << "Options: " << endl << endl;
  
  for (int i = 0; i < desc.size(); i++) {
    desc[i].dumpDescription(os);
    os << endl << endl;
  }
  
  os << endl << endl;
}