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