CommandLineArguments.cxx 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. /*=========================================================================
  2. Program: KWSys - Kitware System Library
  3. Module: $RCSfile$
  4. Copyright (c) Kitware, Inc., Insight Consortium. All rights reserved.
  5. See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
  6. This software is distributed WITHOUT ANY WARRANTY; without even
  7. the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  8. PURPOSE. See the above copyright notices for more information.
  9. =========================================================================*/
  10. #include "kwsysPrivate.h"
  11. #include KWSYS_HEADER(CommandLineArguments.hxx)
  12. #include KWSYS_HEADER(Configure.hxx)
  13. #include KWSYS_HEADER(stl/vector)
  14. #include KWSYS_HEADER(stl/map)
  15. #include KWSYS_HEADER(stl/set)
  16. #include KWSYS_HEADER(ios/sstream)
  17. #include KWSYS_HEADER(ios/iostream)
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #ifdef _MSC_VER
  21. # pragma warning (disable: 4786)
  22. #endif
  23. namespace KWSYS_NAMESPACE
  24. {
  25. //----------------------------------------------------------------------------
  26. //============================================================================
  27. class CommandLineArgumentsString : public kwsys_stl::string
  28. {
  29. public:
  30. typedef kwsys_stl::string StdString;
  31. CommandLineArgumentsString(): StdString() {}
  32. CommandLineArgumentsString(const value_type* s): StdString(s) {}
  33. CommandLineArgumentsString(const value_type* s, size_type n): StdString(s, n) {}
  34. CommandLineArgumentsString(const StdString& s, size_type pos=0, size_type n=npos):
  35. StdString(s, pos, n) {}
  36. };
  37. struct CommandLineArgumentsCallbackStructure
  38. {
  39. const char* Argument;
  40. int ArgumentType;
  41. CommandLineArguments::CallbackType Callback;
  42. void* CallData;
  43. void* Variable;
  44. int VariableType;
  45. const char* Help;
  46. };
  47. class CommandLineArgumentsVectorOfStrings :
  48. public kwsys_stl::vector<CommandLineArgumentsString> {};
  49. class CommandLineArgumentsSetOfStrings :
  50. public kwsys_stl::set<CommandLineArgumentsString> {};
  51. class CommandLineArgumentsMapOfStrucs :
  52. public kwsys_stl::map<CommandLineArgumentsString,
  53. CommandLineArgumentsCallbackStructure> {};
  54. class CommandLineArgumentsInternal
  55. {
  56. public:
  57. CommandLineArgumentsInternal()
  58. {
  59. this->UnknownArgumentCallback = 0;
  60. this->ClientData = 0;
  61. this->LastArgument = 0;
  62. }
  63. typedef CommandLineArgumentsVectorOfStrings VectorOfStrings;
  64. typedef CommandLineArgumentsMapOfStrucs CallbacksMap;
  65. typedef CommandLineArgumentsString String;
  66. typedef CommandLineArgumentsSetOfStrings SetOfStrings;
  67. VectorOfStrings Argv;
  68. String Argv0;
  69. CallbacksMap Callbacks;
  70. CommandLineArguments::ErrorCallbackType UnknownArgumentCallback;
  71. void* ClientData;
  72. VectorOfStrings::size_type LastArgument;
  73. };
  74. //============================================================================
  75. //----------------------------------------------------------------------------
  76. //----------------------------------------------------------------------------
  77. CommandLineArguments::CommandLineArguments()
  78. {
  79. this->Internals = new CommandLineArguments::Internal;
  80. this->Help = "";
  81. this->LineLength = 80;
  82. }
  83. //----------------------------------------------------------------------------
  84. CommandLineArguments::~CommandLineArguments()
  85. {
  86. delete this->Internals;
  87. }
  88. //----------------------------------------------------------------------------
  89. void CommandLineArguments::Initialize(int argc, const char* const argv[])
  90. {
  91. int cc;
  92. this->Initialize();
  93. this->Internals->Argv0 = argv[0];
  94. for ( cc = 1; cc < argc; cc ++ )
  95. {
  96. this->ProcessArgument(argv[cc]);
  97. }
  98. }
  99. //----------------------------------------------------------------------------
  100. void CommandLineArguments::Initialize(int argc, char* argv[])
  101. {
  102. this->Initialize(argc, static_cast<const char* const*>(argv));
  103. }
  104. //----------------------------------------------------------------------------
  105. void CommandLineArguments::Initialize()
  106. {
  107. this->Internals->Argv.clear();
  108. this->Internals->LastArgument = 0;
  109. }
  110. //----------------------------------------------------------------------------
  111. void CommandLineArguments::ProcessArgument(const char* arg)
  112. {
  113. this->Internals->Argv.push_back(arg);
  114. }
  115. //----------------------------------------------------------------------------
  116. int CommandLineArguments::Parse()
  117. {
  118. CommandLineArguments::Internal::VectorOfStrings::size_type cc;
  119. CommandLineArguments::Internal::VectorOfStrings matches;
  120. for ( cc = 0; cc < this->Internals->Argv.size(); cc ++ )
  121. {
  122. this->Internals->LastArgument = cc;
  123. matches.clear();
  124. CommandLineArguments::Internal::String& arg = this->Internals->Argv[cc];
  125. CommandLineArguments::Internal::CallbacksMap::iterator it;
  126. // Does the argument match to any we know about?
  127. for ( it = this->Internals->Callbacks.begin();
  128. it != this->Internals->Callbacks.end();
  129. it ++ )
  130. {
  131. const CommandLineArguments::Internal::String& parg = it->first;
  132. CommandLineArgumentsCallbackStructure *cs = &it->second;
  133. if (cs->ArgumentType == CommandLineArguments::NO_ARGUMENT ||
  134. cs->ArgumentType == CommandLineArguments::SPACE_ARGUMENT)
  135. {
  136. if ( arg == parg )
  137. {
  138. matches.push_back(parg);
  139. }
  140. }
  141. else if ( arg.find( parg ) == 0 )
  142. {
  143. matches.push_back(parg);
  144. }
  145. }
  146. if ( matches.size() > 0 )
  147. {
  148. // Ok, we found one or more arguments that match what user specified.
  149. // Let's find the longest one.
  150. CommandLineArguments::Internal::VectorOfStrings::size_type kk;
  151. CommandLineArguments::Internal::VectorOfStrings::size_type maxidx = 0;
  152. CommandLineArguments::Internal::String::size_type maxlen = 0;
  153. for ( kk = 0; kk < matches.size(); kk ++ )
  154. {
  155. if ( matches[kk].size() > maxlen )
  156. {
  157. maxlen = matches[kk].size();
  158. maxidx = kk;
  159. }
  160. }
  161. // So, the longest one is probably the right one. Now see if it has any
  162. // additional value
  163. const char* value = 0;
  164. CommandLineArgumentsCallbackStructure *cs
  165. = &this->Internals->Callbacks[matches[maxidx]];
  166. const CommandLineArguments::Internal::String& sarg = matches[maxidx];
  167. if ( cs->ArgumentType == NO_ARGUMENT )
  168. {
  169. // No value
  170. }
  171. else if ( cs->ArgumentType == SPACE_ARGUMENT )
  172. {
  173. if ( cc == this->Internals->Argv.size()-1 )
  174. {
  175. this->Internals->LastArgument --;
  176. return 0;
  177. }
  178. // Value is the next argument
  179. value = this->Internals->Argv[cc+1].c_str();
  180. cc ++;
  181. }
  182. else if ( cs->ArgumentType == EQUAL_ARGUMENT )
  183. {
  184. if ( arg.size() == sarg.size() || *(arg.c_str() + sarg.size()) != '=' )
  185. {
  186. this->Internals->LastArgument --;
  187. return 0;
  188. }
  189. // Value is everythng followed the '=' sign
  190. value = arg.c_str() + sarg.size()+1;
  191. }
  192. else if ( cs->ArgumentType == CONCAT_ARGUMENT )
  193. {
  194. // Value is whatever follows the argument
  195. value = arg.c_str() + sarg.size();
  196. }
  197. // Call the callback
  198. if ( cs->Callback )
  199. {
  200. if ( !cs->Callback(sarg.c_str(), value, cs->CallData) )
  201. {
  202. this->Internals->LastArgument --;
  203. return 0;
  204. }
  205. }
  206. if ( cs->Variable )
  207. {
  208. kwsys_stl::string var = "1";
  209. if ( value )
  210. {
  211. var = value;
  212. }
  213. if ( cs->VariableType == CommandLineArguments::INT_TYPE )
  214. {
  215. int* variable = static_cast<int*>(cs->Variable);
  216. char* res = 0;
  217. *variable = strtol(var.c_str(), &res, 10);
  218. //if ( res && *res )
  219. // {
  220. // Can handle non-int
  221. // }
  222. }
  223. else if ( cs->VariableType == CommandLineArguments::DOUBLE_TYPE )
  224. {
  225. double* variable = static_cast<double*>(cs->Variable);
  226. char* res = 0;
  227. *variable = strtod(var.c_str(), &res);
  228. //if ( res && *res )
  229. // {
  230. // Can handle non-int
  231. // }
  232. }
  233. else if ( cs->VariableType == CommandLineArguments::STRING_TYPE )
  234. {
  235. char** variable = static_cast<char**>(cs->Variable);
  236. if ( *variable )
  237. {
  238. delete [] *variable;
  239. *variable = 0;
  240. }
  241. *variable = new char[ strlen(var.c_str()) + 1 ];
  242. strcpy(*variable, var.c_str());
  243. }
  244. else if ( cs->VariableType == CommandLineArguments::STL_STRING_TYPE )
  245. {
  246. kwsys_stl::string* variable = static_cast<kwsys_stl::string*>(cs->Variable);
  247. *variable = var;
  248. }
  249. else if ( cs->VariableType == CommandLineArguments::BOOL_TYPE )
  250. {
  251. bool* variable = static_cast<bool*>(cs->Variable);
  252. if ( var == "1" || var == "ON" || var == "TRUE" || var == "true" || var == "on" ||
  253. var == "True" || var == "yes" || var == "Yes" || var == "YES" )
  254. {
  255. *variable = true;
  256. }
  257. else
  258. {
  259. *variable = false;
  260. }
  261. }
  262. else
  263. {
  264. kwsys_ios::cerr << "Got unknown argument type: \"" << cs->VariableType << "\"" << kwsys_ios::endl;
  265. this->Internals->LastArgument --;
  266. return 0;
  267. }
  268. }
  269. }
  270. else
  271. {
  272. // Handle unknown arguments
  273. if ( this->Internals->UnknownArgumentCallback )
  274. {
  275. if ( !this->Internals->UnknownArgumentCallback(arg.c_str(),
  276. this->Internals->ClientData) )
  277. {
  278. this->Internals->LastArgument --;
  279. return 0;
  280. }
  281. return 1;
  282. }
  283. else
  284. {
  285. kwsys_ios::cerr << "Got unknown argument: \"" << arg.c_str() << "\"" << kwsys_ios::endl;
  286. this->Internals->LastArgument --;
  287. return 0;
  288. }
  289. }
  290. }
  291. return 1;
  292. }
  293. //----------------------------------------------------------------------------
  294. void CommandLineArguments::GetRemainingArguments(int* argc, char*** argv)
  295. {
  296. CommandLineArguments::Internal::VectorOfStrings::size_type size
  297. = this->Internals->Argv.size() - this->Internals->LastArgument + 1;
  298. CommandLineArguments::Internal::VectorOfStrings::size_type cc;
  299. // Copy Argv0 as the first argument
  300. char** args = new char*[ size ];
  301. args[0] = new char[ this->Internals->Argv0.size() + 1 ];
  302. strcpy(args[0], this->Internals->Argv0.c_str());
  303. int cnt = 1;
  304. // Copy everything after the LastArgument, since that was not parsed.
  305. for ( cc = this->Internals->LastArgument+1;
  306. cc < this->Internals->Argv.size(); cc ++ )
  307. {
  308. args[cnt] = new char[ this->Internals->Argv[cc].size() + 1];
  309. strcpy(args[cnt], this->Internals->Argv[cc].c_str());
  310. cnt ++;
  311. }
  312. *argc = cnt;
  313. *argv = args;
  314. }
  315. //----------------------------------------------------------------------------
  316. void CommandLineArguments::AddCallback(const char* argument, ArgumentTypeEnum type,
  317. CallbackType callback, void* call_data, const char* help)
  318. {
  319. CommandLineArgumentsCallbackStructure s;
  320. s.Argument = argument;
  321. s.ArgumentType = type;
  322. s.Callback = callback;
  323. s.CallData = call_data;
  324. s.VariableType = CommandLineArguments::NO_VARIABLE_TYPE;
  325. s.Variable = 0;
  326. s.Help = help;
  327. this->Internals->Callbacks[argument] = s;
  328. this->GenerateHelp();
  329. }
  330. //----------------------------------------------------------------------------
  331. void CommandLineArguments::AddArgument(const char* argument, ArgumentTypeEnum type,
  332. VariableTypeEnum vtype, void* variable, const char* help)
  333. {
  334. CommandLineArgumentsCallbackStructure s;
  335. s.Argument = argument;
  336. s.ArgumentType = type;
  337. s.Callback = 0;
  338. s.CallData = 0;
  339. s.VariableType = vtype;
  340. s.Variable = variable;
  341. s.Help = help;
  342. this->Internals->Callbacks[argument] = s;
  343. this->GenerateHelp();
  344. }
  345. //----------------------------------------------------------------------------
  346. void CommandLineArguments::AddArgument(const char* argument, ArgumentTypeEnum type,
  347. int* variable, const char* help)
  348. {
  349. this->AddArgument(argument, type, CommandLineArguments::INT_TYPE, variable, help);
  350. }
  351. //----------------------------------------------------------------------------
  352. void CommandLineArguments::AddArgument(const char* argument, ArgumentTypeEnum type,
  353. double* variable, const char* help)
  354. {
  355. this->AddArgument(argument, type, CommandLineArguments::DOUBLE_TYPE, variable, help);
  356. }
  357. //----------------------------------------------------------------------------
  358. void CommandLineArguments::AddArgument(const char* argument, ArgumentTypeEnum type,
  359. char** variable, const char* help)
  360. {
  361. this->AddArgument(argument, type, CommandLineArguments::STRING_TYPE, variable, help);
  362. }
  363. //----------------------------------------------------------------------------
  364. void CommandLineArguments::AddArgument(const char* argument, ArgumentTypeEnum type,
  365. kwsys_stl::string* variable, const char* help)
  366. {
  367. this->AddArgument(argument, type, CommandLineArguments::STL_STRING_TYPE, variable, help);
  368. }
  369. //----------------------------------------------------------------------------
  370. void CommandLineArguments::AddArgument(const char* argument, ArgumentTypeEnum type,
  371. bool* variable, const char* help)
  372. {
  373. this->AddArgument(argument, type, CommandLineArguments::BOOL_TYPE, variable, help);
  374. }
  375. //----------------------------------------------------------------------------
  376. void CommandLineArguments::AddBooleanArgument(const char* argument, bool*
  377. variable, const char* help)
  378. {
  379. this->AddArgument(argument, CommandLineArguments::NO_ARGUMENT,
  380. CommandLineArguments::BOOL_TYPE, variable, help);
  381. }
  382. //----------------------------------------------------------------------------
  383. void CommandLineArguments::AddBooleanArgument(const char* argument, int*
  384. variable, const char* help)
  385. {
  386. this->AddArgument(argument, CommandLineArguments::NO_ARGUMENT,
  387. CommandLineArguments::INT_TYPE, variable, help);
  388. }
  389. //----------------------------------------------------------------------------
  390. void CommandLineArguments::SetClientData(void* client_data)
  391. {
  392. this->Internals->ClientData = client_data;
  393. }
  394. //----------------------------------------------------------------------------
  395. void CommandLineArguments::SetUnknownArgumentCallback(
  396. CommandLineArguments::ErrorCallbackType callback)
  397. {
  398. this->Internals->UnknownArgumentCallback = callback;
  399. }
  400. //----------------------------------------------------------------------------
  401. const char* CommandLineArguments::GetHelp(const char* arg)
  402. {
  403. CommandLineArguments::Internal::CallbacksMap::iterator it
  404. = this->Internals->Callbacks.find(arg);
  405. if ( it == this->Internals->Callbacks.end() )
  406. {
  407. return 0;
  408. }
  409. // Since several arguments may point to the same argument, find the one this
  410. // one point to if this one is pointing to another argument.
  411. CommandLineArgumentsCallbackStructure *cs = &(it->second);
  412. while ( 1 )
  413. {
  414. CommandLineArguments::Internal::CallbacksMap::iterator hit
  415. = this->Internals->Callbacks.find(cs->Help);
  416. if ( hit == this->Internals->Callbacks.end() )
  417. {
  418. break;
  419. }
  420. cs = &(hit->second);
  421. }
  422. return cs->Help;
  423. }
  424. //----------------------------------------------------------------------------
  425. void CommandLineArguments::SetLineLength(unsigned int ll)
  426. {
  427. if ( ll < 9 || ll > 1000 )
  428. {
  429. return;
  430. }
  431. this->LineLength = ll;
  432. this->GenerateHelp();
  433. }
  434. //----------------------------------------------------------------------------
  435. const char* CommandLineArguments::GetArgv0()
  436. {
  437. return this->Internals->Argv0.c_str();
  438. }
  439. //----------------------------------------------------------------------------
  440. unsigned int CommandLineArguments::GetLastArgument()
  441. {
  442. return this->Internals->LastArgument + 1;
  443. }
  444. //----------------------------------------------------------------------------
  445. void CommandLineArguments::GenerateHelp()
  446. {
  447. kwsys_ios::ostringstream str;
  448. // Collapse all arguments into the map of vectors of all arguments that do
  449. // the same thing.
  450. CommandLineArguments::Internal::CallbacksMap::iterator it;
  451. typedef kwsys_stl::map<CommandLineArguments::Internal::String,
  452. CommandLineArguments::Internal::SetOfStrings > MapArgs;
  453. MapArgs mp;
  454. MapArgs::iterator mpit, smpit;
  455. for ( it = this->Internals->Callbacks.begin();
  456. it != this->Internals->Callbacks.end();
  457. it ++ )
  458. {
  459. CommandLineArgumentsCallbackStructure *cs = &(it->second);
  460. mpit = mp.find(cs->Help);
  461. if ( mpit != mp.end() )
  462. {
  463. mpit->second.insert(it->first);
  464. mp[it->first].insert(it->first);
  465. }
  466. else
  467. {
  468. mp[it->first].insert(it->first);
  469. }
  470. }
  471. for ( it = this->Internals->Callbacks.begin();
  472. it != this->Internals->Callbacks.end();
  473. it ++ )
  474. {
  475. CommandLineArgumentsCallbackStructure *cs = &(it->second);
  476. mpit = mp.find(cs->Help);
  477. if ( mpit != mp.end() )
  478. {
  479. mpit->second.insert(it->first);
  480. smpit = mp.find(it->first);
  481. CommandLineArguments::Internal::SetOfStrings::iterator sit;
  482. for ( sit = smpit->second.begin(); sit != smpit->second.end(); sit++ )
  483. {
  484. mpit->second.insert(*sit);
  485. }
  486. mp.erase(smpit);
  487. }
  488. else
  489. {
  490. mp[it->first].insert(it->first);
  491. }
  492. }
  493. // Find the length of the longest string
  494. CommandLineArguments::Internal::String::size_type maxlen = 0;
  495. for ( mpit = mp.begin();
  496. mpit != mp.end();
  497. mpit ++ )
  498. {
  499. CommandLineArguments::Internal::SetOfStrings::iterator sit;
  500. for ( sit = mpit->second.begin(); sit != mpit->second.end(); sit++ )
  501. {
  502. CommandLineArguments::Internal::String::size_type clen = sit->size();
  503. switch ( this->Internals->Callbacks[*sit].ArgumentType )
  504. {
  505. case CommandLineArguments::NO_ARGUMENT: clen += 0; break;
  506. case CommandLineArguments::CONCAT_ARGUMENT: clen += 3; break;
  507. case CommandLineArguments::SPACE_ARGUMENT: clen += 4; break;
  508. case CommandLineArguments::EQUAL_ARGUMENT: clen += 4; break;
  509. }
  510. if ( clen > maxlen )
  511. {
  512. maxlen = clen;
  513. }
  514. }
  515. }
  516. // Create format for that string
  517. char format[80];
  518. sprintf(format, " %%-%ds ", static_cast<unsigned int>(maxlen));
  519. maxlen += 4; // For the space before and after the option
  520. // Print help for each option
  521. for ( mpit = mp.begin();
  522. mpit != mp.end();
  523. mpit ++ )
  524. {
  525. CommandLineArguments::Internal::SetOfStrings::iterator sit;
  526. for ( sit = mpit->second.begin(); sit != mpit->second.end(); sit++ )
  527. {
  528. str << kwsys_ios::endl;
  529. char argument[100];
  530. sprintf(argument, sit->c_str());
  531. switch ( this->Internals->Callbacks[*sit].ArgumentType )
  532. {
  533. case CommandLineArguments::NO_ARGUMENT: break;
  534. case CommandLineArguments::CONCAT_ARGUMENT: strcat(argument, "opt"); break;
  535. case CommandLineArguments::SPACE_ARGUMENT: strcat(argument, " opt"); break;
  536. case CommandLineArguments::EQUAL_ARGUMENT: strcat(argument, "=opt"); break;
  537. }
  538. char buffer[80];
  539. sprintf(buffer, format, argument);
  540. str << buffer;
  541. }
  542. const char* ptr = this->Internals->Callbacks[mpit->first].Help;
  543. int len = strlen(ptr);
  544. int cnt = 0;
  545. while ( len > 0)
  546. {
  547. // If argument with help is longer than line length, split it on previous
  548. // space (or tab) and continue on the next line
  549. CommandLineArguments::Internal::String::size_type cc;
  550. for ( cc = 0; ptr[cc]; cc ++ )
  551. {
  552. if ( *ptr == ' ' || *ptr == '\t' )
  553. {
  554. ptr ++;
  555. len --;
  556. }
  557. }
  558. if ( cnt > 0 )
  559. {
  560. for ( cc = 0; cc < maxlen; cc ++ )
  561. {
  562. str << " ";
  563. }
  564. }
  565. CommandLineArguments::Internal::String::size_type skip = len;
  566. if ( skip > this->LineLength - maxlen )
  567. {
  568. skip = this->LineLength - maxlen;
  569. for ( cc = skip-1; cc > 0; cc -- )
  570. {
  571. if ( ptr[cc] == ' ' || ptr[cc] == '\t' )
  572. {
  573. break;
  574. }
  575. }
  576. if ( cc != 0 )
  577. {
  578. skip = cc;
  579. }
  580. }
  581. str.write(ptr, skip);
  582. str << kwsys_ios::endl;
  583. ptr += skip;
  584. len -= skip;
  585. cnt ++;
  586. }
  587. }
  588. /*
  589. // This can help debugging help string
  590. str << endl;
  591. unsigned int cc;
  592. for ( cc = 0; cc < this->LineLength; cc ++ )
  593. {
  594. str << cc % 10;
  595. }
  596. str << endl;
  597. */
  598. this->Help = str.str();
  599. }
  600. } // namespace KWSYS_NAMESPACE