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