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