]> git.saurik.com Git - wxWidgets.git/blame - src/common/cmdline.cpp
small cleanup
[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{
3f2bcf34
VZ
529 for ( size_t i = 0; i < m_data->m_options.Count(); i++ )
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 {
2b5f62a0 598 errorMsg << wxString::Format(_("Unknown long option '%s'"), name.c_str()) << wxT("\n");
250b589f 599 }
9f83044f 600 }
250b589f 601 else
9f83044f 602 {
250b589f
JS
603 optInd = wxNOT_FOUND; // Sanity check
604
605 // Print the argument including leading "--"
606 name.Prepend( wxT("--") );
2b5f62a0 607 errorMsg << wxString::Format(_("Unknown option '%s'"), name.c_str()) << wxT("\n");
9f83044f 608 }
250b589f 609
9f83044f
VZ
610 }
611 else
612 {
36ebb50a 613 isLong = false;
9f83044f
VZ
614
615 // a short one: as they can be cumulated, we try to find the
616 // longest substring which is a valid option
617 const wxChar *p = arg.c_str() + 1;
250b589f
JS
618
619 name = GetShortOptionName(p);
9f83044f
VZ
620
621 size_t len = name.length();
622 do
623 {
624 if ( len == 0 )
625 {
626 // we couldn't find a valid option name in the
627 // beginning of this string
2b5f62a0 628 errorMsg << wxString::Format(_("Unknown option '%s'"), name.c_str()) << wxT("\n");
9f83044f
VZ
629
630 break;
631 }
632 else
633 {
634 optInd = m_data->FindOption(name.Left(len));
635
636 // will try with one character less the next time
637 len--;
638 }
639 }
640 while ( optInd == wxNOT_FOUND );
641
2822ee33 642 len++; // compensates extra len-- above
4f40f5e3 643 if ( (optInd != wxNOT_FOUND) && (len != name.length()) )
9f83044f 644 {
2822ee33
VZ
645 // first of all, the option name is only part of this
646 // string
647 name = name.Left(len);
648
9f83044f
VZ
649 // our option is only part of this argument, there is
650 // something else in it - it is either the value of this
651 // option or other switches if it is a switch
652 if ( m_data->m_options[(size_t)optInd].kind
653 == wxCMD_LINE_SWITCH )
654 {
655 // pretend that all the rest of the argument is the
656 // next argument, in fact
657 wxString arg2 = arg[0u];
2822ee33 658 arg2 += arg.Mid(len + 1); // +1 for leading '-'
9f83044f 659
df5168c4
MB
660 m_data->m_arguments.insert
661 (m_data->m_arguments.begin() + n + 1, arg2);
4f40f5e3 662 count++;
9f83044f
VZ
663 }
664 //else: it's our value, we'll deal with it below
665 }
666 }
667
668 if ( optInd == wxNOT_FOUND )
669 {
36ebb50a 670 ok = false;
9f83044f
VZ
671
672 continue; // will break, in fact
673 }
674
675 wxCmdLineOption& opt = m_data->m_options[(size_t)optInd];
676 if ( opt.kind == wxCMD_LINE_SWITCH )
677 {
678 // nothing more to do
679 opt.SetHasValue();
680
681 if ( opt.flags & wxCMD_LINE_OPTION_HELP )
682 {
36ebb50a 683 helpRequested = true;
9f83044f
VZ
684
685 // it's not an error, but we still stop here
36ebb50a 686 ok = false;
9f83044f
VZ
687 }
688 }
689 else
690 {
691 // get the value
692
693 // +1 for leading '-'
694 const wxChar *p = arg.c_str() + 1 + name.length();
695 if ( isLong )
696 {
697 p++; // for another leading '-'
698
699 if ( *p++ != _T('=') )
700 {
2b5f62a0 701 errorMsg << wxString::Format(_("Option '%s' requires a value, '=' expected."), name.c_str()) << wxT("\n");
9f83044f 702
36ebb50a 703 ok = false;
9f83044f
VZ
704 }
705 }
706 else
707 {
708 switch ( *p )
709 {
f6bcfd97 710 case _T('='):
9f83044f
VZ
711 case _T(':'):
712 // the value follows
713 p++;
714 break;
715
716 case 0:
717 // the value is in the next argument
718 if ( ++n == count )
719 {
720 // ... but there is none
74698d3a 721 errorMsg << wxString::Format(_("Option '%s' requires a value."),
2b5f62a0 722 name.c_str()) << wxT("\n");
9f83044f 723
36ebb50a 724 ok = false;
9f83044f
VZ
725 }
726 else
727 {
728 // ... take it from there
729 p = m_data->m_arguments[n].c_str();
730 }
731 break;
732
733 default:
f6bcfd97
BP
734 // the value is right here: this may be legal or
735 // not depending on the option style
736 if ( opt.flags & wxCMD_LINE_NEEDS_SEPARATOR )
737 {
74698d3a 738 errorMsg << wxString::Format(_("Separator expected after the option '%s'."),
2b5f62a0 739 name.c_str()) << wxT("\n");
f6bcfd97 740
36ebb50a 741 ok = false;
f6bcfd97 742 }
9f83044f
VZ
743 }
744 }
745
746 if ( ok )
747 {
748 wxString value = p;
749 switch ( opt.type )
750 {
751 default:
752 wxFAIL_MSG( _T("unknown option type") );
753 // still fall through
754
755 case wxCMD_LINE_VAL_STRING:
756 opt.SetStrVal(value);
757 break;
758
759 case wxCMD_LINE_VAL_NUMBER:
760 {
761 long val;
762 if ( value.ToLong(&val) )
763 {
764 opt.SetLongVal(val);
765 }
766 else
767 {
74698d3a 768 errorMsg << wxString::Format(_("'%s' is not a correct numeric value for option '%s'."),
2b5f62a0 769 value.c_str(), name.c_str()) << wxT("\n");
9f83044f 770
36ebb50a 771 ok = false;
9f83044f
VZ
772 }
773 }
774 break;
775
e2b87f38 776#if wxUSE_DATETIME
9f83044f
VZ
777 case wxCMD_LINE_VAL_DATE:
778 {
779 wxDateTime dt;
780 const wxChar *res = dt.ParseDate(value);
781 if ( !res || *res )
782 {
74698d3a 783 errorMsg << wxString::Format(_("Option '%s': '%s' cannot be converted to a date."),
2b5f62a0 784 name.c_str(), value.c_str()) << wxT("\n");
9f83044f 785
36ebb50a 786 ok = false;
9f83044f
VZ
787 }
788 else
789 {
790 opt.SetDateVal(dt);
791 }
792 }
793 break;
e2b87f38 794#endif // wxUSE_DATETIME
9f83044f
VZ
795 }
796 }
797 }
798 }
799 else
800 {
801 // a parameter
802 if ( currentParam < countParam )
803 {
804 wxCmdLineParam& param = m_data->m_paramDesc[currentParam];
805
806 // TODO check the param type
807
df5168c4 808 m_data->m_parameters.push_back(arg);
9f83044f
VZ
809
810 if ( !(param.flags & wxCMD_LINE_PARAM_MULTIPLE) )
811 {
812 currentParam++;
813 }
814 else
815 {
816 wxASSERT_MSG( currentParam == countParam - 1,
fbdcff4a 817 _T("all parameters after the one with wxCMD_LINE_PARAM_MULTIPLE style are ignored") );
9f83044f
VZ
818
819 // remember that we did have this last repeatable parameter
36ebb50a 820 hadRepeatableParam = true;
9f83044f
VZ
821 }
822 }
823 else
824 {
2b5f62a0 825 errorMsg << wxString::Format(_("Unexpected parameter '%s'"), arg.c_str()) << wxT("\n");
9f83044f 826
36ebb50a 827 ok = false;
9f83044f
VZ
828 }
829 }
830 }
831
832 // verify that all mandatory options were given
833 if ( ok )
834 {
835 size_t countOpt = m_data->m_options.GetCount();
836 for ( size_t n = 0; ok && (n < countOpt); n++ )
837 {
838 wxCmdLineOption& opt = m_data->m_options[n];
839 if ( (opt.flags & wxCMD_LINE_OPTION_MANDATORY) && !opt.HasValue() )
840 {
841 wxString optName;
842 if ( !opt.longName )
843 {
844 optName = opt.shortName;
845 }
846 else
847 {
33293a32
VZ
848 if ( AreLongOptionsEnabled() )
849 {
850 optName.Printf( _("%s (or %s)"),
851 opt.shortName.c_str(),
852 opt.longName.c_str() );
853 }
854 else
855 {
856 optName.Printf( wxT("%s"),
857 opt.shortName.c_str() );
858 }
9f83044f
VZ
859 }
860
74698d3a 861 errorMsg << wxString::Format(_("The value for the option '%s' must be specified."),
2b5f62a0 862 optName.c_str()) << wxT("\n");
9f83044f 863
36ebb50a 864 ok = false;
9f83044f
VZ
865 }
866 }
867
868 for ( ; ok && (currentParam < countParam); currentParam++ )
869 {
870 wxCmdLineParam& param = m_data->m_paramDesc[currentParam];
871 if ( (currentParam == countParam - 1) &&
872 (param.flags & wxCMD_LINE_PARAM_MULTIPLE) &&
873 hadRepeatableParam )
874 {
875 // special case: currentParam wasn't incremented, but we did
876 // have it, so don't give error
877 continue;
878 }
879
880 if ( !(param.flags & wxCMD_LINE_PARAM_OPTIONAL) )
881 {
74698d3a 882 errorMsg << wxString::Format(_("The required parameter '%s' was not specified."),
2b5f62a0 883 param.description.c_str()) << wxT("\n");
9f83044f 884
36ebb50a 885 ok = false;
9f83044f
VZ
886 }
887 }
888 }
889
e9c54ec3
VZ
890 // if there was an error during parsing the command line, show this error
891 // and also the usage message if it had been requested
892 if ( !ok && (!errorMsg.empty() || (helpRequested && showUsage)) )
9f83044f 893 {
74698d3a 894 wxMessageOutput* msgOut = wxMessageOutput::Get();
74698d3a 895 if ( msgOut )
e9c54ec3
VZ
896 {
897 wxString usage;
898 if ( showUsage )
899 usage = GetUsageString();
900
74698d3a 901 msgOut->Printf( wxT("%s%s"), usage.c_str(), errorMsg.c_str() );
e9c54ec3
VZ
902 }
903 else
904 {
905 wxFAIL_MSG( _T("no wxMessageOutput object?") );
906 }
9f83044f
VZ
907 }
908
909 return ok ? 0 : helpRequested ? -1 : 1;
910}
911
912// ----------------------------------------------------------------------------
913// give the usage message
914// ----------------------------------------------------------------------------
915
916void wxCmdLineParser::Usage()
74698d3a 917{
74698d3a
MB
918 wxMessageOutput* msgOut = wxMessageOutput::Get();
919 if ( msgOut )
e9c54ec3
VZ
920 {
921 msgOut->Printf( wxT("%s"), GetUsageString().c_str() );
922 }
923 else
924 {
925 wxFAIL_MSG( _T("no wxMessageOutput object?") );
926 }
74698d3a
MB
927}
928
929wxString wxCmdLineParser::GetUsageString()
9f83044f 930{
a2ec9439
VZ
931 wxString appname;
932 if ( m_data->m_arguments.empty() )
2822ee33 933 {
a2ec9439
VZ
934 if ( wxTheApp )
935 appname = wxTheApp->GetAppName();
936 }
937 else // use argv[0]
938 {
939 appname = wxFileName(m_data->m_arguments[0]).GetName();
2822ee33
VZ
940 }
941
f6bcfd97
BP
942 // we construct the brief cmd line desc on the fly, but not the detailed
943 // help message below because we want to align the options descriptions
944 // and for this we must first know the longest one of them
74698d3a 945 wxString usage;
f6bcfd97 946 wxArrayString namesOptions, descOptions;
74698d3a 947
e9c54ec3 948 if ( !m_data->m_logo.empty() )
74698d3a
MB
949 {
950 usage << m_data->m_logo << _T('\n');
951 }
952
953 usage << wxString::Format(_("Usage: %s"), appname.c_str());
9f83044f 954
f6bcfd97
BP
955 // the switch char is usually '-' but this can be changed with
956 // SetSwitchChars() and then the first one of possible chars is used
957 wxChar chSwitch = !m_data->m_switchChars ? _T('-')
958 : m_data->m_switchChars[0u];
959
250b589f 960 bool areLongOptionsEnabled = AreLongOptionsEnabled();
9f83044f
VZ
961 size_t n, count = m_data->m_options.GetCount();
962 for ( n = 0; n < count; n++ )
963 {
964 wxCmdLineOption& opt = m_data->m_options[n];
965
74698d3a 966 usage << _T(' ');
9f83044f
VZ
967 if ( !(opt.flags & wxCMD_LINE_OPTION_MANDATORY) )
968 {
74698d3a 969 usage << _T('[');
9f83044f
VZ
970 }
971
be03c0ec
VZ
972 if ( !opt.shortName.empty() )
973 {
74698d3a 974 usage << chSwitch << opt.shortName;
be03c0ec 975 }
250b589f 976 else if ( areLongOptionsEnabled && !opt.longName.empty() )
be03c0ec 977 {
74698d3a 978 usage << _T("--") << opt.longName;
be03c0ec
VZ
979 }
980 else
981 {
250b589f
JS
982 if (!opt.longName.empty())
983 {
984 wxFAIL_MSG( wxT("option with only a long name while long ")
985 wxT("options are disabled") );
986 }
987 else
988 {
989 wxFAIL_MSG( _T("option without neither short nor long name") );
990 }
be03c0ec 991 }
f6bcfd97
BP
992
993 wxString option;
be03c0ec
VZ
994
995 if ( !opt.shortName.empty() )
996 {
997 option << _T(" ") << chSwitch << opt.shortName;
998 }
999
250b589f 1000 if ( areLongOptionsEnabled && !opt.longName.empty() )
9f83044f 1001 {
be03c0ec
VZ
1002 option << (option.empty() ? _T(" ") : _T(", "))
1003 << _T("--") << opt.longName;
9f83044f
VZ
1004 }
1005
1006 if ( opt.kind != wxCMD_LINE_SWITCH )
1007 {
1008 wxString val;
1009 val << _T('<') << GetTypeName(opt.type) << _T('>');
74698d3a 1010 usage << _T(' ') << val;
f6bcfd97 1011 option << (!opt.longName ? _T(':') : _T('=')) << val;
9f83044f
VZ
1012 }
1013
1014 if ( !(opt.flags & wxCMD_LINE_OPTION_MANDATORY) )
1015 {
74698d3a 1016 usage << _T(']');
9f83044f
VZ
1017 }
1018
df5168c4
MB
1019 namesOptions.push_back(option);
1020 descOptions.push_back(opt.description);
9f83044f
VZ
1021 }
1022
1023 count = m_data->m_paramDesc.GetCount();
1024 for ( n = 0; n < count; n++ )
1025 {
1026 wxCmdLineParam& param = m_data->m_paramDesc[n];
1027
74698d3a 1028 usage << _T(' ');
9f83044f
VZ
1029 if ( param.flags & wxCMD_LINE_PARAM_OPTIONAL )
1030 {
74698d3a 1031 usage << _T('[');
9f83044f
VZ
1032 }
1033
74698d3a 1034 usage << param.description;
9f83044f
VZ
1035
1036 if ( param.flags & wxCMD_LINE_PARAM_MULTIPLE )
1037 {
74698d3a 1038 usage << _T("...");
9f83044f
VZ
1039 }
1040
1041 if ( param.flags & wxCMD_LINE_PARAM_OPTIONAL )
1042 {
74698d3a 1043 usage << _T(']');
9f83044f
VZ
1044 }
1045 }
1046
74698d3a 1047 usage << _T('\n');
f6bcfd97
BP
1048
1049 // now construct the detailed help message
1050 size_t len, lenMax = 0;
df5168c4 1051 count = namesOptions.size();
f6bcfd97
BP
1052 for ( n = 0; n < count; n++ )
1053 {
1054 len = namesOptions[n].length();
1055 if ( len > lenMax )
1056 lenMax = len;
1057 }
1058
f6bcfd97
BP
1059 for ( n = 0; n < count; n++ )
1060 {
1061 len = namesOptions[n].length();
74698d3a
MB
1062 usage << namesOptions[n]
1063 << wxString(_T(' '), lenMax - len) << _T('\t')
1064 << descOptions[n]
1065 << _T('\n');
f6bcfd97
BP
1066 }
1067
74698d3a 1068 return usage;
9f83044f
VZ
1069}
1070
1071// ----------------------------------------------------------------------------
31f6de22 1072// private functions
9f83044f
VZ
1073// ----------------------------------------------------------------------------
1074
1075static wxString GetTypeName(wxCmdLineParamType type)
1076{
1077 wxString s;
1078 switch ( type )
1079 {
1080 default:
1081 wxFAIL_MSG( _T("unknown option type") );
1082 // still fall through
1083
e9c54ec3
VZ
1084 case wxCMD_LINE_VAL_STRING:
1085 s = _("str");
1086 break;
1087
1088 case wxCMD_LINE_VAL_NUMBER:
1089 s = _("num");
1090 break;
1091
1092 case wxCMD_LINE_VAL_DATE:
1093 s = _("date");
1094 break;
9f83044f
VZ
1095 }
1096
1097 return s;
1098}
1e6feb95 1099
250b589f
JS
1100/*
1101Returns a string which is equal to the string pointed to by p, but up to the
1102point where p contains an character that's not allowed.
1103Allowable characters are letters and numbers, and characters pointed to by
1104the parameter allowedChars.
1105
1106For example, if p points to "abcde-@-_", and allowedChars is "-_",
1107this function returns "abcde-".
1108*/
1109static wxString GetOptionName(const wxChar *p,
1110 const wxChar *allowedChars)
1111{
1112 wxString argName;
1113
1114 while ( *p && (wxIsalnum(*p) || wxStrchr(allowedChars, *p)) )
1115 {
1116 argName += *p++;
1117 }
1118
1119 return argName;
1120}
1121
1122// Besides alphanumeric characters, short and long options can
1123// have other characters.
1124
1125// A short option additionally can have these
1126#define wxCMD_LINE_CHARS_ALLOWED_BY_SHORT_OPTION wxT("_?")
1127
1128// A long option can have the same characters as a short option and a '-'.
1129#define wxCMD_LINE_CHARS_ALLOWED_BY_LONG_OPTION \
1130 wxCMD_LINE_CHARS_ALLOWED_BY_SHORT_OPTION wxT("-")
1131
1132static wxString GetShortOptionName(const wxChar *p)
1133{
1134 return GetOptionName(p, wxCMD_LINE_CHARS_ALLOWED_BY_SHORT_OPTION);
1135}
1136
1137static wxString GetLongOptionName(const wxChar *p)
1138{
1139 return GetOptionName(p, wxCMD_LINE_CHARS_ALLOWED_BY_LONG_OPTION);
1140}
1141
1e6feb95 1142#endif // wxUSE_CMDLINE_PARSER
31f6de22
VZ
1143
1144// ----------------------------------------------------------------------------
1145// global functions
1146// ----------------------------------------------------------------------------
1147
217f9d07
VZ
1148/*
1149 This function is mainly used under Windows (as under Unix we always get the
7a1f9c09
VZ
1150 command line arguments as argc/argv anyhow) and so it tries to follow
1151 Windows conventions for the command line handling, not Unix ones. For
1152 instance, backslash is not special except when it precedes double quote when
1153 it does quote it.
217f9d07
VZ
1154 */
1155
31f6de22
VZ
1156/* static */
1157wxArrayString wxCmdLineParser::ConvertStringToArgs(const wxChar *p)
1158{
1159 wxArrayString args;
1160
1161 wxString arg;
1162 arg.reserve(1024);
1163
7a1f9c09 1164 bool isInsideQuotes = false;
31f6de22
VZ
1165 for ( ;; )
1166 {
1167 // skip white space
1168 while ( *p == _T(' ') || *p == _T('\t') )
1169 p++;
1170
1171 // anything left?
1172 if ( *p == _T('\0') )
1173 break;
1174
1175 // parse this parameter
7a1f9c09
VZ
1176 bool endParam = false;
1177 bool lastBS = false;
1178 for ( arg.clear(); !endParam; p++ )
31f6de22 1179 {
31f6de22
VZ
1180 switch ( *p )
1181 {
93b79b6b 1182 case _T('"'):
7a1f9c09 1183 if ( !lastBS )
31f6de22 1184 {
31f6de22 1185 isInsideQuotes = !isInsideQuotes;
31f6de22 1186
7a1f9c09
VZ
1187 // don't put quote in arg
1188 continue;
1189 }
1190 //else: quote has no special meaning but the backslash
1191 // still remains -- makes no sense but this is what
1192 // Windows does
31f6de22
VZ
1193 break;
1194
1195 case _T(' '):
1196 case _T('\t'):
7a1f9c09 1197 // backslash does *not* quote the space, only quotes do
217f9d07 1198 if ( isInsideQuotes )
31f6de22 1199 {
7a1f9c09 1200 // skip assignment below
31f6de22
VZ
1201 break;
1202 }
7a1f9c09 1203 // fall through
31f6de22
VZ
1204
1205 case _T('\0'):
36ebb50a 1206 endParam = true;
93b79b6b 1207
36ebb50a
VZ
1208 break;
1209 }
1210
1211 if ( endParam )
1212 {
1213 break;
31f6de22
VZ
1214 }
1215
7a1f9c09 1216 lastBS = *p == _T('\\');
31f6de22 1217
7a1f9c09 1218 arg += *p;
31f6de22
VZ
1219 }
1220
df5168c4 1221 args.push_back(arg);
31f6de22
VZ
1222 }
1223
1224 return args;
1225}