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