00001
00002
00003 #include "ProgramOptions.h"
00004 #include "format_paragraph.h"
00005 #include <iostream>
00006 #include <sstream>
00007
00008 std::ostream& operator<<(std::ostream& os, const ProgramOption& po)
00009 {
00010 os << "Option '" << po.GetShortOption() << "' or '" << po.GetLongOption() << "'";
00011 if (po.IsCaseSensitive())
00012 os << " (case sensitive)";
00013 switch(po.GetType())
00014 {
00015 case ProgramOption::opt_boolean:
00016 os << " with";
00017 if (!po.IsDefined())
00018 os << " default";
00019 os << " boolean value " << po.GetBoolValue();
00020 break;
00021 case ProgramOption::opt_string:
00022 os << " with";
00023 if (!po.IsDefined())
00024 os << " default";
00025 os << " string value '" << po.GetStringValue() << "'";
00026 break;
00027 case ProgramOption::opt_long:
00028 os << " with";
00029 if (!po.IsDefined())
00030 os << " default";
00031 os << " long value " << po.GetLongValue();
00032 os << " (" << po.GetLongValueMin() << ", " << po.GetLongValueMax() << ")";
00033 break;
00034 }
00035 return os;
00036 }
00037
00038 ProgramOptions::ProgramOptions()
00039 {
00040 m_sOptionChars = "-/";
00041 }
00042
00043 ProgramOptions::~ProgramOptions()
00044 {
00045 for (option_iterator i = m_options.begin(); i != m_options.end(); ++i)
00046 delete *i;
00047 m_options.clear();
00048 }
00049
00050 void ProgramOptions::AddBoolean(
00051 char cShortOption, const char* pszLongOption, bool bDefault,
00052 bool bCaseSensitive, const char* pszHelpText)
00053 {
00054 ProgramOption* po =
00055 new ProgramOption(
00056 cShortOption,
00057 pszLongOption,
00058 ProgramOption::opt_boolean,
00059 false
00060 );
00061 po->m_bValue = bDefault;
00062 po->m_bCaseSensitive = bCaseSensitive;
00063 safe_assign(po->m_sHelpText, pszHelpText);
00064 Add(po);
00065 }
00066
00067 void ProgramOptions::AddString(
00068 char cShortOption, const char* pszLongOption, const char* pszDefault,
00069 bool bCaseSensitive,
00070 const char* pszParameterHint, const char* pszHelpText)
00071 {
00072 ProgramOption* po =
00073 new ProgramOption(
00074 cShortOption,
00075 pszLongOption,
00076 ProgramOption::opt_string,
00077 true
00078 );
00079 safe_assign(po->m_sValue, pszDefault);
00080 po->m_bCaseSensitive = bCaseSensitive;
00081 safe_assign(po->m_sHelpText, pszHelpText);
00082 safe_assign(po->m_sParameterHint, pszParameterHint);
00083 Add(po);
00084 }
00085
00086 void ProgramOptions::AddLong(
00087 char cShortOption, const char* pszLongOption, long lDefault,
00088 long lMin, long lMax,
00089 bool bCaseSensitive,
00090 const char* pszParameterHint, const char* pszHelpText)
00091 {
00092 ProgramOption* po =
00093 new ProgramOption(
00094 cShortOption,
00095 pszLongOption,
00096 ProgramOption::opt_long,
00097 true
00098 );
00099 po->m_lValue = lDefault;
00100 po->m_lMinValue = lMin;
00101 po->m_lMaxValue = lMax;
00102 po->m_bCaseSensitive = bCaseSensitive;
00103 safe_assign(po->m_sHelpText, pszHelpText);
00104 safe_assign(po->m_sParameterHint, pszParameterHint);
00105 Add(po);
00106 }
00107
00108 void ProgramOptions::PrintHelp(std::ostream& os, int width) const
00109 {
00110 char optchar = m_sOptionChars[0];
00111 for (ProgramOptions::const_option_iterator o = options_begin(); o != options_end(); ++o)
00112 {
00113 const ProgramOption& po = **o;
00114 if (po.HasShortOption())
00115 {
00116 os << optchar << po.GetShortOption();
00117 if (po.HasParameter() && !po.GetParameterHint().empty())
00118 os << " " << po.GetParameterHint();
00119 }
00120 if (po.HasLongOption())
00121 {
00122 if (po.HasShortOption())
00123 os << ", ";
00124 os << optchar << po.GetLongOption() << " ";
00125 if (po.HasParameter() && !po.GetParameterHint().empty())
00126 os << po.GetParameterHint();
00127 }
00128 os << std::endl;
00129 os << format_paragraph(po.GetHelpText().c_str(), 2, width-1);
00130 }
00131 }
00132
00133 void ProgramOptions::Add(ProgramOption* pOption)
00134 {
00135 m_options.push_back(pOption);
00136 }
00137
00138 void ProgramOptions::AddParameter(const char* psz)
00139 {
00140 std::string s;
00141 safe_assign(s, psz);
00142 m_parameters.push_back(s);
00143 }
00144
00145 bool ProgramOptions::IsOptionChar(char c) const
00146 {
00147 return (m_sOptionChars.find(c) != std::string::npos);
00148 }
00149
00150 ProgramOption* ProgramOptions::GetOption(char c) const
00151 {
00152 for (const_option_iterator i = options_begin(); i != options_end(); ++i)
00153 {
00154 ProgramOption* pOption = *i;
00155 if (pOption->IsOption(c))
00156 return pOption;
00157 }
00158 return 0;
00159 }
00160
00161 ProgramOption* ProgramOptions::GetOption(const char* psz) const
00162 {
00163 for (const_option_iterator i = options_begin(); i != options_end(); ++i)
00164 {
00165 ProgramOption* pOption = *i;
00166 if (pOption->IsOption(psz))
00167 return pOption;
00168 }
00169 return 0;
00170 }
00171
00172 std::string ProgramOptions::GetStringParameter(const char* psz) const
00173 {
00174 std::string s;
00175 size_t len = strlen(psz);
00176 if (len > 2)
00177 {
00178 const char* pszLast = psz + len-1;
00179 if (*psz == '\"' && *pszLast == '\"')
00180 s.assign(psz+1, len-2);
00181 else
00182 s.assign(psz, len);
00183 }
00184 else
00185 {
00186 safe_assign(s, psz);
00187 }
00188 return s;
00189 }
00190
00191 bool ProgramOptions::Parse(int argc, char* argv[], std::locale loc)
00192 {
00193 ProgramOption* pParameterOption = 0;
00194 for (int arg = 1; arg < argc; ++arg)
00195 {
00196 char optChar = argv[arg][0];
00197 if (IsOptionChar(optChar) && !pParameterOption)
00198 {
00199
00200 const char* pszOption = &argv[arg][1];
00201 ProgramOption* pOption = GetOption(pszOption);
00202 if (pOption)
00203 {
00204 if (pOption->HasParameter())
00205 pParameterOption = pOption;
00206 else
00207 pOption->SetBoolValue();
00208 }
00209 else
00210 {
00211
00212 bool bFoundSomething = false;
00213 for (char* pOpt = &argv[arg][1]; *pOpt; ++pOpt)
00214 {
00215 pOption = GetOption(*pOpt);
00216 if (pOption)
00217 {
00218 bFoundSomething = true;
00219 if (pOption->HasParameter())
00220 {
00221 if (!pParameterOption)
00222 {
00223 pParameterOption = pOption;
00224 }
00225 else
00226 {
00227 std::cerr << "Invalid usage of two options with parameters in " << argv[arg] << std::endl;
00228 return false;
00229 }
00230 }
00231 else
00232 pOption->SetBoolValue();
00233 }
00234 }
00235 if (!bFoundSomething)
00236 {
00237 std::cerr << "Unknown option " << argv[arg] << std::endl;
00238 return false;
00239 }
00240 }
00241 }
00242 else
00243 {
00244
00245 if (pParameterOption)
00246 {
00247 switch (pParameterOption->GetType())
00248 {
00249 case ProgramOption::opt_long:
00250 {
00251 std::istringstream is(argv[arg]);
00252 is.imbue(loc);
00253 long l;
00254 is >> l;
00255
00256 if (!(!is.fail() && !is.good() && pParameterOption->SetLongValue(l)))
00257 {
00258 std::cerr << "Invalid parameter : " << argv[arg] << std::endl;
00259 std::cerr << "for " << *pParameterOption << std::endl;
00260 return false;
00261 }
00262 }
00263 break;
00264 case ProgramOption::opt_string:
00265 pParameterOption->SetStringValue(GetStringParameter(argv[arg]).c_str());
00266 break;
00267 }
00268 }
00269 else
00270 {
00271 AddParameter(GetStringParameter(argv[arg]).c_str());
00272 }
00273 pParameterOption = 0;
00274 }
00275 }
00276
00277 if (pParameterOption)
00278 {
00279 std::cerr << "Missing parameter for " << *pParameterOption << std::endl;
00280 return false;
00281 }
00282 return true;
00283 }
00284
00285 bool ProgramOptions::GetBoolValue(char option) const
00286 {
00287 ProgramOption* pOption = GetOption(option);
00288 if (pOption)
00289 return pOption->GetBoolValue();
00290 else
00291 return false;
00292 }
00293
00294 bool ProgramOptions::GetBoolValue(const char* pszOption) const
00295 {
00296 ProgramOption* pOption = GetOption(pszOption);
00297 if (pOption)
00298 return pOption->GetBoolValue();
00299 else
00300 return false;
00301 }
00302
00303 long ProgramOptions::GetLongValue(char option) const
00304 {
00305 ProgramOption* pOption = GetOption(option);
00306 if (pOption)
00307 return pOption->GetLongValue();
00308 else
00309 return 0;
00310 }
00311
00312 long ProgramOptions::GetLongValue(const char* pszOption) const
00313 {
00314 ProgramOption* pOption = GetOption(pszOption);
00315 if (pOption)
00316 return pOption->GetLongValue();
00317 else
00318 return 0;
00319 }
00320
00321 std::string ProgramOptions::GetStringValue(char option) const
00322 {
00323 ProgramOption* pOption = GetOption(option);
00324 if (pOption)
00325 return pOption->GetStringValue();
00326 else
00327 return "";
00328 }
00329
00330 std::string ProgramOptions::GetStringValue(const char* pszOption) const
00331 {
00332 ProgramOption* pOption = GetOption(pszOption);
00333 if (pOption)
00334 return pOption->GetStringValue();
00335 else
00336 return "";
00337 }
00338
00339 std::ostream& operator<<(std::ostream& os, const ProgramOptions& pos)
00340 {
00341 for (ProgramOptions::const_option_iterator o = pos.options_begin(); o != pos.options_end(); ++o)
00342 os << **o << std::endl;
00343 os << (unsigned long)pos.m_parameters.size() << " parameters:" << std::endl;
00344 for (ProgramOptions::const_parameter_iterator p = pos.parameter_begin(); p != pos.parameter_end(); ++p)
00345 os << *p << std::endl;
00346 return os;
00347 }
00348
00349
00350 #ifdef _DEBUG
00351 void test_programoptions()
00352 {
00353 ProgramOptions po1;
00354 po1.AddBoolean('q', "quiet", false);
00355 po1.AddLong('w', "timeout", 1000, 0);
00356 po1.AddLong('p', "pause", 1000, 0);
00357 po1.AddString('o', "outputfile");
00358 po1.AddString('g', "targetfile");
00359 char* t1[] = {
00360 "dummy",
00361 "-timeout",
00362 "999",
00363 "-p",
00364 "500",
00365 "-qo",
00366 "\"C:\\Documents and Settings\\erik\\My Documents\"",
00367 "localhost"
00368 };
00369 po1.Parse(8, t1);
00370 std::cout << po1 << std::endl;
00371
00372 ProgramOptions po2;
00373 po2.AddBoolean('q', "quiet", false);
00374 po2.AddLong('w', "timeout", 1000, 0);
00375 po2.AddLong('p', "pause", 1000, 0);
00376 po2.AddString('o', "outputfile");
00377 po2.AddString('g', "targetfile");
00378 po2.AddLong('l', "longlimittest1", 1, 1, 5);
00379 po2.AddLong('L', "longlimittest2", 1, 1, 5, true);
00380 char* t2[] = {
00381 "dummy",
00382 "-qp",
00383 "1234",
00384 "-Lq",
00385 "3",
00386 "-longlimittest",
00387 "6"
00388 };
00389 po2.Parse(7, t2);
00390 std::cout << po2 << std::endl;
00391
00392 ProgramOptions po3;
00393 po3.AddBoolean('q', "quiet", false);
00394 po3.AddLong('w', "timeout", 1000, 0);
00395 po3.AddLong('p', "pause", 1000, 0);
00396 po3.AddString('o', "outputfile");
00397 po3.AddString('g', "targetfile");
00398 char* t3[] = {
00399 "dummy",
00400 "-p",
00401 "1,4"
00402 };
00403 po3.Parse(3, t3);
00404 std::cout << po3 << std::endl;
00405
00406 ProgramOptions po4;
00407 po4.AddBoolean('q', "quiet", false);
00408 po4.AddLong('w', "timeout", 1000, 0);
00409 po4.AddLong('p', "pause", 1000, 0);
00410 po4.AddString('o', "outputfile");
00411 po4.AddString('g', "targetfile");
00412 char* t4[] = {
00413 "dummy",
00414 "-p"
00415 };
00416 po4.Parse(2, t4);
00417 std::cout << po4 << std::endl;
00418
00419 ProgramOptions po5;
00420 po5.AddBoolean('q', "quiet", false);
00421 po5.AddLong('w', "timeout", 1000, 0);
00422 po5.AddLong('p', "pause", 1000, 0);
00423 po5.AddString('o', "outputfile");
00424 po5.AddString('g', "targetfile");
00425 char* t5[] = {
00426 "dummy",
00427 "-pqw",
00428 "1234"
00429 };
00430 po5.Parse(3, t5);
00431 std::cout << po5 << std::endl;
00432
00433 ProgramOptions po6;
00434 po6.AddBoolean(
00435 'q', "quiet",
00436 false, false,
00437 "Quiet operation. Only errors are sent to console."
00438 );
00439 po6.AddLong(
00440 'w', "timeout",
00441 1000, 0, LONG_MAX,
00442 false,
00443 "ms", "Time in milliseconds to wait while sending to and receiving from targets."
00444 );
00445 po6.AddLong('\0', "pause",
00446 1000, 0, LONG_MAX,
00447 false,
00448 "ms", "Time in milliseconds to pause between sending to and receiving from all targets."
00449 );
00450 po6.AddString(
00451 'o', "outputfile",
00452 0, false,
00453 "filename", ""
00454 );
00455 po6.AddString(
00456 'g', "targetfile",
00457 0, false,
00458 "filename", "Reads targets from file with one target per line. Targets in file are added to targets specified as parameters."
00459 );
00460 po6.PrintHelp(std::cout);
00461 }
00462
00463
00464
00465
00466
00467
00468
00469 #endif // _DEBUG