home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / wxos2233.zip / wxOS2-2_3_3.zip / wxWindows-2.3.3 / src / common / cmdline.cpp < prev    next >
C/C++ Source or Header  |  2002-09-08  |  39KB  |  1,234 lines

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