blob: 3924b4ac4a15c3997450cb5623dfc0699749380e [file] [log] [blame]
Emeric Vigier2f625822012-08-06 11:09:52 -04001// Copyright (C) 2001-2010 Gianni Mariani
2//
3// This program is free software; you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation; either version 2 of the License, or
6// (at your option) any later version.
7//
8// This program 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// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with this program; if not, write to the Free Software
15// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16//
17// As a special exception, you may use this file as part of a free software
18// library without restriction. Specifically, if other files instantiate
19// templates or use macros or inline functions from this file, or you compile
20// this file and link it with other files to produce an executable, this
21// file does not by itself cause the resulting executable to be covered by
22// the GNU General Public License. This exception does not however
23// invalidate any other reasons why the executable file might be covered by
24// the GNU General Public License.
25//
26// This exception applies only to the code released under the name GNU
27// Common C++. If you copy code from other releases into a copy of GNU
28// Common C++, as the General Public License permits, the exception does
29// not apply to the code that you add in this way. To avoid misleading
30// anyone as to the status of such modified files, you must delete
31// this exception notice from them.
32//
33// If you write modifications of your own for GNU Common C++, it is your choice
34// whether to permit this exception to apply to your modifications.
35// If you do not wish that, delete this exception notice.
36//
37
38//
39// cmdoptns.cpp
40//
41
42#include <cc++/config.h>
43#include <cc++/string.h>
44#include <cc++/thread.h>
45#include <cc++/exception.h>
46#include <cc++/export.h>
47#include <cc++/cmdoptns.h>
48
49#ifndef HAVE_GETOPT_LONG
50// fix problem with vc++ library
51#undef __argc
52#undef __argv
53#include "getopt.h"
54#else
55#include <getopt.h>
56#endif
57
58#ifndef WIN32
59#include <unistd.h>
60#endif
61
62#include <cstdlib>
63#include <iostream>
64#include <fstream>
65
66using std::fstream;
67
68#ifdef CCXX_NAMESPACES
69namespace ost {
70#endif
71
72//
73// In most cases, users will use this default option list.
74//
75CommandOption * defaultCommandOptionList = 0;
76
77CommandOption::CommandOption(
78 const char * inOptionName,
79 const char * inOptionLetter,
80 const char * inDescription,
81 OptionType inOptionType,
82 bool inRequired,
83 CommandOption ** ppNext
84)
85 : optionName( inOptionName ),
86 optionLetter( inOptionLetter ),
87 description( inDescription ),
88 optionType( inOptionType ),
89 required( inRequired ),
90 next( * ppNext ) {
91 * ppNext = this;
92}
93
94CommandOptionWithArg::CommandOptionWithArg(
95 const char * inOptionName,
96 const char * inOptionLetter,
97 const char * inDescription,
98 OptionType inOptionType,
99 bool inRequired,
100 CommandOption ** ppNext
101)
102 : CommandOption(
103 inOptionName,
104 inOptionLetter,
105 inDescription,
106 inOptionType,
107 inRequired,
108 ppNext
109 ), values( 0 ), numValue( 0 )
110{
111}
112
113
114CommandOptionArg::CommandOptionArg(
115 const char * inOptionName,
116 const char * inOptionLetter,
117 const char * inDescription,
118 bool inRequired,
119 CommandOption ** ppNext
120)
121 : CommandOptionWithArg(
122 inOptionName,
123 inOptionLetter,
124 inDescription,
125 hasArg,
126 inRequired,
127 ppNext
128 )
129{
130}
131
132CommandOptionRest::CommandOptionRest(
133 const char * inOptionName,
134 const char * inOptionLetter,
135 const char * inDescription,
136 bool inRequired,
137 CommandOption ** ppNext
138)
139 : CommandOptionWithArg(
140 inOptionName,
141 inOptionLetter,
142 inDescription,
143 trailing,
144 inRequired,
145 ppNext
146 )
147{
148}
149
150CommandOptionCollect::CommandOptionCollect(
151 const char * inOptionName,
152 const char * inOptionLetter,
153 const char * inDescription,
154 bool inRequired,
155 CommandOption ** ppNext
156)
157 : CommandOptionWithArg(
158 inOptionName,
159 inOptionLetter,
160 inDescription,
161 collect,
162 inRequired,
163 ppNext
164 )
165{
166}
167
168CommandOptionNoArg::CommandOptionNoArg(
169 const char * inOptionName,
170 const char * inOptionLetter,
171 const char * inDescription,
172 bool inRequired,
173 CommandOption ** ppNext
174)
175 : CommandOption(
176 inOptionName,
177 inOptionLetter,
178 inDescription,
179 noArg,
180 inRequired,
181 ppNext
182 ), numSet( 0 )
183{
184}
185
186// ======== CommandOption =============================================
187// PURPOSE:
188// CommandOption dummy methods ..
189//
190
191void CommandOption::parseDone( CommandOptionParse * cop )
192{
193}
194
195void CommandOption::foundOption( CommandOptionParse * cop, const char * value)
196{
197}
198
199void CommandOption::foundOption( CommandOptionParse * cop, const char ** value, int num )
200{
201}
202
203void CommandOption::performTask( CommandOptionParse * cop )
204{
205}
206
207bool CommandOption::hasValue()
208{
209 return true;
210}
211
212CommandOption::~CommandOption()
213{
214}
215
216CommandOptionWithArg::~CommandOptionWithArg()
217{
218 if ( values ) {
219 free( values );
220 values = 0;
221 }
222}
223
224CommandOptionParse::~CommandOptionParse(void)
225{
226}
227
228// ======== CommandOptionArg ==========================================
229// PURPOSE:
230// Methods for CommandOptionArg
231//
232
233bool CommandOptionWithArg::hasValue()
234{
235 return numValue > 0;
236}
237
238CommandOptionArg::~CommandOptionArg()
239{
240}
241
242//
243//
244static void my_alloc( char *** vals, int num, int incr )
245{
246 int num_alloc = 0;
247 if ( * vals ) {
248 num_alloc = num | 3;
249 }
250
251 if ( ( incr + num ) > num_alloc ) {
252 int newsiz = ( incr + num ) | 3;
253 * vals = ( char ** ) realloc( * vals, sizeof( ** vals ) * newsiz );
254 }
255}
256
257void CommandOptionWithArg::foundOption( CommandOptionParse * cop, const char * value )
258{
259 if ( value ) {
260 my_alloc( ( char *** ) & values, numValue ? numValue + 1 : 0, 1 );
261 values[ numValue ++ ] = value;
262 values[ numValue ] = 0;
263 }
264}
265
266void CommandOptionWithArg::foundOption( CommandOptionParse * cop, const char ** value, int num )
267{
268 my_alloc( ( char *** ) & values, numValue ? numValue + 1 : 0, num + 1 );
269
270 int j = 0;
271 for ( int i = numValue; j < num; i ++, j ++ ) {
272 values[ i ] = value[ j ];
273 }
274 numValue += num;
275 values[ numValue ] = 0;
276}
277
278
279void CommandOptionNoArg::foundOption( CommandOptionParse * cop, const char * value)
280{
281 numSet ++;
282}
283
284// ======== CommandOptionParse ========================================
285// PURPOSE:
286// methods for CommandOptionParse
287//
288
289class CommandOptionParse_impl : public CommandOptionParse {
290public:
291
292 const char * comment;
293 int num_options;
294 struct option * long_options;
295 CommandOption ** opt_list;
296 CommandOption ** co_list;
297 char * optstring;
298 int argc;
299 char ** argv;
300 bool has_err;
301 char * fail_arg;
302 bool usage_string_set;
303 bool required_errors_set;
304 String error_msgs;
305 CommandOption * fail_option;
306 CommandOption * trailing;
307
308 String usage_string;
309
310 virtual ~CommandOptionParse_impl() {
311 delete[] opt_list;
312 delete[] co_list;
313 delete[] optstring;
314 delete[] long_options;
315 }
316
317 CommandOptionParse_impl(
318 int in_argc,
319 char ** in_argv,
320 const char * in_comment,
321 CommandOption * options
322 ) :
323 comment( in_comment ),
324 argc( in_argc ),
325 argv( in_argv ),
326 has_err( false ),
327 fail_arg( 0 ),
328 usage_string_set( false ),
329 required_errors_set( false ),
330 error_msgs( "" ),
331 fail_option( 0 ),
332 trailing(0) {
333
334 // First need to count all options.
335
336 CommandOption * to = options;
337 int ocnt = 0;
338 int ccnt = 0;
339 int flag;
340
341 while ( to ) {
342 if ( to->optionName ) ocnt ++;
343 ccnt ++;
344 to = to->next;
345 }
346
347 num_options = ccnt;
348#ifdef __KCC
349 co_list = new (CommandOption **)[ocnt];
350 opt_list = new (CommandOption **)[ccnt];
351#else
352 // fix compiling bug in vc++
353 typedef CommandOption* PCommandOption;
354 co_list = new PCommandOption[ocnt];
355 opt_list = new PCommandOption[ccnt];
356#endif
357 long_options = new option[ccnt+1];
358 optstring = new char[ 2*ccnt+2 ];
359
360 // initialize the last option count
361 long_options[ ocnt ].name = 0;
362 long_options[ ocnt ].has_arg = 0;
363 long_options[ ocnt ].flag = 0;
364 long_options[ ocnt ].val = 0;
365
366 char *tos = optstring;
367 *(tos++) = '+';
368 to = options;
369 while ( to ) {
370
371 if ( to->optionType == CommandOption::trailing ) {
372 if ( ! trailing ) {
373 trailing = to;
374 }
375 } else if ( to->optionType == CommandOption::collect ) {
376 trailing = to;
377 }
378
379 opt_list[ -- ccnt ] = to;
380
381 if ( to->optionName ) {
382 -- ocnt;
383 co_list[ ocnt ] = to;
384 long_options[ ocnt ].name = to->optionName;
385 long_options[ ocnt ].has_arg = to->optionType == CommandOption::hasArg;
386 long_options[ ocnt ].flag = & flag;
387 long_options[ ocnt ].val = ocnt;
388 }
389
390 if ( to->optionLetter && to->optionLetter[ 0 ] ) {
391 * tos ++ = to->optionLetter[ 0 ];
392 if ( to->optionType == CommandOption::hasArg ) {
393 * tos ++ = ':';
394 }
395 }
396
397
398 to = to->next;
399 }
400 * tos = 0;
401
402 int c;
403 int optionIndex;
404
405 opterr = 0; // tell getopt_long not to print any errors
406 flag = -1;
407 while ( optind < argc ) {
408
409 if (
410 (
411 c = getopt_long(
412 argc, argv, optstring, long_options, &optionIndex
413 )
414 ) == -1
415 ) {
416 if ( ! trailing ) {
417 break;
418 } else if ( trailing->optionType == CommandOption::trailing ) {
419 break;
420 } else {
421 optarg = argv[ optind ];
422 optind ++;
423 to = trailing;
424 }
425
426 } else if ( flag != -1 ) {
427 to = co_list[ flag ];
428 flag = -1;
429 } else if ( c == '?' ) {
430
431 if ( optind < 2 ) {
432 fail_arg = argv[ optind ];
433 } else {
434 fail_arg = argv[ optind - 1 ];
435 }
436
437 has_err = true;
438
439 return;
440
441 } else {
442
443 // need to search through the options.
444
445 for ( int i = 0; i < num_options; i ++ ) {
446
447 to = opt_list[ i ];
448 if ( ! to->optionLetter ) continue;
449
450 if ( c == to->optionLetter[ 0 ] ) {
451
452 break;
453 }
454 }
455 // assert( to );
456 }
457
458 // do we terminate here ?
459 if ( to->optionType == CommandOption::trailing ) {
460 break;
461 }
462
463 if ( c != ':' ) {
464 to->foundOption( this, optarg );
465 } else {
466 has_err = true;
467 fail_option = to;
468 break;
469 }
470
471 }
472
473 if ( optind < argc ) {
474 if ( trailing ) {
475 trailing->foundOption(
476 this,
477 ( const char ** ) ( argv + optind ),
478 argc - optind
479 );
480 } else {
481 has_err = true;
482 fail_arg = argv[ optind ];
483 }
484 }
485
486 // Now check to see that all required args made it !
487
488 for ( int i = 0; i < num_options; i ++ ) {
489 CommandOption * toq = opt_list[ i ];
490
491 // Tell this parameter that it's done now.
492 toq->parseDone( this );
493
494 if ( toq->required && ! toq->hasValue() ) {
495 has_err = true;
496 break;
497 }
498 }
499
500 }
501
502 bool argsHaveError();
503
504 virtual const char * printUsage();
505 virtual const char * printErrors();
506
507 void makePrintErrors() {
508 if ( required_errors_set ) return;
509 required_errors_set = true;
510
511 if ( fail_arg ) {
512 error_msgs = error_msgs + "Unknown/malformed option '" + fail_arg + "' \n";
513 } else if ( fail_option ) {
514 String name;
515 bool name_msg;
516 if ( fail_option->optionName ) {
517 name_msg = true;
518 name = fail_option->optionName;
519 } else if ( fail_option->optionLetter ) {
520 name_msg = true;
521 name = fail_option->optionLetter;
522 } else if ( fail_option == trailing ) {
523 name_msg = false;
524 } else {
525 name = "--option with no name--";
526 name_msg = true;
527 }
528 if ( name_msg ) {
529 error_msgs = error_msgs + "Option '" + name + "' requires value\n";
530 }
531 } else if ( has_err ) {
532
533 // loop thru all required args
534
535 for ( int i = 0; i < num_options; i ++ ) {
536 CommandOption * to = opt_list[ i ];
537
538 if ( to->required && ! to->hasValue() ) {
539 error_msgs = error_msgs + "Value required for option '";
540
541 if ( to->optionName ) {
542 error_msgs = error_msgs + "--" + to->optionName;
543 } else if ( to->optionLetter && to->optionLetter[ 0 ] ) {
544 error_msgs = error_msgs + '-' + to->optionLetter[ 0 ];
545 } else {
546 error_msgs = error_msgs + to->description;
547 }
548
549 error_msgs = error_msgs + "' is missing\n";
550 }
551 }
552
553 }
554
555 }
556
557
558 void makePrintUsage() {
559 if ( usage_string_set ) return;
560
561 String str( "" );
562
563 String str_argv0 = argv[ 0 ];
564
565 str = str + (char *)"Usage : ";
566
567 String::size_type slashpos = str_argv0.rfind('/');
568 if ( slashpos > str_argv0.length() ) {
569 slashpos = 0;
570 } else {
571 slashpos ++;
572 }
573
574 str.append( str_argv0, slashpos, str_argv0.length() - slashpos );
575
576 str = str + ' ' + comment + '\n';
577
578 for ( int i = 0; i < num_options; i ++ ) {
579
580 CommandOption * to = opt_list[ i ];
581 char * begin = (char *)"\t";
582 char * obegin = (char *)"\t";
583
584 to = opt_list[ i ];
585
586 if ( to->optionLetter && to->optionLetter[ 0 ] ) {
587 str = str + begin + '-' + to->optionLetter[ 0 ];
588 begin = (char *)", ";
589 obegin = (char *)" - ";
590 }
591
592 if ( to->optionName ) {
593 str = str + begin + "--" + to->optionName;
594 begin = (char *)", ";
595 obegin = (char *)" - ";
596 }
597
598 if ( to->optionType == CommandOption::hasArg ) {
599 str = str + begin + " <value>";
600 } else if ( to->optionType == CommandOption::trailing ) {
601 str = str + begin + " <rest of command...>";
602 } else if ( to->optionType == CommandOption::collect ) {
603 str = str + begin + " <...>";
604 }
605
606 str = str + obegin + to->description + "\n";
607 }
608
609 usage_string = str;
610 }
611
612 virtual void registerError( const char * errMsg ) {
613 error_msgs = error_msgs + errMsg + '\n';
614 has_err = true;
615 }
616
617 virtual void performTask() {
618 for ( int i = 0; i < num_options; i ++ ) {
619 CommandOption * to = opt_list[ i ];
620
621 // Each parameter has this invoked
622 to->performTask( this );
623
624 }
625 }
626
627};
628
629CommandOptionParse * makeCommandOptionParse(
630 int argc,
631 char ** argv,
632 const char * comment,
633 CommandOption * options
634) {
635 return new CommandOptionParse_impl( argc, argv, comment, options );
636}
637
638bool CommandOptionParse_impl::argsHaveError()
639{
640 return has_err;
641}
642
643const char * CommandOptionParse_impl::printUsage()
644{
645 makePrintUsage();
646 return usage_string.c_str();
647}
648
649const char * CommandOptionParse_impl::printErrors()
650{
651 makePrintErrors();
652 return error_msgs.c_str();
653}
654
655#ifdef CCXX_NAMESPACES
656}
657#endif
658
659/** EMACS **
660 * Local variables:
661 * mode: c++
662 * c-basic-offset: 4
663 * End:
664 */
665