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