1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        common/cmdline.cpp 
   3 // Purpose:     wxCmdLineParser implementation 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 2000 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> 
   9 // Licence:     wxWindows license 
  10 /////////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  21     #pragma implementation "cmdline.h" 
  24 // For compilers that support precompilation, includes "wx.h". 
  25 #include "wx/wxprec.h" 
  32     #include "wx/string.h" 
  35     #include "wx/dynarray.h" 
  38 #include "wx/datetime.h" 
  39 #include "wx/cmdline.h" 
  41 // ---------------------------------------------------------------------------- 
  43 // ---------------------------------------------------------------------------- 
  45 static wxString 
GetTypeName(wxCmdLineParamType type
); 
  47 // ---------------------------------------------------------------------------- 
  49 // ---------------------------------------------------------------------------- 
  51 // an internal representation of an option 
  52 struct wxCmdLineOption
 
  54     wxCmdLineOption(wxCmdLineEntryType k
, 
  58                     wxCmdLineParamType typ
, 
  73     // can't use union easily here, so just store all possible data fields, we 
  74     // don't waste much (might still use union later if the number of supported 
  75     // types increases, so always use the accessor functions and don't access 
  76     // the fields directly!) 
  78     void Check(wxCmdLineParamType typ
) const 
  80         wxASSERT_MSG( type 
== typ
, _T("type mismatch in wxCmdLineOption") ); 
  83     long GetLongVal() const 
  84         { Check(wxCMD_LINE_VAL_NUMBER
); return m_longVal
; } 
  85     const wxString
& GetStrVal() const 
  86         { Check(wxCMD_LINE_VAL_STRING
); return m_strVal
;  } 
  87     const wxDateTime
& GetDateVal() const 
  88         { Check(wxCMD_LINE_VAL_DATE
);   return m_dateVal
; } 
  90     void SetLongVal(long val
) 
  91         { Check(wxCMD_LINE_VAL_NUMBER
); m_longVal 
= val
; m_hasVal 
= TRUE
; } 
  92     void SetStrVal(const wxString
& val
) 
  93         { Check(wxCMD_LINE_VAL_STRING
); m_strVal 
= val
; m_hasVal 
= TRUE
; } 
  94     void SetDateVal(const wxDateTime val
) 
  95         { Check(wxCMD_LINE_VAL_DATE
); m_dateVal 
= val
; m_hasVal 
= TRUE
; } 
  97     void SetHasValue() { m_hasVal 
= TRUE
; } 
  98     bool HasValue() const { return m_hasVal
; } 
 101     wxCmdLineEntryType kind
; 
 102     wxString shortName
, longName
, description
; 
 103     wxCmdLineParamType type
; 
 111     wxDateTime m_dateVal
; 
 114 struct wxCmdLineParam
 
 116     wxCmdLineParam(const wxString
& desc
, 
 117                    wxCmdLineParamType typ
, 
 125     wxString description
; 
 126     wxCmdLineParamType type
; 
 130 WX_DECLARE_OBJARRAY(wxCmdLineOption
, wxArrayOptions
); 
 131 WX_DECLARE_OBJARRAY(wxCmdLineParam
, wxArrayParams
); 
 133 #include "wx/arrimpl.cpp" 
 135 WX_DEFINE_OBJARRAY(wxArrayOptions
); 
 136 WX_DEFINE_OBJARRAY(wxArrayParams
); 
 138 // the parser internal state 
 139 struct wxCmdLineParserData
 
 142     wxString m_switchChars
;     // characters which may start an option 
 144     bool m_enableLongOptions
;   // TRUE if long options are enabled 
 147     wxArrayString m_arguments
;  // == argv, argc == m_arguments.GetCount() 
 148     wxArrayOptions m_options
;   // all possible options and switchrs 
 149     wxArrayParams m_paramDesc
;  // description of all possible params 
 150     wxArrayString m_parameters
; // all params found 
 153     wxCmdLineParserData(); 
 154     void SetArguments(int argc
, char **argv
); 
 155     void SetArguments(const wxString
& cmdline
); 
 157     int FindOption(const wxString
& name
); 
 158     int FindOptionByLongName(const wxString
& name
); 
 161 // ============================================================================ 
 163 // ============================================================================ 
 165 // ---------------------------------------------------------------------------- 
 166 // wxCmdLineParserData 
 167 // ---------------------------------------------------------------------------- 
 169 wxCmdLineParserData::wxCmdLineParserData() 
 171     m_enableLongOptions 
= TRUE
; 
 173     m_switchChars 
= _T("-"); 
 175     m_switchChars 
= _T("/-"); 
 179 void wxCmdLineParserData::SetArguments(int argc
, char **argv
) 
 183     for ( int n 
= 0; n 
< argc
; n
++ ) 
 185         m_arguments
.Add(argv
[n
]); 
 189 void wxCmdLineParserData::SetArguments(const wxString
& cmdline
) 
 191     // either use wxMSW wxApp::ConvertToStandardCommandArgs() or move its logic 
 192     // here and use this method from it - but don't duplicate the code 
 194     wxFAIL_MSG(_T("TODO")); 
 197 int wxCmdLineParserData::FindOption(const wxString
& name
) 
 199     size_t count 
= m_options
.GetCount(); 
 200     for ( size_t n 
= 0; n 
< count
; n
++ ) 
 202         if ( m_options
[n
].shortName 
== name 
) 
 212 int wxCmdLineParserData::FindOptionByLongName(const wxString
& name
) 
 214     size_t count 
= m_options
.GetCount(); 
 215     for ( size_t n 
= 0; n 
< count
; n
++ ) 
 217         if ( m_options
[n
].longName 
== name 
) 
 227 // ---------------------------------------------------------------------------- 
 228 // construction and destruction 
 229 // ---------------------------------------------------------------------------- 
 231 void wxCmdLineParser::Init() 
 233     m_data 
= new wxCmdLineParserData
; 
 236 void wxCmdLineParser::SetCmdLine(int argc
, char **argv
) 
 238     m_data
->SetArguments(argc
, argv
); 
 241 void wxCmdLineParser::SetCmdLine(const wxString
& cmdline
) 
 243     m_data
->SetArguments(cmdline
); 
 246 wxCmdLineParser::~wxCmdLineParser() 
 251 // ---------------------------------------------------------------------------- 
 253 // ---------------------------------------------------------------------------- 
 255 void wxCmdLineParser::SetSwitchChars(const wxString
& switchChars
) 
 257     m_data
->m_switchChars 
= switchChars
; 
 260 void wxCmdLineParser::EnableLongOptions(bool enable
) 
 262     m_data
->m_enableLongOptions 
= enable
; 
 265 // ---------------------------------------------------------------------------- 
 266 // command line construction 
 267 // ---------------------------------------------------------------------------- 
 269 void wxCmdLineParser::SetDesc(const wxCmdLineEntryDesc 
*desc
) 
 273         switch ( desc
->kind 
) 
 275             case wxCMD_LINE_SWITCH
: 
 276                 AddSwitch(desc
->shortName
, desc
->longName
, desc
->description
); 
 279             case wxCMD_LINE_OPTION
: 
 280                 AddOption(desc
->shortName
, desc
->longName
, desc
->description
, 
 281                           desc
->type
, desc
->flags
); 
 284             case wxCMD_LINE_PARAM
: 
 285                 AddParam(desc
->description
, desc
->type
, desc
->flags
); 
 289                 wxFAIL_MSG( _T("unknown command line entry type") ); 
 290                 // still fall through 
 292             case wxCMD_LINE_NONE
: 
 298 void wxCmdLineParser::AddSwitch(const wxString
& shortName
, 
 299                                 const wxString
& longName
, 
 300                                 const wxString
& desc
, 
 303     wxASSERT_MSG( m_data
->FindOption(shortName
) == wxNOT_FOUND
, 
 304                   _T("duplicate switch") ); 
 306     wxCmdLineOption 
*option 
= new wxCmdLineOption(wxCMD_LINE_SWITCH
, 
 307                                                   shortName
, longName
, desc
, 
 308                                                   wxCMD_LINE_VAL_NONE
, flags
); 
 310     m_data
->m_options
.Add(option
); 
 313 void wxCmdLineParser::AddOption(const wxString
& shortName
, 
 314                                 const wxString
& longName
, 
 315                                 const wxString
& desc
, 
 316                                 wxCmdLineParamType type
, 
 319     wxASSERT_MSG( m_data
->FindOption(shortName
) == wxNOT_FOUND
, 
 320                   _T("duplicate option") ); 
 322     wxCmdLineOption 
*option 
= new wxCmdLineOption(wxCMD_LINE_OPTION
, 
 323                                                   shortName
, longName
, desc
, 
 326     m_data
->m_options
.Add(option
); 
 329 void wxCmdLineParser::AddParam(const wxString
& desc
, 
 330                                wxCmdLineParamType type
, 
 333     // do some consistency checks: a required parameter can't follow an 
 334     // optional one and nothing should follow a parameter with MULTIPLE flag 
 336     if ( !m_data
->m_paramDesc
.IsEmpty() ) 
 338         wxCmdLineParam
& param 
= m_data
->m_paramDesc
.Last(); 
 340         wxASSERT_MSG( !(param
.flags 
& wxCMD_LINE_PARAM_MULTIPLE
), 
 341                       _T("all parameters after the one with " 
 342                          "wxCMD_LINE_PARAM_MULTIPLE style will be ignored") ); 
 344         if ( !(flags 
& wxCMD_LINE_PARAM_OPTIONAL
) ) 
 346             wxASSERT_MSG( !(param
.flags 
& wxCMD_LINE_PARAM_OPTIONAL
), 
 347                           _T("a required parameter can't follow an " 
 353     wxCmdLineParam 
*param 
= new wxCmdLineParam(desc
, type
, flags
); 
 355     m_data
->m_paramDesc
.Add(param
); 
 358 // ---------------------------------------------------------------------------- 
 359 // access to parse command line 
 360 // ---------------------------------------------------------------------------- 
 362 bool wxCmdLineParser::Found(const wxString
& name
) const 
 364     int i 
= m_data
->FindOption(name
); 
 365     wxCHECK_MSG( i 
!= wxNOT_FOUND
, FALSE
, _T("unknown switch") ); 
 367     wxCmdLineOption
& opt 
= m_data
->m_options
[(size_t)i
]; 
 368     if ( !opt
.HasValue() ) 
 374 bool wxCmdLineParser::Found(const wxString
& name
, wxString 
*value
) const 
 376     int i 
= m_data
->FindOption(name
); 
 377     wxCHECK_MSG( i 
!= wxNOT_FOUND
, FALSE
, _T("unknown option") ); 
 379     wxCmdLineOption
& opt 
= m_data
->m_options
[(size_t)i
]; 
 380     if ( !opt
.HasValue() ) 
 383     wxCHECK_MSG( value
, FALSE
, _T("NULL pointer in wxCmdLineOption::Found") ); 
 385     *value 
= opt
.GetStrVal(); 
 390 bool wxCmdLineParser::Found(const wxString
& name
, long *value
) const 
 392     int i 
= m_data
->FindOption(name
); 
 393     wxCHECK_MSG( i 
!= wxNOT_FOUND
, FALSE
, _T("unknown option") ); 
 395     wxCmdLineOption
& opt 
= m_data
->m_options
[(size_t)i
]; 
 396     if ( !opt
.HasValue() ) 
 399     wxCHECK_MSG( value
, FALSE
, _T("NULL pointer in wxCmdLineOption::Found") ); 
 401     *value 
= opt
.GetLongVal(); 
 406 bool wxCmdLineParser::Found(const wxString
& name
, wxDateTime 
*value
) const 
 408     int i 
= m_data
->FindOption(name
); 
 409     wxCHECK_MSG( i 
!= wxNOT_FOUND
, FALSE
, _T("unknown option") ); 
 411     wxCmdLineOption
& opt 
= m_data
->m_options
[(size_t)i
]; 
 412     if ( !opt
.HasValue() ) 
 415     wxCHECK_MSG( value
, FALSE
, _T("NULL pointer in wxCmdLineOption::Found") ); 
 417     *value 
= opt
.GetDateVal(); 
 422 size_t wxCmdLineParser::GetParamCount() const 
 424     return m_data
->m_parameters
.GetCount(); 
 427 wxString 
wxCmdLineParser::GetParam(size_t n
) const 
 429     return m_data
->m_parameters
[n
]; 
 432 // ---------------------------------------------------------------------------- 
 433 // the real work is done here 
 434 // ---------------------------------------------------------------------------- 
 436 int wxCmdLineParser::Parse() 
 438     bool maybeOption 
= TRUE
;    // can the following arg be an option? 
 439     bool ok 
= TRUE
;             // TRUE until an error is detected 
 440     bool helpRequested 
= FALSE
; // TRUE if "-h" was given 
 441     bool hadRepeatableParam 
= FALSE
; // TRUE if found param with MULTIPLE flag 
 443     size_t currentParam 
= 0;    // the index in m_paramDesc 
 445     size_t countParam 
= m_data
->m_paramDesc
.GetCount(); 
 449     size_t count 
= m_data
->m_arguments
.GetCount(); 
 450     for ( size_t n 
= 1; ok 
&& (n 
< count
); n
++ )    // 0 is program name 
 452         arg 
= m_data
->m_arguments
[n
]; 
 454         // special case: "--" should be discarded and all following arguments 
 455         // should be considered as parameters, even if they start with '-' and 
 456         // not like options (this is POSIX-like) 
 457         if ( arg 
== _T("--") ) 
 464         // empty argument or just '-' is not an option but a parameter 
 465         if ( maybeOption 
&& arg
.length() > 1 && 
 466                 wxStrchr(m_data
->m_switchChars
, arg
[0u]) ) 
 470             int optInd 
= wxNOT_FOUND
;   // init to suppress warnings 
 472             // an option or a switch: find whether it's a long or a short one 
 473             if ( m_data
->m_enableLongOptions 
&& 
 474                     arg
[0u] == _T('-') && arg
[1u] == _T('-') ) 
 479                 const wxChar 
*p 
= arg
.c_str() + 2; 
 480                 while ( wxIsalpha(*p
) || (*p 
== _T('-')) ) 
 485                 optInd 
= m_data
->FindOptionByLongName(name
); 
 486                 if ( optInd 
== wxNOT_FOUND 
) 
 488                     wxLogError(_("Unknown long option '%s'"), name
.c_str()); 
 495                 // a short one: as they can be cumulated, we try to find the 
 496                 // longest substring which is a valid option 
 497                 const wxChar 
*p 
= arg
.c_str() + 1; 
 498                 while ( wxIsalpha(*p
) ) 
 503                 size_t len 
= name
.length(); 
 508                         // we couldn't find a valid option name in the 
 509                         // beginning of this string 
 510                         wxLogError(_("Unknown option '%s'"), name
.c_str()); 
 516                         optInd 
= m_data
->FindOption(name
.Left(len
)); 
 518                         // will try with one character less the next time 
 522                 while ( optInd 
== wxNOT_FOUND 
); 
 524                 if ( (len 
> 0) && (len 
!= name
.length()) ) 
 526                     // our option is only part of this argument, there is 
 527                     // something else in it - it is either the value of this 
 528                     // option or other switches if it is a switch 
 529                     if ( m_data
->m_options
[(size_t)optInd
].kind
 
 530                             == wxCMD_LINE_SWITCH 
) 
 532                         // pretend that all the rest of the argument is the 
 533                         // next argument, in fact 
 534                         wxString arg2 
= arg
[0u]; 
 535                         arg2 
+= name
.Mid(len
); 
 537                         m_data
->m_arguments
.Insert(arg2
, n 
+ 1); 
 539                     //else: it's our value, we'll deal with it below 
 543             if ( optInd 
== wxNOT_FOUND 
) 
 547                 continue;   // will break, in fact 
 550             wxCmdLineOption
& opt 
= m_data
->m_options
[(size_t)optInd
]; 
 551             if ( opt
.kind 
== wxCMD_LINE_SWITCH 
) 
 553                 // nothing more to do 
 556                 if ( opt
.flags 
& wxCMD_LINE_OPTION_HELP 
) 
 558                     helpRequested 
= TRUE
; 
 560                     // it's not an error, but we still stop here 
 568                 // +1 for leading '-' 
 569                 const wxChar 
*p 
= arg
.c_str() + 1 + name
.length(); 
 572                     p
++;    // for another leading '-' 
 574                     if ( *p
++ != _T('=') ) 
 576                         wxLogError(_("Option '%s' requires a value, '=' " 
 577                                      "expected."), name
.c_str()); 
 592                             // the value is in the next argument 
 595                                 // ... but there is none 
 596                                 wxLogError(_("Option '%s' requires a value."), 
 603                                 // ... take it from there 
 604                                 p 
= m_data
->m_arguments
[n
].c_str(); 
 609                             // the value is right here 
 620                             wxFAIL_MSG( _T("unknown option type") ); 
 621                             // still fall through 
 623                         case wxCMD_LINE_VAL_STRING
: 
 624                             opt
.SetStrVal(value
); 
 627                         case wxCMD_LINE_VAL_NUMBER
: 
 630                                 if ( value
.ToLong(&val
) ) 
 636                                     wxLogError(_("'%s' is not a correct " 
 637                                                  "numeric value for option " 
 639                                                value
.c_str(), name
.c_str()); 
 646                         case wxCMD_LINE_VAL_DATE
: 
 649                                 const wxChar 
*res 
= dt
.ParseDate(value
); 
 652                                     wxLogError(_("Options '%s': '%s' cannot " 
 653                                                   "be converted to a date."), 
 654                                                name
.c_str(), value
.c_str()); 
 671             if ( currentParam 
< countParam 
) 
 673                 wxCmdLineParam
& param 
= m_data
->m_paramDesc
[currentParam
]; 
 675                 // TODO check the param type 
 677                 m_data
->m_parameters
.Add(arg
); 
 679                 if ( !(param
.flags 
& wxCMD_LINE_PARAM_MULTIPLE
) ) 
 685                     wxASSERT_MSG( currentParam 
== countParam 
- 1, 
 686                                   _T("all parameters after the one with " 
 687                                      "wxCMD_LINE_PARAM_MULTIPLE style " 
 690                     // remember that we did have this last repeatable parameter 
 691                     hadRepeatableParam 
= TRUE
; 
 696                 wxLogError(_("Unexpected parameter '%s'"), arg
.c_str()); 
 703     // verify that all mandatory options were given 
 706         size_t countOpt 
= m_data
->m_options
.GetCount(); 
 707         for ( size_t n 
= 0; ok 
&& (n 
< countOpt
); n
++ ) 
 709             wxCmdLineOption
& opt 
= m_data
->m_options
[n
]; 
 710             if ( (opt
.flags 
& wxCMD_LINE_OPTION_MANDATORY
) && !opt
.HasValue() ) 
 715                     optName 
= opt
.shortName
; 
 719                     optName
.Printf(_("%s (or %s)"), 
 720                                    opt
.shortName
.c_str(), 
 721                                    opt
.longName
.c_str()); 
 724                 wxLogError(_("The value for the option '%s' must be specified."), 
 731         for ( ; ok 
&& (currentParam 
< countParam
); currentParam
++ ) 
 733             wxCmdLineParam
& param 
= m_data
->m_paramDesc
[currentParam
]; 
 734             if ( (currentParam 
== countParam 
- 1) && 
 735                  (param
.flags 
& wxCMD_LINE_PARAM_MULTIPLE
) && 
 738                 // special case: currentParam wasn't incremented, but we did 
 739                 // have it, so don't give error 
 743             if ( !(param
.flags 
& wxCMD_LINE_PARAM_OPTIONAL
) ) 
 745                 wxLogError(_("The required parameter '%s' was not specified."), 
 746                            param
.description
.c_str()); 
 758     return ok 
? 0 : helpRequested 
? -1 : 1; 
 761 // ---------------------------------------------------------------------------- 
 762 // give the usage message 
 763 // ---------------------------------------------------------------------------- 
 765 void wxCmdLineParser::Usage() 
 767     wxString brief
, detailed
; 
 768     brief
.Printf(_("Usage: %s"), wxTheApp
->GetAppName().c_str()); 
 770     size_t n
, count 
= m_data
->m_options
.GetCount(); 
 771     for ( n 
= 0; n 
< count
; n
++ ) 
 773         wxCmdLineOption
& opt 
= m_data
->m_options
[n
]; 
 776         if ( !(opt
.flags 
& wxCMD_LINE_OPTION_MANDATORY
) ) 
 781         brief 
<< _T('-') << opt
.shortName
; 
 782         detailed 
<< _T("  -") << opt
.shortName
; 
 783         if ( !!opt
.longName 
) 
 785             detailed 
<< _T("  --") << opt
.longName
; 
 788         if ( opt
.kind 
!= wxCMD_LINE_SWITCH 
) 
 791             val 
<< _T('<') << GetTypeName(opt
.type
) << _T('>'); 
 792             brief 
<< _T(' ') << val
; 
 793             detailed 
<< (!opt
.longName 
? _T(':') : _T('=')) << val
; 
 796         if ( !(opt
.flags 
& wxCMD_LINE_OPTION_MANDATORY
) ) 
 801         detailed 
<< _T('\t') << opt
.description 
<< _T('\n'); 
 804     count 
= m_data
->m_paramDesc
.GetCount(); 
 805     for ( n 
= 0; n 
< count
; n
++ ) 
 807         wxCmdLineParam
& param 
= m_data
->m_paramDesc
[n
]; 
 810         if ( param
.flags 
& wxCMD_LINE_PARAM_OPTIONAL 
) 
 815         brief 
<< param
.description
; 
 817         if ( param
.flags 
& wxCMD_LINE_PARAM_MULTIPLE 
) 
 822         if ( param
.flags 
& wxCMD_LINE_PARAM_OPTIONAL 
) 
 829     wxLogMessage(detailed
); 
 832 // ---------------------------------------------------------------------------- 
 834 // ---------------------------------------------------------------------------- 
 836 static wxString 
GetTypeName(wxCmdLineParamType type
) 
 842             wxFAIL_MSG( _T("unknown option type") ); 
 843             // still fall through 
 845         case wxCMD_LINE_VAL_STRING
: s 
= _("str"); break; 
 846         case wxCMD_LINE_VAL_NUMBER
: s 
= _("num"); break; 
 847         case wxCMD_LINE_VAL_DATE
:   s 
= _("date"); break;