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