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