1. compilation fixes for TB_REPLACEBITMAP and FONTENUMPROC
[wxWidgets.git] / src / common / cmdline.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: common/cmdline.cpp
3 // Purpose: wxCmdLineParser implementation
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 05.01.00
7 // RCS-ID: $Id$
8 // Copyright: (c) 2000 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows license
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "cmdline.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #ifndef WX_PRECOMP
32 #include "wx/string.h"
33 #include "wx/log.h"
34 #include "wx/intl.h"
35 #include "wx/dynarray.h"
36 #endif //WX_PRECOMP
37
38 #include "wx/datetime.h"
39 #include "wx/cmdline.h"
40
41 // ----------------------------------------------------------------------------
42 // private functions
43 // ----------------------------------------------------------------------------
44
45 static wxString GetTypeName(wxCmdLineParamType type);
46
47 // ----------------------------------------------------------------------------
48 // private classes
49 // ----------------------------------------------------------------------------
50
51 // an internal representation of an option
52 struct wxCmdLineOption
53 {
54 wxCmdLineOption(wxCmdLineEntryType k,
55 const wxString& shrt,
56 const wxString& lng,
57 const wxString& desc,
58 wxCmdLineParamType typ,
59 int fl)
60 {
61 kind = k;
62
63 shortName = shrt;
64 longName = lng;
65 description = desc;
66
67 type = typ;
68 flags = fl;
69
70 m_hasVal = FALSE;
71 }
72
73 // can't use union easily here, so just store all possible data fields, we
74 // don't waste much (might still use union later if the number of supported
75 // types increases, so always use the accessor functions and don't access
76 // the fields directly!)
77
78 void Check(wxCmdLineParamType typ) const
79 {
80 wxASSERT_MSG( type == typ, _T("type mismatch in wxCmdLineOption") );
81 }
82
83 long GetLongVal() const
84 { Check(wxCMD_LINE_VAL_NUMBER); return m_longVal; }
85 const wxString& GetStrVal() const
86 { Check(wxCMD_LINE_VAL_STRING); return m_strVal; }
87 const wxDateTime& GetDateVal() const
88 { Check(wxCMD_LINE_VAL_DATE); return m_dateVal; }
89
90 void SetLongVal(long val)
91 { Check(wxCMD_LINE_VAL_NUMBER); m_longVal = val; m_hasVal = TRUE; }
92 void SetStrVal(const wxString& val)
93 { Check(wxCMD_LINE_VAL_STRING); m_strVal = val; m_hasVal = TRUE; }
94 void SetDateVal(const wxDateTime val)
95 { Check(wxCMD_LINE_VAL_DATE); m_dateVal = val; m_hasVal = TRUE; }
96
97 void SetHasValue() { m_hasVal = TRUE; }
98 bool HasValue() const { return m_hasVal; }
99
100 public:
101 wxCmdLineEntryType kind;
102 wxString shortName, longName, description;
103 wxCmdLineParamType type;
104 int flags;
105
106 private:
107 bool m_hasVal;
108
109 long m_longVal;
110 wxString m_strVal;
111 wxDateTime m_dateVal;
112 };
113
114 struct wxCmdLineParam
115 {
116 wxCmdLineParam(const wxString& desc,
117 wxCmdLineParamType typ,
118 int fl)
119 : description(desc)
120 {
121 type = typ;
122 flags = fl;
123 }
124
125 wxString description;
126 wxCmdLineParamType type;
127 int flags;
128 };
129
130 WX_DECLARE_OBJARRAY(wxCmdLineOption, wxArrayOptions);
131 WX_DECLARE_OBJARRAY(wxCmdLineParam, wxArrayParams);
132
133 #include "wx/arrimpl.cpp"
134
135 WX_DEFINE_OBJARRAY(wxArrayOptions);
136 WX_DEFINE_OBJARRAY(wxArrayParams);
137
138 // the parser internal state
139 struct wxCmdLineParserData
140 {
141 // options
142 wxString m_switchChars; // characters which may start an option
143
144 bool m_enableLongOptions; // TRUE if long options are enabled
145
146 // cmd line data
147 wxArrayString m_arguments; // == argv, argc == m_arguments.GetCount()
148 wxArrayOptions m_options; // all possible options and switchrs
149 wxArrayParams m_paramDesc; // description of all possible params
150 wxArrayString m_parameters; // all params found
151
152 // methods
153 wxCmdLineParserData();
154 void SetArguments(int argc, char **argv);
155 void SetArguments(const wxString& cmdline);
156
157 int FindOption(const wxString& name);
158 int FindOptionByLongName(const wxString& name);
159 };
160
161 // ============================================================================
162 // implementation
163 // ============================================================================
164
165 // ----------------------------------------------------------------------------
166 // wxCmdLineParserData
167 // ----------------------------------------------------------------------------
168
169 wxCmdLineParserData::wxCmdLineParserData()
170 {
171 m_enableLongOptions = TRUE;
172 #ifdef __UNIX_LIKE__
173 m_switchChars = _T("-");
174 #else // !Unix
175 m_switchChars = _T("/-");
176 #endif
177 }
178
179 void wxCmdLineParserData::SetArguments(int argc, char **argv)
180 {
181 m_arguments.Empty();
182
183 for ( int n = 0; n < argc; n++ )
184 {
185 m_arguments.Add(argv[n]);
186 }
187 }
188
189 void wxCmdLineParserData::SetArguments(const wxString& cmdline)
190 {
191 // either use wxMSW wxApp::ConvertToStandardCommandArgs() or move its logic
192 // here and use this method from it - but don't duplicate the code
193
194 wxFAIL_MSG(_T("TODO"));
195 }
196
197 int wxCmdLineParserData::FindOption(const wxString& name)
198 {
199 size_t count = m_options.GetCount();
200 for ( size_t n = 0; n < count; n++ )
201 {
202 if ( m_options[n].shortName == name )
203 {
204 // found
205 return n;
206 }
207 }
208
209 return wxNOT_FOUND;
210 }
211
212 int wxCmdLineParserData::FindOptionByLongName(const wxString& name)
213 {
214 size_t count = m_options.GetCount();
215 for ( size_t n = 0; n < count; n++ )
216 {
217 if ( m_options[n].longName == name )
218 {
219 // found
220 return n;
221 }
222 }
223
224 return wxNOT_FOUND;
225 }
226
227 // ----------------------------------------------------------------------------
228 // construction and destruction
229 // ----------------------------------------------------------------------------
230
231 void wxCmdLineParser::Init()
232 {
233 m_data = new wxCmdLineParserData;
234 }
235
236 void wxCmdLineParser::SetCmdLine(int argc, char **argv)
237 {
238 m_data->SetArguments(argc, argv);
239 }
240
241 void wxCmdLineParser::SetCmdLine(const wxString& cmdline)
242 {
243 m_data->SetArguments(cmdline);
244 }
245
246 wxCmdLineParser::~wxCmdLineParser()
247 {
248 delete m_data;
249 }
250
251 // ----------------------------------------------------------------------------
252 // options
253 // ----------------------------------------------------------------------------
254
255 void wxCmdLineParser::SetSwitchChars(const wxString& switchChars)
256 {
257 m_data->m_switchChars = switchChars;
258 }
259
260 void wxCmdLineParser::EnableLongOptions(bool enable)
261 {
262 m_data->m_enableLongOptions = enable;
263 }
264
265 // ----------------------------------------------------------------------------
266 // command line construction
267 // ----------------------------------------------------------------------------
268
269 void wxCmdLineParser::SetDesc(const wxCmdLineEntryDesc *desc)
270 {
271 for ( ;; desc++ )
272 {
273 switch ( desc->kind )
274 {
275 case wxCMD_LINE_SWITCH:
276 AddSwitch(desc->shortName, desc->longName, desc->description);
277 break;
278
279 case wxCMD_LINE_OPTION:
280 AddOption(desc->shortName, desc->longName, desc->description,
281 desc->type, desc->flags);
282 break;
283
284 case wxCMD_LINE_PARAM:
285 AddParam(desc->description, desc->type, desc->flags);
286 break;
287
288 default:
289 wxFAIL_MSG( _T("unknown command line entry type") );
290 // still fall through
291
292 case wxCMD_LINE_NONE:
293 return;
294 }
295 }
296 }
297
298 void wxCmdLineParser::AddSwitch(const wxString& shortName,
299 const wxString& longName,
300 const wxString& desc,
301 int flags)
302 {
303 wxASSERT_MSG( m_data->FindOption(shortName) == wxNOT_FOUND,
304 _T("duplicate switch") );
305
306 wxCmdLineOption *option = new wxCmdLineOption(wxCMD_LINE_SWITCH,
307 shortName, longName, desc,
308 wxCMD_LINE_VAL_NONE, flags);
309
310 m_data->m_options.Add(option);
311 }
312
313 void wxCmdLineParser::AddOption(const wxString& shortName,
314 const wxString& longName,
315 const wxString& desc,
316 wxCmdLineParamType type,
317 int flags)
318 {
319 wxASSERT_MSG( m_data->FindOption(shortName) == wxNOT_FOUND,
320 _T("duplicate option") );
321
322 wxCmdLineOption *option = new wxCmdLineOption(wxCMD_LINE_OPTION,
323 shortName, longName, desc,
324 type, flags);
325
326 m_data->m_options.Add(option);
327 }
328
329 void wxCmdLineParser::AddParam(const wxString& desc,
330 wxCmdLineParamType type,
331 int flags)
332 {
333 // do some consistency checks: a required parameter can't follow an
334 // optional one and nothing should follow a parameter with MULTIPLE flag
335 #ifdef __WXDEBUG__
336 if ( !m_data->m_paramDesc.IsEmpty() )
337 {
338 wxCmdLineParam& param = m_data->m_paramDesc.Last();
339
340 wxASSERT_MSG( !(param.flags & wxCMD_LINE_PARAM_MULTIPLE),
341 _T("all parameters after the one with "
342 "wxCMD_LINE_PARAM_MULTIPLE style will be ignored") );
343
344 if ( !(flags & wxCMD_LINE_PARAM_OPTIONAL) )
345 {
346 wxASSERT_MSG( !(param.flags & wxCMD_LINE_PARAM_OPTIONAL),
347 _T("a required parameter can't follow an "
348 "optional one") );
349 }
350 }
351 #endif // Debug
352
353 wxCmdLineParam *param = new wxCmdLineParam(desc, type, flags);
354
355 m_data->m_paramDesc.Add(param);
356 }
357
358 // ----------------------------------------------------------------------------
359 // access to parse command line
360 // ----------------------------------------------------------------------------
361
362 bool wxCmdLineParser::Found(const wxString& name) const
363 {
364 int i = m_data->FindOption(name);
365 wxCHECK_MSG( i != wxNOT_FOUND, FALSE, _T("unknown switch") );
366
367 wxCmdLineOption& opt = m_data->m_options[(size_t)i];
368 if ( !opt.HasValue() )
369 return FALSE;
370
371 return TRUE;
372 }
373
374 bool wxCmdLineParser::Found(const wxString& name, wxString *value) const
375 {
376 int i = m_data->FindOption(name);
377 wxCHECK_MSG( i != wxNOT_FOUND, FALSE, _T("unknown option") );
378
379 wxCmdLineOption& opt = m_data->m_options[(size_t)i];
380 if ( !opt.HasValue() )
381 return FALSE;
382
383 wxCHECK_MSG( value, FALSE, _T("NULL pointer in wxCmdLineOption::Found") );
384
385 *value = opt.GetStrVal();
386
387 return TRUE;
388 }
389
390 bool wxCmdLineParser::Found(const wxString& name, long *value) const
391 {
392 int i = m_data->FindOption(name);
393 wxCHECK_MSG( i != wxNOT_FOUND, FALSE, _T("unknown option") );
394
395 wxCmdLineOption& opt = m_data->m_options[(size_t)i];
396 if ( !opt.HasValue() )
397 return FALSE;
398
399 wxCHECK_MSG( value, FALSE, _T("NULL pointer in wxCmdLineOption::Found") );
400
401 *value = opt.GetLongVal();
402
403 return TRUE;
404 }
405
406 bool wxCmdLineParser::Found(const wxString& name, wxDateTime *value) const
407 {
408 int i = m_data->FindOption(name);
409 wxCHECK_MSG( i != wxNOT_FOUND, FALSE, _T("unknown option") );
410
411 wxCmdLineOption& opt = m_data->m_options[(size_t)i];
412 if ( !opt.HasValue() )
413 return FALSE;
414
415 wxCHECK_MSG( value, FALSE, _T("NULL pointer in wxCmdLineOption::Found") );
416
417 *value = opt.GetDateVal();
418
419 return TRUE;
420 }
421
422 size_t wxCmdLineParser::GetParamCount() const
423 {
424 return m_data->m_parameters.GetCount();
425 }
426
427 wxString wxCmdLineParser::GetParam(size_t n) const
428 {
429 return m_data->m_parameters[n];
430 }
431
432 // ----------------------------------------------------------------------------
433 // the real work is done here
434 // ----------------------------------------------------------------------------
435
436 int wxCmdLineParser::Parse()
437 {
438 bool maybeOption = TRUE; // can the following arg be an option?
439 bool ok = TRUE; // TRUE until an error is detected
440 bool helpRequested = FALSE; // TRUE if "-h" was given
441 bool hadRepeatableParam = FALSE; // TRUE if found param with MULTIPLE flag
442
443 size_t currentParam = 0; // the index in m_paramDesc
444
445 size_t countParam = m_data->m_paramDesc.GetCount();
446
447 // parse everything
448 wxString arg;
449 size_t count = m_data->m_arguments.GetCount();
450 for ( size_t n = 1; ok && (n < count); n++ ) // 0 is program name
451 {
452 arg = m_data->m_arguments[n];
453
454 // special case: "--" should be discarded and all following arguments
455 // should be considered as parameters, even if they start with '-' and
456 // not like options (this is POSIX-like)
457 if ( arg == _T("--") )
458 {
459 maybeOption = FALSE;
460
461 continue;
462 }
463
464 // empty argument or just '-' is not an option but a parameter
465 if ( maybeOption && arg.length() > 1 &&
466 wxStrchr(m_data->m_switchChars, arg[0u]) )
467 {
468 bool isLong;
469 wxString name;
470 int optInd = wxNOT_FOUND; // init to suppress warnings
471
472 // an option or a switch: find whether it's a long or a short one
473 if ( m_data->m_enableLongOptions &&
474 arg[0u] == _T('-') && arg[1u] == _T('-') )
475 {
476 // a long one
477 isLong = TRUE;
478
479 const wxChar *p = arg.c_str() + 2;
480 while ( wxIsalpha(*p) || (*p == _T('-')) )
481 {
482 name += *p++;
483 }
484
485 optInd = m_data->FindOptionByLongName(name);
486 if ( optInd == wxNOT_FOUND )
487 {
488 wxLogError(_("Unknown long option '%s'"), name.c_str());
489 }
490 }
491 else
492 {
493 isLong = FALSE;
494
495 // a short one: as they can be cumulated, we try to find the
496 // longest substring which is a valid option
497 const wxChar *p = arg.c_str() + 1;
498 while ( wxIsalpha(*p) )
499 {
500 name += *p++;
501 }
502
503 size_t len = name.length();
504 do
505 {
506 if ( len == 0 )
507 {
508 // we couldn't find a valid option name in the
509 // beginning of this string
510 wxLogError(_("Unknown option '%s'"), name.c_str());
511
512 break;
513 }
514 else
515 {
516 optInd = m_data->FindOption(name.Left(len));
517
518 // will try with one character less the next time
519 len--;
520 }
521 }
522 while ( optInd == wxNOT_FOUND );
523
524 if ( (len > 0) && (len != name.length()) )
525 {
526 // our option is only part of this argument, there is
527 // something else in it - it is either the value of this
528 // option or other switches if it is a switch
529 if ( m_data->m_options[(size_t)optInd].kind
530 == wxCMD_LINE_SWITCH )
531 {
532 // pretend that all the rest of the argument is the
533 // next argument, in fact
534 wxString arg2 = arg[0u];
535 arg2 += name.Mid(len);
536
537 m_data->m_arguments.Insert(arg2, n + 1);
538 }
539 //else: it's our value, we'll deal with it below
540 }
541 }
542
543 if ( optInd == wxNOT_FOUND )
544 {
545 ok = FALSE;
546
547 continue; // will break, in fact
548 }
549
550 wxCmdLineOption& opt = m_data->m_options[(size_t)optInd];
551 if ( opt.kind == wxCMD_LINE_SWITCH )
552 {
553 // nothing more to do
554 opt.SetHasValue();
555
556 if ( opt.flags & wxCMD_LINE_OPTION_HELP )
557 {
558 helpRequested = TRUE;
559
560 // it's not an error, but we still stop here
561 ok = FALSE;
562 }
563 }
564 else
565 {
566 // get the value
567
568 // +1 for leading '-'
569 const wxChar *p = arg.c_str() + 1 + name.length();
570 if ( isLong )
571 {
572 p++; // for another leading '-'
573
574 if ( *p++ != _T('=') )
575 {
576 wxLogError(_("Option '%s' requires a value, '=' "
577 "expected."), name.c_str());
578
579 ok = FALSE;
580 }
581 }
582 else
583 {
584 switch ( *p )
585 {
586 case _T(':'):
587 // the value follows
588 p++;
589 break;
590
591 case 0:
592 // the value is in the next argument
593 if ( ++n == count )
594 {
595 // ... but there is none
596 wxLogError(_("Option '%s' requires a value."),
597 name.c_str());
598
599 ok = FALSE;
600 }
601 else
602 {
603 // ... take it from there
604 p = m_data->m_arguments[n].c_str();
605 }
606 break;
607
608 default:
609 // the value is right here
610 ;
611 }
612 }
613
614 if ( ok )
615 {
616 wxString value = p;
617 switch ( opt.type )
618 {
619 default:
620 wxFAIL_MSG( _T("unknown option type") );
621 // still fall through
622
623 case wxCMD_LINE_VAL_STRING:
624 opt.SetStrVal(value);
625 break;
626
627 case wxCMD_LINE_VAL_NUMBER:
628 {
629 long val;
630 if ( value.ToLong(&val) )
631 {
632 opt.SetLongVal(val);
633 }
634 else
635 {
636 wxLogError(_("'%s' is not a correct "
637 "numeric value for option "
638 "'%s'."),
639 value.c_str(), name.c_str());
640
641 ok = FALSE;
642 }
643 }
644 break;
645
646 case wxCMD_LINE_VAL_DATE:
647 {
648 wxDateTime dt;
649 const wxChar *res = dt.ParseDate(value);
650 if ( !res || *res )
651 {
652 wxLogError(_("Options '%s': '%s' cannot "
653 "be converted to a date."),
654 name.c_str(), value.c_str());
655
656 ok = FALSE;
657 }
658 else
659 {
660 opt.SetDateVal(dt);
661 }
662 }
663 break;
664 }
665 }
666 }
667 }
668 else
669 {
670 // a parameter
671 if ( currentParam < countParam )
672 {
673 wxCmdLineParam& param = m_data->m_paramDesc[currentParam];
674
675 // TODO check the param type
676
677 m_data->m_parameters.Add(arg);
678
679 if ( !(param.flags & wxCMD_LINE_PARAM_MULTIPLE) )
680 {
681 currentParam++;
682 }
683 else
684 {
685 wxASSERT_MSG( currentParam == countParam - 1,
686 _T("all parameters after the one with "
687 "wxCMD_LINE_PARAM_MULTIPLE style "
688 "are ignored") );
689
690 // remember that we did have this last repeatable parameter
691 hadRepeatableParam = TRUE;
692 }
693 }
694 else
695 {
696 wxLogError(_("Unexpected parameter '%s'"), arg.c_str());
697
698 ok = FALSE;
699 }
700 }
701 }
702
703 // verify that all mandatory options were given
704 if ( ok )
705 {
706 size_t countOpt = m_data->m_options.GetCount();
707 for ( size_t n = 0; ok && (n < countOpt); n++ )
708 {
709 wxCmdLineOption& opt = m_data->m_options[n];
710 if ( (opt.flags & wxCMD_LINE_OPTION_MANDATORY) && !opt.HasValue() )
711 {
712 wxString optName;
713 if ( !opt.longName )
714 {
715 optName = opt.shortName;
716 }
717 else
718 {
719 optName.Printf(_("%s (or %s)"),
720 opt.shortName.c_str(),
721 opt.longName.c_str());
722 }
723
724 wxLogError(_("The value for the option '%s' must be specified."),
725 optName.c_str());
726
727 ok = FALSE;
728 }
729 }
730
731 for ( ; ok && (currentParam < countParam); currentParam++ )
732 {
733 wxCmdLineParam& param = m_data->m_paramDesc[currentParam];
734 if ( (currentParam == countParam - 1) &&
735 (param.flags & wxCMD_LINE_PARAM_MULTIPLE) &&
736 hadRepeatableParam )
737 {
738 // special case: currentParam wasn't incremented, but we did
739 // have it, so don't give error
740 continue;
741 }
742
743 if ( !(param.flags & wxCMD_LINE_PARAM_OPTIONAL) )
744 {
745 wxLogError(_("The required parameter '%s' was not specified."),
746 param.description.c_str());
747
748 ok = FALSE;
749 }
750 }
751 }
752
753 if ( !ok )
754 {
755 Usage();
756 }
757
758 return ok ? 0 : helpRequested ? -1 : 1;
759 }
760
761 // ----------------------------------------------------------------------------
762 // give the usage message
763 // ----------------------------------------------------------------------------
764
765 void wxCmdLineParser::Usage()
766 {
767 wxString brief, detailed;
768 brief.Printf(_("Usage: %s"), wxTheApp->GetAppName().c_str());
769
770 size_t n, count = m_data->m_options.GetCount();
771 for ( n = 0; n < count; n++ )
772 {
773 wxCmdLineOption& opt = m_data->m_options[n];
774
775 brief << _T(' ');
776 if ( !(opt.flags & wxCMD_LINE_OPTION_MANDATORY) )
777 {
778 brief << _T('[');
779 }
780
781 brief << _T('-') << opt.shortName;
782 detailed << _T(" -") << opt.shortName;
783 if ( !!opt.longName )
784 {
785 detailed << _T(" --") << opt.longName;
786 }
787
788 if ( opt.kind != wxCMD_LINE_SWITCH )
789 {
790 wxString val;
791 val << _T('<') << GetTypeName(opt.type) << _T('>');
792 brief << _T(' ') << val;
793 detailed << (!opt.longName ? _T(':') : _T('=')) << val;
794 }
795
796 if ( !(opt.flags & wxCMD_LINE_OPTION_MANDATORY) )
797 {
798 brief << _T(']');
799 }
800
801 detailed << _T('\t') << opt.description << _T('\n');
802 }
803
804 count = m_data->m_paramDesc.GetCount();
805 for ( n = 0; n < count; n++ )
806 {
807 wxCmdLineParam& param = m_data->m_paramDesc[n];
808
809 brief << _T(' ');
810 if ( param.flags & wxCMD_LINE_PARAM_OPTIONAL )
811 {
812 brief << _T('[');
813 }
814
815 brief << param.description;
816
817 if ( param.flags & wxCMD_LINE_PARAM_MULTIPLE )
818 {
819 brief << _T("...");
820 }
821
822 if ( param.flags & wxCMD_LINE_PARAM_OPTIONAL )
823 {
824 brief << _T(']');
825 }
826 }
827
828 wxLogMessage(brief);
829 wxLogMessage(detailed);
830 }
831
832 // ----------------------------------------------------------------------------
833 // global functions
834 // ----------------------------------------------------------------------------
835
836 static wxString GetTypeName(wxCmdLineParamType type)
837 {
838 wxString s;
839 switch ( type )
840 {
841 default:
842 wxFAIL_MSG( _T("unknown option type") );
843 // still fall through
844
845 case wxCMD_LINE_VAL_STRING: s = _("str"); break;
846 case wxCMD_LINE_VAL_NUMBER: s = _("num"); break;
847 case wxCMD_LINE_VAL_DATE: s = _("date"); break;
848 }
849
850 return s;
851 }