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