00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include "xrb_commandlineparser.hpp"
00012
00013 namespace Xrb
00014 {
00015
00016
00017
00018
00019
00020 CommandLineParser::~CommandLineParser () { }
00021
00022 void CommandLineParser::Parse (Sint32 argc, char const *const *argv)
00023 {
00024 ASSERT1(argc >= 1);
00025 ASSERT1(argv != NULL);
00026
00027 try
00028 {
00029 while (++argv, --argc > 0)
00030 {
00031 ASSERT1(*argv != NULL);
00032 char const *arg = *argv;
00033 if (*arg == '-' && arg != std::string("-") && arg != std::string("--"))
00034 {
00035 ++arg;
00036
00037 char const *next_arg = (argc == 1) ? NULL : *(argv + 1);
00038
00039 if (*arg == '-')
00040 {
00041 if (HandleLongNameOption(++arg, next_arg))
00042 ++argv, --argc;
00043 }
00044 else
00045 {
00046 if (HandleShortNameOption(arg, next_arg))
00047 ++argv, --argc;
00048 }
00049 }
00050 else
00051 {
00052 (this->*m_non_option_argument_handler_method)(arg);
00053 }
00054 }
00055 }
00056 catch (std::string const &exception)
00057 {
00058 std::cerr << exception << "\n";
00059 m_parse_succeeded = false;
00060 }
00061 }
00062
00063 void CommandLineParser::PrintHelpMessage (std::ostream &stream) const
00064 {
00065 if (!m_program_description.empty())
00066 stream << m_program_description << "\n\n";
00067
00068 stream << "usage: " << m_executable_path << " " << m_usage_message << "\n";
00069
00070 if (!IsAControlOption(m_option[0]))
00071 stream << "\n";
00072
00073 for (CommandLineOption const *option = m_option,
00074 *option_end = m_option + m_option_count;
00075 option != option_end;
00076 ++option)
00077 {
00078 if (IsAControlOption(*option))
00079 {
00080 if (option->m_description.empty())
00081 stream << std::endl;
00082 else
00083 stream << "\n" << option->m_description << "\n" << std::endl;
00084 }
00085 else
00086 {
00087 ASSERT1(option->m_short_name != '\0' || !option->m_long_name.empty());
00088
00089 if (option->m_short_name == '\0')
00090 stream << " ";
00091 else
00092 stream << "-" << option->m_short_name;
00093
00094 if (option->m_short_name != '\0' && !option->m_long_name.empty())
00095 stream << ",";
00096
00097 if (!option->m_long_name.empty())
00098 stream << "--" << option->m_long_name;
00099
00100 if (option->m_requires_an_argument)
00101 stream << " <argument>";
00102
00103 if (!option->m_description.empty())
00104 stream << "\n" << option->m_description;
00105
00106 stream << std::endl;
00107 }
00108 }
00109 }
00110
00111 bool CommandLineParser::IsAControlOption (CommandLineOption const &option)
00112 {
00113 return option.m_short_name == '\n';
00114 }
00115
00116 bool CommandLineParser::IsAShortNameCollision (CommandLineOption const &option_0, CommandLineOption const &option_1)
00117 {
00118 if (IsAControlOption(option_0) || IsAControlOption(option_1))
00119 return false;
00120
00121 if (option_0.m_short_name == '\0' || option_1.m_short_name == '\0')
00122 return false;
00123
00124 return option_0.m_short_name == option_1.m_short_name;
00125 }
00126
00127 bool CommandLineParser::IsALongNameCollision (CommandLineOption const &option_0, CommandLineOption const &option_1)
00128 {
00129 if (IsAControlOption(option_0) || IsAControlOption(option_1))
00130 return false;
00131
00132 if (option_0.m_long_name.empty() || option_1.m_long_name.empty())
00133 return false;
00134
00135 return option_0.m_long_name == option_1.m_long_name;
00136 }
00137
00138 void CommandLineParser::PerformOptionConsistencyCheck () const
00139 {
00140
00141
00142 for (CommandLineOption const *option = m_option,
00143 *option_end = m_option + m_option_count;
00144 option != option_end;
00145 ++option)
00146 {
00147 if (IsAControlOption(*option))
00148 {
00149 ASSERT0(option->m_long_name.empty() && "must not supply a long name for a control option");
00150 ASSERT0(option->m_handler_method_with_argument == NULL && "must not supply a handler method for a control option");
00151 ASSERT0(option->m_handler_method_without_argument == NULL && "must not supply a handler method for a control option");
00152 }
00153 else
00154 {
00155 if (option->m_short_name != '\0')
00156 ASSERT0(option->m_short_name != ' ' && option->m_short_name != '\t' && option->m_short_name != '\n' && "must not use whitespace in option short names");
00157
00158 if (!option->m_long_name.empty())
00159 ASSERT0(option->m_long_name.find_first_of(" \t\n") == std::string::npos && "must not use whitespace in option long names");
00160
00161 if (option->m_requires_an_argument)
00162 {
00163 ASSERT0(option->m_handler_method_with_argument != NULL && "must specify an argument-accepting handler method for a normal argument-accepting option");
00164 ASSERT0(option->m_handler_method_without_argument == NULL && "must not specify a no-argument handler method for a normal argument-accepting option");
00165 }
00166 else
00167 {
00168 ASSERT0(option->m_handler_method_with_argument == NULL && "must not specify an argument-accepting handler method for a normal no-argument option");
00169 ASSERT0(option->m_handler_method_without_argument != NULL && "must specify a no-argument handler method for a normal no-argument option");
00170 }
00171 }
00172 }
00173
00174
00175 for (CommandLineOption const *option_0 = m_option,
00176 *option_end_0 = m_option + m_option_count;
00177 option_0 != option_end_0;
00178 ++option_0)
00179 {
00180
00181
00182 for (CommandLineOption const *option_1 = option_0 + 1;
00183 option_1 != option_end_0;
00184 ++option_1)
00185 {
00186 ASSERT0(!IsAShortNameCollision(*option_0, *option_1) && "option short-name collision");
00187 ASSERT0(!IsALongNameCollision(*option_0, *option_1) && "option long-name collision");
00188 }
00189 }
00190 }
00191
00192 bool CommandLineParser::HandleShortNameOption (
00193 char const *arg,
00194 char const *const next_arg)
00195 {
00196 ASSERT1(arg != NULL);
00197
00198
00199 if (*arg == '\0')
00200 throw std::string("error: no option letter specified after \"-\"");
00201
00202 CommandLineOption const *option = FindOptionByShortName(*arg);
00203 if (option == NULL)
00204 throw std::string("error: no such option: -") + *arg;
00205
00206 ++arg;
00207
00208 if (*arg != '\0')
00209 {
00210
00211
00212 if (option->m_requires_an_argument)
00213 {
00214 ASSERT1(option->m_handler_method_with_argument != NULL);
00215 ASSERT1(option->m_handler_method_without_argument == NULL);
00216 (this->*(option->m_handler_method_with_argument))(std::string(arg));
00217 }
00218
00219 else
00220 {
00221
00222
00223 while (true)
00224 {
00225
00226 ASSERT1(option->m_handler_method_with_argument == NULL);
00227 ASSERT1(option->m_handler_method_without_argument != NULL);
00228 (this->*(option->m_handler_method_without_argument))();
00229
00230 if (*arg == '\0')
00231 break;
00232
00233 option = FindOptionByShortName(*arg);
00234 if (option == NULL)
00235 throw std::string("error: no such option: -") + *arg;
00236 else if (option->m_requires_an_argument)
00237 throw std::string("error: may not concatenate short options which require an argument (option -") + *arg + ")";
00238
00239 ++arg;
00240 }
00241 }
00242
00243 return false;
00244 }
00245 else
00246 {
00247 if (option->m_requires_an_argument)
00248 {
00249 if (next_arg == NULL)
00250 throw std::string("error: option -") + option->m_short_name + " requires an argument";
00251
00252
00253 ASSERT1(option->m_handler_method_with_argument != NULL);
00254 ASSERT1(option->m_handler_method_without_argument == NULL);
00255 (this->*(option->m_handler_method_with_argument))(std::string(next_arg));
00256
00257 return true;
00258 }
00259 else
00260 {
00261
00262 ASSERT1(option->m_handler_method_with_argument == NULL);
00263 ASSERT1(option->m_handler_method_without_argument != NULL);
00264 (this->*(option->m_handler_method_without_argument))();
00265
00266 return false;
00267 }
00268 }
00269 }
00270
00271 bool CommandLineParser::HandleLongNameOption (
00272 char const *arg,
00273 char const *const next_arg)
00274 {
00275 ASSERT1(arg != NULL);
00276
00277
00278 if (*arg == '\0')
00279 throw std::string("error: no option name specified after \"--\"");
00280
00281 CommandLineOption const *option = FindOptionByLongName(arg);
00282 if (option == NULL)
00283 throw std::string("error: no such option --") + arg;
00284
00285 arg += option->m_long_name.length();
00286
00287 if (*arg != '\0')
00288 {
00289 if (!option->m_requires_an_argument)
00290 throw std::string("error: option --") + option->m_long_name + " does not take an argument";
00291
00292 ASSERT1(*arg == '=');
00293 ++arg;
00294
00295
00296 ASSERT1(option->m_handler_method_with_argument != NULL);
00297 ASSERT1(option->m_handler_method_without_argument == NULL);
00298 (this->*(option->m_handler_method_with_argument))(std::string(arg));
00299
00300 return false;
00301 }
00302 else
00303 {
00304 if (option->m_requires_an_argument)
00305 {
00306 if (next_arg == NULL)
00307 throw std::string("error: option --") + option->m_long_name + " requires an argument";
00308
00309
00310 ASSERT1(option->m_handler_method_with_argument != NULL);
00311 ASSERT1(option->m_handler_method_without_argument == NULL);
00312 (this->*(option->m_handler_method_with_argument))(std::string(next_arg));
00313
00314 return true;
00315 }
00316 else
00317 {
00318
00319 ASSERT1(option->m_handler_method_with_argument == NULL);
00320 ASSERT1(option->m_handler_method_without_argument != NULL);
00321 (this->*(option->m_handler_method_without_argument))();
00322
00323 return false;
00324 }
00325 }
00326 }
00327
00328 CommandLineOption const *CommandLineParser::FindOptionByShortName (char const short_name) const
00329 {
00330 ASSERT1(short_name != '\0');
00331
00332 for (CommandLineOption const *option = m_option,
00333 *option_end = m_option + m_option_count;
00334 option != option_end;
00335 ++option)
00336 {
00337 if (option->m_short_name != '\0' && short_name == option->m_short_name)
00338 return option;
00339 }
00340
00341 return NULL;
00342 }
00343
00344 CommandLineOption const *CommandLineParser::FindOptionByLongName (char const *const long_name) const
00345 {
00346 ASSERT1(long_name != NULL);
00347 ASSERT1(*long_name != '\0');
00348
00349 std::string option_name = long_name;
00350 if (option_name.find_first_of('=') >= 0)
00351 option_name = option_name.substr(0, option_name.find_first_of('='));
00352
00353 for (CommandLineOption const *option = m_option,
00354 *option_end = m_option + m_option_count;
00355 option != option_end;
00356 ++option)
00357 {
00358 if (!option->m_long_name.empty() && option_name == option->m_long_name)
00359 return option;
00360 }
00361
00362 return NULL;
00363 }
00364
00365 }