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