]> git.saurik.com Git - wxWidgets.git/blame - src/common/cmdline.cpp
Misc validity fixes to samples/xrc/rc/*.xrc.
[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
9f83044f 7// Copyright: (c) 2000 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
65571936 8// Licence: wxWindows licence
9f83044f
VZ
9///////////////////////////////////////////////////////////////////////////////
10
11// ============================================================================
12// declarations
13// ============================================================================
14
15// ----------------------------------------------------------------------------
16// headers
17// ----------------------------------------------------------------------------
18
9f83044f
VZ
19// For compilers that support precompilation, includes "wx.h".
20#include "wx/wxprec.h"
21
22#ifdef __BORLANDC__
23 #pragma hdrstop
24#endif
25
26#ifndef WX_PRECOMP
ad9835c9 27 #include "wx/dynarray.h"
9f83044f
VZ
28 #include "wx/string.h"
29 #include "wx/log.h"
30 #include "wx/intl.h"
2822ee33 31 #include "wx/app.h"
9f83044f
VZ
32#endif //WX_PRECOMP
33
ad9835c9
WS
34#include "wx/cmdline.h"
35
36#if wxUSE_CMDLINE_PARSER
37
ff0ea71c 38#include <ctype.h>
19f31097 39#include <locale.h> // for LC_ALL
ff0ea71c 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"
0a4daf39 45#include "wx/scopeguard.h"
9f83044f
VZ
46
47// ----------------------------------------------------------------------------
48// private functions
49// ----------------------------------------------------------------------------
50
51static wxString GetTypeName(wxCmdLineParamType type);
52
86501081
VS
53static wxString GetOptionName(wxString::const_iterator p,
54 wxString::const_iterator end,
55 const wxChar *allowedChars);
250b589f 56
86501081
VS
57static wxString GetShortOptionName(wxString::const_iterator p,
58 wxString::const_iterator end);
250b589f 59
86501081
VS
60static wxString GetLongOptionName(wxString::const_iterator p,
61 wxString::const_iterator end);
250b589f 62
9f83044f 63// ----------------------------------------------------------------------------
250b589f 64// private structs
9f83044f
VZ
65// ----------------------------------------------------------------------------
66
67// an internal representation of an option
68struct wxCmdLineOption
69{
70 wxCmdLineOption(wxCmdLineEntryType k,
71 const wxString& shrt,
72 const wxString& lng,
73 const wxString& desc,
74 wxCmdLineParamType typ,
75 int fl)
76 {
e559d790 77 // wxCMD_LINE_USAGE_TEXT uses only description, shortName and longName is empty
e559d790
VZ
78 if ( k != wxCMD_LINE_USAGE_TEXT )
79 {
80 wxASSERT_MSG
81 (
82 !shrt.empty() || !lng.empty(),
83 wxT("option should have at least one name")
84 );
85
86 wxASSERT_MSG
87 (
88 GetShortOptionName(shrt.begin(), shrt.end()).Len() == shrt.Len(),
89 wxT("Short option contains invalid characters")
90 );
91
92 wxASSERT_MSG
93 (
94 GetLongOptionName(lng.begin(), lng.end()).Len() == lng.Len(),
95 wxT("Long option contains invalid characters")
96 );
97 }
250b589f 98
9f83044f
VZ
99 kind = k;
100
101 shortName = shrt;
102 longName = lng;
103 description = desc;
104
105 type = typ;
106 flags = fl;
107
dd18cc65 108 Reset();
9f83044f
VZ
109 }
110
111 // can't use union easily here, so just store all possible data fields, we
112 // don't waste much (might still use union later if the number of supported
113 // types increases, so always use the accessor functions and don't access
114 // the fields directly!)
115
6f2a55e3 116 void Check(wxCmdLineParamType WXUNUSED_UNLESS_DEBUG(typ)) const
9f83044f 117 {
9a83f860 118 wxASSERT_MSG( type == typ, wxT("type mismatch in wxCmdLineOption") );
9f83044f
VZ
119 }
120
b1859b1a
VZ
121 double GetDoubleVal() const
122 { Check(wxCMD_LINE_VAL_DOUBLE); return m_doubleVal; }
9f83044f
VZ
123 long GetLongVal() const
124 { Check(wxCMD_LINE_VAL_NUMBER); return m_longVal; }
125 const wxString& GetStrVal() const
126 { Check(wxCMD_LINE_VAL_STRING); return m_strVal; }
e2b87f38 127#if wxUSE_DATETIME
9f83044f
VZ
128 const wxDateTime& GetDateVal() const
129 { Check(wxCMD_LINE_VAL_DATE); return m_dateVal; }
e2b87f38 130#endif // wxUSE_DATETIME
9f83044f 131
b1859b1a
VZ
132 void SetDoubleVal(double val)
133 { Check(wxCMD_LINE_VAL_DOUBLE); m_doubleVal = val; m_hasVal = true; }
9f83044f 134 void SetLongVal(long val)
36ebb50a 135 { Check(wxCMD_LINE_VAL_NUMBER); m_longVal = val; m_hasVal = true; }
9f83044f 136 void SetStrVal(const wxString& val)
36ebb50a 137 { Check(wxCMD_LINE_VAL_STRING); m_strVal = val; m_hasVal = true; }
e2b87f38 138#if wxUSE_DATETIME
fbfb8bcc 139 void SetDateVal(const wxDateTime& val)
36ebb50a 140 { Check(wxCMD_LINE_VAL_DATE); m_dateVal = val; m_hasVal = true; }
e2b87f38 141#endif // wxUSE_DATETIME
9f83044f 142
dd18cc65 143 void SetHasValue() { m_hasVal = true; }
9f83044f
VZ
144 bool HasValue() const { return m_hasVal; }
145
a6bf0c95
VZ
146 void SetNegated() { m_isNegated = true; }
147 bool IsNegated() const { return m_isNegated; }
148
dd18cc65
VZ
149 // Reset to the initial state, called before parsing another command line.
150 void Reset()
151 {
152 m_hasVal =
153 m_isNegated = false;
154 }
155
9f83044f
VZ
156public:
157 wxCmdLineEntryType kind;
be03c0ec
VZ
158 wxString shortName,
159 longName,
160 description;
9f83044f
VZ
161 wxCmdLineParamType type;
162 int flags;
163
164private:
165 bool m_hasVal;
a6bf0c95 166 bool m_isNegated;
9f83044f 167
b1859b1a 168 double m_doubleVal;
9f83044f
VZ
169 long m_longVal;
170 wxString m_strVal;
e2b87f38 171#if wxUSE_DATETIME
9f83044f 172 wxDateTime m_dateVal;
e2b87f38 173#endif // wxUSE_DATETIME
9f83044f
VZ
174};
175
176struct wxCmdLineParam
177{
178 wxCmdLineParam(const wxString& desc,
179 wxCmdLineParamType typ,
180 int fl)
181 : description(desc)
182 {
183 type = typ;
184 flags = fl;
185 }
186
187 wxString description;
188 wxCmdLineParamType type;
189 int flags;
190};
191
192WX_DECLARE_OBJARRAY(wxCmdLineOption, wxArrayOptions);
193WX_DECLARE_OBJARRAY(wxCmdLineParam, wxArrayParams);
194
195#include "wx/arrimpl.cpp"
196
4115960d
VZ
197WX_DEFINE_OBJARRAY(wxArrayOptions)
198WX_DEFINE_OBJARRAY(wxArrayParams)
9f83044f
VZ
199
200// the parser internal state
201struct wxCmdLineParserData
202{
203 // options
204 wxString m_switchChars; // characters which may start an option
36ebb50a 205 bool m_enableLongOptions; // true if long options are enabled
e612f101 206 wxString m_logo; // some extra text to show in Usage()
9f83044f
VZ
207
208 // cmd line data
209 wxArrayString m_arguments; // == argv, argc == m_arguments.GetCount()
d3a0a0ee 210 wxArrayOptions m_options; // all possible options and switches
9f83044f
VZ
211 wxArrayParams m_paramDesc; // description of all possible params
212 wxArrayString m_parameters; // all params found
213
214 // methods
215 wxCmdLineParserData();
ff1ce997
VZ
216 void SetArguments(int argc, char **argv);
217#if wxUSE_UNICODE
55b2b0d8 218 void SetArguments(int argc, wxChar **argv);
541ea80f 219 void SetArguments(int argc, const wxCmdLineArgsArray& argv);
ff1ce997 220#endif // wxUSE_UNICODE
9f83044f
VZ
221 void SetArguments(const wxString& cmdline);
222
223 int FindOption(const wxString& name);
224 int FindOptionByLongName(const wxString& name);
225};
226
227// ============================================================================
228// implementation
229// ============================================================================
230
231// ----------------------------------------------------------------------------
232// wxCmdLineParserData
233// ----------------------------------------------------------------------------
234
235wxCmdLineParserData::wxCmdLineParserData()
236{
36ebb50a 237 m_enableLongOptions = true;
9f83044f 238#ifdef __UNIX_LIKE__
9a83f860 239 m_switchChars = wxT("-");
9f83044f 240#else // !Unix
9a83f860 241 m_switchChars = wxT("/-");
9f83044f
VZ
242#endif
243}
244
0a4daf39
VZ
245namespace
246{
247
248// Small helper function setting locale for all categories.
249//
250// We define it because wxSetlocale() can't be easily used with wxScopeGuard as
251// it has several overloads -- while this one can.
252inline char *SetAllLocaleFacets(const char *loc)
253{
254 return wxSetlocale(LC_ALL, loc);
255}
256
257} // private namespace
258
ff1ce997
VZ
259void wxCmdLineParserData::SetArguments(int argc, char **argv)
260{
261 m_arguments.clear();
262
0a4daf39
VZ
263 // Command-line arguments are supposed to be in the user locale encoding
264 // (what else?) but wxLocale probably wasn't initialized yet as we're
265 // called early during the program startup and so our locale might not have
266 // been set from the environment yet. To work around this problem we
267 // temporarily change the locale here. The only drawback is that changing
268 // the locale is thread-unsafe but precisely because we're called so early
269 // it's hopefully safe to assume that no other threads had been created yet.
270 char * const locOld = SetAllLocaleFacets("");
271 wxON_BLOCK_EXIT1( SetAllLocaleFacets, locOld );
272
ff1ce997
VZ
273 for ( int n = 0; n < argc; n++ )
274 {
0a4daf39
VZ
275 // try to interpret the string as being in the current locale
276 wxString arg(argv[n]);
277
278 // but just in case we guessed wrongly and the conversion failed, do
279 // try to salvage at least something
280 if ( arg.empty() && argv[n][0] != '\0' )
281 arg = wxString(argv[n], wxConvISO8859_1);
282
283 m_arguments.push_back(arg);
ff1ce997
VZ
284 }
285}
286
287#if wxUSE_UNICODE
288
55b2b0d8 289void wxCmdLineParserData::SetArguments(int argc, wxChar **argv)
9f83044f 290{
df5168c4 291 m_arguments.clear();
9f83044f
VZ
292
293 for ( int n = 0; n < argc; n++ )
294 {
df5168c4 295 m_arguments.push_back(argv[n]);
9f83044f
VZ
296 }
297}
298
541ea80f
VZ
299void wxCmdLineParserData::SetArguments(int WXUNUSED(argc),
300 const wxCmdLineArgsArray& argv)
301{
302 m_arguments = argv.GetArguments();
303}
304
ff1ce997
VZ
305#endif // wxUSE_UNICODE
306
97d59046 307void wxCmdLineParserData::SetArguments(const wxString& cmdLine)
9f83044f 308{
df5168c4 309 m_arguments.clear();
97d59046 310
c0f420c1
WS
311 if(wxTheApp && wxTheApp->argc > 0)
312 m_arguments.push_back(wxTheApp->argv[0]);
313 else
314 m_arguments.push_back(wxEmptyString);
97d59046 315
31f6de22 316 wxArrayString args = wxCmdLineParser::ConvertStringToArgs(cmdLine);
97d59046 317
31f6de22 318 WX_APPEND_ARRAY(m_arguments, args);
9f83044f
VZ
319}
320
321int wxCmdLineParserData::FindOption(const wxString& name)
322{
bf188f1a 323 if ( !name.empty() )
9f83044f 324 {
bf188f1a
VZ
325 size_t count = m_options.GetCount();
326 for ( size_t n = 0; n < count; n++ )
9f83044f 327 {
bf188f1a
VZ
328 if ( m_options[n].shortName == name )
329 {
330 // found
331 return n;
332 }
9f83044f
VZ
333 }
334 }
335
336 return wxNOT_FOUND;
337}
338
339int wxCmdLineParserData::FindOptionByLongName(const wxString& name)
340{
341 size_t count = m_options.GetCount();
342 for ( size_t n = 0; n < count; n++ )
343 {
344 if ( m_options[n].longName == name )
345 {
346 // found
347 return n;
348 }
349 }
350
351 return wxNOT_FOUND;
352}
353
354// ----------------------------------------------------------------------------
355// construction and destruction
356// ----------------------------------------------------------------------------
357
358void wxCmdLineParser::Init()
359{
360 m_data = new wxCmdLineParserData;
361}
362
ff1ce997
VZ
363void wxCmdLineParser::SetCmdLine(int argc, char **argv)
364{
365 m_data->SetArguments(argc, argv);
366}
367
368#if wxUSE_UNICODE
369
55b2b0d8 370void wxCmdLineParser::SetCmdLine(int argc, wxChar **argv)
9f83044f
VZ
371{
372 m_data->SetArguments(argc, argv);
373}
374
541ea80f
VZ
375void wxCmdLineParser::SetCmdLine(int argc, const wxCmdLineArgsArray& argv)
376{
377 m_data->SetArguments(argc, argv);
378}
379
ff1ce997
VZ
380#endif // wxUSE_UNICODE
381
9f83044f
VZ
382void wxCmdLineParser::SetCmdLine(const wxString& cmdline)
383{
384 m_data->SetArguments(cmdline);
385}
386
387wxCmdLineParser::~wxCmdLineParser()
388{
389 delete m_data;
390}
391
392// ----------------------------------------------------------------------------
393// options
394// ----------------------------------------------------------------------------
395
396void wxCmdLineParser::SetSwitchChars(const wxString& switchChars)
397{
398 m_data->m_switchChars = switchChars;
399}
400
401void wxCmdLineParser::EnableLongOptions(bool enable)
402{
403 m_data->m_enableLongOptions = enable;
404}
405
779288b4 406bool wxCmdLineParser::AreLongOptionsEnabled() const
250b589f
JS
407{
408 return m_data->m_enableLongOptions;
409}
410
e612f101
VZ
411void wxCmdLineParser::SetLogo(const wxString& logo)
412{
413 m_data->m_logo = logo;
414}
415
9f83044f
VZ
416// ----------------------------------------------------------------------------
417// command line construction
418// ----------------------------------------------------------------------------
419
420void wxCmdLineParser::SetDesc(const wxCmdLineEntryDesc *desc)
421{
422 for ( ;; desc++ )
423 {
424 switch ( desc->kind )
425 {
426 case wxCMD_LINE_SWITCH:
49751a58
VZ
427 AddSwitch(desc->shortName, desc->longName,
428 wxGetTranslation(desc->description),
e612f101 429 desc->flags);
9f83044f
VZ
430 break;
431
432 case wxCMD_LINE_OPTION:
49751a58
VZ
433 AddOption(desc->shortName, desc->longName,
434 wxGetTranslation(desc->description),
9f83044f
VZ
435 desc->type, desc->flags);
436 break;
437
438 case wxCMD_LINE_PARAM:
49751a58
VZ
439 AddParam(wxGetTranslation(desc->description),
440 desc->type, desc->flags);
9f83044f
VZ
441 break;
442
e559d790
VZ
443 case wxCMD_LINE_USAGE_TEXT:
444 AddUsageText(wxGetTranslation(desc->description));
445 break;
446
9f83044f 447 default:
9a83f860 448 wxFAIL_MSG( wxT("unknown command line entry type") );
9f83044f
VZ
449 // still fall through
450
451 case wxCMD_LINE_NONE:
452 return;
453 }
454 }
455}
456
457void wxCmdLineParser::AddSwitch(const wxString& shortName,
458 const wxString& longName,
459 const wxString& desc,
460 int flags)
461{
462 wxASSERT_MSG( m_data->FindOption(shortName) == wxNOT_FOUND,
9a83f860 463 wxT("duplicate switch") );
9f83044f
VZ
464
465 wxCmdLineOption *option = new wxCmdLineOption(wxCMD_LINE_SWITCH,
466 shortName, longName, desc,
467 wxCMD_LINE_VAL_NONE, flags);
468
469 m_data->m_options.Add(option);
470}
471
472void wxCmdLineParser::AddOption(const wxString& shortName,
473 const wxString& longName,
474 const wxString& desc,
475 wxCmdLineParamType type,
476 int flags)
477{
478 wxASSERT_MSG( m_data->FindOption(shortName) == wxNOT_FOUND,
9a83f860 479 wxT("duplicate option") );
9f83044f
VZ
480
481 wxCmdLineOption *option = new wxCmdLineOption(wxCMD_LINE_OPTION,
482 shortName, longName, desc,
483 type, flags);
484
485 m_data->m_options.Add(option);
486}
487
488void wxCmdLineParser::AddParam(const wxString& desc,
489 wxCmdLineParamType type,
490 int flags)
491{
492 // do some consistency checks: a required parameter can't follow an
493 // optional one and nothing should follow a parameter with MULTIPLE flag
4b6a582b 494#if wxDEBUG_LEVEL
9f83044f
VZ
495 if ( !m_data->m_paramDesc.IsEmpty() )
496 {
497 wxCmdLineParam& param = m_data->m_paramDesc.Last();
498
499 wxASSERT_MSG( !(param.flags & wxCMD_LINE_PARAM_MULTIPLE),
9a83f860 500 wxT("all parameters after the one with wxCMD_LINE_PARAM_MULTIPLE style will be ignored") );
9f83044f
VZ
501
502 if ( !(flags & wxCMD_LINE_PARAM_OPTIONAL) )
503 {
504 wxASSERT_MSG( !(param.flags & wxCMD_LINE_PARAM_OPTIONAL),
9a83f860 505 wxT("a required parameter can't follow an optional one") );
9f83044f
VZ
506 }
507 }
4b6a582b 508#endif // wxDEBUG_LEVEL
9f83044f
VZ
509
510 wxCmdLineParam *param = new wxCmdLineParam(desc, type, flags);
511
512 m_data->m_paramDesc.Add(param);
513}
514
e559d790
VZ
515void wxCmdLineParser::AddUsageText(const wxString& text)
516{
517 wxASSERT_MSG( !text.empty(), wxT("text can't be empty") );
518
519 wxCmdLineOption *option = new wxCmdLineOption(wxCMD_LINE_USAGE_TEXT,
520 wxEmptyString, wxEmptyString,
521 text, wxCMD_LINE_VAL_NONE, 0);
522
523 m_data->m_options.Add(option);
524}
525
9f83044f
VZ
526// ----------------------------------------------------------------------------
527// access to parse command line
528// ----------------------------------------------------------------------------
529
530bool wxCmdLineParser::Found(const wxString& name) const
a6bf0c95
VZ
531{
532 return FoundSwitch(name) != wxCMD_SWITCH_NOT_FOUND;
533}
534
535wxCmdLineSwitchState wxCmdLineParser::FoundSwitch(const wxString& name) const
9f83044f
VZ
536{
537 int i = m_data->FindOption(name);
bf188f1a
VZ
538 if ( i == wxNOT_FOUND )
539 i = m_data->FindOptionByLongName(name);
540
a6bf0c95 541 wxCHECK_MSG( i != wxNOT_FOUND, wxCMD_SWITCH_NOT_FOUND, wxT("unknown switch") );
9f83044f
VZ
542
543 wxCmdLineOption& opt = m_data->m_options[(size_t)i];
544 if ( !opt.HasValue() )
a6bf0c95 545 return wxCMD_SWITCH_NOT_FOUND;
9f83044f 546
a6bf0c95 547 return opt.IsNegated() ? wxCMD_SWITCH_OFF : wxCMD_SWITCH_ON;
9f83044f
VZ
548}
549
550bool wxCmdLineParser::Found(const wxString& name, wxString *value) const
551{
552 int i = m_data->FindOption(name);
bf188f1a
VZ
553 if ( i == wxNOT_FOUND )
554 i = m_data->FindOptionByLongName(name);
555
9a83f860 556 wxCHECK_MSG( i != wxNOT_FOUND, false, wxT("unknown option") );
9f83044f
VZ
557
558 wxCmdLineOption& opt = m_data->m_options[(size_t)i];
559 if ( !opt.HasValue() )
36ebb50a 560 return false;
9f83044f 561
9a83f860 562 wxCHECK_MSG( value, false, wxT("NULL pointer in wxCmdLineOption::Found") );
9f83044f
VZ
563
564 *value = opt.GetStrVal();
565
36ebb50a 566 return true;
9f83044f
VZ
567}
568
569bool wxCmdLineParser::Found(const wxString& name, long *value) const
570{
571 int i = m_data->FindOption(name);
bf188f1a
VZ
572 if ( i == wxNOT_FOUND )
573 i = m_data->FindOptionByLongName(name);
574
9a83f860 575 wxCHECK_MSG( i != wxNOT_FOUND, false, wxT("unknown option") );
9f83044f
VZ
576
577 wxCmdLineOption& opt = m_data->m_options[(size_t)i];
578 if ( !opt.HasValue() )
36ebb50a 579 return false;
9f83044f 580
9a83f860 581 wxCHECK_MSG( value, false, wxT("NULL pointer in wxCmdLineOption::Found") );
9f83044f
VZ
582
583 *value = opt.GetLongVal();
584
36ebb50a 585 return true;
9f83044f
VZ
586}
587
b1859b1a
VZ
588bool wxCmdLineParser::Found(const wxString& name, double *value) const
589{
590 int i = m_data->FindOption(name);
591 if ( i == wxNOT_FOUND )
592 i = m_data->FindOptionByLongName(name);
593
9a83f860 594 wxCHECK_MSG( i != wxNOT_FOUND, false, wxT("unknown option") );
b1859b1a
VZ
595
596 wxCmdLineOption& opt = m_data->m_options[(size_t)i];
597 if ( !opt.HasValue() )
598 return false;
599
9a83f860 600 wxCHECK_MSG( value, false, wxT("NULL pointer in wxCmdLineOption::Found") );
b1859b1a
VZ
601
602 *value = opt.GetDoubleVal();
603
604 return true;
605}
606
e2b87f38 607#if wxUSE_DATETIME
9f83044f
VZ
608bool wxCmdLineParser::Found(const wxString& name, wxDateTime *value) const
609{
610 int i = m_data->FindOption(name);
bf188f1a
VZ
611 if ( i == wxNOT_FOUND )
612 i = m_data->FindOptionByLongName(name);
613
9a83f860 614 wxCHECK_MSG( i != wxNOT_FOUND, false, wxT("unknown option") );
9f83044f
VZ
615
616 wxCmdLineOption& opt = m_data->m_options[(size_t)i];
617 if ( !opt.HasValue() )
36ebb50a 618 return false;
9f83044f 619
9a83f860 620 wxCHECK_MSG( value, false, wxT("NULL pointer in wxCmdLineOption::Found") );
9f83044f
VZ
621
622 *value = opt.GetDateVal();
623
36ebb50a 624 return true;
9f83044f 625}
e2b87f38 626#endif // wxUSE_DATETIME
9f83044f
VZ
627
628size_t wxCmdLineParser::GetParamCount() const
629{
df5168c4 630 return m_data->m_parameters.size();
9f83044f
VZ
631}
632
633wxString wxCmdLineParser::GetParam(size_t n) const
634{
9a83f860 635 wxCHECK_MSG( n < GetParamCount(), wxEmptyString, wxT("invalid param index") );
8c9892c2 636
9f83044f
VZ
637 return m_data->m_parameters[n];
638}
639
07d09af8
JS
640// Resets switches and options
641void wxCmdLineParser::Reset()
642{
b4a980f4 643 for ( size_t i = 0; i < m_data->m_options.GetCount(); i++ )
3f2bcf34 644 {
dd18cc65 645 m_data->m_options[i].Reset();
3f2bcf34 646 }
07d09af8
JS
647}
648
649
9f83044f
VZ
650// ----------------------------------------------------------------------------
651// the real work is done here
652// ----------------------------------------------------------------------------
653
be03c0ec 654int wxCmdLineParser::Parse(bool showUsage)
9f83044f 655{
36ebb50a
VZ
656 bool maybeOption = true; // can the following arg be an option?
657 bool ok = true; // true until an error is detected
658 bool helpRequested = false; // true if "-h" was given
659 bool hadRepeatableParam = false; // true if found param with MULTIPLE flag
9f83044f
VZ
660
661 size_t currentParam = 0; // the index in m_paramDesc
662
663 size_t countParam = m_data->m_paramDesc.GetCount();
74698d3a 664 wxString errorMsg;
9f83044f 665
3f2bcf34 666 Reset();
07d09af8 667
9f83044f
VZ
668 // parse everything
669 wxString arg;
df5168c4 670 size_t count = m_data->m_arguments.size();
9f83044f
VZ
671 for ( size_t n = 1; ok && (n < count); n++ ) // 0 is program name
672 {
673 arg = m_data->m_arguments[n];
674
675 // special case: "--" should be discarded and all following arguments
676 // should be considered as parameters, even if they start with '-' and
677 // not like options (this is POSIX-like)
9a83f860 678 if ( arg == wxT("--") )
9f83044f 679 {
36ebb50a 680 maybeOption = false;
9f83044f
VZ
681
682 continue;
683 }
a4ea083b
SC
684#ifdef __WXOSX__
685 if ( arg == wxT("-ApplePersistenceIgnoreState") )
686 {
687 maybeOption = false;
688
689 continue;
690 }
691#endif
692
9f83044f
VZ
693 // empty argument or just '-' is not an option but a parameter
694 if ( maybeOption && arg.length() > 1 &&
86501081
VS
695 // FIXME-UTF8: use wc_str() after removing ANSI build
696 wxStrchr(m_data->m_switchChars.c_str(), arg[0u]) )
9f83044f
VZ
697 {
698 bool isLong;
699 wxString name;
700 int optInd = wxNOT_FOUND; // init to suppress warnings
701
702 // an option or a switch: find whether it's a long or a short one
9a83f860 703 if ( arg.length() >= 3 && arg[0u] == wxT('-') && arg[1u] == wxT('-') )
9f83044f
VZ
704 {
705 // a long one
36ebb50a 706 isLong = true;
9f83044f 707
250b589f 708 // Skip leading "--"
86501081 709 wxString::const_iterator p = arg.begin() + 2;
250b589f
JS
710
711 bool longOptionsEnabled = AreLongOptionsEnabled();
712
86501081 713 name = GetLongOptionName(p, arg.end());
250b589f
JS
714
715 if (longOptionsEnabled)
9f83044f 716 {
1a63798c
VZ
717 wxString errorOpt;
718
250b589f
JS
719 optInd = m_data->FindOptionByLongName(name);
720 if ( optInd == wxNOT_FOUND )
721 {
1a63798c
VZ
722 // Check if this could be a negatable long option.
723 if ( name.Last() == '-' )
724 {
725 name.RemoveLast();
726
727 optInd = m_data->FindOptionByLongName(name);
728 if ( optInd != wxNOT_FOUND )
729 {
730 if ( !(m_data->m_options[optInd].flags &
731 wxCMD_LINE_SWITCH_NEGATABLE) )
732 {
733 errorOpt.Printf
734 (
735 _("Option '%s' can't be negated"),
736 name
737 );
738 optInd = wxNOT_FOUND;
739 }
740 }
741 }
742
743 if ( optInd == wxNOT_FOUND )
744 {
745 if ( errorOpt.empty() )
746 {
747 errorOpt.Printf
748 (
749 _("Unknown long option '%s'"),
750 name
751 );
752 }
753
754 errorMsg << errorOpt << wxT('\n');
755 }
250b589f 756 }
9f83044f 757 }
250b589f 758 else
9f83044f 759 {
250b589f
JS
760 optInd = wxNOT_FOUND; // Sanity check
761
762 // Print the argument including leading "--"
763 name.Prepend( wxT("--") );
abce4ee6 764 errorMsg << wxString::Format(_("Unknown option '%s'"), name.c_str())
9a83f860 765 << wxT('\n');
9f83044f 766 }
250b589f 767
9f83044f 768 }
abce4ee6 769 else // not a long option
9f83044f 770 {
36ebb50a 771 isLong = false;
9f83044f
VZ
772
773 // a short one: as they can be cumulated, we try to find the
774 // longest substring which is a valid option
86501081 775 wxString::const_iterator p = arg.begin() + 1;
250b589f 776
86501081 777 name = GetShortOptionName(p, arg.end());
9f83044f
VZ
778
779 size_t len = name.length();
780 do
781 {
782 if ( len == 0 )
783 {
784 // we couldn't find a valid option name in the
785 // beginning of this string
abce4ee6 786 errorMsg << wxString::Format(_("Unknown option '%s'"), name.c_str())
9a83f860 787 << wxT('\n');
9f83044f
VZ
788
789 break;
790 }
791 else
792 {
793 optInd = m_data->FindOption(name.Left(len));
794
795 // will try with one character less the next time
796 len--;
797 }
798 }
799 while ( optInd == wxNOT_FOUND );
800
2822ee33 801 len++; // compensates extra len-- above
4f40f5e3 802 if ( (optInd != wxNOT_FOUND) && (len != name.length()) )
9f83044f 803 {
2822ee33
VZ
804 // first of all, the option name is only part of this
805 // string
806 name = name.Left(len);
807
9f83044f
VZ
808 // our option is only part of this argument, there is
809 // something else in it - it is either the value of this
810 // option or other switches if it is a switch
811 if ( m_data->m_options[(size_t)optInd].kind
812 == wxCMD_LINE_SWITCH )
813 {
a6bf0c95
VZ
814 // if the switch is negatable and it is just followed
815 // by '-' the '-' is considered to be part of this
816 // switch
817 if ( (m_data->m_options[(size_t)optInd].flags &
818 wxCMD_LINE_SWITCH_NEGATABLE) &&
819 arg[len] == '-' )
820 ++len;
821
9f83044f
VZ
822 // pretend that all the rest of the argument is the
823 // next argument, in fact
824 wxString arg2 = arg[0u];
2822ee33 825 arg2 += arg.Mid(len + 1); // +1 for leading '-'
9f83044f 826
df5168c4
MB
827 m_data->m_arguments.insert
828 (m_data->m_arguments.begin() + n + 1, arg2);
4f40f5e3 829 count++;
758f356c
VZ
830
831 // only leave the part which wasn't extracted into the
832 // next argument in this one
833 arg = arg.Left(len + 1);
9f83044f
VZ
834 }
835 //else: it's our value, we'll deal with it below
836 }
837 }
838
839 if ( optInd == wxNOT_FOUND )
840 {
36ebb50a 841 ok = false;
9f83044f
VZ
842
843 continue; // will break, in fact
844 }
845
abce4ee6
VZ
846 // look at what follows:
847
848 // +1 for leading '-'
86501081
VS
849 wxString::const_iterator p = arg.begin() + 1 + name.length();
850 wxString::const_iterator end = arg.end();
851
abce4ee6 852 if ( isLong )
86501081 853 ++p; // for another leading '-'
abce4ee6 854
9f83044f
VZ
855 wxCmdLineOption& opt = m_data->m_options[(size_t)optInd];
856 if ( opt.kind == wxCMD_LINE_SWITCH )
857 {
abce4ee6 858 // we must check that there is no value following the switch
a6bf0c95
VZ
859 bool negated = (opt.flags & wxCMD_LINE_SWITCH_NEGATABLE) &&
860 p != arg.end() && *p == '-';
861
862 if ( !negated && p != arg.end() )
9f83044f 863 {
abce4ee6 864 errorMsg << wxString::Format(_("Unexpected characters following option '%s'."), name.c_str())
9a83f860 865 << wxT('\n');
36ebb50a 866 ok = false;
9f83044f 867 }
abce4ee6
VZ
868 else // no value, as expected
869 {
870 // nothing more to do
871 opt.SetHasValue();
a6bf0c95
VZ
872 if ( negated )
873 opt.SetNegated();
abce4ee6
VZ
874
875 if ( opt.flags & wxCMD_LINE_OPTION_HELP )
876 {
877 helpRequested = true;
878
879 // it's not an error, but we still stop here
880 ok = false;
881 }
882 }
9f83044f 883 }
abce4ee6 884 else // it's an option. not a switch
9f83044f 885 {
1e678b9b 886 switch ( p == end ? '\0' : (*p).GetValue() )
9f83044f 887 {
1e678b9b
VZ
888 case '=':
889 case ':':
bdee3769
VZ
890 // the value follows
891 ++p;
892 break;
9f83044f 893
1e678b9b 894 case '\0':
bdee3769
VZ
895 // the value is in the next argument
896 if ( ++n == count )
897 {
898 // ... but there is none
899 errorMsg << wxString::Format(_("Option '%s' requires a value."),
900 name.c_str())
9a83f860 901 << wxT('\n');
bdee3769
VZ
902
903 ok = false;
904 }
905 else
906 {
907 // ... take it from there
908 p = m_data->m_arguments[n].begin();
909 end = m_data->m_arguments[n].end();
910 }
911 break;
f6bcfd97 912
bdee3769
VZ
913 default:
914 // the value is right here: this may be legal or
915 // not depending on the option style
916 if ( opt.flags & wxCMD_LINE_NEEDS_SEPARATOR )
917 {
918 errorMsg << wxString::Format(_("Separator expected after the option '%s'."),
919 name.c_str())
9a83f860 920 << wxT('\n');
bdee3769
VZ
921
922 ok = false;
923 }
9f83044f
VZ
924 }
925
926 if ( ok )
927 {
86501081 928 wxString value(p, end);
9f83044f
VZ
929 switch ( opt.type )
930 {
931 default:
9a83f860 932 wxFAIL_MSG( wxT("unknown option type") );
9f83044f
VZ
933 // still fall through
934
935 case wxCMD_LINE_VAL_STRING:
936 opt.SetStrVal(value);
937 break;
938
939 case wxCMD_LINE_VAL_NUMBER:
940 {
941 long val;
942 if ( value.ToLong(&val) )
943 {
944 opt.SetLongVal(val);
945 }
946 else
947 {
74698d3a 948 errorMsg << wxString::Format(_("'%s' is not a correct numeric value for option '%s'."),
abce4ee6 949 value.c_str(), name.c_str())
9a83f860 950 << wxT('\n');
9f83044f 951
36ebb50a 952 ok = false;
9f83044f
VZ
953 }
954 }
955 break;
956
b1859b1a
VZ
957 case wxCMD_LINE_VAL_DOUBLE:
958 {
959 double val;
960 if ( value.ToDouble(&val) )
961 {
962 opt.SetDoubleVal(val);
963 }
964 else
965 {
966 errorMsg << wxString::Format(_("'%s' is not a correct numeric value for option '%s'."),
967 value.c_str(), name.c_str())
9a83f860 968 << wxT('\n');
b1859b1a
VZ
969
970 ok = false;
971 }
972 }
973 break;
974
e2b87f38 975#if wxUSE_DATETIME
9f83044f
VZ
976 case wxCMD_LINE_VAL_DATE:
977 {
978 wxDateTime dt;
daa35097
VZ
979 wxString::const_iterator endDate;
980 if ( !dt.ParseDate(value, &endDate) || endDate != value.end() )
9f83044f 981 {
74698d3a 982 errorMsg << wxString::Format(_("Option '%s': '%s' cannot be converted to a date."),
abce4ee6 983 name.c_str(), value.c_str())
9a83f860 984 << wxT('\n');
9f83044f 985
36ebb50a 986 ok = false;
9f83044f
VZ
987 }
988 else
989 {
990 opt.SetDateVal(dt);
991 }
992 }
993 break;
e2b87f38 994#endif // wxUSE_DATETIME
9f83044f
VZ
995 }
996 }
997 }
998 }
abce4ee6 999 else // not an option, must be a parameter
9f83044f 1000 {
9f83044f
VZ
1001 if ( currentParam < countParam )
1002 {
1003 wxCmdLineParam& param = m_data->m_paramDesc[currentParam];
1004
1005 // TODO check the param type
1006
df5168c4 1007 m_data->m_parameters.push_back(arg);
9f83044f
VZ
1008
1009 if ( !(param.flags & wxCMD_LINE_PARAM_MULTIPLE) )
1010 {
1011 currentParam++;
1012 }
1013 else
1014 {
1015 wxASSERT_MSG( currentParam == countParam - 1,
9a83f860 1016 wxT("all parameters after the one with wxCMD_LINE_PARAM_MULTIPLE style are ignored") );
9f83044f
VZ
1017
1018 // remember that we did have this last repeatable parameter
36ebb50a 1019 hadRepeatableParam = true;
9f83044f
VZ
1020 }
1021 }
1022 else
1023 {
abce4ee6 1024 errorMsg << wxString::Format(_("Unexpected parameter '%s'"), arg.c_str())
9a83f860 1025 << wxT('\n');
9f83044f 1026
36ebb50a 1027 ok = false;
9f83044f
VZ
1028 }
1029 }
1030 }
1031
1032 // verify that all mandatory options were given
1033 if ( ok )
1034 {
1035 size_t countOpt = m_data->m_options.GetCount();
1036 for ( size_t n = 0; ok && (n < countOpt); n++ )
1037 {
1038 wxCmdLineOption& opt = m_data->m_options[n];
1039 if ( (opt.flags & wxCMD_LINE_OPTION_MANDATORY) && !opt.HasValue() )
1040 {
1041 wxString optName;
1042 if ( !opt.longName )
1043 {
1044 optName = opt.shortName;
1045 }
1046 else
1047 {
33293a32
VZ
1048 if ( AreLongOptionsEnabled() )
1049 {
1050 optName.Printf( _("%s (or %s)"),
1051 opt.shortName.c_str(),
1052 opt.longName.c_str() );
1053 }
1054 else
1055 {
1056 optName.Printf( wxT("%s"),
1057 opt.shortName.c_str() );
1058 }
9f83044f
VZ
1059 }
1060
74698d3a 1061 errorMsg << wxString::Format(_("The value for the option '%s' must be specified."),
abce4ee6 1062 optName.c_str())
9a83f860 1063 << wxT('\n');
9f83044f 1064
36ebb50a 1065 ok = false;
9f83044f
VZ
1066 }
1067 }
1068
1069 for ( ; ok && (currentParam < countParam); currentParam++ )
1070 {
1071 wxCmdLineParam& param = m_data->m_paramDesc[currentParam];
1072 if ( (currentParam == countParam - 1) &&
1073 (param.flags & wxCMD_LINE_PARAM_MULTIPLE) &&
1074 hadRepeatableParam )
1075 {
1076 // special case: currentParam wasn't incremented, but we did
1077 // have it, so don't give error
1078 continue;
1079 }
1080
1081 if ( !(param.flags & wxCMD_LINE_PARAM_OPTIONAL) )
1082 {
74698d3a 1083 errorMsg << wxString::Format(_("The required parameter '%s' was not specified."),
abce4ee6 1084 param.description.c_str())
9a83f860 1085 << wxT('\n');
9f83044f 1086
36ebb50a 1087 ok = false;
9f83044f
VZ
1088 }
1089 }
1090 }
1091
e9c54ec3
VZ
1092 // if there was an error during parsing the command line, show this error
1093 // and also the usage message if it had been requested
1094 if ( !ok && (!errorMsg.empty() || (helpRequested && showUsage)) )
9f83044f 1095 {
74698d3a 1096 wxMessageOutput* msgOut = wxMessageOutput::Get();
74698d3a 1097 if ( msgOut )
e9c54ec3
VZ
1098 {
1099 wxString usage;
1100 if ( showUsage )
1101 usage = GetUsageString();
1102
74698d3a 1103 msgOut->Printf( wxT("%s%s"), usage.c_str(), errorMsg.c_str() );
e9c54ec3
VZ
1104 }
1105 else
1106 {
9a83f860 1107 wxFAIL_MSG( wxT("no wxMessageOutput object?") );
e9c54ec3 1108 }
9f83044f
VZ
1109 }
1110
1111 return ok ? 0 : helpRequested ? -1 : 1;
1112}
1113
1114// ----------------------------------------------------------------------------
1115// give the usage message
1116// ----------------------------------------------------------------------------
1117
779288b4 1118void wxCmdLineParser::Usage() const
74698d3a 1119{
74698d3a
MB
1120 wxMessageOutput* msgOut = wxMessageOutput::Get();
1121 if ( msgOut )
e9c54ec3
VZ
1122 {
1123 msgOut->Printf( wxT("%s"), GetUsageString().c_str() );
1124 }
1125 else
1126 {
9a83f860 1127 wxFAIL_MSG( wxT("no wxMessageOutput object?") );
e9c54ec3 1128 }
74698d3a
MB
1129}
1130
779288b4 1131wxString wxCmdLineParser::GetUsageString() const
9f83044f 1132{
a2ec9439
VZ
1133 wxString appname;
1134 if ( m_data->m_arguments.empty() )
2822ee33 1135 {
a2ec9439
VZ
1136 if ( wxTheApp )
1137 appname = wxTheApp->GetAppName();
1138 }
1139 else // use argv[0]
1140 {
1141 appname = wxFileName(m_data->m_arguments[0]).GetName();
2822ee33
VZ
1142 }
1143
f6bcfd97
BP
1144 // we construct the brief cmd line desc on the fly, but not the detailed
1145 // help message below because we want to align the options descriptions
1146 // and for this we must first know the longest one of them
74698d3a 1147 wxString usage;
f6bcfd97 1148 wxArrayString namesOptions, descOptions;
74698d3a 1149
e9c54ec3 1150 if ( !m_data->m_logo.empty() )
74698d3a 1151 {
9a83f860 1152 usage << m_data->m_logo << wxT('\n');
74698d3a
MB
1153 }
1154
1155 usage << wxString::Format(_("Usage: %s"), appname.c_str());
9f83044f 1156
f6bcfd97
BP
1157 // the switch char is usually '-' but this can be changed with
1158 // SetSwitchChars() and then the first one of possible chars is used
9a83f860 1159 wxChar chSwitch = !m_data->m_switchChars ? wxT('-')
f6bcfd97
BP
1160 : m_data->m_switchChars[0u];
1161
250b589f 1162 bool areLongOptionsEnabled = AreLongOptionsEnabled();
9f83044f
VZ
1163 size_t n, count = m_data->m_options.GetCount();
1164 for ( n = 0; n < count; n++ )
1165 {
1166 wxCmdLineOption& opt = m_data->m_options[n];
a6bf0c95 1167 wxString option, negator;
9f83044f 1168
e559d790 1169 if ( opt.kind != wxCMD_LINE_USAGE_TEXT )
9f83044f 1170 {
9a83f860 1171 usage << wxT(' ');
e559d790
VZ
1172 if ( !(opt.flags & wxCMD_LINE_OPTION_MANDATORY) )
1173 {
9a83f860 1174 usage << wxT('[');
e559d790 1175 }
9f83044f 1176
a6bf0c95
VZ
1177 if ( opt.flags & wxCMD_LINE_SWITCH_NEGATABLE )
1178 negator = wxT("[-]");
1179
e559d790 1180 if ( !opt.shortName.empty() )
250b589f 1181 {
a6bf0c95 1182 usage << chSwitch << opt.shortName << negator;
e559d790
VZ
1183 }
1184 else if ( areLongOptionsEnabled && !opt.longName.empty() )
1185 {
a6bf0c95 1186 usage << wxT("--") << opt.longName << negator;
250b589f
JS
1187 }
1188 else
1189 {
e559d790
VZ
1190 if (!opt.longName.empty())
1191 {
1192 wxFAIL_MSG( wxT("option with only a long name while long ")
1193 wxT("options are disabled") );
1194 }
1195 else
1196 {
9a83f860 1197 wxFAIL_MSG( wxT("option without neither short nor long name") );
e559d790 1198 }
250b589f 1199 }
f6bcfd97 1200
e559d790
VZ
1201 if ( !opt.shortName.empty() )
1202 {
9a83f860 1203 option << wxT(" ") << chSwitch << opt.shortName;
e559d790 1204 }
be03c0ec 1205
e559d790
VZ
1206 if ( areLongOptionsEnabled && !opt.longName.empty() )
1207 {
9a83f860
VZ
1208 option << (option.empty() ? wxT(" ") : wxT(", "))
1209 << wxT("--") << opt.longName;
e559d790 1210 }
9f83044f 1211
e559d790
VZ
1212 if ( opt.kind != wxCMD_LINE_SWITCH )
1213 {
1214 wxString val;
9a83f860
VZ
1215 val << wxT('<') << GetTypeName(opt.type) << wxT('>');
1216 usage << wxT(' ') << val;
1217 option << (!opt.longName ? wxT(':') : wxT('=')) << val;
e559d790 1218 }
9f83044f 1219
e559d790
VZ
1220 if ( !(opt.flags & wxCMD_LINE_OPTION_MANDATORY) )
1221 {
9a83f860 1222 usage << wxT(']');
e559d790 1223 }
9f83044f
VZ
1224 }
1225
df5168c4
MB
1226 namesOptions.push_back(option);
1227 descOptions.push_back(opt.description);
9f83044f
VZ
1228 }
1229
1230 count = m_data->m_paramDesc.GetCount();
1231 for ( n = 0; n < count; n++ )
1232 {
1233 wxCmdLineParam& param = m_data->m_paramDesc[n];
1234
9a83f860 1235 usage << wxT(' ');
9f83044f
VZ
1236 if ( param.flags & wxCMD_LINE_PARAM_OPTIONAL )
1237 {
9a83f860 1238 usage << wxT('[');
9f83044f
VZ
1239 }
1240
74698d3a 1241 usage << param.description;
9f83044f
VZ
1242
1243 if ( param.flags & wxCMD_LINE_PARAM_MULTIPLE )
1244 {
9a83f860 1245 usage << wxT("...");
9f83044f
VZ
1246 }
1247
1248 if ( param.flags & wxCMD_LINE_PARAM_OPTIONAL )
1249 {
9a83f860 1250 usage << wxT(']');
9f83044f
VZ
1251 }
1252 }
1253
9a83f860 1254 usage << wxT('\n');
f6bcfd97 1255
d3a0a0ee
VZ
1256 // set to number of our own options, not counting the standard ones
1257 count = namesOptions.size();
1258
1259 // get option names & descriptions for standard options, if any:
1260 wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
1261 wxString stdDesc;
1262 if ( traits )
1263 stdDesc = traits->GetStandardCmdLineOptions(namesOptions, descOptions);
1264
f6bcfd97
BP
1265 // now construct the detailed help message
1266 size_t len, lenMax = 0;
d3a0a0ee 1267 for ( n = 0; n < namesOptions.size(); n++ )
f6bcfd97
BP
1268 {
1269 len = namesOptions[n].length();
1270 if ( len > lenMax )
1271 lenMax = len;
1272 }
1273
d3a0a0ee 1274 for ( n = 0; n < namesOptions.size(); n++ )
f6bcfd97 1275 {
d3a0a0ee 1276 if ( n == count )
9a83f860 1277 usage << wxT('\n') << stdDesc;
d3a0a0ee 1278
f6bcfd97 1279 len = namesOptions[n].length();
e559d790
VZ
1280 // desc contains text if name is empty
1281 if (len == 0)
1282 {
9a83f860 1283 usage << descOptions[n] << wxT('\n');
e559d790
VZ
1284 }
1285 else
1286 {
1287 usage << namesOptions[n]
9a83f860 1288 << wxString(wxT(' '), lenMax - len) << wxT('\t')
e559d790 1289 << descOptions[n]
9a83f860 1290 << wxT('\n');
e559d790 1291 }
f6bcfd97
BP
1292 }
1293
74698d3a 1294 return usage;
9f83044f
VZ
1295}
1296
1297// ----------------------------------------------------------------------------
31f6de22 1298// private functions
9f83044f
VZ
1299// ----------------------------------------------------------------------------
1300
1301static wxString GetTypeName(wxCmdLineParamType type)
1302{
1303 wxString s;
1304 switch ( type )
1305 {
1306 default:
9a83f860 1307 wxFAIL_MSG( wxT("unknown option type") );
9f83044f
VZ
1308 // still fall through
1309
e9c54ec3
VZ
1310 case wxCMD_LINE_VAL_STRING:
1311 s = _("str");
1312 break;
1313
1314 case wxCMD_LINE_VAL_NUMBER:
1315 s = _("num");
1316 break;
1317
b1859b1a
VZ
1318 case wxCMD_LINE_VAL_DOUBLE:
1319 s = _("double");
1320 break;
1321
e9c54ec3
VZ
1322 case wxCMD_LINE_VAL_DATE:
1323 s = _("date");
1324 break;
9f83044f
VZ
1325 }
1326
1327 return s;
1328}
1e6feb95 1329
250b589f
JS
1330/*
1331Returns a string which is equal to the string pointed to by p, but up to the
1332point where p contains an character that's not allowed.
1333Allowable characters are letters and numbers, and characters pointed to by
1334the parameter allowedChars.
1335
1336For example, if p points to "abcde-@-_", and allowedChars is "-_",
1337this function returns "abcde-".
1338*/
86501081
VS
1339static wxString GetOptionName(wxString::const_iterator p,
1340 wxString::const_iterator end,
1341 const wxChar *allowedChars)
250b589f
JS
1342{
1343 wxString argName;
1344
86501081 1345 while ( p != end && (wxIsalnum(*p) || wxStrchr(allowedChars, *p)) )
250b589f
JS
1346 {
1347 argName += *p++;
1348 }
1349
1350 return argName;
1351}
1352
1353// Besides alphanumeric characters, short and long options can
1354// have other characters.
1355
1356// A short option additionally can have these
1357#define wxCMD_LINE_CHARS_ALLOWED_BY_SHORT_OPTION wxT("_?")
1358
1359// A long option can have the same characters as a short option and a '-'.
1360#define wxCMD_LINE_CHARS_ALLOWED_BY_LONG_OPTION \
1361 wxCMD_LINE_CHARS_ALLOWED_BY_SHORT_OPTION wxT("-")
1362
86501081
VS
1363static wxString GetShortOptionName(wxString::const_iterator p,
1364 wxString::const_iterator end)
250b589f 1365{
86501081 1366 return GetOptionName(p, end, wxCMD_LINE_CHARS_ALLOWED_BY_SHORT_OPTION);
250b589f
JS
1367}
1368
86501081
VS
1369static wxString GetLongOptionName(wxString::const_iterator p,
1370 wxString::const_iterator end)
250b589f 1371{
86501081 1372 return GetOptionName(p, end, wxCMD_LINE_CHARS_ALLOWED_BY_LONG_OPTION);
250b589f
JS
1373}
1374
1e6feb95 1375#endif // wxUSE_CMDLINE_PARSER
31f6de22
VZ
1376
1377// ----------------------------------------------------------------------------
1378// global functions
1379// ----------------------------------------------------------------------------
1380
217f9d07
VZ
1381/*
1382 This function is mainly used under Windows (as under Unix we always get the
7a1f9c09
VZ
1383 command line arguments as argc/argv anyhow) and so it tries to follow
1384 Windows conventions for the command line handling, not Unix ones. For
1385 instance, backslash is not special except when it precedes double quote when
1386 it does quote it.
8dd4c611
VZ
1387
1388 TODO: Rewrite this to follow the even more complicated rule used by Windows
1389 CommandLineToArgv():
1390
1391 * A string of backslashes not followed by a quotation mark has no special
1392 meaning.
1393 * An even number of backslashes followed by a quotation mark is treated as
1394 pairs of protected backslashes, followed by a word terminator.
1395 * An odd number of backslashes followed by a quotation mark is treated as
1396 pairs of protected backslashes, followed by a protected quotation mark.
1397
1398 See http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx
1399
1400 It could also be useful to provide a converse function which is also
1401 non-trivial, see
1402
1403 http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx
217f9d07
VZ
1404 */
1405
31f6de22 1406/* static */
a4761b4c
VZ
1407wxArrayString
1408wxCmdLineParser::ConvertStringToArgs(const wxString& cmdline,
1409 wxCmdLineSplitType type)
31f6de22
VZ
1410{
1411 wxArrayString args;
1412
1413 wxString arg;
1414 arg.reserve(1024);
1415
5769cf0f 1416 const wxString::const_iterator end = cmdline.end();
86501081
VS
1417 wxString::const_iterator p = cmdline.begin();
1418
31f6de22
VZ
1419 for ( ;; )
1420 {
1421 // skip white space
5769cf0f 1422 while ( p != end && (*p == ' ' || *p == '\t') )
86501081 1423 ++p;
31f6de22
VZ
1424
1425 // anything left?
5769cf0f 1426 if ( p == end )
31f6de22
VZ
1427 break;
1428
1429 // parse this parameter
a4761b4c
VZ
1430 bool lastBS = false,
1431 isInsideQuotes = false;
1432 wxChar chDelim = '\0';
5769cf0f 1433 for ( arg.clear(); p != end; ++p )
31f6de22 1434 {
5769cf0f 1435 const wxChar ch = *p;
a4761b4c
VZ
1436
1437 if ( type == wxCMD_LINE_SPLIT_DOS )
31f6de22 1438 {
a4761b4c 1439 if ( ch == '"' )
5769cf0f 1440 {
a4761b4c
VZ
1441 if ( !lastBS )
1442 {
1443 isInsideQuotes = !isInsideQuotes;
93b79b6b 1444
a4761b4c
VZ
1445 // don't put quote in arg
1446 continue;
1447 }
1448 //else: quote has no special meaning but the backslash
1449 // still remains -- makes no sense but this is what
1450 // Windows does
1451 }
1452 // note that backslash does *not* quote the space, only quotes do
1453 else if ( !isInsideQuotes && (ch == ' ' || ch == '\t') )
1454 {
1455 ++p; // skip this space anyhow
1456 break;
5769cf0f 1457 }
a4761b4c 1458
28d4f49b 1459 lastBS = !lastBS && ch == '\\';
36ebb50a 1460 }
a4761b4c 1461 else // type == wxCMD_LINE_SPLIT_UNIX
36ebb50a 1462 {
a4761b4c
VZ
1463 if ( !lastBS )
1464 {
1465 if ( isInsideQuotes )
1466 {
1467 if ( ch == chDelim )
1468 {
1469 isInsideQuotes = false;
1470
1471 continue; // don't use the quote itself
1472 }
1473 }
1474 else // not in quotes and not escaped
1475 {
1476 if ( ch == '\'' || ch == '"' )
1477 {
1478 isInsideQuotes = true;
1479 chDelim = ch;
31f6de22 1480
a4761b4c
VZ
1481 continue; // don't use the quote itself
1482 }
1483
1484 if ( ch == ' ' || ch == '\t' )
1485 {
1486 ++p; // skip this space anyhow
1487 break;
1488 }
1489 }
1490
1491 lastBS = ch == '\\';
1492 if ( lastBS )
1493 continue;
1494 }
1495 else // escaped by backslash, just use as is
1496 {
1497 lastBS = false;
1498 }
1499 }
31f6de22 1500
5769cf0f 1501 arg += ch;
31f6de22
VZ
1502 }
1503
df5168c4 1504 args.push_back(arg);
31f6de22
VZ
1505 }
1506
1507 return args;
1508}