00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00038 #include "blocxx/BLOCXX_config.h"
00039 #include "blocxx/CmdLineParser.hpp"
00040 #include "blocxx/Array.hpp"
00041 #include "blocxx/ExceptionIds.hpp"
00042 #include "blocxx/StringBuffer.hpp"
00043 #include "blocxx/Assertion.hpp"
00044
00045 #include <algorithm>
00046
00047
00048 namespace BLOCXX_NAMESPACE
00049 {
00050
00051 BLOCXX_DEFINE_EXCEPTION_WITH_ID(CmdLineParser)
00052
00053 namespace
00054 {
00056 struct longOptIs
00057 {
00058 longOptIs(const String& longOpt) : m_longOpt(longOpt) {}
00059
00060 bool operator()(const CmdLineParser::Option& x) const
00061 {
00062 if (x.longopt != 0)
00063 {
00064 return m_longOpt == x.longopt;
00065 }
00066 return false;
00067 }
00068
00069 String m_longOpt;
00070 };
00071
00073 struct shortOptIs
00074 {
00075 shortOptIs(char shortOpt) : m_shortOpt(shortOpt) {}
00076
00077 bool operator()(const CmdLineParser::Option& x) const
00078 {
00079 return m_shortOpt == x.shortopt;
00080 }
00081
00082 char m_shortOpt;
00083 };
00084
00085 }
00086
00088 CmdLineParser::CmdLineParser(int argc, char const* const* const argv_, const Option* options, EAllowNonOptionArgsFlag allowNonOptionArgs)
00089 {
00090 BLOCXX_ASSERT(argc > 0);
00091 BLOCXX_ASSERT(argv_ != 0);
00092 BLOCXX_ASSERT(options != 0);
00093 char const* const* argv = argv_;
00094 char const* const* argvEnd = argv + argc;
00095
00096
00097 const Option* optionsEnd(options);
00098 while (optionsEnd->shortopt != '\0' || optionsEnd->longopt != 0)
00099 {
00100 ++optionsEnd;
00101 }
00102
00103
00104 ++argv;
00105 while (argv != argvEnd)
00106 {
00107 BLOCXX_ASSERT(*argv != 0);
00108 String arg(*argv);
00109
00110
00111 if ((arg.length() >= 2) && (arg[0] == '-'))
00112 {
00113 const Option* theOpt(0);
00114 bool longOpt = false;
00115 if (arg[1] == '-')
00116 {
00117
00118 longOpt = true;
00119 arg = arg.substring(2);
00120
00121 size_t idx = arg.indexOf('=');
00122 String argNoVal;
00123 if (idx != String::npos)
00124 {
00125 argNoVal = arg.substring(0, idx);
00126 }
00127 else
00128 {
00129 argNoVal = arg;
00130 }
00131 theOpt = std::find_if (options, optionsEnd, longOptIs(argNoVal));
00132 }
00133 else
00134 {
00135 longOpt = false;
00136 arg = arg.substring(1);
00137 theOpt = std::find_if (options, optionsEnd, shortOptIs(arg[0]));
00138 }
00139
00140 if (theOpt == optionsEnd)
00141 {
00142 BLOCXX_THROW_ERR(CmdLineParserException, arg.c_str(), E_INVALID_OPTION);
00143 }
00144
00145 if (theOpt->argtype == E_NO_ARG)
00146 {
00147 m_parsedOptions[theOpt->id];
00148 ++argv;
00149 continue;
00150 }
00151
00152 String val;
00153 if ((theOpt->argtype == E_OPTIONAL_ARG) && (theOpt->defaultValue != 0))
00154 {
00155 val = theOpt->defaultValue;
00156 }
00157
00158 const char* p = ::strchr(arg.c_str(), '=');
00159 if (p)
00160 {
00161
00162 val = String(p+1);
00163 }
00164 else
00165 {
00166
00167 if (longOpt == false && arg.length() > 1)
00168 {
00169 val = arg.substring(1);
00170 }
00171
00172 else if (argv+1 != argvEnd)
00173 {
00174
00175 if ( **(argv+1) != '-' || (**(argv+1) == '-' && String(*(argv+1)).length() == 1) )
00176 {
00177 val = *(argv+1);
00178 ++argv;
00179 }
00180 }
00181 }
00182
00183
00184 if (theOpt->argtype == E_REQUIRED_ARG && val.empty())
00185 {
00186 BLOCXX_THROW_ERR(CmdLineParserException, arg.c_str(), E_MISSING_ARGUMENT);
00187 }
00188
00189 m_parsedOptions[theOpt->id].push_back(val);
00190 }
00191 else
00192 {
00193 if (allowNonOptionArgs == E_NON_OPTION_ARGS_INVALID)
00194 {
00195 BLOCXX_THROW_ERR(CmdLineParserException, arg.c_str(), E_INVALID_NON_OPTION_ARG);
00196 }
00197 else
00198 {
00199 m_nonOptionArgs.push_back(arg);
00200 }
00201 }
00202 ++argv;
00203 }
00204 }
00205
00207
00208 String
00209 CmdLineParser::getUsage(const Option* options, unsigned int maxColumns)
00210 {
00211
00212
00213
00214
00215
00216
00217 const unsigned int NUM_OPTION_COLUMNS = 28;
00218 StringBuffer usage("Options:\n");
00219
00220
00221 for (const Option* curOption = options; curOption->shortopt != '\0' || curOption->longopt != 0; ++curOption)
00222 {
00223 StringBuffer curLine;
00224 curLine += " ";
00225 if (curOption->shortopt != '\0')
00226 {
00227 curLine += '-';
00228 curLine += curOption->shortopt;
00229 if (curOption->longopt != 0)
00230 {
00231 curLine += ", ";
00232 }
00233 }
00234 if (curOption->longopt != 0)
00235 {
00236 curLine += "--";
00237 curLine += curOption->longopt;
00238 }
00239
00240 if (curOption->argtype == E_REQUIRED_ARG)
00241 {
00242 curLine += " <arg>";
00243 }
00244 else if (curOption->argtype == E_OPTIONAL_ARG)
00245 {
00246 curLine += " [arg]";
00247 }
00248
00249 size_t bufferlen = (curLine.length() >= NUM_OPTION_COLUMNS-1) ? 1 : (NUM_OPTION_COLUMNS - curLine.length());
00250 for (size_t i = 0; i < bufferlen; ++i)
00251 {
00252 curLine += ' ';
00253 }
00254
00255 if (curOption->description != 0)
00256 {
00257 curLine += curOption->description;
00258 }
00259
00260 if (curOption->defaultValue != 0)
00261 {
00262 curLine += " (default is ";
00263 curLine += curOption->defaultValue;
00264 curLine += ')';
00265 }
00266
00267
00268 while (curLine.length() > maxColumns || curLine.toString().indexOf('\n') != String::npos)
00269 {
00270 String curLineStr(curLine.toString());
00271
00272 size_t newlineIdx = curLineStr.indexOf('\n');
00273
00274
00275 size_t lastSpaceIdx = curLineStr.lastIndexOf(' ', maxColumns);
00276
00277 size_t cutIdx = 0;
00278 size_t nextLineBeginIdx = 0;
00279 if (newlineIdx <= maxColumns)
00280 {
00281 cutIdx = newlineIdx;
00282 nextLineBeginIdx = newlineIdx + 1;
00283 }
00284 else if (lastSpaceIdx > NUM_OPTION_COLUMNS)
00285 {
00286 cutIdx = lastSpaceIdx;
00287 nextLineBeginIdx = lastSpaceIdx + 1;
00288 }
00289 else
00290 {
00291
00292 cutIdx = maxColumns;
00293 nextLineBeginIdx = maxColumns;
00294 }
00295
00296
00297 usage += curLineStr.substring(0, cutIdx);
00298 usage += '\n';
00299
00300
00301 StringBuffer spaces;
00302 for (size_t i = 0; i < NUM_OPTION_COLUMNS; ++i)
00303 {
00304 spaces += ' ';
00305 }
00306 curLine = spaces.releaseString() + curLineStr.substring(nextLineBeginIdx);
00307 }
00308
00309 curLine += '\n';
00310 usage += curLine;
00311 }
00312 return usage.releaseString();
00313 }
00314
00316 String
00317 CmdLineParser::getOptionValue(int id, const char* defaultValue) const
00318 {
00319 optionsMap_t::const_iterator ci = m_parsedOptions.find(id);
00320 if (ci != m_parsedOptions.end() && ci->second.size() > 0)
00321 {
00322
00323 return ci->second[ci->second.size()-1];
00324 }
00325 return defaultValue;
00326 }
00327
00329 String
00330 CmdLineParser::mustGetOptionValue(int id, const char* exceptionMessage) const
00331 {
00332 optionsMap_t::const_iterator ci = m_parsedOptions.find(id);
00333 if (ci != m_parsedOptions.end() && ci->second.size() > 0)
00334 {
00335
00336 return ci->second[ci->second.size()-1];
00337 }
00338 BLOCXX_THROW_ERR(CmdLineParserException, exceptionMessage, E_MISSING_OPTION);
00339 }
00340
00342 StringArray
00343 CmdLineParser::getOptionValueList(int id) const
00344 {
00345 StringArray rval;
00346 optionsMap_t::const_iterator ci = m_parsedOptions.find(id);
00347 if (ci != m_parsedOptions.end() && ci->second.size() > 0)
00348 {
00349 rval = ci->second;
00350 }
00351 return rval;
00352 }
00353
00355 StringArray
00356 CmdLineParser::mustGetOptionValueList(int id, const char* exceptionMessage) const
00357 {
00358 optionsMap_t::const_iterator ci = m_parsedOptions.find(id);
00359 if (ci != m_parsedOptions.end() && ci->second.size() > 0)
00360 {
00361 return ci->second;
00362 }
00363 BLOCXX_THROW_ERR(CmdLineParserException, exceptionMessage, E_MISSING_OPTION);
00364 }
00365
00367 bool
00368 CmdLineParser::isSet(int id) const
00369 {
00370 return m_parsedOptions.count(id) > 0;
00371 }
00372
00374 size_t
00375 CmdLineParser::getNonOptionCount () const
00376 {
00377 return m_nonOptionArgs.size();
00378 }
00379
00381 String
00382 CmdLineParser::getNonOptionArg(size_t n) const
00383 {
00384 return m_nonOptionArgs[n];
00385 }
00386
00388 StringArray
00389 CmdLineParser::getNonOptionArgs() const
00390 {
00391 return m_nonOptionArgs;
00392 }
00393
00394
00395
00396 }
00397
00398
00399