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