Logo Search packages:      
Sourcecode: tagcoll version File versions

CommandlineParser.cc

#include "CommandlineParser.h"
#include <tagcoll/stringf.h>
#include <assert.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>

using namespace std;
using namespace stringf;

string CommandlineParser::WordWrapper::get(unsigned int width) throw ()
{
      if (cursor >= s.size())
            return "";
      
      // Find the last work break before `width'
      unsigned int brk = cursor;
      for (unsigned int j = cursor; j < s.size() && j < cursor + width; j++)
      {
            if (s[j] == '\n')
            {
                  brk = j;
                  break;
            } else if (!isspace(s[j]) && (j + 1 == s.size() || isspace(s[j + 1])))
                  brk = j + 1;
      }
      if (brk == cursor)
            brk = cursor + width;
      
      string res;
      if (brk >= s.size())
      {
            res = string(s, cursor, string::npos);
            cursor = s.size();
      } else {
            res = string(s, cursor, brk - cursor);
            cursor = brk;
            while (cursor < s.size() && isspace(s[cursor]))
                  cursor++;
      }
      return res;
}

/*
string CommandlineParser::WordWrapper::get(unsigned int width) throw ()
{
      if (i >= s.size())
            return "";
      
      int k = width;
      while (k > 0 && k + i < s.size() && !isspace(s[i + k]))
            k--;
      if (k == 0)
            k = width;

      string res(s, i, k);
      i += k;
      return res;
}
*/

int CommandlineParser::option::intVal() const throw ()
{
      return atoi(_value.c_str());
}

const CommandlineParser::option& CommandlineParser::get(const string& name) const throw ()
{
      map<string, int>::const_iterator i = byname.find(name);
      if (i == byname.end())
      {
            fprintf(stderr, "Program error: requested info about nonexistant commandline option \"%.*s\".\nAvailable: \n", PFSTR(name));
            for (i = byname.begin(); i != byname.end(); i++)
                  fprintf(stderr, "  \"%.*s\"\n", PFSTR(i->first));
      }
      assert (i != byname.end());
      const option& o = options[i->second];
      return o;
}

void CommandlineParser::add(const string& name, char shortopt, const string& longopt, const
            string& help, const string& valname, bool val_optional) throw ()
{
      options.push_back(option(name, shortopt, longopt, help, valname, val_optional));
      if (shortopt)
            shortopts.insert(pair<char, int>(shortopt, options.size() - 1));
      if (longopt.size())
            longopts.insert(pair<string, int>(longopt, options.size() - 1));
      byname.insert(pair<string, int>(name, options.size() - 1));
}

void CommandlineParser::printHelp() throw ()
{
      char* columns = getenv("COLUMNS");
      int width = columns ? atoi(columns) : 80;
      unsigned int summax = 0;
      vector<string> summaries;

      // Print the first usage line
      fprintf(stderr, "Usage: %.*s %.*s\n",
                  PFSTR(argv0), PFSTR(cmdline_summary));
      
      // Prepare switch summaries and compute their maximum width
      for (unsigned int i = 0; i < options.size(); i++)
      {
            string s;
            if (options[i]._shortopt)
                  s += string("-") + options[i]._shortopt;
            if (options[i]._longopt.size())
            {
                  if (s.size())
                        s += ", ";
                  s += "--" + options[i]._longopt;
            }
            if (options[i]._valname.size())
            {
                  string vname = options[i]._val_optional ?
                                          "[" + options[i]._valname + "]"
                                          : options[i]._valname;
                  if (options[i]._longopt.size())
                        s += "=" + vname;
                  else
                        s += " " + vname;
            }
            if (s.size() > summax)
                  summax = s.size();
            summaries.push_back(s);
      }

      // Print the help
      for (unsigned int i = 0; i < options.size(); i++)
      {
            WordWrapper ww(options[i]._help);

            string h = ww.get(width - summax - 4);
            fprintf(stderr, "  %-*.*s  %.*s\n",
                              summax, summax, summaries[i].c_str(), PFSTR(h));

            while (ww.hasData())
            {
                  string h = ww.get(width - summax - 4);
                  fprintf(stderr, "  %-*.*s  %.*s\n",
                              summax, summax, "", PFSTR(h));
            }
      }

      fputc('\n', stderr);

      fprintf(stderr, "%.*s\n", PFSTR(description));
      /*
      WordWrapper ww(description);
      while (ww.hasData())
      {
            string h = ww.get(width);
            fprintf(stderr, "%.*s\n", PFSTR(h));
      }
      */
}

