00001 00002 // 00003 // Copyright (C) 2000 00004 // Ralf Westram 00005 // (Coded@ReallySoft.de) 00006 // 00007 // Permission to use, copy, modify, distribute and sell this software 00008 // and its documentation for any purpose is hereby granted without fee, 00009 // provided that the above copyright notice appear in all copies and 00010 // that both that copyright notice and this permission notice appear 00011 // in supporting documentation. Ralf Westram makes no 00012 // representations about the suitability of this software for any 00013 // purpose. It is provided "as is" without express or implied warranty. 00014 // 00015 // This code is part of my library. 00016 // You may find a more recent version at http://www.reallysoft.de/ 00017 // 00019 00020 #include "Option.h" 00021 #include "Tools.h" 00022 #include "Directory.h" 00023 #include "sys_dep.h" 00024 #include "path.h" 00025 00026 #include <iomanip> 00027 #include <algorithm> 00028 #include <functional> 00029 #include <cstring> 00030 00031 #if defined(DUMP) 00032 //#define DUMP_PARSE 00033 #endif // DUMP 00034 00035 #if defined(DEBUG) 00036 // #define PATH_DETECT 00037 #endif // DEBUG 00038 00039 using namespace std; 00040 using namespace rs; 00041 using namespace rs::cmdline; 00042 using namespace rs::err; 00043 using namespace rs::str; 00044 using namespace rs::file; 00045 using namespace rs::posix; 00046 00047 typedef list<CmdLineOption*> Options; 00048 typedef list<CmdLineArgument*> Arguments; 00049 typedef list<CmdLineAnnotation*> Annotations; 00050 00051 static Options *options = 0; 00052 static Arguments *arguments = 0; 00053 static Annotations *annotations = 0; 00054 00055 static bool initialized = false; // was ProgramInfo::init called ? 00056 static size_t longest_arg_name_length = 6; // 6=length("<opts>") 00057 static size_t longest_optarg_name_length = 0; 00058 00059 static bool options_are_case_sensitive = false; // will be automatically set to true, if options 00060 // with same letter but different case occur 00061 00062 static Terminator *there_can_only_be_one = 0; // will install my own terminator function (real_terminate) 00063 00064 // -------------------------------------------------------------------------------- 00065 // inline void test_initialized(string where_tested) 00066 // -------------------------------------------------------------------------------- 00067 inline void test_initialized(string where_tested) { 00068 if (!initialized) { 00069 string msg = "ProgramInfo::init() not called in "+where_tested; 00070 throw InternalError(msg); 00071 } 00072 } 00073 00074 // -------------------------------------------------------------------------------- 00075 // class InfoError : public Error 00076 // -------------------------------------------------------------------------------- 00077 00079 class InfoError : public Error { 00080 const ProgramInfo& info; 00081 public: 00082 InfoError(const ProgramInfo& info_, bool real_error_ /*= true*/, const string& msg_) 00083 : Error(real_error_, msg_), 00084 info(info_) 00085 { 00086 } 00087 InfoError(const ProgramInfo& info_, const string& msg_) 00088 : Error(true, msg_), 00089 info(info_) 00090 { 00091 } 00092 virtual ~InfoError() throw() {} 00093 00094 virtual void print(ostream& out) const { 00095 info.infos(out); 00096 Error::print(out); 00097 } 00098 }; 00099 00100 // ----------------------------------------- 00101 // class StringError : public Error 00102 // ----------------------------------------- 00103 00105 class StringError : public Error { 00106 string s; 00107 public: 00108 StringError(const string& msg_, const string& extra_message) 00109 : Error(true, msg_) 00110 , s(extra_message) 00111 { 00112 } 00113 virtual ~StringError() throw() {} 00114 00115 virtual void print(ostream& out) const { 00116 Error::print(out); 00117 out << s; 00118 } 00119 }; 00120 00121 // -------------------------------------------------------------------------------- 00122 // CmdLineOption::CmdLineOption(char optChar_, const char *helptext_) 00123 // -------------------------------------------------------------------------------- 00124 CmdLineOption::CmdLineOption(char optChar_, const char *helptext_) 00125 : CmdLineHelpText(helptext_) 00126 { 00127 #if defined(DUMP_PARSE) 00128 cout << "CmdLineOption(" << optChar_ << ", " << helptext_ << ")\n"; 00129 #endif // DUMP_PARSE 00130 used = false; 00131 optChar = optChar_; 00132 if (!options) options = new Options(); 00133 options->push_back(this); 00134 } 00135 00136 CmdLineOption::~CmdLineOption() { 00137 } 00138 00139 void CmdLineOption::print(ostream& out) const { 00140 out << '-' << optChar << string(longest_optarg_name_length+1, ' '); 00141 } 00142 00143 // --------------------------------------------------------------------------------- 00144 // CmdLineAnnotation::CmdLineAnnotation(const char *helptext_, bool spaced_) 00145 // --------------------------------------------------------------------------------- 00146 CmdLineAnnotation::CmdLineAnnotation(const char *helptext_, bool spaced_) 00147 : CmdLineHelpText(helptext_) 00148 { 00149 print_infrontof_option = options ? options->size() : 0; 00150 if (!annotations) annotations = new Annotations(); 00151 annotations->push_back(this); 00152 spaced = spaced_; 00153 } 00154 00155 // -------------------------------------------------------------------------------- 00156 // CmdLineArgumentOption::CmdLineArgumentOption(char optChar_, const char *name_, const char *helptext_, const char *value_) 00157 // -------------------------------------------------------------------------------- 00158 CmdLineArgumentOption::CmdLineArgumentOption(char optChar_, const char *name_, const char *helptext_, const char *value_) 00159 : CmdLineOption(optChar_, helptext_) 00160 , CmdLineArgument(name_, helptext_, value_, false) 00161 , CmdLineHelpText(helptext_) 00162 { 00163 #if defined(DUMP_PARSE) 00164 cout << "CmdLineArgumentOption(" << optChar_ << ", " << name_ << ", " << helptext_ << ", " << value_ << ")\n"; 00165 #endif // DUMP_PARSE 00166 if (value_) { 00167 if (value_[0]==0) { 00168 cerr << "Empty default argument for option -" << optChar_ << endl; 00169 throw InternalError("see above"); 00170 } 00171 CmdLineOption::set(); 00172 } 00173 longest_optarg_name_length = max(get_name().length(), longest_optarg_name_length); 00174 } 00175 00176 // -------------------------------------------------------------------------------- 00177 // CmdLineArgumentOption::~CmdLineArgumentOption() 00178 // -------------------------------------------------------------------------------- 00179 CmdLineArgumentOption::~CmdLineArgumentOption() { 00180 } 00181 00182 // -------------------------------------------------------------------------------- 00183 // void CmdLineArgumentOption::print(ostream& out) const 00184 // -------------------------------------------------------------------------------- 00185 void CmdLineArgumentOption::print(ostream& out) const { 00186 out << '-' << get_optChar() << get_name() << string(longest_optarg_name_length-get_name().length()+1, ' '); 00187 } 00188 00189 // ------------------------------------------ 00190 // int CmdLineArgument::getNumeric() 00191 // ------------------------------------------ 00192 int CmdLineArgument::getNumeric() { 00193 string val = get(); 00194 int digits = 0; 00195 int xs = 0; 00196 int len = val.length(); 00197 00198 int p; 00199 for (p=0; p<len; ++p) { 00200 char c = val[p]; 00201 if (c>='0' && c<='9') { 00202 ++digits; 00203 } 00204 else { 00205 if (c=='x') ++xs; 00206 } 00207 } 00208 00209 int result = 0; 00210 if (len==digits) { // only digits found 00211 result = atoi(val.c_str()); 00212 } 00213 else { 00214 if ((xs+digits)==len && xs==1 && val[0]=='0' && val[p]=='x') { // hex-number 00215 result = strtoul(val.c_str()+2, 0, 16); 00216 } 00217 else { 00218 throw Error(str::strf("Numeric argument awaited in <%s> '%s' (dec or hex '0x..') ", 00219 name.c_str(), val.c_str())); 00220 } 00221 } 00222 00223 return result; 00224 } 00225 00226 // -------------------------------------------- 00227 // double CmdLineArgument::getDouble() 00228 // -------------------------------------------- 00229 double CmdLineArgument::getDouble() { 00230 string val = get(); 00231 int digits = 0; 00232 int kommas = 0; 00233 int len = val.length(); 00234 00235 int p; 00236 for (p=0; p<len; ++p) { 00237 char c = val[p]; 00238 if (c>='0' && c<='9') { 00239 ++digits; 00240 } 00241 else { 00242 if (c=='.') ++kommas; 00243 else if (c==',') { ++kommas; val[p] = '.'; } 00244 } 00245 } 00246 00247 double result = 0; 00248 if (len==digits) { // only digits found 00249 result = atoi(val.c_str()); 00250 } 00251 else { 00252 if ((kommas+digits) == len && kommas==1) { // floating point 00253 result = atof(val.c_str()); 00254 } 00255 else { 00256 throw Error(str::strf("Floating point argument awaited in <%s> '%s'", 00257 name.c_str(), val.c_str())); 00258 } 00259 } 00260 00261 return result; 00262 } 00263 00264 // -------------------------------------------------------------------------------- 00265 // CmdLineArgument::CmdLineArgument(const char *name_, const char *helptext_, const char *value_, bool is_real_arg) 00266 // -------------------------------------------------------------------------------- 00267 CmdLineArgument::CmdLineArgument(const char *name_, const char *helptext_, const char *value_, bool is_real_arg) 00268 : CmdLineHelpText(helptext_), 00269 name(name_), 00270 value(value_ ? value_ : ""), 00271 has_default_value(false), 00272 really_used(false) 00273 { 00274 #if defined(DUMP_PARSE) 00275 cout << "CmdLineArgument(" << name_ << ", " << helptext_ << ", " << int(is_real_arg) << ")\n"; 00276 #endif // DUMP_PARSE 00277 if (is_real_arg) { 00278 if (!arguments) arguments = new Arguments(0); 00279 arguments->push_back(this); 00280 longest_arg_name_length = max(get_name().length(), longest_arg_name_length); 00281 } 00282 } 00283 00284 namespace rs { 00285 namespace cmdline { 00286 // ------------------------------------------------------ 00287 // bool acceptAnyArgument(const string */*arg*/) 00288 // ------------------------------------------------------ 00289 bool acceptAnyArgument(const string */*arg*/) { 00290 return true; 00291 } 00292 }; 00293 }; 00294 00295 // -------------------------------------------------------------------------------- 00296 // CmdLineArgument::~CmdLineArgument() 00297 // -------------------------------------------------------------------------------- 00298 CmdLineArgument::~CmdLineArgument() { 00299 } 00300 00301 // start of implementation of class RepeatableCmdLineArgument: 00302 00303 // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 00304 // RepeatableCmdLineArgument::RepeatableCmdLineArgument(const char *name_, const char *helptext_, int min_occur_, int max_occur_, CmdLineArgumentPredicate isArg_, bool is_real_arg) 00305 // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 00306 RepeatableCmdLineArgument::RepeatableCmdLineArgument(const char *name_, const char *helptext_, int min_occur_, int max_occur_, CmdLineArgumentPredicate isArg_, bool is_real_arg) 00307 : CmdLineArgument(name_, helptext_, 0, is_real_arg) 00308 , CmdLineHelpText(helptext_) 00309 { 00310 min_occur = min_occur_; 00311 max_occur = max_occur_; 00312 isArg = isArg_; 00313 count = 0; 00314 00315 // do not set longest_arg_name_length here (it's set in cons of CmdLineArgument) 00316 // and we don't want to know the length of '[<arg>]+' but of '<arg>' 00317 00318 //longest_arg_name_length = max(get_name().length(), longest_arg_name_length); 00319 } 00320 00321 // ----------------------------------------------------------- 00322 // string RepeatableCmdLineArgument::get_name() const 00323 // ----------------------------------------------------------- 00324 string RepeatableCmdLineArgument::get_name() const { 00325 string Name = string("[") + CmdLineArgument::get_name() + "]"; 00326 00327 if (min_occur==0) { 00328 if (max_occur>1) Name = Name+"*"; 00329 } 00330 else { 00331 Name = Name+"+"; 00332 } 00333 return Name; 00334 } 00335 00336 // ------------------------------------------------ 00337 // string RepeatableCmdLineArgument::get() 00338 // ------------------------------------------------ 00339 string RepeatableCmdLineArgument::get() 00340 { 00341 string result = CmdLineArgument::get(); 00342 if (!result.empty()) seen_values.push_back(result); // store as seen 00343 if (repeated_values.empty()) { 00344 CmdLineArgument::set(""); 00345 } 00346 else { 00347 CmdLineArgument::set(repeated_values.front()); 00348 repeated_values.pop_front(); 00349 } 00350 return result; 00351 } 00352 00353 // ------------------------------------------------ 00354 // void RepeatableCmdLineArgument::reset() 00355 // ------------------------------------------------ 00356 void RepeatableCmdLineArgument::reset() 00357 { 00358 while (!get().empty()) ; // read leftover arguments 00359 repeated_values = seen_values; 00360 seen_values.clear(); 00361 get(); 00362 } 00363 00364 // -end- of implementation of class RepeatableCmdLineArgument. 00365 00366 // -------------------------------------------------------------------------------- 00367 // class HasOptChar : public unary_function<CmdLineOption*, char> 00368 // -------------------------------------------------------------------------------- 00370 class HasOptChar : public unary_function<CmdLineOption*, char> { 00371 char optChar; 00372 public: 00373 explicit HasOptChar(char optChar_) :optChar(optChar_) {} 00374 bool operator() (const CmdLineOption* const & opt) const { return optChar==opt->get_optChar(); } 00375 }; 00376 00377 // start of implementation of class RepeatedCmdLineArgumentOption: 00378 00379 // ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- 00380 // RepeatedCmdLineArgumentOption::RepeatedCmdLineArgumentOption(char optChar_, const char *argname_, char separator_, const char *helptext_, const char *value_) 00381 // ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- 00382 RepeatedCmdLineArgumentOption::RepeatedCmdLineArgumentOption(char optChar_, const char *argname_, char separator_, const char *helptext_, const char *value_) 00383 : CmdLineOption(optChar_, helptext_) 00384 , RepeatableCmdLineArgument(argname_, helptext_, 1, -1, acceptAnyArgument, false) 00385 , CmdLineHelpText(helptext_) 00386 , separator(separator_) 00387 { 00388 if (value_) set(value_, true); 00389 longest_optarg_name_length = max(get_name().length(), longest_optarg_name_length); 00390 } 00391 00392 // ---------------------------------------------------------------------------------------- 00393 // void RepeatedCmdLineArgumentOption::set(string value_, bool set_default_value) 00394 // ---------------------------------------------------------------------------------------- 00395 void RepeatedCmdLineArgumentOption::set(string value_, bool set_default_value) { 00396 size_t start = 0; 00397 size_t seppos; 00398 string next_arg; 00399 00400 CmdLineOption::set(); 00401 while (string::npos != (seppos = value_.find(separator, start))) { 00402 next_arg = value_.substr(start, seppos-start); 00403 if (!next_arg.empty()) RepeatableCmdLineArgument::set(next_arg, set_default_value); 00404 start = seppos+1; 00405 } 00406 next_arg = value_.substr(start); 00407 if (!next_arg.empty()) RepeatableCmdLineArgument::set(next_arg, set_default_value); 00408 } 00409 00410 // --------------------------------------------------------------- 00411 // string RepeatedCmdLineArgumentOption::get_name() const 00412 // --------------------------------------------------------------- 00413 string RepeatedCmdLineArgumentOption::get_name() const { 00414 const char *Name = CmdLineArgument::get_name().c_str(); 00415 return strf("%s[%c%s]*", Name, separator, Name); 00416 } 00417 00418 // ---------------------------------------------------------------------- 00419 // void RepeatedCmdLineArgumentOption::print(ostream& out) const 00420 // ---------------------------------------------------------------------- 00421 void RepeatedCmdLineArgumentOption::print(ostream& out) const 00422 { 00423 string Name = get_name(); 00424 int spaces = longest_optarg_name_length - Name.length() + 1; 00425 assert(spaces >= 0); 00426 out << '-' << get_optChar() << Name << string(spaces, ' '); 00427 } 00428 00429 // -end- of implementation of class RepeatedCmdLineArgumentOption. 00430 00431 // start of implementation of class ProgramInfo: 00432 00433 // -------------------------------------------------------------------------------------------------------------------------------------------------------------- 00434 // ProgramInfo::ProgramInfo(const char *program_name_, int main_version_, int sub_version_, const char *program_description_, const char *creation_date_) 00435 // -------------------------------------------------------------------------------------------------------------------------------------------------------------- 00436 ProgramInfo::ProgramInfo(const char *program_name_, int main_version_, int sub_version_, const char *program_description_, const char *creation_date_) 00437 : program_name(program_name_), 00438 main_version(main_version_), 00439 sub_version(sub_version_), 00440 program_description(program_description_), 00441 creation_date(creation_date_), 00442 any_option_given(false) 00443 { 00444 Error::set_module_name(program_name_); 00445 } 00446 00447 // -------------------------------------------------------------------------------- 00448 // static bool test_existing_path(const string& candidate,string& result) 00449 // -------------------------------------------------------------------------------- 00450 static bool test_existing_path(const string& candidate,string& result) { 00451 string canonical; 00452 bool wrong_path = false; 00453 00454 #if defined(PATH_DETECT) 00455 cout << "candidate='" << candidate << "'\n"; 00456 #endif // PATH_DETECT 00457 00458 try { canonical = canonicalize(candidate); } 00459 catch (IOError&) { wrong_path = true; } 00460 00461 #if defined(PATH_DETECT) 00462 if (!canonical.empty()) cout << "canonical='" << canonical << "'\n"; 00463 #endif // PATH_DETECT 00464 00465 if (!wrong_path && 00466 nonnull(canonical.length()) && 00467 (fileExists(canonical) || fileExists(canonical+".exe"))) 00468 { 00469 #if defined(PATH_DETECT) 00470 cout << canonical << " exists!\n"; 00471 #endif // PATH_DETECT 00472 result = extractDirectory(canonical); 00473 #if defined(PATH_DETECT) 00474 cout << "result='" << result << "'\n"; 00475 #endif // PATH_DETECT 00476 assert(!result.empty()); 00477 return true; 00478 } 00479 00480 // if (candidate.find(".exe") == string::npos) { 00481 // return test_existing_path(candidate+".exe", result); 00482 // } 00483 return false; 00484 } 00485 00486 // -------------------------------------------------------------- 00487 // void ProgramInfo::setProgramPaths(const char *argv0) 00488 // -------------------------------------------------------------- 00489 void ProgramInfo::setProgramPaths(const char *argv0_) { 00490 call_path = getcwd(); 00491 string argv0 = argv0_; 00492 00493 #if defined(PATH_DETECT) 00494 cout << "argv0='" << argv0 << "'\n"; 00495 cout << "call_path='" << call_path << "'\n"; 00496 #endif // PATH_DETECT 00497 00498 if (!test_existing_path(argv0, program_path) && 00499 !test_existing_path(string("./")+argv0, program_path)) 00500 { 00501 sys_dep::Path path; // reads $PATH from environment 00502 for (vector<string>::const_iterator dir = path.begin(); dir != path.end(); ++dir) { 00503 if (test_existing_path(*dir+'/'+argv0_, program_path)) break; 00504 } 00505 } 00506 00507 if (program_path.empty()) { 00508 throw Error(strf("Can't detect location of %s.exe -- please call with full pathname", program_name.c_str())); 00509 } 00510 00511 #if defined(PATH_DETECT) 00512 cout << "program_path='" << program_path << "'\n"; 00513 #endif // PATH_DETECT 00514 } 00515 // --------------------------------------------------------- 00516 // void ProgramInfo::init(int argc_, char* argv_[]) 00517 // --------------------------------------------------------- 00518 void ProgramInfo::init(int argc_, char* argv_[]) { 00519 // install terminator 00520 if (there_can_only_be_one == 0) there_can_only_be_one = new Terminator(); 00521 00522 // test option integrity: 00523 if (options) { 00524 bool char_used[256]; 00525 00526 for (int i=0; i<256; ++i) char_used[i] = false; 00527 00528 for (Options::iterator o = options->begin(); o!=options->end(); ++o) { 00529 char c = (*o)->get_optChar(); 00530 00531 if (char_used[c]) { 00532 throw InternalError(string("Duplicated usage of option character '")+c+"'"); 00533 } 00534 char_used[c] = true; 00535 if (!options_are_case_sensitive && isalpha(c)) { 00536 if (islower(c)) { 00537 if (char_used[toupper(c)]) options_are_case_sensitive = true; 00538 } 00539 else { 00540 if (char_used[tolower(c)]) options_are_case_sensitive = true; 00541 } 00542 } 00543 } 00544 } 00545 00546 // set defaults for options and arguments 00547 if (options) { 00548 for (Options::iterator o=options->begin(); o!=options->end(); ++o) { 00549 CmdLineArgumentOption *arg_opt = dynamic_cast<CmdLineArgumentOption*>(*o); 00550 if (arg_opt) { // is an argument option 00551 arg_opt->set_has_default(); 00552 } 00553 } 00554 } 00555 00556 if (arguments) { 00557 for (Arguments::iterator a=arguments->begin(); a!=arguments->end(); ++a) { 00558 (*a)->set_has_default(); 00559 } 00560 } 00561 00562 bool allow_multiple_usage = false; // warn if an option is used more than once 00563 00564 // proceed through command line 00565 Arguments::iterator next_arg; 00566 if (arguments) next_arg = arguments->begin(); 00567 00568 // setProgramPaths("once"); 00569 setProgramPaths(argv_[0]); 00570 00571 for (int a=1; a<argc_; a++) { 00572 const char *arg = argv_[a]; 00573 00574 #if defined(DUMP_PARSE) 00575 cout << "Parse arg #" << a << ": '" << arg << "'\n"; 00576 #endif // DUMP_PARSE 00577 00578 // test for help-requests 00579 if ((arg[0]=='/' || arg[0]=='-') && 00580 (arg[1]=='?' || arg[1]=='h' || (arg[1]=='-' && strcmp(arg+2, "help")==0)) ) 00581 { 00582 throw InfoError(*this, false, "Help requested"); 00583 } 00584 00585 if (arg[0]=='-') { // option found 00586 bool internal_option = false; 00587 if (arg[1] == '-') { // maybe an internal option 00588 const char *longopt = arg+2; 00589 internal_option = true; 00590 if (rs::str::stricmp(longopt, "multi") == 0) { 00591 allow_multiple_usage = true; 00592 } 00593 else if (stricmp(string(longopt), "multi") == 0) { 00594 } 00595 else { 00596 throw StringError(string("Unknown long option '")+arg+"'", 00597 "Valid options are:\n" 00598 " --help show command line help\n" 00599 " --multi allow to use args multiple (behind this option)\n" 00600 ); 00601 } 00602 } 00603 00604 if (!internal_option) { 00605 size_t pos = 1; 00606 bool done = false; 00607 char switchChar = arg[0]; 00608 00609 while (!done) { 00610 char optChar = arg[pos]; 00611 if (!optChar) { 00612 if (pos==1) throw InfoError(*this, string("character awaited behind '") + switchChar + '\''); 00613 break; 00614 } 00615 00616 CmdLineOption *found_option = 0; 00617 00618 if (options) { 00619 Options::iterator found = find_if(options->begin(), options->end(), HasOptChar(optChar)); 00620 if (found!=options->end()) { 00621 found_option = *found; 00622 } 00623 } 00624 00625 if (!found_option) { 00626 throw InfoError(*this, string("Unknown option '") + switchChar + optChar + '\'' ); 00627 } 00628 00629 any_option_given = true; 00630 if (found_option->reallyUsed()) { // option was already set 00631 if (!allow_multiple_usage) { 00632 throw InfoError(*this, string("Option '")+switchChar+optChar+"' was already used (preceed with --multi to avoid this warning)."); 00633 } 00634 } 00635 00636 if (found_option->takes_argument()) { 00637 CmdLineArgumentOption *arg_option = static_cast<CmdLineArgumentOption*>(found_option); 00638 string arg_value; 00639 00640 if (arg[pos+1]==0) { // arg_value is in next argv_ 00641 if (a<(argc_-1)) { // there is another arg 00642 arg_value = argv_[++a]; 00643 } 00644 } 00645 else { // arg_value appeded to option 00646 arg_value = string(arg, pos+1, string::npos); 00647 } 00648 00649 if (arg_value.empty()) { 00650 throw InfoError(*this, string("Option ") + switchChar + optChar + " awaits argument " + arg_option->get_name()); 00651 } 00652 arg_option->set(arg_value); 00653 done = true; 00654 } 00655 else { 00656 found_option->set(); 00657 } 00658 pos++; 00659 } 00660 } 00661 } 00662 else { // it seems to be an argument 00663 bool arg_handler_found = false; // detected correct arg handler? 00664 bool inc_arg_handler = true; // continue with next arg handler afterwards 00665 00666 while (!arg_handler_found) { 00667 if (!arguments || next_arg==arguments->end()) { 00668 throw InfoError(*this, string("Superfluous argument '")+arg+'\''); 00669 } 00670 00671 RepeatableCmdLineArgument *repeatable = dynamic_cast<RepeatableCmdLineArgument*>(*next_arg); 00672 00673 if (repeatable) { 00674 inc_arg_handler = false; 00675 if (repeatable->get_count() < repeatable->get_min_occur()) { // if we have not scanned enough of this yet 00676 arg_handler_found = true; // use it anyway 00677 } 00678 else { // we have scanned enough of it 00679 int max_occur = repeatable->get_max_occur(); 00680 assert(max_occur==-1 || repeatable->get_count()<max_occur); // otherwise next_arg should have been incremented before 00681 string arg_str(arg); 00682 if (repeatable->isArg(&arg_str)) { // should be added to repeatable 00683 arg_handler_found = true; 00684 if (repeatable->get_count()==(max_occur-1)) { 00685 inc_arg_handler = true; 00686 } 00687 } 00688 else { 00689 ++next_arg; // try next arg 00690 } 00691 } 00692 } 00693 else { 00694 arg_handler_found = true; 00695 } 00696 } 00697 00698 (*next_arg)->set(arg); 00699 if (inc_arg_handler) ++next_arg; 00700 } 00701 } 00702 00703 if (arguments) { 00704 if (next_arg!=arguments->end()) { 00705 string err("Missing arguments:"); 00706 bool raise_error = false; 00707 00708 do { 00709 RepeatableCmdLineArgument *repeated = dynamic_cast<RepeatableCmdLineArgument*>(*next_arg); 00710 bool is_missing = true; 00711 00712 if (repeated) { 00713 if (repeated->get_count() >= repeated->get_min_occur()) { // may be enough 00714 if (repeated->isArg(0)) { // argument should be missing 00715 is_missing = false; 00716 } 00717 } 00718 } 00719 00720 if (is_missing) { 00721 err += ' '; 00722 err += (*next_arg)->get_name(); 00723 raise_error = true; 00724 } 00725 next_arg++; 00726 } 00727 while(next_arg!=arguments->end()); 00728 00729 if (raise_error) throw InfoError(*this, err); 00730 } 00731 } 00732 00733 initialized = true; 00734 } 00735 00736 // -------------------------------------------------------------------------------- 00737 // void ProgramInfo::infos(ostream& out) const 00738 // -------------------------------------------------------------------------------- 00739 00742 void ProgramInfo::infos(ostream& out) const { 00743 size_t border = 1; 00744 size_t tty_width = 80; 00745 size_t width = tty_width-2*border; 00746 string left(border, ' '); 00747 string line = left+string(width, '-')+'\n'; 00748 00749 out << "\n" 00750 << line 00751 << left << program_name << ' '; 00752 out << int(main_version) << '.' << setw(2) << setfill('0') << int(sub_version); 00753 out << " --- (C) " << creation_date << " by " << COPYRIGHT_BY() << "\n"; 00754 out << line 00755 << left << "Purpose: "; output::printIndented(out, 9, border, width, program_description); 00756 out << left << "Usage : " << program_name; 00757 00758 if (options) out << " <opts>"; 00759 00760 if (arguments) { 00761 for (Arguments::iterator i=arguments->begin(); i!=arguments->end(); i++) { 00762 out << ' ' << (*i)->get_name(); 00763 } 00764 } 00765 out << "\n" 00766 << "\n"; 00767 00768 // show detailed info on arguments: 00769 if (arguments) { 00770 for (Arguments::iterator i=arguments->begin(); i!=arguments->end(); i++) { 00771 string arg_left = (*i)->CmdLineArgument::get_name(); 00772 unsigned len = arg_left.length(); 00773 00774 if (len<longest_arg_name_length) { 00775 unsigned spaces = longest_arg_name_length-len; 00776 arg_left += string(spaces, ' '); 00777 len += spaces; 00778 } 00779 00780 arg_left += "::= "; 00781 len += 4; 00782 00783 out << left << arg_left; output::printIndented(out, len, border, width, (*i)->get_helptext()); 00784 } 00785 out << "\n"; 00786 } 00787 00788 // show detailed info on options: 00789 string opt_left = "<opts>"; 00790 // unsigned len1 = opt_left.length(); 00791 00792 // if (len1<longest_arg_name_length) { 00793 // unsigned spaces = longest_arg_name_length-len1; 00794 // opt_left += string(spaces, ' '); 00795 // len1 += spaces; 00796 // } 00797 00798 opt_left += "::= "; 00799 // len1 += 4; 00800 unsigned len1 = opt_left.length(); 00801 00802 if (!options) options = new Options(); 00803 if (!annotations) annotations = new Annotations(); 00804 00805 Options::iterator i = options->begin(); 00806 Annotations::iterator j = annotations->begin(); 00807 int printed = 0; 00808 00809 while (i!=options->end()) { 00810 out << left << opt_left; 00811 if (i==options->begin()) { 00812 opt_left = string(len1, ' '); 00813 assert(opt_left.length()==len1); 00814 } 00815 00816 if (j!=annotations->end()) { 00817 if ((*j)->get_print_infrontof_option()==printed) { 00818 output::printIndented(out, len1, border, width, 00819 (*j)->print_spaced() 00820 ? string("\n")+(*j)->get_helptext()+"\n" 00821 : (*j)->get_helptext()); 00822 00823 out << left << opt_left; 00824 ++j; 00825 } 00826 } 00827 00828 (*i)->print(out); 00829 output::printIndented(out, len1 + 2 + longest_optarg_name_length + 1, border, width, (*i)->get_helptext()); 00830 00831 ++i; 00832 ++printed; 00833 } 00834 00835 if (j!=annotations->end()) { 00836 opt_left = string(len1, ' '); 00837 while (j!=annotations->end()) { 00838 output::printIndented(out, printed ? len1 : 0, border, width, string("\n")+(*j)->get_helptext()+"\n"); 00839 ++j; 00840 } 00841 } 00842 00843 out << line 00844 << left << "Contact: " << MAIL_CONTACT(program_name) << "\n" 00845 << line; 00846 } 00847 00848 // -------------------------------------------------------------------------------- 00849 // static string countOptions(CmdLineOptionArray optList, size_t& used_options, const string& trennwort, bool return_used_options) 00850 // -------------------------------------------------------------------------------- 00851 static string countOptions(CmdLineOptionArray optList, size_t& used_options, const string& trennwort, bool return_used_options) { 00852 test_initialized("countOptions"); 00853 00854 used_options = 0; 00855 unsigned i; 00856 00857 for (i = 0; optList[i]; i++) { 00858 if (optList[i]->reallyUsed()) used_options++; 00859 } 00860 00861 unsigned size = i; 00862 size_t counter = 0; 00863 string opts; 00864 for (i = 0; optList[i]; i++) { 00865 if (return_used_options==false || optList[i]->reallyUsed()) { // insert option into return-string? 00866 counter++; 00867 bool is_first = counter==1; 00868 bool is_last = return_used_options ? (counter==used_options) : (counter==size); 00869 00870 if (is_last) { 00871 opts = opts+' '+trennwort; 00872 } 00873 else if (!is_first) { 00874 opts += ','; 00875 } 00876 opts = opts+" -" + optList[i]->get_optChar(); 00877 } 00878 } 00879 00880 return opts; 00881 } 00882 00883 // -------------------------------------------------------------------------------------------- 00884 // void ProgramInfo::exclude(CmdLineOptionArray optList1, CmdLineOptionArray optList2) 00885 // -------------------------------------------------------------------------------------------- 00886 void ProgramInfo::exclude(CmdLineOptionArray optList1, CmdLineOptionArray optList2) { 00887 test_initialized("exclude"); 00888 00889 for (unsigned i1=0; optList1[i1]; ++i1) { 00890 if (optList1[i1]->reallyUsed()) { // used? 00891 for (unsigned i2=0; optList2[i2]; ++i2) { 00892 if (optList2[i2]->reallyUsed()) { // used? 00893 if (optList1[i1]!=optList2[i2]) { // not same option 00894 throw InfoError(*this, string("Option -")+optList1[i1]->get_optChar()+ 00895 " can't be used together with -"+optList2[i2]->get_optChar()); 00896 } 00897 } 00898 } 00899 } 00900 } 00901 } 00902 00903 // -------------------------------------------------------------------------------- 00904 // void ProgramInfo::needOne(CmdLineOptionArray optList) 00905 // -------------------------------------------------------------------------------- 00906 void ProgramInfo::needOne(CmdLineOptionArray optList) { 00907 size_t Count; 00908 string ex_opt = countOptions(optList, Count, "or", false); 00909 #if defined(DEBUG) 00910 cerr << "needOne: '" << ex_opt << "'\n"; 00911 #endif // DEBUG 00912 if (Count==0) { 00913 string msg = "You have to specify one of the options"+ex_opt+'.'; 00914 throw InfoError(*this, msg); 00915 } 00916 } 00917 00918 // -------------------------------------------------------------------------------- 00919 // void ProgramInfo::neededBy(const CmdLineOption& drug, const CmdLineOption& addict) 00920 // -------------------------------------------------------------------------------- 00921 void ProgramInfo::neededBy(const CmdLineOption& drug, const CmdLineOption& addict) { 00922 if (addict.reallyUsed() && !drug) { 00923 throw InfoError(*this, string("Specifying option -")+addict.get_optChar()+ " does not make sense without option -"+drug.get_optChar()); 00924 } 00925 } 00926 // -------------------------------------------------------------- 00927 // void ProgramInfo::throw_Error(const string& msg) const 00928 // -------------------------------------------------------------- 00929 void ProgramInfo::throw_Error(const string& msg) const { 00930 throw InfoError(*this, msg); 00931 } 00932 00933 // ---------------------------------------------------- 00934 // const string& ProgramInfo::CallPath() const 00935 // ---------------------------------------------------- 00936 const string& ProgramInfo::CallPath() const 00937 { 00938 test_initialized("CallPath"); 00939 return call_path; 00940 } 00941 00942 // ------------------------------------------------------- 00943 // const string& ProgramInfo::ProgramPath() const 00944 // ------------------------------------------------------- 00945 const string& ProgramInfo::ProgramPath() const 00946 { 00947 test_initialized("ProgramPath"); 00948 return program_path; 00949 } 00950 00951 // -end- of implementation of class ProgramInfo. 00952
Contact me in case of errors or questions. This documentation is powered by ![]() |
(C) 2000-2002 ![]() |