1 //==============================================================================
2 //
3 // This file is part of GPSTk, the GPS Toolkit.
4 //
5 // The GPSTk is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU Lesser General Public License as published
7 // by the Free Software Foundation; either version 3.0 of the License, or
8 // any later version.
9 //
10 // The GPSTk is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with GPSTk; if not, write to the Free Software Foundation,
17 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
18 //
19 // This software was developed by Applied Research Laboratories at the
20 // University of Texas at Austin.
21 // Copyright 2004-2020, The Board of Regents of The University of Texas System
22 //
23 //==============================================================================
24
25 //==============================================================================
26 //
27 // This software was developed by Applied Research Laboratories at the
28 // University of Texas at Austin, under contract to an agency or agencies
29 // within the U.S. Department of Defense. The U.S. Government retains all
30 // rights to use, duplicate, distribute, disclose, or release this software.
31 //
32 // Pursuant to DoD Directive 523024
33 //
34 // DISTRIBUTION STATEMENT A: This software has been approved for public
35 // release, distribution is unlimited.
36 //
37 //==============================================================================
38
39
40 // CommandLine.cpp Command line options and argument processing.
41 //
42 // NB LOG(DEBUG) (only) appears here, but --debug is not going to turn it on;
43 // for it to work, LOGlevel = ConfigureLOG:Level("DEBUG"); must be set explicitly
44 // before calling these routines; then debug will be set here and LOG(DEBUG) will work
45 // NB default for debug is -1
46
47 // TD what if a vector option is given a duplicate value? ignore - let caller handle
48 // TD does Validate test two targets are equal? do we care?
49 // TD consider adding '[or -<shortopt>]' to syntax description
50 // TD is there a bug if --debugN appears twice?
51 // TD other bool types
52 // typeBool: --opt => target = true;
53 // typeBoolValue: --opt T => target = true;
54 // and --opt F => target = false;
55 // typeBoolToggle: --opt => target = !target;
56
57 // system
58 #include <fstream>
59 #include <cmath>
60 // gpstk
61 #include "Exception.hpp"
62 #include "StringUtils.hpp"
63 #include "stl_helpers.hpp"
64 // geomatics
65 #include "logstream.hpp"
66 #include "expandtilde.hpp"
67
68 #include "CommandLine.hpp"
69
70 using namespace std;
71 using namespace gpstk;
72 using namespace StringUtils;
73
74 // -----------------------------------------------------------------------------------
75 // the main entry point
76 /**
77 * @throw Exception
78 */
ProcessCommandLine(int argc,char ** argv,string PrgmDesc,string & Usage,string & Errors,vector<string> & Unrecog)79 int CommandLine::ProcessCommandLine(int argc, char** argv, string PrgmDesc,
80 string& Usage, string& Errors, vector<string>& Unrecog)
81 {
82 try {
83 int i,j;
84 string option,word;
85
86 // if caller has set LOGlevel to DEBUG already, set debug here
87 if(ConfigureLOG::ReportingLevel() >= ConfigureLOG::Level("DEBUG")) {
88 debug = ConfigureLOG::ReportingLevel()-4;
89 LOG(DEBUG) << "CommandLine sets debug to "
90 << ConfigureLOG::ToString(ConfigureLOG::ReportingLevel());
91 }
92
93 // preliminaries ------------------------------------------------------
94 helponly = foundErrors = false;
95 Usage = string();
96 Errors = string();
97 Unrecog.clear();
98
99 if(syntaxPageBuilt == 0) {
100 word = string(argv[0]);
101 string::size_type pos = word.find_last_of("/\\");
102 if(pos != string::npos) word = word.substr(pos+1);
103 syntaxPage = "Usage: " + word + " [option] ...";
104 syntaxPageBuilt = 1;
105 }
106 if(syntaxPageBuilt == 1) {
107 syntaxPage += "\n" + PrgmDesc + "\n";
108 syntaxPageBuilt = 2;
109 }
110
111 // validate the command line ------------------------------------------
112 if(!ValidateCommandLine(word)) { // word is a dummy here
113 Errors = word;
114 return -3;
115 }
116
117 // build the syntax page ----------------------------------------------
118 BuildSyntaxPage();
119
120 // define usage by getting the syntax page
121 Usage = SyntaxPage();
122
123 // pre-processing -----------------------------------------------------
124 // no args means help
125 if(argc==1 && requireOneArg) helponly = true;
126
127 // PreProcessArgs pulls out help, debug, verbose, file, deprecated and ignore args
128 vector<string> Args;
129 for(j=1; j<argc; j++) PreProcessArgs(argv[j],Args,Errors);
130 LOG(DEBUG) << "Return from CommandLine::PreProcessArgs: help is "
131 << (help ? "T":"F") << " and helponly is " << (helponly ? "T":"F");
132
133 // PreProcessArgs may have removed all cmd line args....
134 if((requireOneArg && Args.size() == 0) || helponly) help = true;
135
136 if(debug >= 0) {
137 ostringstream oss;
138 oss << "CommandLine argument list passed to parser:" << endl;
139 for(size_t k=0; k<Args.size(); k++)
140 oss << " arg[" << k << "] = " << Args[k] << endl;
141
142 word = oss.str();
143 stripTrailing(word,'\n');
144 LOG(DEBUG) << word;
145 }
146
147 // parse the command line ----------------------------------------------
148 // fill values for each option, and Errors and Unrecog
149 Parse(Args, Errors, Unrecog);
150
151 // post-process: pull out the parsed input --------------------------------
152 Postprocess(Errors, Unrecog);
153
154 // help exit
155 if(help) return 1;
156
157 // error exit
158 if(!Errors.empty()) {
159 foundErrors = true;
160 return -1;
161 }
162
163 // normal exit
164 return 0;
165
166 } // end try
167 catch(Exception& e) { GPSTK_RETHROW(e); }
168 catch(std::exception& e) {
169 Exception E("std except: "+string(e.what())); GPSTK_THROW(E);
170 }
171 catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
172 }
173
174 // -----------------------------------------------------------------------------------
175 // public
176 /**
177 * @throw Exception
178 */
DumpConfiguration(ostream & os,string tag)179 void CommandLine::DumpConfiguration(ostream& os, string tag)
180 {
181 try {
182 size_t i,j;
183 double d;
184 string str;
185 RinexSatID sat;
186 vector<string> values;
187 string::size_type pos;
188
189 // loop over all options, output of the form:
190 // Description (--option) : value
191 for(i=0; i<options.size(); i++) {
192 if(!tag.empty()) os << tag << " ";
193 if(options[i].predesc.size()) {
194 str = options[i].predesc;
195 if(str[0] == '#' || str[0] == '\n') ; // do nothing
196 else str = string(" ") + str;
197 os << str << endl;
198 if(!tag.empty()) os << tag << " ";
199 }
200
201 str = options[i].desc;
202 os << " " << str << " (--" << options[i].longOpt << ") : ";
203
204 str = string();
205 switch(options[i].type) {
206 case typeBool:
207 os << (*((bool *)(options[i].p_output)) ? "true" : "false");
208 break;
209 case typeInt:
210 os << *((int *)(options[i].p_output));
211 break;
212 case typeVectorInt: {
213 vector<int> ints(*((vector<int> *)(options[i].p_output)));
214 if(ints.size() == 0)
215 os << "<none>";
216 else for(j=0; j<ints.size(); j++)
217 os << ints[j] << (j==ints.size()-1 ? "" : ",");
218 }
219 break;
220 case typeDouble:
221 d = *((double *)(options[i].p_output));
222 if(d == 0 || fabs(d) >= 0.1)
223 os << fixed << setprecision(2) << d;
224 else if(fabs(d) >= 0.01)
225 os << fixed << setprecision(3) << d;
226 else
227 os << scientific << setprecision(2) << d;
228 break;
229 case typeString:
230 if((*((string *)(options[i].p_output))).empty())
231 os << "<none>";
232 else
233 str = *((string *)(options[i].p_output));
234 break;
235 case typeVectorString:
236 values = *((vector<string> *)(options[i].p_output));
237 if(values.size() == 0)
238 os << "<none>";
239 else for(j=0; j<values.size(); j++)
240 str += values[j] + (j==values.size()-1 ? "" : ",");
241 break;
242 case typeSat: // RinexSatID
243 sat = *(RinexSatID *)(options[i].p_output);
244 if(sat.id == -1)
245 os << "<none>";
246 else
247 os << sat.toString();
248 break;
249 case typeVectorSat: { // vector<RinexSatID>
250 vector<RinexSatID> sats(*((vector<RinexSatID> *)(options[i].p_output)));
251 if(sats.size() == 0) os << "<none>";
252 else
253 for(j=0; j<sats.size(); j++)
254 str += sats[j].toString() + (j==sats.size()-1 ? "" : ",");
255 }
256 break;
257 case typeUndefined:
258 case typeCount:
259 break;
260 } // end switch(type)
261
262 if(!str.empty()) {
263 pos = str.find_first_of(" \t");
264 if(pos != string::npos) str = string("\"") + str + string("\"");
265 os << str;
266 }
267
268 os << endl;
269
270 } // end loop over options
271
272 // add verbose, debug and help
273 os << " Print extended output, including cmdline summary (--verbose) : "
274 << (verbose?"true":"false") << endl;
275 os << " Print debug output at level DEBUGn [n=0-7] (--debug<n>) : "
276 << debug << endl;
277 os << " Print this syntax page and quit (--help) : "
278 << (help?"true":"false") << endl;
279
280 } // end try
281 catch(Exception& e) { GPSTK_RETHROW(e); }
282 catch(std::exception& e) {
283 Exception E("std except: "+string(e.what())); GPSTK_THROW(E);
284 }
285 catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
286 }
287
288 // the rest are private
289 // -----------------------------------------------------------------------------------
290 /**
291 * @throw Exception
292 */
ValidateCommandLine(string & msg)293 bool CommandLine::ValidateCommandLine(string& msg)
294 {
295 try {
296 bool isValid(true);
297 size_t i,j;
298 string tag("Error (CommandLine): option ");
299 ostringstream oss;
300
301 LOG(DEBUG) << "CommandLine::ValidateCommandLine()";
302 // are any valid options also being ignored?
303 for(i=0; i<options.size(); i++) {
304 string opt("--" + options[i].longOpt);
305 //LOG(DEBUG) << "Is " << opt << " valid?";
306 if(vectorindex(ignore_opts_with_arg,opt) != -1) {
307 isValid = false;
308 oss << tag << options[i].longOpt
309 << " (with arg) is both valid and to be ignored.\n";
310 }
311 //LOG(DEBUG) << " ignore_opts_with_arg ok";
312 if(vectorindex(ignore_opts_without_arg,opt) != -1) {
313 isValid = false;
314 oss << tag << options[i].longOpt
315 << " (w/o arg) is both valid and to be ignored.\n";
316 }
317 //LOG(DEBUG) << " ignore_opts_without_arg ok";
318 if(vectorindex(ignore_on_opts,opt) != -1) {
319 isValid = false;
320 oss << tag << options[i].longOpt
321 << " is both valid and an 'ignore on' option.\n";
322 }
323 //LOG(DEBUG) << " ignore_on_opts ok";
324 if(vectorindex(ignore_off_opts,opt) != -1) {
325 isValid = false;
326 oss << tag << options[i].longOpt
327 << " is both valid and an 'ignore off' option.\n";
328 }
329 //LOG(DEBUG) << " ignore_off_opts ok";
330
331 for(j=0; j<i; j++) {
332 if(options[i].longOpt == options[j].longOpt) {
333 isValid = false;
334 oss << tag << "'" << options[i].longOpt << "' is repeated.\n";
335 }
336 if(options[i].shortOpt != 0 && options[i].shortOpt == options[j].shortOpt) {
337 isValid = false;
338 oss << tag << "'" << options[i].longOpt
339 << "' short form is already used in option '" << options[j].longOpt
340 << "'.\n";
341 }
342 }
343 }
344
345 // deprecated options
346 map<string,string>::const_iterator it;
347 for(it=deprec_opts.begin(); it != deprec_opts.end(); ++it) {
348 LOG(DEBUG) << "Test deprec option " << it->first << " -> " << it->second;
349 bool found(false);
350 for(i=0; i<options.size(); i++) {
351 if(it->second == string("--") + options[i].longOpt ||
352 it->second == string("-") + options[i].shortOpt)
353 { found=true; break; }
354 }
355 if(!found) {
356 isValid = false;
357 oss << tag << it->second << ", the replacement for deprecated option "
358 << it->first << ", is not found.\n";
359 }
360 }
361 //LOG(DEBUG) << " deprec_opts ok";
362
363 msg = oss.str();
364 if(!msg.empty()) { LOG(DEBUG) << "ValidateCommandLine finds " << msg; }
365
366 return isValid;
367
368 } // end try
369 catch(Exception& e) { GPSTK_RETHROW(e); }
370 catch(std::exception& e) {
371 Exception E("std except: "+string(e.what())); GPSTK_THROW(E);
372 }
373 catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
374 }
375
376 // -----------------------------------------------------------------------------------
377 /**
378 * @throw Exception
379 */
BuildSyntaxPage(void)380 void CommandLine::BuildSyntaxPage(void)
381 {
382 try {
383 size_t i,j,k;
384 double d;
385 string option,deflt;
386
387 // first determine the max size of the string '--option <arg>'
388 optionsize = 0; // member data
389 for(i=0; i<options.size(); i++) {
390 j = options[i].longOpt.length() + options[i].arg.length() + 7;
391 if(j > optionsize) optionsize = j;
392 }
393
394 // build the command line ----------------------------------------
395 for(i=0; i<options.size(); i++) {
396 // ignore invalid types
397 if(options[i].type < typeBool || options[i].type >= typeCount)
398 continue; // throw?
399
400 // get the padded string ' --option <arg> '
401 option = " --" + options[i].longOpt;
402 if(options[i].arg.length()) option += " <" + options[i].arg + "> ";
403 option = leftJustify(option,optionsize);
404
405 // get the default string
406 switch(options[i].type) {
407 case typeBool:
408 deflt = ( *(bool *)(options[i].p_output) ? "do" : "don't");
409 break;
410 case typeInt:
411 deflt = asString(*(int *)(options[i].p_output));
412 break;
413 case typeVectorInt: {
414 vector<int> *iptr = (vector<int> *)(options[i].p_output);
415 deflt = string();
416 for(j=0; j<iptr->size(); j++)
417 deflt += (j>0 ? ",":"") + asString((*iptr)[j]);
418 }
419 break;
420 case typeDouble:
421 d = *((double *)(options[i].p_output));
422 if(d == 0 || fabs(d) >= 0.1)
423 deflt = asString(d,2);
424 else if(fabs(d) >= 0.01)
425 deflt = asString(d,3);
426 else
427 {
428 deflt = floatFormat(d, FFLead::NonZero, 3, 2);
429 }
430 break;
431 case typeString:
432 deflt = *(string *)(options[i].p_output);
433 break;
434 case typeVectorString: {
435 vector<string> *sptr = (vector<string> *)(options[i].p_output);
436 deflt = string("");
437 for(j=0; j<sptr->size(); j++)
438 deflt += (j>0 ? ",":"") + (*sptr)[j];
439 }
440 break;
441 case typeSat: {
442 RinexSatID *gptr = (RinexSatID *)(options[i].p_output);
443 if(gptr->id != -1) deflt = (*gptr).toString();
444 else deflt = string("");
445 }
446 break;
447 case typeVectorSat: {
448 vector<RinexSatID> *gptr = (vector<RinexSatID> *)(options[i].p_output);
449 deflt = string("");
450 for(k=0,j=0; j<gptr->size(); j++)
451 if((*gptr)[j].id > 0)
452 deflt += (k++>0 ? ",":"") + (*gptr)[j].toString();
453 }
454 break;
455 case typeUndefined:
456 case typeCount:
457 break;
458 }
459
460 // build the syntax line
461 options[i].syntax = options[i].predesc
462 + (options[i].predesc.size() ? "\n" : "")
463 + option // --opt <arg>
464 + options[i].desc // desc
465 + (options[i].repeat ? " [repeatable]" : "") // add 'repeat'
466 + " (" + deflt + ")"; // add default
467 }
468
469 } // end try
470 catch(Exception& e) { GPSTK_RETHROW(e); }
471 catch(std::exception& e) {
472 Exception E("std except: "+string(e.what())); GPSTK_THROW(E);
473 }
474 catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
475 }
476
477 // -----------------------------------------------------------------------------------
478 // re-entrant!
479 /**
480 * @throw Exception
481 */
PreProcessArgs(const char * in_arg,vector<string> & Args,string & Errors)482 void CommandLine::PreProcessArgs(const char *in_arg, vector<string>& Args,
483 string& Errors)
484 {
485 try {
486 static bool found_cfg_file=false;
487 static bool ignore_opts=false; // ignore args for now...
488 static bool ignore_once=false; // ignore one argument
489 int i,k;
490 string msg,sarg(in_arg);
491
492 if(sarg == string()) return;
493 LOG(DEBUG) << "CommandLine::PreProcess arg " << sarg;
494
495 if(ignore_once) { ignore_once = false; return; }
496
497 // ignore these
498 for(i=0; i<ignore_opts_without_arg.size(); i++) { // note that -- is included
499 if(sarg == ignore_opts_without_arg[i]) {
500 LOG(DEBUG) << "CommandLine::PreProcess: ignoring option " << sarg;
501 return;
502 }
503 }
504 // ignore these and the follow arg
505 for(i=0; i<ignore_opts_with_arg.size(); i++) { // note that -- is included
506 if(sarg == ignore_opts_with_arg[i]) {
507 LOG(DEBUG) << "CommandLine::PreProcess: ignoring option " << sarg
508 << " and its argument";
509 ignore_once = true;
510 return;
511 }
512 }
513 // handle 'ignore on' args
514 for(i=0; i<ignore_on_opts.size(); i++) { // note that -- is included
515 if(sarg == ignore_on_opts[i]) {
516 LOG(DEBUG) << "CommandLine::PreProcess: start ignoring options: " << sarg;
517 ignore_opts = true;
518 return;
519 }
520 }
521 // handle 'ignore off' args
522 for(i=0; i<ignore_off_opts.size(); i++) { // note that -- is included
523 if(sarg == ignore_off_opts[i]) {
524 LOG(DEBUG) << "CommandLine::PreProcess: stop ignoring options: " << sarg;
525 ignore_opts = false;
526 return;
527 }
528 }
529 // it ignoring is 'on'
530 if(ignore_opts) {
531 LOG(DEBUG) << "CommandLine::PreProcess: ignoring option " << sarg;
532 return;
533 }
534
535 // config file
536 if(sarg == "--file" || sarg == "-f") // strip out before found_cfg_file
537 found_cfg_file = true;
538
539 // NB second test (in_arg[0]...) is for -f <file> embedded in another cfg file *1*
540 else if(found_cfg_file || (in_arg[0]=='-' && in_arg[1]=='f')) {
541 string filename(in_arg);
542 if(!found_cfg_file) filename.erase(0,2); else found_cfg_file = false;
543 LOG(DEBUG) << "CommandLine::PreProcess found a file of options: " << filename;
544 ifstream infile(filename.c_str());
545 if(!infile) {
546 LOG(ERROR) << "Error: could not open options file " << filename;
547 return;
548 }
549
550 bool again_cfg_file=false;
551 string buffer,word;
552 while(1) {
553 getline(infile,buffer);
554 stripTrailing(buffer,'\r');
555 //LOG(DEBUG) << "Line in file " << filename << " is " << buffer;
556
557 // process the buffer before checking eof or bad b/c there can be
558 // a line at EOF that has no CRLF...
559 while(!buffer.empty()) {
560 word = firstWord(buffer);
561 if(again_cfg_file) {
562 word = "-f" + word; // see above *1*
563 again_cfg_file = false;
564 PreProcessArgs(word.c_str(),Args,Errors);
565 }
566 else if(word[0] == '#') { // skip to end of line
567 buffer = "";
568 }
569 else if(word == "--file" || word == "-f")
570 again_cfg_file = true;
571 else if(word[0] == '"') {
572 word = stripFirstWord(buffer,'"');
573 buffer = "dummy " + buffer; // to be stripped below
574 PreProcessArgs(word.c_str(),Args,Errors);
575 }
576 else
577 PreProcessArgs(word.c_str(),Args,Errors);
578
579 word = stripFirstWord(buffer);
580 }
581 if(infile.eof() || !infile.good()) break;
582 } // end loop over file
583
584 ignore_opts = false;
585 }
586
587 // other args to strip out
588 else if(sarg==string("-h") || sarg==string("--help")) {
589 help = true;
590 LOG(DEBUG) << "CommandLine::PreProcess found help option";
591 }
592 else if(sarg.substr(0,2)==string("-d") || sarg.substr(0,7)==string("--debug")) {
593 string level("DEBUG");
594 if(sarg==string("-d") || sarg==string("--debug"))
595 debug = 0;
596 else
597 debug = asInt(sarg[1]=='d' ? sarg.substr(2) : sarg.substr(7));
598
599 if(debug >= 0 && debug <= 7) {
600 if(debug > 0) level += asString(debug);
601 ConfigureLOG::ReportingLevel() = ConfigureLOG::Level(level.c_str());
602 LOG(DEBUG) << "CommandLine found debug option at level " << debug
603 << ", logging level "
604 << ConfigureLOG::ToString(ConfigureLOG::ReportingLevel());
605 verbose = true; // NB debug implies verbose
606 }
607 }
608 else if(sarg==string("-v") || sarg==string("--verbose")) {
609 verbose = true;
610 // do NOT overwrite debug setting else serious subtle problems
611 if(debug == -1)
612 ConfigureLOG::ReportingLevel() = ConfigureLOG::Level("VERBOSE");
613 LOG(DEBUG) << "CommandLine::PreProcess found the verbose option";
614 }
615
616 // deprecated options
617 // note that -- is included in both key and value
618 else if(deprec_opts.find(sarg) != deprec_opts.end())
619 Args.push_back(deprec_opts[sarg]);
620
621 // regular argument
622 else {
623 LOG(DEBUG) << "CommandLine::PreProcess found regular arg " << sarg;
624 Args.push_back(sarg);
625 }
626 }
627 catch(Exception& e) { GPSTK_RETHROW(e); }
628 catch(std::exception& e) {
629 Exception E("std except: "+string(e.what())); GPSTK_THROW(E);
630 }
631 catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
632 }
633
634 // -----------------------------------------------------------------------------------
635 /**
636 * @throw Exception
637 */
expand_args(vector<string> & oldvalues,vector<string> & newvalues,string & msg)638 void expand_args(vector<string>& oldvalues, vector<string>& newvalues, string& msg)
639 {
640 try {
641 string arg;
642
643 for(size_t k=0; k<oldvalues.size(); k++) { // first split values on ','
644 while(!(arg = stripFirstWord(oldvalues[k],',')).empty()) {
645 if(arg.substr(0,1) == "@") { // list file
646 // remove the '@'
647 string filename(arg.substr(1));
648
649 // expand ~ in the filename
650 expand_filename(filename);
651
652 // open the file, read it and get new values (expandtilde.cpp)
653 if(!expand_list_file(filename,newvalues)) {
654 msg += " Error - Argument list file " + filename
655 + " could not be opened.";
656 continue;
657 }
658 else LOG(DEBUG) << "Opened arg list file " << filename;
659 }
660 else // just a value
661 newvalues.push_back(arg);
662
663 } // end while
664 } // end for
665 } // end try
666 catch(Exception& e) { GPSTK_RETHROW(e); }
667 catch(std::exception& e) {
668 Exception E("std except: "+string(e.what())); GPSTK_THROW(E);
669 }
670 catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
671 }
672
673 // -----------------------------------------------------------------------------------
674 // fill values for each option, and Errors and Unrecog
675 /**
676 * @throw Exception
677 */
Parse(vector<string> & Args,string & Errors,vector<string> & Unrecog)678 void CommandLine::Parse(vector<string>& Args, string& Errors, vector<string>& Unrecog)
679 {
680 try {
681 size_t i,j;
682 string arg,val;
683
684 // parse args, saving values -----------------------
685 i = 0;
686 while(i < Args.size()) {
687 arg = Args[i]; // arg
688 // does it match an option?
689 for(j=0; j<options.size(); ++j) {
690 if((arg.size() == 2 && arg[0] == '-' && arg[1] == options[j].shortOpt) ||
691 (arg.substr(0,2) == "--" && arg.substr(2) == options[j].longOpt))
692 { // match
693 if(options[j].type > typeBool) { // find value
694 if(i == Args.size()-1) {
695 Errors += "Error - option " + arg + " without value.\n";
696 break;
697 }
698 else if(Args[i+1].substr(0,2) == "--") {
699 Errors += "Error - option " + arg + " without value.\n";
700 break;
701 }
702 else val = Args[++i]; // value
703 }
704 else val = string("T"); // bool 'value'
705
706 LOG(DEBUG) << "CommandLine::Parse found arg[" << i << "] "
707 << arg << " = " << val;
708 options[j].values.push_back(val); // save it
709
710 break;
711 } // end match
712 } // end loop over options
713
714 if(j == options.size()) {
715 Unrecog.push_back(Args[i]);
716 LOG(DEBUG) << "CommandLine::Parse found unrecognized arg[" << i << "] "
717 << Args[i];
718 }
719
720 // next arg
721 ++i;
722
723 } // end loop over args
724
725 // were all required options found? -----------------------
726 // were any non-repeatable options repeated? --------------
727 for(j=0; j<options.size(); ++j) {
728
729 // do this here, after PreProcArguments() and before the code below
730 // define callers special targets, using values found in PreProcessArgs
731 if(help && options[j].longOpt == string("help")) {
732 LOG(DEBUG) << "CommandLine::Parse found help option and help";
733 *((bool *)(options[j].p_output)) = help;
734 options[j].values.push_back(string("T"));
735 }
736 else if(verbose && options[j].longOpt == string("verbose")) {
737 LOG(DEBUG) << "CommandLine::Parse found verbose option and verbose";
738 *((bool *)(options[j].p_output)) = verbose;
739 options[j].values.push_back(string("T"));
740 }
741 else if(debug > -1 && options[j].longOpt == string("debug")) {
742 LOG(DEBUG) << "CommandLine::Parse found debug option and debug = " << debug;
743 *((int *)(options[j].p_output)) = debug;
744 options[j].values.push_back(asString(debug));
745 }
746
747 // required options that are not found
748 if(options[j].required && options[j].values.size() == 0)
749 Errors += "Required option " + options[j].longOpt + " is not found.\n";
750
751 // non-repeatable options that are repeated
752 if(!options[j].repeat && options[j].values.size() > 1)
753 Errors += "Not-repeatable option "+options[j].longOpt+" was repeated.\n";
754 }
755
756 }
757 catch(Exception& e) { GPSTK_RETHROW(e); }
758 catch(std::exception& e) {
759 Exception E("std except: "+string(e.what())); GPSTK_THROW(E);
760 }
761 catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
762 }
763
764 // -----------------------------------------------------------------------------------
765 /**
766 * @throw Exception
767 */
SyntaxPage(void)768 string CommandLine::SyntaxPage(void)
769 {
770 try {
771 if(syntaxPageBuilt == 2) {
772 if(options.size() > 0)
773 for(int i=0; i<options.size(); i++)
774 if(options[i].doc)
775 syntaxPage += options[i].syntax + "\n";
776
777 // add verbose, debug and help, which are always there...
778 syntaxPage += //"# (automatic options)\n" +
779 leftJustify(" --verbose",optionsize)
780 + "Print extended output, including cmdline summary (don't)\n";
781 syntaxPage += leftJustify(" --debug<n>",optionsize)
782 + "Print debug output at LOGlevel n [n=0-7] (-1)\n";
783 syntaxPage += leftJustify(" --help",optionsize)
784 + "Print this syntax page and quit (don't)";
785
786 syntaxPageBuilt = 3;
787 }
788
789 return syntaxPage;
790 }
791 catch(Exception& e) { GPSTK_RETHROW(e); }
792 catch(std::exception& e) {
793 Exception E("std except: "+string(e.what())); GPSTK_THROW(E);
794 }
795 catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
796 }
797
798 // -----------------------------------------------------------------------------------
799 /**
800 * @throw Exception
801 */
Postprocess(string & Errors,vector<string> & Unrecog)802 void CommandLine::Postprocess(string& Errors, vector<string>& Unrecog)
803 {
804 try {
805 size_t i,k;
806 RinexSatID sat;
807 string msg,errStr;
808 vector<string> values;
809 ostringstream oss;
810
811 // loop over options, parse out @file and value,value, and assign targets
812 for(i=0; i<options.size(); i++) {
813
814 // was the option even found on the command line?
815 if(options[i].values.size() == 0) continue;
816 LOG(DEBUG) << "CommandLine::Postprocess parse " << options[i].longOpt
817 << " of type " << options[i].type << (options[i].doc ? "":" undocumented");
818
819 // bool is special b/c values are ignored
820 if(options[i].type == typeBool) {
821 *((bool *)(options[i].p_output)) = ( options[i].toggle ?
822 ! (*((bool *)(options[i].p_output))) : true );
823 continue;
824 }
825
826 // get the array of values (strings)
827 values = options[i].values;
828
829 // parse out 'value,value' and '@file.lst'
830 if(options[i].expand && (options[i].type == typeVectorString ||
831 options[i].type == typeVectorSat ||
832 options[i].type == typeVectorInt))
833 {
834 vector<string> nvalues;
835 expand_args(values, nvalues, errStr);
836 if(!errStr.empty())
837 oss << errStr << " for option --" << options[i].longOpt << "\n";
838 values = nvalues;
839 }
840
841 switch(options[i].type) {
842 case typeBool: // bool -- done above
843 break;
844
845 case typeInt:
846 if(!isDigitString(values[0]))
847 oss << "Error: non-integer value for --" << options[i].longOpt
848 << ": " << values[0] << endl;
849 else
850 *((int *)(options[i].p_output)) = asInt(values[0]);
851 break;
852
853 case typeVectorInt:
854 for(k=0; k<values.size(); k++) {
855 if(!isDigitString(values[k]))
856 oss << "Error: non-integer value for --" << options[i].longOpt
857 << ": " << values[k] << endl;
858 else
859 ((vector<int> *)(options[i].p_output))->push_back(asInt(values[k]));
860 }
861 break;
862
863 case typeDouble:
864 if(!isScientificString(values[0]))
865 oss << "Error: invalid value for --" << options[i].longOpt
866 << ": " << values[0] << endl;
867 else
868 *((double *)(options[i].p_output))=asDouble(values[0]);
869 break;
870
871 case typeString:
872 *((string *)(options[i].p_output)) = values[0];
873 break;
874
875 case typeVectorString:
876 for(k=0; k<values.size(); k++)
877 ((vector<string> *)(options[i].p_output))->push_back(values[k]);
878 break;
879
880 case typeSat:
881 sat = RinexSatID(values[0]);
882 *(RinexSatID *)(options[i].p_output) = sat;
883 break;
884
885 case typeVectorSat:
886 for(k=0; k<values.size(); k++) {
887 sat = RinexSatID(values[k]);
888 ((vector<RinexSatID> *)(options[i].p_output))->push_back(sat);
889 }
890 break;
891 case typeUndefined:
892 case typeCount:
893 break;
894 }
895
896 } // end loop over options
897
898 // NO! ResCor has commas in REdit cmds.
899 // parse out @file and value,value from unrecognized args
900 //vector<string> nvalues;
901 //expand_args(Unrecog, nvalues, errStr);
902 //if(!errStr.empty()) Errors += errStr + " (an unrecognized arg)\n";
903 //Unrecog = nvalues;
904
905 Errors += oss.str();
906
907 } // end try
908 catch(Exception& e) { GPSTK_RETHROW(e); }
909 catch(std::exception& e) {
910 Exception E("std except: "+string(e.what())); GPSTK_THROW(E);
911 }
912 catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
913 }
914
915 // -----------------------------------------------------------------------------------
916 // -----------------------------------------------------------------------------------
917