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