]> git.saurik.com Git - wxWidgets.git/blame - src/common/cmdline.cpp
Warning and build fixes focused with Borland and Watcom.
[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 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 );
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
df5168c4 248 m_arguments.push_back(wxTheApp->GetAppName());
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{
2822ee33
VZ
932 wxString appname = wxTheApp->GetAppName();
933 if ( !appname )
934 {
df5168c4 935 wxCHECK_MSG( m_data->m_arguments.size() != 0, wxEmptyString,
74698d3a 936 _T("no program name") );
2822ee33
VZ
937
938 appname = wxFileNameFromPath(m_data->m_arguments[0]);
939 wxStripExtension(appname);
940 }
941
f6bcfd97
BP
942 // we construct the brief cmd line desc on the fly, but not the detailed
943 // help message below because we want to align the options descriptions
944 // and for this we must first know the longest one of them
74698d3a 945 wxString usage;
f6bcfd97 946 wxArrayString namesOptions, descOptions;
74698d3a 947
e9c54ec3 948 if ( !m_data->m_logo.empty() )
74698d3a
MB
949 {
950 usage << m_data->m_logo << _T('\n');
951 }
952
953 usage << wxString::Format(_("Usage: %s"), appname.c_str());
9f83044f 954
f6bcfd97
BP
955 // the switch char is usually '-' but this can be changed with
956 // SetSwitchChars() and then the first one of possible chars is used
957 wxChar chSwitch = !m_data->m_switchChars ? _T('-')
958 : m_data->m_switchChars[0u];
959
250b589f 960 bool areLongOptionsEnabled = AreLongOptionsEnabled();
9f83044f
VZ
961 size_t n, count = m_data->m_options.GetCount();
962 for ( n = 0; n < count; n++ )
963 {
964 wxCmdLineOption& opt = m_data->m_options[n];
965
74698d3a 966 usage << _T(' ');
9f83044f
VZ
967 if ( !(opt.flags & wxCMD_LINE_OPTION_MANDATORY) )
968 {
74698d3a 969 usage << _T('[');
9f83044f
VZ
970 }
971
be03c0ec
VZ
972 if ( !opt.shortName.empty() )
973 {
74698d3a 974 usage << chSwitch << opt.shortName;
be03c0ec 975 }
250b589f 976 else if ( areLongOptionsEnabled && !opt.longName.empty() )
be03c0ec 977 {
74698d3a 978 usage << _T("--") << opt.longName;
be03c0ec
VZ
979 }
980 else
981 {
250b589f
JS
982 if (!opt.longName.empty())
983 {
984 wxFAIL_MSG( wxT("option with only a long name while long ")
985 wxT("options are disabled") );
986 }
987 else
988 {
989 wxFAIL_MSG( _T("option without neither short nor long name") );
990 }
be03c0ec 991 }
f6bcfd97
BP
992
993 wxString option;
be03c0ec
VZ
994
995 if ( !opt.shortName.empty() )
996 {
997 option << _T(" ") << chSwitch << opt.shortName;
998 }
999
250b589f 1000 if ( areLongOptionsEnabled && !opt.longName.empty() )
9f83044f 1001 {
be03c0ec
VZ
1002 option << (option.empty() ? _T(" ") : _T(", "))
1003 << _T("--") << opt.longName;
9f83044f
VZ
1004 }
1005
1006 if ( opt.kind != wxCMD_LINE_SWITCH )
1007 {
1008 wxString val;
1009 val << _T('<') << GetTypeName(opt.type) << _T('>');
74698d3a 1010 usage << _T(' ') << val;
f6bcfd97 1011 option << (!opt.longName ? _T(':') : _T('=')) << val;
9f83044f
VZ
1012 }
1013
1014 if ( !(opt.flags & wxCMD_LINE_OPTION_MANDATORY) )
1015 {
74698d3a 1016 usage << _T(']');
9f83044f
VZ
1017 }
1018
df5168c4
MB
1019 namesOptions.push_back(option);
1020 descOptions.push_back(opt.description);
9f83044f
VZ
1021 }
1022
1023 count = m_data->m_paramDesc.GetCount();
1024 for ( n = 0; n < count; n++ )
1025 {
1026 wxCmdLineParam& param = m_data->m_paramDesc[n];
1027
74698d3a 1028 usage << _T(' ');
9f83044f
VZ
1029 if ( param.flags & wxCMD_LINE_PARAM_OPTIONAL )
1030 {
74698d3a 1031 usage << _T('[');
9f83044f
VZ
1032 }
1033
74698d3a 1034 usage << param.description;
9f83044f
VZ
1035
1036 if ( param.flags & wxCMD_LINE_PARAM_MULTIPLE )
1037 {
74698d3a 1038 usage << _T("...");
9f83044f
VZ
1039 }
1040
1041 if ( param.flags & wxCMD_LINE_PARAM_OPTIONAL )
1042 {
74698d3a 1043 usage << _T(']');
9f83044f
VZ
1044 }
1045 }
1046
74698d3a 1047 usage << _T('\n');
f6bcfd97
BP
1048
1049 // now construct the detailed help message
1050 size_t len, lenMax = 0;
df5168c4 1051 count = namesOptions.size();
f6bcfd97
BP
1052 for ( n = 0; n < count; n++ )
1053 {
1054 len = namesOptions[n].length();
1055 if ( len > lenMax )
1056 lenMax = len;
1057 }
1058
f6bcfd97
BP
1059 for ( n = 0; n < count; n++ )
1060 {
1061 len = namesOptions[n].length();
74698d3a
MB
1062 usage << namesOptions[n]
1063 << wxString(_T(' '), lenMax - len) << _T('\t')
1064 << descOptions[n]
1065 << _T('\n');
f6bcfd97
BP
1066 }
1067
74698d3a 1068 return usage;
9f83044f
VZ
1069}
1070
1071// ----------------------------------------------------------------------------
31f6de22 1072// private functions
9f83044f
VZ
1073// ----------------------------------------------------------------------------
1074
1075static wxString GetTypeName(wxCmdLineParamType type)
1076{
1077 wxString s;
1078 switch ( type )
1079 {
1080 default:
1081 wxFAIL_MSG( _T("unknown option type") );
1082 // still fall through
1083
e9c54ec3
VZ
1084 case wxCMD_LINE_VAL_STRING:
1085 s = _("str");
1086 break;
1087
1088 case wxCMD_LINE_VAL_NUMBER:
1089 s = _("num");
1090 break;
1091
1092 case wxCMD_LINE_VAL_DATE:
1093 s = _("date");
1094 break;
9f83044f
VZ
1095 }
1096
1097 return s;
1098}
1e6feb95 1099
250b589f
JS
1100/*
1101Returns a string which is equal to the string pointed to by p, but up to the
1102point where p contains an character that's not allowed.
1103Allowable characters are letters and numbers, and characters pointed to by
1104the parameter allowedChars.
1105
1106For example, if p points to "abcde-@-_", and allowedChars is "-_",
1107this function returns "abcde-".
1108*/
1109static wxString GetOptionName(const wxChar *p,
1110 const wxChar *allowedChars)
1111{
1112 wxString argName;
1113
1114 while ( *p && (wxIsalnum(*p) || wxStrchr(allowedChars, *p)) )
1115 {
1116 argName += *p++;
1117 }
1118
1119 return argName;
1120}
1121
1122// Besides alphanumeric characters, short and long options can
1123// have other characters.
1124
1125// A short option additionally can have these
1126#define wxCMD_LINE_CHARS_ALLOWED_BY_SHORT_OPTION wxT("_?")
1127
1128// A long option can have the same characters as a short option and a '-'.
1129#define wxCMD_LINE_CHARS_ALLOWED_BY_LONG_OPTION \
1130 wxCMD_LINE_CHARS_ALLOWED_BY_SHORT_OPTION wxT("-")
1131
1132static wxString GetShortOptionName(const wxChar *p)
1133{
1134 return GetOptionName(p, wxCMD_LINE_CHARS_ALLOWED_BY_SHORT_OPTION);
1135}
1136
1137static wxString GetLongOptionName(const wxChar *p)
1138{
1139 return GetOptionName(p, wxCMD_LINE_CHARS_ALLOWED_BY_LONG_OPTION);
1140}
1141
1e6feb95 1142#endif // wxUSE_CMDLINE_PARSER
31f6de22
VZ
1143
1144// ----------------------------------------------------------------------------
1145// global functions
1146// ----------------------------------------------------------------------------
1147
217f9d07
VZ
1148/*
1149 This function is mainly used under Windows (as under Unix we always get the
7a1f9c09
VZ
1150 command line arguments as argc/argv anyhow) and so it tries to follow
1151 Windows conventions for the command line handling, not Unix ones. For
1152 instance, backslash is not special except when it precedes double quote when
1153 it does quote it.
217f9d07
VZ
1154 */
1155
31f6de22
VZ
1156/* static */
1157wxArrayString wxCmdLineParser::ConvertStringToArgs(const wxChar *p)
1158{
1159 wxArrayString args;
1160
1161 wxString arg;
1162 arg.reserve(1024);
1163
7a1f9c09 1164 bool isInsideQuotes = false;
31f6de22
VZ
1165 for ( ;; )
1166 {
1167 // skip white space
1168 while ( *p == _T(' ') || *p == _T('\t') )
1169 p++;
1170
1171 // anything left?
1172 if ( *p == _T('\0') )
1173 break;
1174
1175 // parse this parameter
7a1f9c09
VZ
1176 bool endParam = false;
1177 bool lastBS = false;
1178 for ( arg.clear(); !endParam; p++ )
31f6de22 1179 {
31f6de22
VZ
1180 switch ( *p )
1181 {
93b79b6b 1182 case _T('"'):
7a1f9c09 1183 if ( !lastBS )
31f6de22 1184 {
31f6de22 1185 isInsideQuotes = !isInsideQuotes;
31f6de22 1186
7a1f9c09
VZ
1187 // don't put quote in arg
1188 continue;
1189 }
1190 //else: quote has no special meaning but the backslash
1191 // still remains -- makes no sense but this is what
1192 // Windows does
31f6de22
VZ
1193 break;
1194
1195 case _T(' '):
1196 case _T('\t'):
7a1f9c09 1197 // backslash does *not* quote the space, only quotes do
217f9d07 1198 if ( isInsideQuotes )
31f6de22 1199 {
7a1f9c09 1200 // skip assignment below
31f6de22
VZ
1201 break;
1202 }
7a1f9c09 1203 // fall through
31f6de22
VZ
1204
1205 case _T('\0'):
36ebb50a 1206 endParam = true;
93b79b6b 1207
36ebb50a
VZ
1208 break;
1209 }
1210
1211 if ( endParam )
1212 {
1213 break;
31f6de22
VZ
1214 }
1215
7a1f9c09 1216 lastBS = *p == _T('\\');
31f6de22 1217
7a1f9c09 1218 arg += *p;
31f6de22
VZ
1219 }
1220
df5168c4 1221 args.push_back(arg);
31f6de22
VZ
1222 }
1223
1224 return args;
1225}
1226