bool CommandlineParser::parseLongOption(const string& name, const string& value)
      throw ()
{
      // Have argument
      map<string, int>::iterator o = longopts.find(name);
      if (o == longopts.end())
      {
            fprintf(stderr, "Unknown option name: %.*s\n", PFSTR(name));
            return false;
      }
      options[o->second]._defined = true;
      
      if (options[o->second]._valname.size())
      {
            if (value.size())
                  options[o->second]._value = value;
            else if (!options[o->second]._val_optional)
            {
                  fprintf(stderr, "Option %.*s requires the %.*s argument\n",
                              PFSTR(name), PFSTR(options[o->second]._valname));
                  return false;
            }
      }
      return true;
}

bool CommandlineParser::parseShortOption(char opt, const string& value)
      throw ()
{
      // Have argument
      map<char, int>::iterator o = shortopts.find(opt);
      if (o == shortopts.end())
      {
            fprintf(stderr, "Unknown option: `%c'\n", opt);
            return false;
      }
      options[o->second]._defined = true;

      if (options[o->second]._valname.size())
      {
            if (value.size())
                  options[o->second]._value = value;
            else if (!options[o->second]._val_optional)
            {
                  fprintf(stderr, "Option `%c' requires the %.*s argument\n",
                              opt, PFSTR(options[o->second]._valname));
                  return false;
            }
      }
      return true;
}

bool CommandlineParser::parse(int& argc, const char**& argv) throw ()
{
      int nargc = 1;
      bool success = true;
      bool parses_opts = true;

      for (int i = 1; i < argc; i++)
      {
            if (parses_opts && argv[i][0] == '-' && argv[i][1] != 0)
            {
                  if (argv[i][1] == '-')
                  {
                        if (argv[i][2] == 0)
                              // -- option terminator
                              parses_opts = false;
                        else
                        {
                              // Long option
                              string optName = string(argv[i], 2, string::npos);
                              unsigned int eqsign = optName.find('=');
                              if (eqsign == string::npos)
                              {
                                    // No argument
                                    if (!parseLongOption(optName))
                                          success = false;
                              } else {
                                    // With argument
                                    if (!parseLongOption(optName.substr(0, eqsign),
                                                      optName.substr(eqsign + 1)))
                                          success = false;
                              }
                        }
                  }
                  else
                  {
                        // Short option(s)
                        if (argv[i][2] == 0)
                        {
                              // Unpacked short option, might have an argument
                              char opt = argv[i][1];
                              map<char, int>::iterator o = shortopts.find(opt);
                              if (o == shortopts.end())
                              {
                                    fprintf(stderr, "Unknown option: `%c'\n", opt);
                                    return false;
                              }
                              if (options[o->second]._valname.size())
                              {
                                    // Accepts arguments
                                    if (argv[i + 1] && argv[i + 1][0] != '-')
                                    {
                                          // Is followed by an argument
                                          i++;
                                          if (!parseShortOption(opt, argv[i]))
                                                success = false;
                                    } else
                                          // Is not followed by an argument
                                          if (!parseShortOption(opt))
                                                success = false;
                              } else
                                    // Does not accept arguments
                                    if (!parseShortOption(opt))
                                          success = false;
                        } else {
                              // Packed short options
                              for (const char* s = argv[i] + 1; *s; s++)
                                    if (!parseShortOption(*s))
                                          success = false;
                        }
                  }
            }
            else
            {
                  // Not a switch: keep it in the new argv
                  if (i != nargc)
                        argv[nargc] = argv[i];
                  nargc++;
            }     
      }
      argc = nargc;
      argv[nargc] = 0;
      return success;
}

// vim:set ts=4 sw=4:

Generated by  Doxygen 1.6.0   Back to index