CommandOptionParser.cpp

Go to the documentation of this file.
00001 #pragma ident "$Id: CommandOptionParser.cpp 2390 2010-04-14 17:34:03Z architest $"
00002 
00003 
00004 
00005 //============================================================================
00006 //
00007 //  This file is part of GPSTk, the GPS Toolkit.
00008 //
00009 //  The GPSTk is free software; you can redistribute it and/or modify
00010 //  it under the terms of the GNU Lesser General Public License as published
00011 //  by the Free Software Foundation; either version 2.1 of the License, or
00012 //  any later version.
00013 //
00014 //  The GPSTk is distributed in the hope that it will be useful,
00015 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017 //  GNU Lesser General Public License for more details.
00018 //
00019 //  You should have received a copy of the GNU Lesser General Public
00020 //  License along with GPSTk; if not, write to the Free Software Foundation,
00021 //  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 //  
00023 //  Copyright 2004, The University of Texas at Austin
00024 //
00025 //============================================================================
00026 
00027 //============================================================================
00028 //
00029 //This software developed by Applied Research Laboratories at the University of
00030 //Texas at Austin, under contract to an agency or agencies within the U.S. 
00031 //Department of Defense. The U.S. Government retains all rights to use,
00032 //duplicate, distribute, disclose, or release this software. 
00033 //
00034 //Pursuant to DoD Directive 523024 
00035 //
00036 // DISTRIBUTION STATEMENT A: This software has been approved for public 
00037 //                           release, distribution is unlimited.
00038 //
00039 //=============================================================================
00040 
00041 
00042 
00043 
00044 
00045 
00051 #include <cstring>
00052 #include "CommandOptionParser.hpp"
00053 
00054 #include "StringUtils.hpp"
00055 
00056 using namespace std;
00057 using namespace gpstk::StringUtils;
00058 
00059 #ifdef _MSC_VER
00060    #if ( _MSC_VER < 1600 )
00061    #define min(VAL1, VAL2) _cpp_min(VAL1, VAL2)
00062    #define max(VAL1, VAL2) _cpp_max(VAL1, VAL2)
00063    #endif
00064 #endif
00065 
00066 namespace gpstk
00067 {
00068 
00069       // parses the command line input
00070    void
00071    CommandOptionParser::parseOptions(int argc,
00072                                      char* argv[])
00073    {
00074          // this maps the index of optionVec to the command line options
00075       CommandOptionMap com;
00076       
00077          // keep track of the order of command options
00078       unsigned int order = 0;
00079 
00080          // get the program name.  If there's a / in it, take the part
00081          // after the last / as the program name
00082       progName = string(argv[0]);
00083       string::size_type slashPos = progName.rfind('/');
00084       if (slashPos != string::npos)
00085          progName = progName.substr(slashPos + 1);
00086 
00087       string shortOptString;
00088       struct option* optArray = NULL;
00089       unsigned long optArraySize = 0;
00090 
00091       CommandOption *trailing = NULL;
00092 
00093          // build the getopt and getopt_long inputs
00094       CommandOptionVec::size_type index;
00095       for(index = 0; index < optionVec.size(); index++)
00096       {
00097             // add short options
00098          switch (optionVec[index]->optType)
00099          {
00100             case CommandOption::trailingType:
00101                if (trailing)
00102                   errorStrings.push_back("More than one trailing argument"
00103                                          " object used (programming error");
00104                else
00105                   trailing = optionVec[index];
00106                break;
00107             case CommandOption::stdType:
00108                if (optionVec[index]->shortOpt != 0)
00109                {
00110                   shortOptString += optionVec[index]->toGetoptShortOption();
00111                   com[string(1,optionVec[index]->shortOpt)] = optionVec[index];
00112                }
00113 
00114                   // add long options
00115                if (!optionVec[index]->longOpt.empty())
00116                {
00117                   resizeOptionArray(optArray, optArraySize);
00118                   optArray[optArraySize - 1] = 
00119                      optionVec[index]->toGetoptLongOption();
00120                   com[optionVec[index]->longOpt] = optionVec[index];
00121                }
00122 
00123                   // keep track of whether or not there are required or
00124                   // optional arguments
00125                if (optionVec[index]->required)
00126                   hasRequiredArguments = true;
00127                else
00128                   hasOptionalArguments = true;
00129                break;
00130             default:
00131                   // do nothing
00132                break;
00133          }
00134       }
00135 
00136          // add the getopt_long terminator value
00137       resizeOptionArray(optArray, optArraySize);
00138       struct option lastOption = {0,0,0,0};
00139       optArray[optArraySize - 1] = lastOption;
00140 
00141          // use '+' to make getopt not mangle the inputs (if i remember right)
00142       shortOptString.insert((string::size_type)0, (string::size_type)1, '+');
00143 
00144       int cha;
00145       int optionIndex;
00146 
00147          // disable internal error messages
00148       opterr = 0;
00149 
00150       while (optind < argc)
00151       {
00152          if ((cha = getopt_long(argc, argv, shortOptString.c_str(),
00153                                 optArray, &optionIndex)) == -1)
00154          {
00155             if (!trailing)
00156                errorStrings.push_back("Excess arguments");
00157             break;
00158          }
00159 
00160          order++;
00161 
00162             // Solaris uses '?' for all getopt errors.  Linux uses '?'
00163             // for unknown options and ':' for options that require
00164             // arguments but don't have then. That's why the error
00165             // message is "option error" cause we can't differentiate
00166             // what the REAL error is...
00167          if ((cha == '?') || (cha == ':'))
00168          {
00169                // get the argument that had the error and write an
00170                // error string
00171             string errorArg;
00172                // for a character option error
00173             if (optopt != 0)
00174                errorArg = string(1, (char)optopt);
00175                // for a getopt_long error
00176             else
00177                errorArg = argv[optind - 1];
00178             errorStrings.push_back(string("Option error: " + errorArg));
00179          }
00180             // otherwise this is probably a found option
00181          else
00182          {
00183             string thisOption;
00184 
00185                // determine whether it found the short or long version
00186             if (cha != 0)
00187                thisOption = string(1,(char)cha);
00188             else
00189                thisOption = string(optArray[optionIndex].name);
00190                
00191                // try to find the option in our option map
00192             map<string, CommandOption*>::iterator itr = com.find(thisOption);
00193 
00194             if (itr != com.end())
00195             {
00196                CommandOption* pickedOption = (*itr).second;
00197                   // if there is an value for this option...
00198                if (optarg)
00199                {
00200                   if (pickedOption->optFlag == CommandOption::noArgument)
00201                   {
00202                      errorStrings.push_back(string("Option ") +
00203                                             thisOption +
00204                                             string(" has an argument but it shouldn't."));
00205                   }
00206                      // add this argument to the picked option and
00207                      // increment the count
00208                   else if (pickedOption->optFlag == CommandOption::hasArgument)
00209                   {
00210                      pickedOption->value.push_back(string(optarg));
00211                      pickedOption->count++;
00212                      pickedOption->order = order;
00213                   }
00214 
00215                }
00216                   // no value for option...
00217                else
00218                {
00219                   if (pickedOption->optFlag == CommandOption::hasArgument)
00220                   {
00221                      errorStrings.push_back(string("Option ") +
00222                                             thisOption +
00223                                             string(" has no argument when it should."));
00224                   }
00225                      // increment the picked option's count
00226                   else if (pickedOption->optFlag == CommandOption::noArgument)
00227                   {
00228                      pickedOption->count++;
00229                      pickedOption->order = order;
00230                   }
00231                }
00232             } // itr != end()
00233             else
00234             {
00235                errorStrings.push_back("Unknown option error");               
00236             }
00237          } // else cha ==
00238       }  // getopt_long
00239 
00240          // check for remaining arguments
00241       if (optind < argc)
00242       {
00243          if (trailing)
00244          {
00245             int i;
00246             for(i = optind; i < argc; i++)
00247             {
00248                trailing->value.push_back(string(argv[i]));
00249                trailing->count++;
00250             }
00251          }
00252             // the case where trailing==null is handled above
00253       }
00254 
00255       for(index = 0; index < optionVec.size(); index++)
00256       {
00257          string retVal = optionVec[index]->checkArguments();
00258          if (!retVal.empty())
00259             errorStrings.push_back(retVal);
00260 
00261             // check max count
00262          if (optionVec[index]->maxCount != 0)
00263          {
00264             if (optionVec[index]->count > optionVec[index]->maxCount)
00265             {
00266                string errstr("Option ");
00267                errstr += optionVec[index]->getOptionString();
00268                errstr += string(" appeared more times than allowed.");
00269                errorStrings.push_back(errstr);
00270             }
00271          }
00272       }
00273    
00274       delete [] optArray;
00275    }
00276 
00277    ostream& CommandOptionParser::dumpErrors(ostream& out)
00278    {
00279       vector<string>::size_type index;
00280       for(index = 0; index < errorStrings.size(); index++)
00281          out << errorStrings[index] << endl;
00282       return out;
00283    }
00284 
00285       // prints the required arguments first (if any) then the optional
00286       // ones (if any)
00287    ostream& CommandOptionParser::displayUsage(ostream& out, bool doPretty)
00288    {
00289       CommandOptionVec::size_type index;
00290       CommandOption *trailing = NULL;
00291 
00292       char *colch = getenv("COLUMNS");
00293       int columns = 80;
00294       unsigned maxlen = 0;
00295       if (colch)
00296       {
00297          string colStr(colch);
00298          columns = asInt(colStr);
00299       }
00300 
00301          // find the trailing argument if any, and max option string length
00302       for (index = 0; index < optionVec.size(); index++)
00303       {
00304          if (optionVec[index]->optType == CommandOption::trailingType)
00305             trailing = optionVec[index];
00306          else if (optionVec[index]->optType == CommandOption::stdType)
00307             maxlen = std::max(maxlen,
00308                unsigned(optionVec[index]->getFullOptionString().length()));
00309       }
00310 
00311       out << "Usage: " << progName;
00312       if (hasRequiredArguments || hasOptionalArguments)
00313          out << " [OPTION] ...";
00314       if (trailing)
00315          out << " " << trailing->description;
00316       out << endl
00317           << (doPretty ? prettyPrint(text,"\n","","",columns) : text);
00318 // << endl
00319 //          << endl 
00320 //          << "Command options:" << endl;
00321       
00322       for(int required = 1; required >= 0; required--)
00323       {
00324          if (required==1 && hasRequiredArguments)
00325             out << endl << "Required arguments:" << endl;
00326          else if (required==0 && hasOptionalArguments)
00327             out << endl << "Optional arguments:" << endl;
00328 
00329          for(index = 0; index < optionVec.size(); index++)
00330          {
00331             if ((optionVec[index]->required == (required==1)) &&
00332                 (optionVec[index]->optType == CommandOption::stdType))
00333             {
00334                string optstr(optionVec[index]->getFullOptionString());
00335                string desc(optionVec[index]->description);
00336                string indent(maxlen, ' ');
00337 
00338                if(doPretty) {
00339                   leftJustify(optstr, maxlen);
00340                   prettyPrint(desc, "\n", indent, optstr, columns);
00341                }
00342                out << desc;
00343                if(!doPretty) out << endl;
00344             }
00345          }
00346       }
00347 
00348       return out;
00349    }
00350 
00351       // resizes the array for getopt_long
00352    void CommandOptionParser::resizeOptionArray(struct option *&oldArray,
00353                                                unsigned long& oldSize)
00354    {
00355       struct option* newArray = new struct option[1 + oldSize];
00356       std::memcpy(newArray, oldArray, oldSize * sizeof(struct option));
00357       delete [] oldArray;
00358       oldArray = newArray;
00359       newArray = NULL;
00360       oldSize += 1;
00361    }
00362 
00363 }  // end namespace gpstk

Generated on Thu Feb 9 03:30:55 2012 for GPS ToolKit Software Library by  doxygen 1.3.9.1