1 /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2 *
3 * This library is open source and may be redistributed and/or modified under
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version. The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * OpenSceneGraph Public License for more details.
12 */
13
14 #include <stdlib.h>
15 #include <string.h>
16
17 #include <osg/ArgumentParser>
18 #include <osg/ApplicationUsage>
19 #include <osg/Math>
20 #include <osg/Notify>
21
22 #include <set>
23 #include <iostream>
24
25 using namespace osg;
26
isOption(const char * str)27 bool ArgumentParser::isOption(const char* str)
28 {
29 return str && str[0]=='-';
30 }
31
isString(const char * str)32 bool ArgumentParser::isString(const char* str)
33 {
34 if (!str) return false;
35 return true;
36 //return !isOption(str);
37 }
38
isBool(const char * str)39 bool ArgumentParser::isBool(const char* str)
40 {
41 if (!str) return false;
42
43 return (strcmp(str,"True")==0 || strcmp(str,"true")==0 || strcmp(str,"TRUE")==0 ||
44 strcmp(str,"False")==0 || strcmp(str,"false")==0 || strcmp(str,"FALSE")==0 ||
45 strcmp(str,"0")==0 || strcmp(str,"1")==0);
46 }
47
isNumber(const char * str)48 bool ArgumentParser::isNumber(const char* str)
49 {
50 if (!str) return false;
51
52 bool hadPlusMinus = false;
53 bool hadDecimalPlace = false;
54 bool hadExponent = false;
55 bool couldBeInt = true;
56 bool couldBeFloat = true;
57 int noZeroToNine = 0;
58
59 const char* ptr = str;
60
61 // check if could be a hex number.
62 if (strncmp(ptr,"0x",2)==0)
63 {
64 // skip over leading 0x, and then go through rest of string
65 // checking to make sure all values are 0...9 or a..f.
66 ptr+=2;
67 while (
68 *ptr!=0 &&
69 ((*ptr>='0' && *ptr<='9') ||
70 (*ptr>='a' && *ptr<='f') ||
71 (*ptr>='A' && *ptr<='F'))
72 )
73 {
74 ++ptr;
75 }
76
77 // got to end of string without failure, therefore must be a hex integer.
78 if (*ptr==0) return true;
79 }
80
81 ptr = str;
82
83 // check if a float or an int.
84 while (*ptr!=0 && couldBeFloat)
85 {
86 if (*ptr=='+' || *ptr=='-')
87 {
88 if (hadPlusMinus)
89 {
90 couldBeInt = false;
91 couldBeFloat = false;
92 } else hadPlusMinus = true;
93 }
94 else if (*ptr>='0' && *ptr<='9')
95 {
96 noZeroToNine++;
97 }
98 else if (*ptr=='.')
99 {
100 if (hadDecimalPlace)
101 {
102 couldBeInt = false;
103 couldBeFloat = false;
104 }
105 else
106 {
107 hadDecimalPlace = true;
108 couldBeInt = false;
109 }
110 }
111 else if (*ptr=='e' || *ptr=='E')
112 {
113 if (hadExponent || noZeroToNine==0)
114 {
115 couldBeInt = false;
116 couldBeFloat = false;
117 }
118 else
119 {
120 hadExponent = true;
121 couldBeInt = false;
122 hadDecimalPlace = false;
123 hadPlusMinus = false;
124 noZeroToNine=0;
125 }
126 }
127 else
128 {
129 couldBeInt = false;
130 couldBeFloat = false;
131 }
132 ++ptr;
133 }
134
135 if (couldBeInt && noZeroToNine>0) return true;
136 if (couldBeFloat && noZeroToNine>0) return true;
137
138 return false;
139
140 }
141
valid(const char * str) const142 bool ArgumentParser::Parameter::valid(const char* str) const
143 {
144 switch(_type)
145 {
146 case Parameter::BOOL_PARAMETER: return isBool(str); break;
147 case Parameter::FLOAT_PARAMETER: return isNumber(str); break;
148 case Parameter::DOUBLE_PARAMETER: return isNumber(str); break;
149 case Parameter::INT_PARAMETER: return isNumber(str); break;
150 case Parameter::UNSIGNED_INT_PARAMETER: return isNumber(str); break;
151 case Parameter::STRING_PARAMETER: return isString(str); break;
152 }
153 return false;
154 }
155
assign(const char * str)156 bool ArgumentParser::Parameter::assign(const char* str)
157 {
158 if (valid(str))
159 {
160 switch(_type)
161 {
162 case Parameter::BOOL_PARAMETER:
163 {
164 *_value._bool = (strcmp(str,"True")==0 || strcmp(str,"true")==0 || strcmp(str,"TRUE")==0);
165 break;
166 }
167 case Parameter::FLOAT_PARAMETER: *_value._float = osg::asciiToFloat(str); break;
168 case Parameter::DOUBLE_PARAMETER: *_value._double = osg::asciiToDouble(str); break;
169 case Parameter::INT_PARAMETER: *_value._int = atoi(str); break;
170 case Parameter::UNSIGNED_INT_PARAMETER: *_value._uint = atoi(str); break;
171 case Parameter::STRING_PARAMETER: *_value._string = str; break;
172 }
173 return true;
174 }
175 else
176 {
177 return false;
178 }
179 }
180
181
182
ArgumentParser(int * argc,char ** argv)183 ArgumentParser::ArgumentParser(int* argc,char **argv):
184 _argc(argc),
185 _argv(argv),
186 _usage(ApplicationUsage::instance())
187 {
188 #ifdef __APPLE__
189 //On OSX, any -psn arguments need to be removed because they will
190 // confuse the application. -psn plus a concatenated argument are
191 // passed by the finder to application bundles
192 for(int pos=1;pos<this->argc();++pos)
193 {
194 if (std::string(_argv[pos]).compare(0, 4, std::string("-psn")) == 0)
195 {
196 remove(pos, 1);
197 }
198 }
199 #endif
200
201 #ifdef WIN32
202 // Remove linefeed from last argument if it exist
203 char* lastline = argc==0 ? 0 : _argv[*argc-1];
204 if (lastline)
205 {
206 int len = strlen(lastline);
207 if (len>0 && lastline[len-1] == '\n') lastline[len-1]= '\0';
208 }
209 #endif
210 }
211
getApplicationName() const212 std::string ArgumentParser::getApplicationName() const
213 {
214 if (_argc && *_argc>0 ) return std::string(_argv[0]);
215 return "";
216 }
217
218
isOption(int pos) const219 bool ArgumentParser::isOption(int pos) const
220 {
221 return pos<*_argc && isOption(_argv[pos]);
222 }
223
isString(int pos) const224 bool ArgumentParser::isString(int pos) const
225 {
226 return pos < *_argc && isString(_argv[pos]);
227 }
228
isNumber(int pos) const229 bool ArgumentParser::isNumber(int pos) const
230 {
231 return pos < *_argc && isNumber(_argv[pos]);
232 }
233
234
find(const std::string & str) const235 int ArgumentParser::find(const std::string& str) const
236 {
237 for(int pos=1;pos<*_argc;++pos)
238 {
239 if (str==_argv[pos])
240 {
241 return pos;
242 }
243 }
244 return -1;
245 }
246
match(int pos,const std::string & str) const247 bool ArgumentParser::match(int pos, const std::string& str) const
248 {
249 return pos<*_argc && str==_argv[pos];
250 }
251
252
containsOptions() const253 bool ArgumentParser::containsOptions() const
254 {
255 for(int pos=1;pos<*_argc;++pos)
256 {
257 if (isOption(pos)) return true;
258 }
259 return false;
260 }
261
262
remove(int pos,int num)263 void ArgumentParser::remove(int pos,int num)
264 {
265 if (num==0) return;
266
267 for(;pos+num<*_argc;++pos)
268 {
269 _argv[pos]=_argv[pos+num];
270 }
271 for(;pos<*_argc;++pos)
272 {
273 _argv[pos]=0;
274 }
275 *_argc-=num;
276 }
277
read(const std::string & str)278 bool ArgumentParser::read(const std::string& str)
279 {
280 int pos=find(str);
281 if (pos<=0) return false;
282 remove(pos);
283 return true;
284 }
285
read(const std::string & str,Parameter value1)286 bool ArgumentParser::read(const std::string& str, Parameter value1)
287 {
288 int pos=find(str);
289 if (pos<=0) return false;
290 return read(pos,str,value1);
291 }
292
read(const std::string & str,Parameter value1,Parameter value2)293 bool ArgumentParser::read(const std::string& str, Parameter value1, Parameter value2)
294 {
295 int pos=find(str);
296 if (pos<=0) return false;
297 return read(pos,str,value1, value2);
298 }
299
read(const std::string & str,Parameter value1,Parameter value2,Parameter value3)300 bool ArgumentParser::read(const std::string& str, Parameter value1, Parameter value2, Parameter value3)
301 {
302 int pos=find(str);
303 if (pos<=0) return false;
304 return read(pos,str,value1, value2, value3);
305 }
306
read(const std::string & str,Parameter value1,Parameter value2,Parameter value3,Parameter value4)307 bool ArgumentParser::read(const std::string& str, Parameter value1, Parameter value2, Parameter value3, Parameter value4)
308 {
309 int pos=find(str);
310 if (pos<=0) return false;
311 return read(pos,str,value1, value2, value3, value4);
312 }
313
read(const std::string & str,Parameter value1,Parameter value2,Parameter value3,Parameter value4,Parameter value5)314 bool ArgumentParser::read(const std::string& str, Parameter value1, Parameter value2, Parameter value3, Parameter value4, Parameter value5)
315 {
316 int pos=find(str);
317 if (pos<=0) return false;
318 return read(pos,str,value1, value2, value3, value4, value5);
319 }
320
read(const std::string & str,Parameter value1,Parameter value2,Parameter value3,Parameter value4,Parameter value5,Parameter value6)321 bool ArgumentParser::read(const std::string& str, Parameter value1, Parameter value2, Parameter value3, Parameter value4, Parameter value5, Parameter value6)
322 {
323 int pos=find(str);
324 if (pos<=0) return false;
325 return read(pos,str,value1, value2, value3, value4, value5, value6);
326 }
327
read(const std::string & str,Parameter value1,Parameter value2,Parameter value3,Parameter value4,Parameter value5,Parameter value6,Parameter value7)328 bool ArgumentParser::read(const std::string& str, Parameter value1, Parameter value2, Parameter value3, Parameter value4, Parameter value5, Parameter value6, Parameter value7)
329 {
330 int pos=find(str);
331 if (pos<=0) return false;
332 return read(pos,str,value1, value2, value3, value4, value5, value6, value7);
333 }
334
read(const std::string & str,Parameter value1,Parameter value2,Parameter value3,Parameter value4,Parameter value5,Parameter value6,Parameter value7,Parameter value8)335 bool ArgumentParser::read(const std::string& str, Parameter value1, Parameter value2, Parameter value3, Parameter value4, Parameter value5, Parameter value6, Parameter value7, Parameter value8)
336 {
337 int pos=find(str);
338 if (pos<=0) return false;
339 return read(pos,str,value1, value2, value3, value4, value5, value6, value7, value8);
340 }
341
342 /** if the argument value at the position pos matches specified string, and subsequent
343 * Parameters are also matched then set the Parameter values and remove the from the list of arguments.*/
read(int pos,const std::string & str)344 bool ArgumentParser::read(int pos, const std::string& str)
345 {
346 if (match(pos,str))
347 {
348 remove(pos,1);
349 return true;
350 }
351 return false;
352 }
353
read(int pos,const std::string & str,Parameter value1)354 bool ArgumentParser::read(int pos, const std::string& str, Parameter value1)
355 {
356 if (match(pos,str))
357 {
358 if ((pos+1)<*_argc)
359 {
360 if (value1.valid(_argv[pos+1]))
361 {
362 value1.assign(_argv[pos+1]);
363 remove(pos,2);
364 return true;
365 }
366 reportError("argument to `"+str+"` is not valid");
367 return false;
368 }
369 reportError("argument to `"+str+"` is missing");
370 return false;
371 }
372 return false;
373 }
374
read(int pos,const std::string & str,Parameter value1,Parameter value2)375 bool ArgumentParser::read(int pos, const std::string& str, Parameter value1, Parameter value2)
376 {
377 if (match(pos,str))
378 {
379 if ((pos+2)<*_argc)
380 {
381 if (value1.valid(_argv[pos+1]) &&
382 value2.valid(_argv[pos+2]))
383 {
384 value1.assign(_argv[pos+1]);
385 value2.assign(_argv[pos+2]);
386 remove(pos,3);
387 return true;
388 }
389 reportError("argument to `"+str+"` is not valid");
390 return false;
391 }
392 reportError("argument to `"+str+"` is missing");
393 return false;
394 }
395 return false;
396 }
397
read(int pos,const std::string & str,Parameter value1,Parameter value2,Parameter value3)398 bool ArgumentParser::read(int pos, const std::string& str, Parameter value1, Parameter value2, Parameter value3)
399 {
400 if (match(pos,str))
401 {
402 if ((pos+3)<*_argc)
403 {
404 if (value1.valid(_argv[pos+1]) &&
405 value2.valid(_argv[pos+2]) &&
406 value3.valid(_argv[pos+3]))
407 {
408 value1.assign(_argv[pos+1]);
409 value2.assign(_argv[pos+2]);
410 value3.assign(_argv[pos+3]);
411 remove(pos,4);
412 return true;
413 }
414 reportError("argument to `"+str+"` is not valid");
415 return false;
416 }
417 reportError("argument to `"+str+"` is missing");
418 return false;
419 }
420 return false;
421 }
422
read(int pos,const std::string & str,Parameter value1,Parameter value2,Parameter value3,Parameter value4)423 bool ArgumentParser::read(int pos, const std::string& str, Parameter value1, Parameter value2, Parameter value3, Parameter value4)
424 {
425 if (match(pos,str))
426 {
427 if ((pos+4)<*_argc)
428 {
429 if (value1.valid(_argv[pos+1]) &&
430 value2.valid(_argv[pos+2]) &&
431 value3.valid(_argv[pos+3]) &&
432 value4.valid(_argv[pos+4]))
433 {
434 value1.assign(_argv[pos+1]);
435 value2.assign(_argv[pos+2]);
436 value3.assign(_argv[pos+3]);
437 value4.assign(_argv[pos+4]);
438 remove(pos,5);
439 return true;
440 }
441 reportError("argument to `"+str+"` is not valid");
442 return false;
443 }
444 reportError("argument to `"+str+"` is missing");
445 return false;
446 }
447 return false;
448 }
449
read(int pos,const std::string & str,Parameter value1,Parameter value2,Parameter value3,Parameter value4,Parameter value5)450 bool ArgumentParser::read(int pos, const std::string& str, Parameter value1, Parameter value2, Parameter value3, Parameter value4, Parameter value5)
451 {
452 if (match(pos,str))
453 {
454 if ((pos+5)<*_argc)
455 {
456 if (value1.valid(_argv[pos+1]) &&
457 value2.valid(_argv[pos+2]) &&
458 value3.valid(_argv[pos+3]) &&
459 value4.valid(_argv[pos+4]) &&
460 value5.valid(_argv[pos+5]))
461 {
462 value1.assign(_argv[pos+1]);
463 value2.assign(_argv[pos+2]);
464 value3.assign(_argv[pos+3]);
465 value4.assign(_argv[pos+4]);
466 value5.assign(_argv[pos+5]);
467 remove(pos,6);
468 return true;
469 }
470 reportError("argument to `"+str+"` is not valid");
471 return false;
472 }
473 reportError("argument to `"+str+"` is missing");
474 return false;
475 }
476 return false;
477 }
478
read(int pos,const std::string & str,Parameter value1,Parameter value2,Parameter value3,Parameter value4,Parameter value5,Parameter value6)479 bool ArgumentParser::read(int pos, const std::string& str, Parameter value1, Parameter value2, Parameter value3, Parameter value4, Parameter value5, Parameter value6)
480 {
481 if (match(pos,str))
482 {
483 if ((pos+6)<*_argc)
484 {
485 if (value1.valid(_argv[pos+1]) &&
486 value2.valid(_argv[pos+2]) &&
487 value3.valid(_argv[pos+3]) &&
488 value4.valid(_argv[pos+4]) &&
489 value5.valid(_argv[pos+5]) &&
490 value6.valid(_argv[pos+6]))
491 {
492 value1.assign(_argv[pos+1]);
493 value2.assign(_argv[pos+2]);
494 value3.assign(_argv[pos+3]);
495 value4.assign(_argv[pos+4]);
496 value5.assign(_argv[pos+5]);
497 value6.assign(_argv[pos+6]);
498 remove(pos,7);
499 return true;
500 }
501 reportError("argument to `"+str+"` is not valid");
502 return false;
503 }
504 reportError("argument to `"+str+"` is missing");
505 return false;
506 }
507 return false;
508 }
509
read(int pos,const std::string & str,Parameter value1,Parameter value2,Parameter value3,Parameter value4,Parameter value5,Parameter value6,Parameter value7)510 bool ArgumentParser::read(int pos, const std::string& str, Parameter value1, Parameter value2, Parameter value3, Parameter value4, Parameter value5, Parameter value6, Parameter value7)
511 {
512 if (match(pos,str))
513 {
514 if ((pos+7)<*_argc)
515 {
516 if (value1.valid(_argv[pos+1]) &&
517 value2.valid(_argv[pos+2]) &&
518 value3.valid(_argv[pos+3]) &&
519 value4.valid(_argv[pos+4]) &&
520 value5.valid(_argv[pos+5]) &&
521 value6.valid(_argv[pos+6]) &&
522 value7.valid(_argv[pos+7]))
523 {
524 value1.assign(_argv[pos+1]);
525 value2.assign(_argv[pos+2]);
526 value3.assign(_argv[pos+3]);
527 value4.assign(_argv[pos+4]);
528 value5.assign(_argv[pos+5]);
529 value6.assign(_argv[pos+6]);
530 value7.assign(_argv[pos+7]);
531 remove(pos,8);
532 return true;
533 }
534 reportError("argument to `"+str+"` is not valid");
535 return false;
536 }
537 reportError("argument to `"+str+"` is missing");
538 return false;
539 }
540 return false;
541 }
542
read(int pos,const std::string & str,Parameter value1,Parameter value2,Parameter value3,Parameter value4,Parameter value5,Parameter value6,Parameter value7,Parameter value8)543 bool ArgumentParser::read(int pos, const std::string& str, Parameter value1, Parameter value2, Parameter value3, Parameter value4, Parameter value5, Parameter value6, Parameter value7, Parameter value8)
544 {
545 if (match(pos,str))
546 {
547 if ((pos+8)<*_argc)
548 {
549 if (value1.valid(_argv[pos+1]) &&
550 value2.valid(_argv[pos+2]) &&
551 value3.valid(_argv[pos+3]) &&
552 value4.valid(_argv[pos+4]) &&
553 value5.valid(_argv[pos+5]) &&
554 value6.valid(_argv[pos+6]) &&
555 value7.valid(_argv[pos+7]) &&
556 value8.valid(_argv[pos+8]))
557 {
558 value1.assign(_argv[pos+1]);
559 value2.assign(_argv[pos+2]);
560 value3.assign(_argv[pos+3]);
561 value4.assign(_argv[pos+4]);
562 value5.assign(_argv[pos+5]);
563 value6.assign(_argv[pos+6]);
564 value7.assign(_argv[pos+7]);
565 value8.assign(_argv[pos+8]);
566 remove(pos,9);
567 return true;
568 }
569 reportError("argument to `"+str+"` is not valid");
570 return false;
571 }
572 reportError("argument to `"+str+"` is missing");
573 return false;
574 }
575 return false;
576 }
577
errors(ErrorSeverity severity) const578 bool ArgumentParser::errors(ErrorSeverity severity) const
579 {
580 for(ErrorMessageMap::const_iterator itr=_errorMessageMap.begin();
581 itr!=_errorMessageMap.end();
582 ++itr)
583 {
584 if (itr->second>=severity) return true;
585 }
586 return false;
587 }
588
reportError(const std::string & message,ErrorSeverity severity)589 void ArgumentParser::reportError(const std::string& message,ErrorSeverity severity)
590 {
591 _errorMessageMap[message]=severity;
592 }
593
reportRemainingOptionsAsUnrecognized(ErrorSeverity severity)594 void ArgumentParser::reportRemainingOptionsAsUnrecognized(ErrorSeverity severity)
595 {
596 std::set<std::string> options;
597 if (_usage.valid())
598 {
599 // parse the usage options to get all the option that the application can potential handle.
600 for(ApplicationUsage::UsageMap::const_iterator itr=_usage->getCommandLineOptions().begin();
601 itr!=_usage->getCommandLineOptions().end();
602 ++itr)
603 {
604 const std::string& option = itr->first;
605 std::string::size_type prevpos = 0, pos = 0;
606 while ((pos=option.find(' ',prevpos))!=std::string::npos)
607 {
608 if (option[prevpos]=='-')
609 {
610 options.insert(std::string(option,prevpos,pos-prevpos));
611 }
612 prevpos=pos+1;
613 }
614 if (option[prevpos]=='-')
615 {
616
617 options.insert(std::string(option,prevpos,std::string::npos));
618 }
619 }
620
621 }
622
623 for(int pos=1;pos<argc();++pos)
624 {
625 // if an option and haven't been previous querried for report as unrecognized.
626 if (isOption(pos) && options.find(_argv[pos])==options.end())
627 {
628 reportError(std::string("unrecognized option ")+std::string(_argv[pos]),severity);
629 }
630 }
631 }
writeErrorMessages(std::ostream & output,ErrorSeverity severity)632 void ArgumentParser::writeErrorMessages(std::ostream& output,ErrorSeverity severity)
633 {
634 for(ErrorMessageMap::iterator itr=_errorMessageMap.begin();
635 itr!=_errorMessageMap.end();
636 ++itr)
637 {
638 if (itr->second>=severity)
639 {
640 output<< getApplicationName() << ": " << itr->first << std::endl;
641 }
642 }
643 }
644
readHelpType()645 ApplicationUsage::Type ArgumentParser::readHelpType()
646 {
647 getApplicationUsage()->addCommandLineOption("-h or --help","Display command line parameters");
648 getApplicationUsage()->addCommandLineOption("--help-env","Display environmental variables available");
649 getApplicationUsage()->addCommandLineOption("--help-keys","Display keyboard & mouse bindings available");
650 getApplicationUsage()->addCommandLineOption("--help-all","Display all command line, env vars and keyboard & mouse bindings.");
651
652 // if user request help write it out to cout.
653 if (read("--help-all")) return ApplicationUsage::HELP_ALL;
654 if (read("-h") || read("--help")) return ApplicationUsage::COMMAND_LINE_OPTION;
655 if (read("--help-env")) return ApplicationUsage::ENVIRONMENTAL_VARIABLE;
656 if (read("--help-keys")) return ApplicationUsage::KEYBOARD_MOUSE_BINDING;
657
658 return ApplicationUsage::NO_HELP;
659 }
660