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