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