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