]> git.saurik.com Git - wxWidgets.git/blame - src/common/cmdline.cpp
new wxASSERT implementation using wxAssert() helper function
[wxWidgets.git] / src / common / cmdline.cpp
CommitLineData
9f83044f
VZ
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
31f6de22
VZ
31#include "wx/cmdline.h"
32
1e6feb95
VZ
33#if wxUSE_CMDLINE_PARSER
34
9f83044f
VZ
35#ifndef WX_PRECOMP
36 #include "wx/string.h"
37 #include "wx/log.h"
38 #include "wx/intl.h"
2822ee33 39 #include "wx/app.h"
9f83044f 40 #include "wx/dynarray.h"
2822ee33 41 #include "wx/filefn.h"
9f83044f
VZ
42#endif //WX_PRECOMP
43
ff0ea71c
GT
44#include <ctype.h>
45
9f83044f 46#include "wx/datetime.h"
9f83044f
VZ
47
48// ----------------------------------------------------------------------------
49// private functions
50// ----------------------------------------------------------------------------
51
52static wxString GetTypeName(wxCmdLineParamType type);
53
54// ----------------------------------------------------------------------------
55// private classes
56// ----------------------------------------------------------------------------
57
58// an internal representation of an option
59struct wxCmdLineOption
60{
61 wxCmdLineOption(wxCmdLineEntryType k,
62 const wxString& shrt,
63 const wxString& lng,
64 const wxString& desc,
65 wxCmdLineParamType typ,
66 int fl)
67 {
bf188f1a
VZ
68 wxASSERT_MSG( !shrt.empty() || !lng.empty(),
69 _T("option should have at least one name") );
70
9f83044f
VZ
71 kind = k;
72
73 shortName = shrt;
74 longName = lng;
75 description = desc;
76
77 type = typ;
78 flags = fl;
79
80 m_hasVal = FALSE;
81 }
82
83 // can't use union easily here, so just store all possible data fields, we
84 // don't waste much (might still use union later if the number of supported
85 // types increases, so always use the accessor functions and don't access
86 // the fields directly!)
87
6f2a55e3 88 void Check(wxCmdLineParamType WXUNUSED_UNLESS_DEBUG(typ)) const
9f83044f
VZ
89 {
90 wxASSERT_MSG( type == typ, _T("type mismatch in wxCmdLineOption") );
91 }
92
93 long GetLongVal() const
94 { Check(wxCMD_LINE_VAL_NUMBER); return m_longVal; }
95 const wxString& GetStrVal() const
96 { Check(wxCMD_LINE_VAL_STRING); return m_strVal; }
97 const wxDateTime& GetDateVal() const
98 { Check(wxCMD_LINE_VAL_DATE); return m_dateVal; }
99
100 void SetLongVal(long val)
101 { Check(wxCMD_LINE_VAL_NUMBER); m_longVal = val; m_hasVal = TRUE; }
102 void SetStrVal(const wxString& val)
103 { Check(wxCMD_LINE_VAL_STRING); m_strVal = val; m_hasVal = TRUE; }
104 void SetDateVal(const wxDateTime val)
105 { Check(wxCMD_LINE_VAL_DATE); m_dateVal = val; m_hasVal = TRUE; }
106
07d09af8 107 void SetHasValue(bool hasValue = TRUE) { m_hasVal = hasValue; }
9f83044f
VZ
108 bool HasValue() const { return m_hasVal; }
109
110public:
111 wxCmdLineEntryType kind;
be03c0ec
VZ
112 wxString shortName,
113 longName,
114 description;
9f83044f
VZ
115 wxCmdLineParamType type;
116 int flags;
117
118private:
119 bool m_hasVal;
120
121 long m_longVal;
122 wxString m_strVal;
123 wxDateTime m_dateVal;
124};
125
126struct wxCmdLineParam
127{
128 wxCmdLineParam(const wxString& desc,
129 wxCmdLineParamType typ,
130 int fl)
131 : description(desc)
132 {
133 type = typ;
134 flags = fl;
135 }
136
137 wxString description;
138 wxCmdLineParamType type;
139 int flags;
140};
141
142WX_DECLARE_OBJARRAY(wxCmdLineOption, wxArrayOptions);
143WX_DECLARE_OBJARRAY(wxCmdLineParam, wxArrayParams);
144
145#include "wx/arrimpl.cpp"
146
147WX_DEFINE_OBJARRAY(wxArrayOptions);
148WX_DEFINE_OBJARRAY(wxArrayParams);
149
150// the parser internal state
151struct wxCmdLineParserData
152{
153 // options
154 wxString m_switchChars; // characters which may start an option
9f83044f 155 bool m_enableLongOptions; // TRUE if long options are enabled
e612f101 156 wxString m_logo; // some extra text to show in Usage()
9f83044f
VZ
157
158 // cmd line data
159 wxArrayString m_arguments; // == argv, argc == m_arguments.GetCount()
160 wxArrayOptions m_options; // all possible options and switchrs
161 wxArrayParams m_paramDesc; // description of all possible params
162 wxArrayString m_parameters; // all params found
163
164 // methods
165 wxCmdLineParserData();
55b2b0d8 166 void SetArguments(int argc, wxChar **argv);
9f83044f
VZ
167 void SetArguments(const wxString& cmdline);
168
169 int FindOption(const wxString& name);
170 int FindOptionByLongName(const wxString& name);
171};
172
173// ============================================================================
174// implementation
175// ============================================================================
176
177// ----------------------------------------------------------------------------
178// wxCmdLineParserData
179// ----------------------------------------------------------------------------
180
181wxCmdLineParserData::wxCmdLineParserData()
182{
183 m_enableLongOptions = TRUE;
184#ifdef __UNIX_LIKE__
185 m_switchChars = _T("-");
186#else // !Unix
187 m_switchChars = _T("/-");
188#endif
189}
190
55b2b0d8 191void wxCmdLineParserData::SetArguments(int argc, wxChar **argv)
9f83044f
VZ
192{
193 m_arguments.Empty();
194
195 for ( int n = 0; n < argc; n++ )
196 {
197 m_arguments.Add(argv[n]);
198 }
199}
200
97d59046 201void wxCmdLineParserData::SetArguments(const wxString& cmdLine)
9f83044f 202{
97d59046
JS
203 m_arguments.Empty();
204
205 m_arguments.Add(wxTheApp->GetAppName());
206
31f6de22 207 wxArrayString args = wxCmdLineParser::ConvertStringToArgs(cmdLine);
97d59046 208
31f6de22 209 WX_APPEND_ARRAY(m_arguments, args);
9f83044f
VZ
210}
211
212int wxCmdLineParserData::FindOption(const wxString& name)
213{
bf188f1a 214 if ( !name.empty() )
9f83044f 215 {
bf188f1a
VZ
216 size_t count = m_options.GetCount();
217 for ( size_t n = 0; n < count; n++ )
9f83044f 218 {
bf188f1a
VZ
219 if ( m_options[n].shortName == name )
220 {
221 // found
222 return n;
223 }
9f83044f
VZ
224 }
225 }
226
227 return wxNOT_FOUND;
228}
229
230int wxCmdLineParserData::FindOptionByLongName(const wxString& name)
231{
232 size_t count = m_options.GetCount();
233 for ( size_t n = 0; n < count; n++ )
234 {
235 if ( m_options[n].longName == name )
236 {
237 // found
238 return n;
239 }
240 }
241
242 return wxNOT_FOUND;
243}
244
245// ----------------------------------------------------------------------------
246// construction and destruction
247// ----------------------------------------------------------------------------
248
249void wxCmdLineParser::Init()
250{
251 m_data = new wxCmdLineParserData;
252}
253
55b2b0d8 254void wxCmdLineParser::SetCmdLine(int argc, wxChar **argv)
9f83044f
VZ
255{
256 m_data->SetArguments(argc, argv);
257}
258
259void wxCmdLineParser::SetCmdLine(const wxString& cmdline)
260{
261 m_data->SetArguments(cmdline);
262}
263
264wxCmdLineParser::~wxCmdLineParser()
265{
266 delete m_data;
267}
268
269// ----------------------------------------------------------------------------
270// options
271// ----------------------------------------------------------------------------
272
273void wxCmdLineParser::SetSwitchChars(const wxString& switchChars)
274{
275 m_data->m_switchChars = switchChars;
276}
277
278void wxCmdLineParser::EnableLongOptions(bool enable)
279{
280 m_data->m_enableLongOptions = enable;
281}
282
e612f101
VZ
283void wxCmdLineParser::SetLogo(const wxString& logo)
284{
285 m_data->m_logo = logo;
286}
287
9f83044f
VZ
288// ----------------------------------------------------------------------------
289// command line construction
290// ----------------------------------------------------------------------------
291
292void wxCmdLineParser::SetDesc(const wxCmdLineEntryDesc *desc)
293{
294 for ( ;; desc++ )
295 {
296 switch ( desc->kind )
297 {
298 case wxCMD_LINE_SWITCH:
e612f101
VZ
299 AddSwitch(desc->shortName, desc->longName, desc->description,
300 desc->flags);
9f83044f
VZ
301 break;
302
303 case wxCMD_LINE_OPTION:
304 AddOption(desc->shortName, desc->longName, desc->description,
305 desc->type, desc->flags);
306 break;
307
308 case wxCMD_LINE_PARAM:
309 AddParam(desc->description, desc->type, desc->flags);
310 break;
311
312 default:
313 wxFAIL_MSG( _T("unknown command line entry type") );
314 // still fall through
315
316 case wxCMD_LINE_NONE:
317 return;
318 }
319 }
320}
321
322void wxCmdLineParser::AddSwitch(const wxString& shortName,
323 const wxString& longName,
324 const wxString& desc,
325 int flags)
326{
327 wxASSERT_MSG( m_data->FindOption(shortName) == wxNOT_FOUND,
328 _T("duplicate switch") );
329
330 wxCmdLineOption *option = new wxCmdLineOption(wxCMD_LINE_SWITCH,
331 shortName, longName, desc,
332 wxCMD_LINE_VAL_NONE, flags);
333
334 m_data->m_options.Add(option);
335}
336
337void wxCmdLineParser::AddOption(const wxString& shortName,
338 const wxString& longName,
339 const wxString& desc,
340 wxCmdLineParamType type,
341 int flags)
342{
343 wxASSERT_MSG( m_data->FindOption(shortName) == wxNOT_FOUND,
344 _T("duplicate option") );
345
346 wxCmdLineOption *option = new wxCmdLineOption(wxCMD_LINE_OPTION,
347 shortName, longName, desc,
348 type, flags);
349
350 m_data->m_options.Add(option);
351}
352
353void wxCmdLineParser::AddParam(const wxString& desc,
354 wxCmdLineParamType type,
355 int flags)
356{
357 // do some consistency checks: a required parameter can't follow an
358 // optional one and nothing should follow a parameter with MULTIPLE flag
359#ifdef __WXDEBUG__
360 if ( !m_data->m_paramDesc.IsEmpty() )
361 {
362 wxCmdLineParam& param = m_data->m_paramDesc.Last();
363
364 wxASSERT_MSG( !(param.flags & wxCMD_LINE_PARAM_MULTIPLE),
fbdcff4a 365 _T("all parameters after the one with wxCMD_LINE_PARAM_MULTIPLE style will be ignored") );
9f83044f
VZ
366
367 if ( !(flags & wxCMD_LINE_PARAM_OPTIONAL) )
368 {
369 wxASSERT_MSG( !(param.flags & wxCMD_LINE_PARAM_OPTIONAL),
fbdcff4a 370 _T("a required parameter can't follow an optional one") );
9f83044f
VZ
371 }
372 }
373#endif // Debug
374
375 wxCmdLineParam *param = new wxCmdLineParam(desc, type, flags);
376
377 m_data->m_paramDesc.Add(param);
378}
379
380// ----------------------------------------------------------------------------
381// access to parse command line
382// ----------------------------------------------------------------------------
383
384bool wxCmdLineParser::Found(const wxString& name) const
385{
386 int i = m_data->FindOption(name);
bf188f1a
VZ
387 if ( i == wxNOT_FOUND )
388 i = m_data->FindOptionByLongName(name);
389
9f83044f
VZ
390 wxCHECK_MSG( i != wxNOT_FOUND, FALSE, _T("unknown switch") );
391
392 wxCmdLineOption& opt = m_data->m_options[(size_t)i];
393 if ( !opt.HasValue() )
394 return FALSE;
395
396 return TRUE;
397}
398
399bool wxCmdLineParser::Found(const wxString& name, wxString *value) const
400{
401 int i = m_data->FindOption(name);
bf188f1a
VZ
402 if ( i == wxNOT_FOUND )
403 i = m_data->FindOptionByLongName(name);
404
9f83044f
VZ
405 wxCHECK_MSG( i != wxNOT_FOUND, FALSE, _T("unknown option") );
406
407 wxCmdLineOption& opt = m_data->m_options[(size_t)i];
408 if ( !opt.HasValue() )
409 return FALSE;
410
411 wxCHECK_MSG( value, FALSE, _T("NULL pointer in wxCmdLineOption::Found") );
412
413 *value = opt.GetStrVal();
414
415 return TRUE;
416}
417
418bool wxCmdLineParser::Found(const wxString& name, long *value) const
419{
420 int i = m_data->FindOption(name);
bf188f1a
VZ
421 if ( i == wxNOT_FOUND )
422 i = m_data->FindOptionByLongName(name);
423
9f83044f
VZ
424 wxCHECK_MSG( i != wxNOT_FOUND, FALSE, _T("unknown option") );
425
426 wxCmdLineOption& opt = m_data->m_options[(size_t)i];
427 if ( !opt.HasValue() )
428 return FALSE;
429
430 wxCHECK_MSG( value, FALSE, _T("NULL pointer in wxCmdLineOption::Found") );
431
432 *value = opt.GetLongVal();
433
434 return TRUE;
435}
436
437bool wxCmdLineParser::Found(const wxString& name, wxDateTime *value) const
438{
439 int i = m_data->FindOption(name);
bf188f1a
VZ
440 if ( i == wxNOT_FOUND )
441 i = m_data->FindOptionByLongName(name);
442
9f83044f
VZ
443 wxCHECK_MSG( i != wxNOT_FOUND, FALSE, _T("unknown option") );
444
445 wxCmdLineOption& opt = m_data->m_options[(size_t)i];
446 if ( !opt.HasValue() )
447 return FALSE;
448
449 wxCHECK_MSG( value, FALSE, _T("NULL pointer in wxCmdLineOption::Found") );
450
451 *value = opt.GetDateVal();
452
453 return TRUE;
454}
455
456size_t wxCmdLineParser::GetParamCount() const
457{
458 return m_data->m_parameters.GetCount();
459}
460
461wxString wxCmdLineParser::GetParam(size_t n) const
462{
8c9892c2
VZ
463 wxCHECK_MSG( n < GetParamCount(), wxEmptyString, _T("invalid param index") );
464
9f83044f
VZ
465 return m_data->m_parameters[n];
466}
467
07d09af8
JS
468// Resets switches and options
469void wxCmdLineParser::Reset()
470{
3f2bcf34
VZ
471 for ( size_t i = 0; i < m_data->m_options.Count(); i++ )
472 {
473 wxCmdLineOption& opt = m_data->m_options[i];
474 opt.SetHasValue(FALSE);
475 }
07d09af8
JS
476}
477
478
9f83044f
VZ
479// ----------------------------------------------------------------------------
480// the real work is done here
481// ----------------------------------------------------------------------------
482
be03c0ec 483int wxCmdLineParser::Parse(bool showUsage)
9f83044f
VZ
484{
485 bool maybeOption = TRUE; // can the following arg be an option?
486 bool ok = TRUE; // TRUE until an error is detected
487 bool helpRequested = FALSE; // TRUE if "-h" was given
488 bool hadRepeatableParam = FALSE; // TRUE if found param with MULTIPLE flag
489
490 size_t currentParam = 0; // the index in m_paramDesc
491
492 size_t countParam = m_data->m_paramDesc.GetCount();
493
3f2bcf34 494 Reset();
07d09af8 495
9f83044f
VZ
496 // parse everything
497 wxString arg;
498 size_t count = m_data->m_arguments.GetCount();
499 for ( size_t n = 1; ok && (n < count); n++ ) // 0 is program name
500 {
501 arg = m_data->m_arguments[n];
502
503 // special case: "--" should be discarded and all following arguments
504 // should be considered as parameters, even if they start with '-' and
505 // not like options (this is POSIX-like)
506 if ( arg == _T("--") )
507 {
508 maybeOption = FALSE;
509
510 continue;
511 }
512
513 // empty argument or just '-' is not an option but a parameter
514 if ( maybeOption && arg.length() > 1 &&
515 wxStrchr(m_data->m_switchChars, arg[0u]) )
516 {
517 bool isLong;
518 wxString name;
519 int optInd = wxNOT_FOUND; // init to suppress warnings
520
521 // an option or a switch: find whether it's a long or a short one
522 if ( m_data->m_enableLongOptions &&
523 arg[0u] == _T('-') && arg[1u] == _T('-') )
524 {
525 // a long one
526 isLong = TRUE;
527
528 const wxChar *p = arg.c_str() + 2;
f6bcfd97 529 while ( wxIsalnum(*p) || (*p == _T('_')) || (*p == _T('-')) )
9f83044f
VZ
530 {
531 name += *p++;
532 }
533
534 optInd = m_data->FindOptionByLongName(name);
535 if ( optInd == wxNOT_FOUND )
536 {
537 wxLogError(_("Unknown long option '%s'"), name.c_str());
538 }
539 }
540 else
541 {
542 isLong = FALSE;
543
544 // a short one: as they can be cumulated, we try to find the
545 // longest substring which is a valid option
546 const wxChar *p = arg.c_str() + 1;
f6bcfd97 547 while ( wxIsalnum(*p) || (*p == _T('_')) )
9f83044f
VZ
548 {
549 name += *p++;
550 }
551
552 size_t len = name.length();
553 do
554 {
555 if ( len == 0 )
556 {
557 // we couldn't find a valid option name in the
558 // beginning of this string
559 wxLogError(_("Unknown option '%s'"), name.c_str());
560
561 break;
562 }
563 else
564 {
565 optInd = m_data->FindOption(name.Left(len));
566
567 // will try with one character less the next time
568 len--;
569 }
570 }
571 while ( optInd == wxNOT_FOUND );
572
2822ee33 573 len++; // compensates extra len-- above
4f40f5e3 574 if ( (optInd != wxNOT_FOUND) && (len != name.length()) )
9f83044f 575 {
2822ee33
VZ
576 // first of all, the option name is only part of this
577 // string
578 name = name.Left(len);
579
9f83044f
VZ
580 // our option is only part of this argument, there is
581 // something else in it - it is either the value of this
582 // option or other switches if it is a switch
583 if ( m_data->m_options[(size_t)optInd].kind
584 == wxCMD_LINE_SWITCH )
585 {
586 // pretend that all the rest of the argument is the
587 // next argument, in fact
588 wxString arg2 = arg[0u];
2822ee33 589 arg2 += arg.Mid(len + 1); // +1 for leading '-'
9f83044f
VZ
590
591 m_data->m_arguments.Insert(arg2, n + 1);
4f40f5e3 592 count++;
9f83044f
VZ
593 }
594 //else: it's our value, we'll deal with it below
595 }
596 }
597
598 if ( optInd == wxNOT_FOUND )
599 {
600 ok = FALSE;
601
602 continue; // will break, in fact
603 }
604
605 wxCmdLineOption& opt = m_data->m_options[(size_t)optInd];
606 if ( opt.kind == wxCMD_LINE_SWITCH )
607 {
608 // nothing more to do
609 opt.SetHasValue();
610
611 if ( opt.flags & wxCMD_LINE_OPTION_HELP )
612 {
613 helpRequested = TRUE;
614
615 // it's not an error, but we still stop here
616 ok = FALSE;
617 }
618 }
619 else
620 {
621 // get the value
622
623 // +1 for leading '-'
624 const wxChar *p = arg.c_str() + 1 + name.length();
625 if ( isLong )
626 {
627 p++; // for another leading '-'
628
629 if ( *p++ != _T('=') )
630 {
fbdcff4a 631 wxLogError(_("Option '%s' requires a value, '=' expected."), name.c_str());
9f83044f
VZ
632
633 ok = FALSE;
634 }
635 }
636 else
637 {
638 switch ( *p )
639 {
f6bcfd97 640 case _T('='):
9f83044f
VZ
641 case _T(':'):
642 // the value follows
643 p++;
644 break;
645
646 case 0:
647 // the value is in the next argument
648 if ( ++n == count )
649 {
650 // ... but there is none
651 wxLogError(_("Option '%s' requires a value."),
652 name.c_str());
653
654 ok = FALSE;
655 }
656 else
657 {
658 // ... take it from there
659 p = m_data->m_arguments[n].c_str();
660 }
661 break;
662
663 default:
f6bcfd97
BP
664 // the value is right here: this may be legal or
665 // not depending on the option style
666 if ( opt.flags & wxCMD_LINE_NEEDS_SEPARATOR )
667 {
668 wxLogError(_("Separator expected after the option '%s'."),
669 name.c_str());
670
671 ok = FALSE;
672 }
9f83044f
VZ
673 }
674 }
675
676 if ( ok )
677 {
678 wxString value = p;
679 switch ( opt.type )
680 {
681 default:
682 wxFAIL_MSG( _T("unknown option type") );
683 // still fall through
684
685 case wxCMD_LINE_VAL_STRING:
686 opt.SetStrVal(value);
687 break;
688
689 case wxCMD_LINE_VAL_NUMBER:
690 {
691 long val;
692 if ( value.ToLong(&val) )
693 {
694 opt.SetLongVal(val);
695 }
696 else
697 {
fbdcff4a 698 wxLogError(_("'%s' is not a correct numeric value for option '%s'."),
9f83044f
VZ
699 value.c_str(), name.c_str());
700
701 ok = FALSE;
702 }
703 }
704 break;
705
706 case wxCMD_LINE_VAL_DATE:
707 {
708 wxDateTime dt;
709 const wxChar *res = dt.ParseDate(value);
710 if ( !res || *res )
711 {
fbdcff4a 712 wxLogError(_("Option '%s': '%s' cannot be converted to a date."),
9f83044f
VZ
713 name.c_str(), value.c_str());
714
715 ok = FALSE;
716 }
717 else
718 {
719 opt.SetDateVal(dt);
720 }
721 }
722 break;
723 }
724 }
725 }
726 }
727 else
728 {
729 // a parameter
730 if ( currentParam < countParam )
731 {
732 wxCmdLineParam& param = m_data->m_paramDesc[currentParam];
733
734 // TODO check the param type
735
736 m_data->m_parameters.Add(arg);
737
738 if ( !(param.flags & wxCMD_LINE_PARAM_MULTIPLE) )
739 {
740 currentParam++;
741 }
742 else
743 {
744 wxASSERT_MSG( currentParam == countParam - 1,
fbdcff4a 745 _T("all parameters after the one with wxCMD_LINE_PARAM_MULTIPLE style are ignored") );
9f83044f
VZ
746
747 // remember that we did have this last repeatable parameter
748 hadRepeatableParam = TRUE;
749 }
750 }
751 else
752 {
753 wxLogError(_("Unexpected parameter '%s'"), arg.c_str());
754
755 ok = FALSE;
756 }
757 }
758 }
759
760 // verify that all mandatory options were given
761 if ( ok )
762 {
763 size_t countOpt = m_data->m_options.GetCount();
764 for ( size_t n = 0; ok && (n < countOpt); n++ )
765 {
766 wxCmdLineOption& opt = m_data->m_options[n];
767 if ( (opt.flags & wxCMD_LINE_OPTION_MANDATORY) && !opt.HasValue() )
768 {
769 wxString optName;
770 if ( !opt.longName )
771 {
772 optName = opt.shortName;
773 }
774 else
775 {
776 optName.Printf(_("%s (or %s)"),
777 opt.shortName.c_str(),
778 opt.longName.c_str());
779 }
780
781 wxLogError(_("The value for the option '%s' must be specified."),
782 optName.c_str());
783
784 ok = FALSE;
785 }
786 }
787
788 for ( ; ok && (currentParam < countParam); currentParam++ )
789 {
790 wxCmdLineParam& param = m_data->m_paramDesc[currentParam];
791 if ( (currentParam == countParam - 1) &&
792 (param.flags & wxCMD_LINE_PARAM_MULTIPLE) &&
793 hadRepeatableParam )
794 {
795 // special case: currentParam wasn't incremented, but we did
796 // have it, so don't give error
797 continue;
798 }
799
800 if ( !(param.flags & wxCMD_LINE_PARAM_OPTIONAL) )
801 {
802 wxLogError(_("The required parameter '%s' was not specified."),
803 param.description.c_str());
804
805 ok = FALSE;
806 }
807 }
808 }
809
be03c0ec 810 if ( !ok && showUsage )
9f83044f
VZ
811 {
812 Usage();
813 }
814
815 return ok ? 0 : helpRequested ? -1 : 1;
816}
817
818// ----------------------------------------------------------------------------
819// give the usage message
820// ----------------------------------------------------------------------------
821
822void wxCmdLineParser::Usage()
823{
2822ee33
VZ
824 wxString appname = wxTheApp->GetAppName();
825 if ( !appname )
826 {
827 wxCHECK_RET( !m_data->m_arguments.IsEmpty(), _T("no program name") );
828
829 appname = wxFileNameFromPath(m_data->m_arguments[0]);
830 wxStripExtension(appname);
831 }
832
f6bcfd97
BP
833 // we construct the brief cmd line desc on the fly, but not the detailed
834 // help message below because we want to align the options descriptions
835 // and for this we must first know the longest one of them
836 wxString brief;
837 wxArrayString namesOptions, descOptions;
2822ee33 838 brief.Printf(_("Usage: %s"), appname.c_str());
9f83044f 839
f6bcfd97
BP
840 // the switch char is usually '-' but this can be changed with
841 // SetSwitchChars() and then the first one of possible chars is used
842 wxChar chSwitch = !m_data->m_switchChars ? _T('-')
843 : m_data->m_switchChars[0u];
844
9f83044f
VZ
845 size_t n, count = m_data->m_options.GetCount();
846 for ( n = 0; n < count; n++ )
847 {
848 wxCmdLineOption& opt = m_data->m_options[n];
849
850 brief << _T(' ');
851 if ( !(opt.flags & wxCMD_LINE_OPTION_MANDATORY) )
852 {
853 brief << _T('[');
854 }
855
be03c0ec
VZ
856 if ( !opt.shortName.empty() )
857 {
858 brief << chSwitch << opt.shortName;
859 }
860 else if ( !opt.longName.empty() )
861 {
862 brief << _T("--") << opt.longName;
863 }
864 else
865 {
866 wxFAIL_MSG( _T("option without neither short nor long name?") );
867 }
f6bcfd97
BP
868
869 wxString option;
be03c0ec
VZ
870
871 if ( !opt.shortName.empty() )
872 {
873 option << _T(" ") << chSwitch << opt.shortName;
874 }
875
876 if ( !opt.longName.empty() )
9f83044f 877 {
be03c0ec
VZ
878 option << (option.empty() ? _T(" ") : _T(", "))
879 << _T("--") << opt.longName;
9f83044f
VZ
880 }
881
882 if ( opt.kind != wxCMD_LINE_SWITCH )
883 {
884 wxString val;
885 val << _T('<') << GetTypeName(opt.type) << _T('>');
886 brief << _T(' ') << val;
f6bcfd97 887 option << (!opt.longName ? _T(':') : _T('=')) << val;
9f83044f
VZ
888 }
889
890 if ( !(opt.flags & wxCMD_LINE_OPTION_MANDATORY) )
891 {
892 brief << _T(']');
893 }
894
f6bcfd97
BP
895 namesOptions.Add(option);
896 descOptions.Add(opt.description);
9f83044f
VZ
897 }
898
899 count = m_data->m_paramDesc.GetCount();
900 for ( n = 0; n < count; n++ )
901 {
902 wxCmdLineParam& param = m_data->m_paramDesc[n];
903
904 brief << _T(' ');
905 if ( param.flags & wxCMD_LINE_PARAM_OPTIONAL )
906 {
907 brief << _T('[');
908 }
909
910 brief << param.description;
911
912 if ( param.flags & wxCMD_LINE_PARAM_MULTIPLE )
913 {
914 brief << _T("...");
915 }
916
917 if ( param.flags & wxCMD_LINE_PARAM_OPTIONAL )
918 {
919 brief << _T(']');
920 }
921 }
922
e612f101
VZ
923 if ( !!m_data->m_logo )
924 {
925 wxLogMessage(m_data->m_logo);
926 }
927
be03c0ec
VZ
928 // in console mode we want to show the brief usage message first, then the
929 // detailed one but in GUI build we give the details first and then the
930 // summary - like this, the brief message appears in the wxLogGui dialog,
931 // as expected
932#if !wxUSE_GUI
9f83044f 933 wxLogMessage(brief);
be03c0ec 934#endif // !wxUSE_GUI
f6bcfd97
BP
935
936 // now construct the detailed help message
937 size_t len, lenMax = 0;
938 count = namesOptions.GetCount();
939 for ( n = 0; n < count; n++ )
940 {
941 len = namesOptions[n].length();
942 if ( len > lenMax )
943 lenMax = len;
944 }
945
946 wxString detailed;
947 for ( n = 0; n < count; n++ )
948 {
949 len = namesOptions[n].length();
950 detailed << namesOptions[n]
951 << wxString(_T(' '), lenMax - len) << _T('\t')
952 << descOptions[n]
953 << _T('\n');
954 }
955
9f83044f 956 wxLogMessage(detailed);
be03c0ec
VZ
957
958 // do it now if not done above
959#if wxUSE_GUI
960 wxLogMessage(brief);
961#endif // wxUSE_GUI
9f83044f
VZ
962}
963
964// ----------------------------------------------------------------------------
31f6de22 965// private functions
9f83044f
VZ
966// ----------------------------------------------------------------------------
967
968static wxString GetTypeName(wxCmdLineParamType type)
969{
970 wxString s;
971 switch ( type )
972 {
973 default:
974 wxFAIL_MSG( _T("unknown option type") );
975 // still fall through
976
977 case wxCMD_LINE_VAL_STRING: s = _("str"); break;
978 case wxCMD_LINE_VAL_NUMBER: s = _("num"); break;
979 case wxCMD_LINE_VAL_DATE: s = _("date"); break;
980 }
981
982 return s;
983}
1e6feb95
VZ
984
985#endif // wxUSE_CMDLINE_PARSER
31f6de22
VZ
986
987// ----------------------------------------------------------------------------
988// global functions
989// ----------------------------------------------------------------------------
990
217f9d07
VZ
991/*
992 This function is mainly used under Windows (as under Unix we always get the
bb10b31d 993 command line arguments as argc/argv anyhow) and so it tries to handle the
217f9d07
VZ
994 Windows path names (separated by backslashes) correctly. For this it only
995 considers that a backslash may be used to escape another backslash (but
996 normally this is _not_ needed) or a quote but nothing else.
997
998 In particular, to pass a single argument containing a space to the program
999 it should be quoted:
1000
1001 myprog.exe foo bar -> argc = 3, argv[1] = "foo", argv[2] = "bar"
1002 myprog.exe "foo bar" -> argc = 2, argv[1] = "foo bar"
1003
1004 To pass an argument containing spaces and quotes, the latter should be
1005 escaped with a backslash:
1006
1007 myprog.exe "foo \"bar\"" -> argc = 2, argv[1] = "foo "bar""
1008
1009 This hopefully matches the conventions used by Explorer/command line
1010 interpreter under Windows. If not, this function should be fixed.
1011 */
1012
31f6de22
VZ
1013/* static */
1014wxArrayString wxCmdLineParser::ConvertStringToArgs(const wxChar *p)
1015{
1016 wxArrayString args;
1017
1018 wxString arg;
1019 arg.reserve(1024);
1020
1021 bool isInsideQuotes = FALSE;
1022 for ( ;; )
1023 {
1024 // skip white space
1025 while ( *p == _T(' ') || *p == _T('\t') )
1026 p++;
1027
1028 // anything left?
1029 if ( *p == _T('\0') )
1030 break;
1031
1032 // parse this parameter
1033 arg.clear();
1034 for ( ;; p++ )
1035 {
1036 // do we have a (lone) backslash?
1037 bool isQuotedByBS = FALSE;
1038 while ( *p == _T('\\') )
1039 {
1040 p++;
1041
1042 // if we have 2 backslashes in a row, output one
1043 if ( isQuotedByBS )
1044 {
1045 arg += _T('\\');
1046 isQuotedByBS = FALSE;
1047 }
1048 else // the next char is quoted
1049 {
1050 isQuotedByBS = TRUE;
1051 }
1052 }
1053
1054 bool skipChar = FALSE,
1055 endParam = FALSE;
1056 switch ( *p )
1057 {
1058 case _T('"'):
1059 if ( !isQuotedByBS )
1060 {
1061 // don't put the quote itself in the arg
1062 skipChar = TRUE;
1063
1064 isInsideQuotes = !isInsideQuotes;
1065 }
1066 //else: insert a literal quote
1067
1068 break;
1069
1070 case _T(' '):
1071 case _T('\t'):
217f9d07
VZ
1072 // we intentionally don't check for preceding backslash
1073 // here as if we allowed it to be used to escape spaces the
1074 // cmd line of the form "foo.exe a:\ c:\bar" wouldn't be
1075 // parsed correctly
1076 if ( isInsideQuotes )
31f6de22
VZ
1077 {
1078 // preserve it, skip endParam below
1079 break;
1080 }
1081 //else: fall through
1082
1083 case _T('\0'):
1084 endParam = TRUE;
1085 break;
4efb5650
VZ
1086
1087 default:
1088 if ( isQuotedByBS )
1089 {
1090 // ignore backslash before an ordinary character - this
1091 // is needed to properly handle the file names under
1092 // Windows appearing in the command line
1093 arg += _T('\\');
1094 }
31f6de22
VZ
1095 }
1096
1097 // end of argument?
1098 if ( endParam )
1099 break;
1100
1101 // otherwise copy this char to arg
1102 if ( !skipChar )
1103 {
1104 arg += *p;
1105 }
1106 }
1107
1108 args.Add(arg);
1109 }
1110
1111 return args;
1112}
1113