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