OW_CmdLineParser.cpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002 * Copyright (C) 2004 Vintela, Inc. All rights reserved.
00003 *
00004 * Redistribution and use in source and binary forms, with or without
00005 * modification, are permitted provided that the following conditions are met:
00006 *
00007 *  - Redistributions of source code must retain the above copyright notice,
00008 *    this list of conditions and the following disclaimer.
00009 *
00010 *  - Redistributions in binary form must reproduce the above copyright notice,
00011 *    this list of conditions and the following disclaimer in the documentation
00012 *    and/or other materials provided with the distribution.
00013 *
00014 *  - Neither the name of Vintela, Inc. nor the names of its
00015 *    contributors may be used to endorse or promote products derived from this
00016 *    software without specific prior written permission.
00017 *
00018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
00019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00020 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00021 * ARE DISCLAIMED. IN NO EVENT SHALL Vintela, Inc. OR THE CONTRIBUTORS
00022 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00023 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00024 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00025 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00026 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00027 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00028 * POSSIBILITY OF SUCH DAMAGE.
00029 *******************************************************************************/
00030 
00035 #include "OW_config.h"
00036 #include "OW_CmdLineParser.hpp"
00037 #include "OW_Array.hpp"
00038 #include "OW_ExceptionIds.hpp"
00039 #include "OW_StringBuffer.hpp"
00040 #include "OW_Assertion.hpp"
00041 
00042 #include <algorithm>
00043 
00044 
00045 namespace OW_NAMESPACE
00046 {
00047 
00048 OW_DEFINE_EXCEPTION_WITH_ID(CmdLineParser)
00049 
00050 namespace
00051 {
00053    struct longOptIs
00054    {
00055       longOptIs(const String& longOpt) : m_longOpt(longOpt) {}
00056 
00057       bool operator()(const CmdLineParser::Option& x) const
00058       {
00059          if (x.longopt != 0)
00060          {
00061             return m_longOpt.startsWith(x.longopt);
00062          }
00063          return false;
00064       }
00065 
00066       String m_longOpt;
00067    };
00068 
00070    struct shortOptIs
00071    {
00072       shortOptIs(char shortOpt) : m_shortOpt(shortOpt) {}
00073 
00074       bool operator()(const CmdLineParser::Option& x) const
00075       {
00076          return m_shortOpt == x.shortopt;
00077       }
00078 
00079       char m_shortOpt;
00080    };
00081 
00082 }
00083 
00085 CmdLineParser::CmdLineParser(int argc, char const* const* const argv_, const Option* options, EAllowNonOptionArgsFlag allowNonOptionArgs)
00086 {
00087    OW_ASSERT(argc > 0); // have to get at least the name
00088    OW_ASSERT(argv_ != 0);
00089    OW_ASSERT(options != 0);
00090    char const* const* argv = argv_;
00091    char const* const* argvEnd = argv + argc;
00092 
00093    // m_options is an array terminated by a final entry that has a '\0' shortopt && 0 longopt.
00094    const Option* optionsEnd(options);
00095    while (optionsEnd->shortopt != '\0' || optionsEnd->longopt != 0)
00096    {
00097       ++optionsEnd;
00098    }
00099 
00100    // skip the first argv, which is the program name and loop through the rest
00101    ++argv;
00102    while (argv != argvEnd)
00103    {
00104       OW_ASSERT(*argv != 0);
00105       String arg(*argv);
00106 
00107       // look for an option
00108       if ((arg.length() >= 2) && (arg[0] == '-'))
00109       {
00110          const Option* theOpt(0);
00111          bool longOpt = false;
00112          if (arg[1] == '-')
00113          {
00114             // long option
00115             longOpt = true;
00116             arg = arg.substring(2); // erase the --
00117             theOpt = std::find_if (options,  optionsEnd, longOptIs(arg));
00118          }
00119          else // short option
00120          {
00121             longOpt = false;
00122             arg = arg.substring(1); // erase the -
00123             theOpt = std::find_if (options,  optionsEnd, shortOptIs(arg[0]));
00124          }
00125 
00126          if (theOpt == optionsEnd)
00127          {
00128             OW_THROW_ERR(CmdLineParserException, arg.c_str(), E_INVALID_OPTION);
00129          }
00130 
00131          if (theOpt->argtype == E_NO_ARG)
00132          {
00133             m_parsedOptions[theOpt->id]; // if one is already there, don't modify it, else insert a new one
00134             ++argv;
00135             continue;
00136          }
00137          // now see if we can get the value
00138          String val;
00139          if ((theOpt->argtype == E_OPTIONAL_ARG) && (theOpt->defaultValue != 0))
00140          {
00141             val = theOpt->defaultValue;
00142          }
00143          
00144          const char* p = ::strchr(arg.c_str(), '=');
00145          if (p)
00146          {
00147             // get everything after the =
00148             val = String(p+1);
00149          }
00150          else
00151          {
00152             // a short option can have the value together with it (i.e. -I/opt/vintela/include)
00153             if (longOpt == false && arg.length() > 1)
00154             {
00155                val = arg.substring(1);
00156             }
00157             // look at the next arg
00158             else if (argv+1 != argvEnd)
00159             {
00160                if (**(argv+1) != '-')
00161                {
00162                   val = *(argv+1);
00163                   ++argv;
00164                }
00165             }
00166          }
00167 
00168          // make sure we got an arg if one is required
00169          if (theOpt->argtype == E_REQUIRED_ARG && val.empty())
00170          {
00171             OW_THROW_ERR(CmdLineParserException, arg.c_str(), E_MISSING_ARGUMENT);
00172          }
00173 
00174          m_parsedOptions[theOpt->id].push_back(val);
00175       }
00176       else
00177       {
00178          if (allowNonOptionArgs == E_NON_OPTION_ARGS_INVALID)
00179          {
00180             OW_THROW_ERR(CmdLineParserException, arg.c_str(), E_INVALID_NON_OPTION_ARG);
00181          }
00182          else
00183          {
00184             m_nonOptionArgs.push_back(arg);
00185          }
00186       }
00187       ++argv;
00188    }
00189 }
00190 
00192 // static
00193 String
00194 CmdLineParser::getUsage(const Option* options, unsigned int maxColumns)
00195 {
00196 // looks like this:
00197 //     "Options:\n"
00198 //     "  -1, --one                 first description\n"
00199 //     "  -2, --two [arg]           second description (default is optional)\n"
00200 //     "  -3, --three <arg>         third description\n"
00201 
00202    const unsigned int NUM_OPTION_COLUMNS = 28;
00203    StringBuffer usage("Options:\n");
00204 
00205    // m_options is an array terminated by a final entry that has a '\0' shortopt && 0 longOpt.
00206    for (const Option* curOption = options; curOption->shortopt != '\0' || curOption->longopt != 0; ++curOption)
00207    {
00208       StringBuffer curLine;
00209       curLine += "  ";
00210       if (curOption->shortopt != '\0')
00211       {
00212          curLine += '-';
00213          curLine += curOption->shortopt;
00214          if (curOption->longopt != 0)
00215          {
00216             curLine += ", ";
00217          }
00218       }
00219       if (curOption->longopt != 0)
00220       {
00221          curLine += "--";
00222          curLine += curOption->longopt;
00223       }
00224 
00225       if (curOption->argtype == E_REQUIRED_ARG)
00226       {
00227          curLine += " <arg>";
00228       }
00229       else if (curOption->argtype == E_OPTIONAL_ARG)
00230       {
00231          curLine += " [arg]";
00232       }
00233 
00234       size_t bufferlen = (curLine.length() >= NUM_OPTION_COLUMNS-1) ? 1 : (NUM_OPTION_COLUMNS - curLine.length());
00235       for (size_t i = 0; i < bufferlen; ++i)
00236       {
00237          curLine += ' ';
00238       }
00239 
00240       if (curOption->description != 0)
00241       {
00242          curLine += curOption->description;
00243       }
00244 
00245       if (curOption->defaultValue != 0)
00246       {
00247          curLine += " (default is ";
00248          curLine += curOption->defaultValue;
00249          curLine += ')';
00250       }
00251 
00252       // now if curLine is too long or contains newlines, we need to wrap it.
00253       while (curLine.length() > maxColumns || curLine.toString().indexOf('\n') != String::npos)
00254       {
00255          String curLineStr(curLine.toString());
00256          // first we look for a \n to cut at
00257          size_t newlineIdx = curLineStr.indexOf('\n');
00258 
00259          // next look for the last space to cut at
00260          size_t lastSpaceIdx = curLineStr.lastIndexOf(' ', maxColumns);
00261 
00262          size_t cutIdx = 0;
00263          size_t nextLineBeginIdx = 0;
00264          if (newlineIdx <= maxColumns)
00265          {
00266             cutIdx = newlineIdx;
00267             nextLineBeginIdx = newlineIdx + 1; // skip the newline
00268          }
00269          else if (lastSpaceIdx > NUM_OPTION_COLUMNS)
00270          {
00271             cutIdx = lastSpaceIdx;
00272             nextLineBeginIdx = lastSpaceIdx + 1; // skip the space
00273          }
00274          else
00275          {
00276             // no space to cut it, just cut it in the middle of a word
00277             cutIdx = maxColumns;
00278             nextLineBeginIdx = maxColumns;
00279          }
00280 
00281          // found a place to cut, so do it.
00282          usage += curLineStr.substring(0, cutIdx);
00283          usage += '\n';
00284 
00285          // cut out the line from curLine
00286          StringBuffer spaces;
00287          for (size_t i = 0; i < NUM_OPTION_COLUMNS; ++i)
00288          {
00289             spaces += ' ';
00290          }
00291          curLine = spaces.releaseString() + curLineStr.substring(nextLineBeginIdx);
00292       }
00293 
00294       curLine += '\n';
00295       usage += curLine;
00296    }
00297    return usage.releaseString();
00298 }
00299 
00301 String
00302 CmdLineParser::getOptionValue(int id, const char* defaultValue) const
00303 {
00304    optionsMap_t::const_iterator ci = m_parsedOptions.find(id);
00305    if (ci != m_parsedOptions.end() && ci->second.size() > 0)
00306    {
00307       // grab the last one
00308       return ci->second[ci->second.size()-1];
00309    }
00310    return defaultValue;
00311 }
00312 
00314 String
00315 CmdLineParser::mustGetOptionValue(int id, const char* exceptionMessage) const
00316 {
00317    optionsMap_t::const_iterator ci = m_parsedOptions.find(id);
00318    if (ci != m_parsedOptions.end() && ci->second.size() > 0)
00319    {
00320       // grab the last one
00321       return ci->second[ci->second.size()-1];
00322    }
00323    OW_THROW_ERR(CmdLineParserException, exceptionMessage, E_MISSING_OPTION);
00324 }
00325 
00327 StringArray
00328 CmdLineParser::getOptionValueList(int id) const
00329 {
00330    StringArray rval;
00331    optionsMap_t::const_iterator ci = m_parsedOptions.find(id);
00332    if (ci != m_parsedOptions.end() && ci->second.size() > 0)
00333    {
00334       rval = ci->second;
00335    }
00336    return rval;
00337 }
00338 
00340 StringArray
00341 CmdLineParser::mustGetOptionValueList(int id, const char* exceptionMessage) const
00342 {
00343    optionsMap_t::const_iterator ci = m_parsedOptions.find(id);
00344    if (ci != m_parsedOptions.end() && ci->second.size() > 0)
00345    {
00346       return ci->second;
00347    }
00348    OW_THROW_ERR(CmdLineParserException, exceptionMessage, E_MISSING_OPTION);
00349 }
00350 
00352 bool
00353 CmdLineParser::isSet(int id) const
00354 {
00355    return m_parsedOptions.count(id) > 0;
00356 }
00357 
00359 size_t
00360 CmdLineParser::getNonOptionCount () const
00361 {
00362    return m_nonOptionArgs.size();
00363 }
00364 
00366 String
00367 CmdLineParser::getNonOptionArg(size_t n) const
00368 {
00369    return m_nonOptionArgs[n];
00370 }
00371 
00373 StringArray
00374 CmdLineParser::getNonOptionArgs() const
00375 {
00376    return m_nonOptionArgs;
00377 }
00378 
00379 
00380 
00381 } // end namespace OW_NAMESPACE
00382 
00383 
00384 

Generated on Thu Feb 9 08:47:56 2006 for openwbem by  doxygen 1.4.6