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