]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/cmdline.cpp
Avoid using wxHtmlTag::HasParam() unnecessarily.
[wxWidgets.git] / src / common / cmdline.cpp
index 6f3b1860e5c552707b74a9e39b7909cabdb25067..e8892831ac90a1f71d1914d7aa51b37818134297 100644 (file)
@@ -4,7 +4,6 @@
 // Author:      Vadim Zeitlin
 // Modified by:
 // Created:     05.01.00
-// RCS-ID:      $Id$
 // Copyright:   (c) 2000 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
 // Licence:     wxWindows licence
 ///////////////////////////////////////////////////////////////////////////////
 #if wxUSE_CMDLINE_PARSER
 
 #include <ctype.h>
+#include <locale.h>             // for LC_ALL
 
 #include "wx/datetime.h"
 #include "wx/msgout.h"
 #include "wx/filename.h"
 #include "wx/apptrait.h"
+#include "wx/scopeguard.h"
 
 // ----------------------------------------------------------------------------
 // private functions
@@ -73,21 +74,27 @@ struct wxCmdLineOption
                     wxCmdLineParamType typ,
                     int fl)
     {
-        wxASSERT_MSG( !shrt.empty() || !lng.empty(),
-                      _T("option should have at least one name") );
-
-        wxASSERT_MSG
-        (
-            GetShortOptionName(shrt.begin(), shrt.end()).Len() == shrt.Len(),
-            wxT("Short option contains invalid characters")
-        );
-
-        wxASSERT_MSG
-        (
-            GetLongOptionName(lng.begin(), lng.end()).Len() == lng.Len(),
-            wxT("Long option contains invalid characters")
-        );
-
+        // wxCMD_LINE_USAGE_TEXT uses only description, shortName and longName is empty
+        if ( k != wxCMD_LINE_USAGE_TEXT )
+        {
+            wxASSERT_MSG
+            (
+                !shrt.empty() || !lng.empty(),
+                 wxT("option should have at least one name")
+            );
+
+            wxASSERT_MSG
+            (
+                GetShortOptionName(shrt.begin(), shrt.end()).Len() == shrt.Len(),
+                wxT("Short option contains invalid characters")
+            );
+
+            wxASSERT_MSG
+            (
+                GetLongOptionName(lng.begin(), lng.end()).Len() == lng.Len(),
+                wxT("Long option contains invalid characters")
+            );
+        }
 
         kind = k;
 
@@ -98,7 +105,7 @@ struct wxCmdLineOption
         type = typ;
         flags = fl;
 
-        m_hasVal = false;
+        Reset();
     }
 
     // can't use union easily here, so just store all possible data fields, we
@@ -108,7 +115,7 @@ struct wxCmdLineOption
 
     void Check(wxCmdLineParamType WXUNUSED_UNLESS_DEBUG(typ)) const
     {
-        wxASSERT_MSG( type == typ, _T("type mismatch in wxCmdLineOption") );
+        wxASSERT_MSG( type == typ, wxT("type mismatch in wxCmdLineOption") );
     }
 
     double GetDoubleVal() const
@@ -133,9 +140,19 @@ struct wxCmdLineOption
         { Check(wxCMD_LINE_VAL_DATE); m_dateVal = val; m_hasVal = true; }
 #endif // wxUSE_DATETIME
 
-    void SetHasValue(bool hasValue = true) { m_hasVal = hasValue; }
+    void SetHasValue() { m_hasVal = true; }
     bool HasValue() const { return m_hasVal; }
 
+    void SetNegated() { m_isNegated = true; }
+    bool IsNegated() const { return m_isNegated; }
+
+    // Reset to the initial state, called before parsing another command line.
+    void Reset()
+    {
+        m_hasVal =
+        m_isNegated = false;
+    }
+
 public:
     wxCmdLineEntryType kind;
     wxString shortName,
@@ -146,6 +163,7 @@ public:
 
 private:
     bool m_hasVal;
+    bool m_isNegated;
 
     double m_doubleVal;
     long m_longVal;
@@ -218,19 +236,51 @@ wxCmdLineParserData::wxCmdLineParserData()
 {
     m_enableLongOptions = true;
 #ifdef __UNIX_LIKE__
-    m_switchChars = _T("-");
+    m_switchChars = wxT("-");
 #else // !Unix
-    m_switchChars = _T("/-");
+    m_switchChars = wxT("/-");
 #endif
 }
 
+namespace
+{
+
+// Small helper function setting locale for all categories.
+//
+// We define it because wxSetlocale() can't be easily used with wxScopeGuard as
+// it has several overloads -- while this one can.
+inline char *SetAllLocaleFacets(const char *loc)
+{
+    return wxSetlocale(LC_ALL, loc);
+}
+
+} // private namespace
+
 void wxCmdLineParserData::SetArguments(int argc, char **argv)
 {
     m_arguments.clear();
 
+    // Command-line arguments are supposed to be in the user locale encoding
+    // (what else?) but wxLocale probably wasn't initialized yet as we're
+    // called early during the program startup and so our locale might not have
+    // been set from the environment yet. To work around this problem we
+    // temporarily change the locale here. The only drawback is that changing
+    // the locale is thread-unsafe but precisely because we're called so early
+    // it's hopefully safe to assume that no other threads had been created yet.
+    char * const locOld = SetAllLocaleFacets("");
+    wxON_BLOCK_EXIT1( SetAllLocaleFacets, locOld );
+
     for ( int n = 0; n < argc; n++ )
     {
-        m_arguments.push_back(wxString::FromAscii(argv[n]));
+        // try to interpret the string as being in the current locale
+        wxString arg(argv[n]);
+
+        // but just in case we guessed wrongly and the conversion failed, do
+        // try to salvage at least something
+        if ( arg.empty() && argv[n][0] != '\0' )
+            arg = wxString(argv[n], wxConvISO8859_1);
+
+        m_arguments.push_back(arg);
     }
 }
 
@@ -390,8 +440,12 @@ void wxCmdLineParser::SetDesc(const wxCmdLineEntryDesc *desc)
                          desc->type, desc->flags);
                 break;
 
+            case wxCMD_LINE_USAGE_TEXT:
+                AddUsageText(wxGetTranslation(desc->description));
+                break;
+
             default:
-                wxFAIL_MSG( _T("unknown command line entry type") );
+                wxFAIL_MSG( wxT("unknown command line entry type") );
                 // still fall through
 
             case wxCMD_LINE_NONE:
@@ -406,7 +460,7 @@ void wxCmdLineParser::AddSwitch(const wxString& shortName,
                                 int flags)
 {
     wxASSERT_MSG( m_data->FindOption(shortName) == wxNOT_FOUND,
-                  _T("duplicate switch") );
+                  wxT("duplicate switch") );
 
     wxCmdLineOption *option = new wxCmdLineOption(wxCMD_LINE_SWITCH,
                                                   shortName, longName, desc,
@@ -422,7 +476,7 @@ void wxCmdLineParser::AddOption(const wxString& shortName,
                                 int flags)
 {
     wxASSERT_MSG( m_data->FindOption(shortName) == wxNOT_FOUND,
-                  _T("duplicate option") );
+                  wxT("duplicate option") );
 
     wxCmdLineOption *option = new wxCmdLineOption(wxCMD_LINE_OPTION,
                                                   shortName, longName, desc,
@@ -437,44 +491,60 @@ void wxCmdLineParser::AddParam(const wxString& desc,
 {
     // do some consistency checks: a required parameter can't follow an
     // optional one and nothing should follow a parameter with MULTIPLE flag
-#ifdef __WXDEBUG__
+#if wxDEBUG_LEVEL
     if ( !m_data->m_paramDesc.IsEmpty() )
     {
         wxCmdLineParam& param = m_data->m_paramDesc.Last();
 
         wxASSERT_MSG( !(param.flags & wxCMD_LINE_PARAM_MULTIPLE),
-                      _T("all parameters after the one with wxCMD_LINE_PARAM_MULTIPLE style will be ignored") );
+                      wxT("all parameters after the one with wxCMD_LINE_PARAM_MULTIPLE style will be ignored") );
 
         if ( !(flags & wxCMD_LINE_PARAM_OPTIONAL) )
         {
             wxASSERT_MSG( !(param.flags & wxCMD_LINE_PARAM_OPTIONAL),
-                          _T("a required parameter can't follow an optional one") );
+                          wxT("a required parameter can't follow an optional one") );
         }
     }
-#endif // Debug
+#endif // wxDEBUG_LEVEL
 
     wxCmdLineParam *param = new wxCmdLineParam(desc, type, flags);
 
     m_data->m_paramDesc.Add(param);
 }
 
+void wxCmdLineParser::AddUsageText(const wxString& text)
+{
+    wxASSERT_MSG( !text.empty(), wxT("text can't be empty") );
+
+    wxCmdLineOption *option = new wxCmdLineOption(wxCMD_LINE_USAGE_TEXT,
+                                                  wxEmptyString, wxEmptyString,
+                                                  text, wxCMD_LINE_VAL_NONE, 0);
+
+    m_data->m_options.Add(option);
+}
+
 // ----------------------------------------------------------------------------
 // access to parse command line
 // ----------------------------------------------------------------------------
 
 bool wxCmdLineParser::Found(const wxString& name) const
+{
+    return FoundSwitch(name) != wxCMD_SWITCH_NOT_FOUND;
+}
+
+wxCmdLineSwitchState wxCmdLineParser::FoundSwitch(const wxString& name) const
 {
     int i = m_data->FindOption(name);
     if ( i == wxNOT_FOUND )
         i = m_data->FindOptionByLongName(name);
 
-    wxCHECK_MSG( i != wxNOT_FOUND, false, _T("unknown switch") );
+    wxCHECK_MSG( i != wxNOT_FOUND, wxCMD_SWITCH_NOT_FOUND, wxT("unknown switch") );
 
     wxCmdLineOption& opt = m_data->m_options[(size_t)i];
     if ( !opt.HasValue() )
-        return false;
+        return wxCMD_SWITCH_NOT_FOUND;
 
-    return true;
+    return opt.IsNegated() ? wxCMD_SWITCH_OFF : wxCMD_SWITCH_ON;
 }
 
 bool wxCmdLineParser::Found(const wxString& name, wxString *value) const
@@ -483,13 +553,13 @@ bool wxCmdLineParser::Found(const wxString& name, wxString *value) const
     if ( i == wxNOT_FOUND )
         i = m_data->FindOptionByLongName(name);
 
-    wxCHECK_MSG( i != wxNOT_FOUND, false, _T("unknown option") );
+    wxCHECK_MSG( i != wxNOT_FOUND, false, wxT("unknown option") );
 
     wxCmdLineOption& opt = m_data->m_options[(size_t)i];
     if ( !opt.HasValue() )
         return false;
 
-    wxCHECK_MSG( value, false, _T("NULL pointer in wxCmdLineOption::Found") );
+    wxCHECK_MSG( value, false, wxT("NULL pointer in wxCmdLineOption::Found") );
 
     *value = opt.GetStrVal();
 
@@ -502,13 +572,13 @@ bool wxCmdLineParser::Found(const wxString& name, long *value) const
     if ( i == wxNOT_FOUND )
         i = m_data->FindOptionByLongName(name);
 
-    wxCHECK_MSG( i != wxNOT_FOUND, false, _T("unknown option") );
+    wxCHECK_MSG( i != wxNOT_FOUND, false, wxT("unknown option") );
 
     wxCmdLineOption& opt = m_data->m_options[(size_t)i];
     if ( !opt.HasValue() )
         return false;
 
-    wxCHECK_MSG( value, false, _T("NULL pointer in wxCmdLineOption::Found") );
+    wxCHECK_MSG( value, false, wxT("NULL pointer in wxCmdLineOption::Found") );
 
     *value = opt.GetLongVal();
 
@@ -521,13 +591,13 @@ bool wxCmdLineParser::Found(const wxString& name, double *value) const
     if ( i == wxNOT_FOUND )
         i = m_data->FindOptionByLongName(name);
 
-    wxCHECK_MSG( i != wxNOT_FOUND, false, _T("unknown option") );
+    wxCHECK_MSG( i != wxNOT_FOUND, false, wxT("unknown option") );
 
     wxCmdLineOption& opt = m_data->m_options[(size_t)i];
     if ( !opt.HasValue() )
         return false;
 
-    wxCHECK_MSG( value, false, _T("NULL pointer in wxCmdLineOption::Found") );
+    wxCHECK_MSG( value, false, wxT("NULL pointer in wxCmdLineOption::Found") );
 
     *value = opt.GetDoubleVal();
 
@@ -541,13 +611,13 @@ bool wxCmdLineParser::Found(const wxString& name, wxDateTime *value) const
     if ( i == wxNOT_FOUND )
         i = m_data->FindOptionByLongName(name);
 
-    wxCHECK_MSG( i != wxNOT_FOUND, false, _T("unknown option") );
+    wxCHECK_MSG( i != wxNOT_FOUND, false, wxT("unknown option") );
 
     wxCmdLineOption& opt = m_data->m_options[(size_t)i];
     if ( !opt.HasValue() )
         return false;
 
-    wxCHECK_MSG( value, false, _T("NULL pointer in wxCmdLineOption::Found") );
+    wxCHECK_MSG( value, false, wxT("NULL pointer in wxCmdLineOption::Found") );
 
     *value = opt.GetDateVal();
 
@@ -562,7 +632,7 @@ size_t wxCmdLineParser::GetParamCount() const
 
 wxString wxCmdLineParser::GetParam(size_t n) const
 {
-    wxCHECK_MSG( n < GetParamCount(), wxEmptyString, _T("invalid param index") );
+    wxCHECK_MSG( n < GetParamCount(), wxEmptyString, wxT("invalid param index") );
 
     return m_data->m_parameters[n];
 }
@@ -572,8 +642,7 @@ void wxCmdLineParser::Reset()
 {
     for ( size_t i = 0; i < m_data->m_options.GetCount(); i++ )
     {
-        wxCmdLineOption& opt = m_data->m_options[i];
-        opt.SetHasValue(false);
+        m_data->m_options[i].Reset();
     }
 }
 
@@ -606,13 +675,21 @@ int wxCmdLineParser::Parse(bool showUsage)
         // special case: "--" should be discarded and all following arguments
         // should be considered as parameters, even if they start with '-' and
         // not like options (this is POSIX-like)
-        if ( arg == _T("--") )
+        if ( arg == wxT("--") )
         {
             maybeOption = false;
 
             continue;
         }
-
+#ifdef __WXOSX__
+        if ( arg == wxT("-ApplePersistenceIgnoreState") )
+        {
+            maybeOption = false;
+            
+            continue;
+        }
+#endif
+        
         // empty argument or just '-' is not an option but a parameter
         if ( maybeOption && arg.length() > 1 &&
                 // FIXME-UTF8: use wc_str() after removing ANSI build
@@ -623,7 +700,7 @@ int wxCmdLineParser::Parse(bool showUsage)
             int optInd = wxNOT_FOUND;   // init to suppress warnings
 
             // an option or a switch: find whether it's a long or a short one
-            if ( arg.length() >= 3 && arg[0u] == _T('-') && arg[1u] == _T('-') )
+            if ( arg.length() >= 3 && arg[0u] == wxT('-') && arg[1u] == wxT('-') )
             {
                 // a long one
                 isLong = true;
@@ -637,11 +714,45 @@ int wxCmdLineParser::Parse(bool showUsage)
 
                 if (longOptionsEnabled)
                 {
+                    wxString errorOpt;
+
                     optInd = m_data->FindOptionByLongName(name);
                     if ( optInd == wxNOT_FOUND )
                     {
-                        errorMsg << wxString::Format(_("Unknown long option '%s'"), name.c_str())
-                                 << _T('\n');
+                        // Check if this could be a negatable long option.
+                        if ( name.Last() == '-' )
+                        {
+                            name.RemoveLast();
+
+                            optInd = m_data->FindOptionByLongName(name);
+                            if ( optInd != wxNOT_FOUND )
+                            {
+                                if ( !(m_data->m_options[optInd].flags &
+                                        wxCMD_LINE_SWITCH_NEGATABLE) )
+                                {
+                                    errorOpt.Printf
+                                             (
+                                              _("Option '%s' can't be negated"),
+                                              name
+                                             );
+                                    optInd = wxNOT_FOUND;
+                                }
+                            }
+                        }
+
+                        if ( optInd == wxNOT_FOUND )
+                        {
+                            if ( errorOpt.empty() )
+                            {
+                                errorOpt.Printf
+                                         (
+                                          _("Unknown long option '%s'"),
+                                          name
+                                         );
+                            }
+
+                            errorMsg << errorOpt << wxT('\n');
+                        }
                     }
                 }
                 else
@@ -651,7 +762,7 @@ int wxCmdLineParser::Parse(bool showUsage)
                     // Print the argument including leading "--"
                     name.Prepend( wxT("--") );
                     errorMsg << wxString::Format(_("Unknown option '%s'"), name.c_str())
-                             << _T('\n');
+                             << wxT('\n');
                 }
 
             }
@@ -673,7 +784,7 @@ int wxCmdLineParser::Parse(bool showUsage)
                         // we couldn't find a valid option name in the
                         // beginning of this string
                         errorMsg << wxString::Format(_("Unknown option '%s'"), name.c_str())
-                                 << _T('\n');
+                                 << wxT('\n');
 
                         break;
                     }
@@ -700,6 +811,14 @@ int wxCmdLineParser::Parse(bool showUsage)
                     if ( m_data->m_options[(size_t)optInd].kind
                             == wxCMD_LINE_SWITCH )
                     {
+                        // if the switch is negatable and it is just followed
+                        // by '-' the '-' is considered to be part of this
+                        // switch
+                        if ( (m_data->m_options[(size_t)optInd].flags &
+                                    wxCMD_LINE_SWITCH_NEGATABLE) &&
+                                arg[len] == '-' )
+                            ++len;
+
                         // pretend that all the rest of the argument is the
                         // next argument, in fact
                         wxString arg2 = arg[0u];
@@ -708,6 +827,10 @@ int wxCmdLineParser::Parse(bool showUsage)
                         m_data->m_arguments.insert
                             (m_data->m_arguments.begin() + n + 1, arg2);
                         count++;
+
+                        // only leave the part which wasn't extracted into the
+                        // next argument in this one
+                        arg = arg.Left(len + 1);
                     }
                     //else: it's our value, we'll deal with it below
                 }
@@ -733,16 +856,21 @@ int wxCmdLineParser::Parse(bool showUsage)
             if ( opt.kind == wxCMD_LINE_SWITCH )
             {
                 // we must check that there is no value following the switch
-                if ( p != arg.end() )
+                bool negated = (opt.flags & wxCMD_LINE_SWITCH_NEGATABLE) &&
+                                    p != arg.end() && *p == '-';
+
+                if ( !negated && p != arg.end() )
                 {
                     errorMsg << wxString::Format(_("Unexpected characters following option '%s'."), name.c_str())
-                             << _T('\n');
+                             << wxT('\n');
                     ok = false;
                 }
                 else // no value, as expected
                 {
                     // nothing more to do
                     opt.SetHasValue();
+                    if ( negated )
+                        opt.SetNegated();
 
                     if ( opt.flags & wxCMD_LINE_OPTION_HELP )
                     {
@@ -755,22 +883,22 @@ int wxCmdLineParser::Parse(bool showUsage)
             }
             else // it's an option. not a switch
             {
-                switch ( (*p).GetValue() )
+                switch ( p == end ? '\0' : (*p).GetValue() )
                 {
-                    case _T('='):
-                    case _T(':'):
+                    case '=':
+                    case ':':
                         // the value follows
                         ++p;
                         break;
 
-                    case 0:
+                    case '\0':
                         // the value is in the next argument
                         if ( ++n == count )
                         {
                             // ... but there is none
                             errorMsg << wxString::Format(_("Option '%s' requires a value."),
                                                          name.c_str())
-                                     << _T('\n');
+                                     << wxT('\n');
 
                             ok = false;
                         }
@@ -789,7 +917,7 @@ int wxCmdLineParser::Parse(bool showUsage)
                         {
                             errorMsg << wxString::Format(_("Separator expected after the option '%s'."),
                                                          name.c_str())
-                                    << _T('\n');
+                                    << wxT('\n');
 
                             ok = false;
                         }
@@ -801,7 +929,7 @@ int wxCmdLineParser::Parse(bool showUsage)
                     switch ( opt.type )
                     {
                         default:
-                            wxFAIL_MSG( _T("unknown option type") );
+                            wxFAIL_MSG( wxT("unknown option type") );
                             // still fall through
 
                         case wxCMD_LINE_VAL_STRING:
@@ -819,7 +947,7 @@ int wxCmdLineParser::Parse(bool showUsage)
                                 {
                                     errorMsg << wxString::Format(_("'%s' is not a correct numeric value for option '%s'."),
                                                                  value.c_str(), name.c_str())
-                                             << _T('\n');
+                                             << wxT('\n');
 
                                     ok = false;
                                 }
@@ -837,7 +965,7 @@ int wxCmdLineParser::Parse(bool showUsage)
                                 {
                                     errorMsg << wxString::Format(_("'%s' is not a correct numeric value for option '%s'."),
                                                                  value.c_str(), name.c_str())
-                                             << _T('\n');
+                                             << wxT('\n');
 
                                     ok = false;
                                 }
@@ -848,12 +976,12 @@ int wxCmdLineParser::Parse(bool showUsage)
                         case wxCMD_LINE_VAL_DATE:
                             {
                                 wxDateTime dt;
-                                const char *res = dt.ParseDate(value);
-                                if ( !res || *res )
+                                wxString::const_iterator endDate;
+                                if ( !dt.ParseDate(value, &endDate) || endDate != value.end() )
                                 {
                                     errorMsg << wxString::Format(_("Option '%s': '%s' cannot be converted to a date."),
                                                                  name.c_str(), value.c_str())
-                                             << _T('\n');
+                                             << wxT('\n');
 
                                     ok = false;
                                 }
@@ -885,7 +1013,7 @@ int wxCmdLineParser::Parse(bool showUsage)
                 else
                 {
                     wxASSERT_MSG( currentParam == countParam - 1,
-                                  _T("all parameters after the one with wxCMD_LINE_PARAM_MULTIPLE style are ignored") );
+                                  wxT("all parameters after the one with wxCMD_LINE_PARAM_MULTIPLE style are ignored") );
 
                     // remember that we did have this last repeatable parameter
                     hadRepeatableParam = true;
@@ -894,7 +1022,7 @@ int wxCmdLineParser::Parse(bool showUsage)
             else
             {
                 errorMsg << wxString::Format(_("Unexpected parameter '%s'"), arg.c_str())
-                         << _T('\n');
+                         << wxT('\n');
 
                 ok = false;
             }
@@ -932,7 +1060,7 @@ int wxCmdLineParser::Parse(bool showUsage)
 
                 errorMsg << wxString::Format(_("The value for the option '%s' must be specified."),
                                              optName.c_str())
-                         << _T('\n');
+                         << wxT('\n');
 
                 ok = false;
             }
@@ -954,7 +1082,7 @@ int wxCmdLineParser::Parse(bool showUsage)
             {
                 errorMsg << wxString::Format(_("The required parameter '%s' was not specified."),
                                              param.description.c_str())
-                         << _T('\n');
+                         << wxT('\n');
 
                 ok = false;
             }
@@ -976,7 +1104,7 @@ int wxCmdLineParser::Parse(bool showUsage)
         }
         else
         {
-            wxFAIL_MSG( _T("no wxMessageOutput object?") );
+            wxFAIL_MSG( wxT("no wxMessageOutput object?") );
         }
     }
 
@@ -996,7 +1124,7 @@ void wxCmdLineParser::Usage() const
     }
     else
     {
-        wxFAIL_MSG( _T("no wxMessageOutput object?") );
+        wxFAIL_MSG( wxT("no wxMessageOutput object?") );
     }
 }
 
@@ -1021,14 +1149,14 @@ wxString wxCmdLineParser::GetUsageString() const
 
     if ( !m_data->m_logo.empty() )
     {
-        usage << m_data->m_logo << _T('\n');
+        usage << m_data->m_logo << wxT('\n');
     }
 
     usage << wxString::Format(_("Usage: %s"), appname.c_str());
 
     // the switch char is usually '-' but this can be changed with
     // SetSwitchChars() and then the first one of possible chars is used
-    wxChar chSwitch = !m_data->m_switchChars ? _T('-')
+    wxChar chSwitch = !m_data->m_switchChars ? wxT('-')
                                              : m_data->m_switchChars[0u];
 
     bool areLongOptionsEnabled = AreLongOptionsEnabled();
@@ -1036,58 +1164,63 @@ wxString wxCmdLineParser::GetUsageString() const
     for ( n = 0; n < count; n++ )
     {
         wxCmdLineOption& opt = m_data->m_options[n];
+        wxString option, negator;
 
-        usage << _T(' ');
-        if ( !(opt.flags & wxCMD_LINE_OPTION_MANDATORY) )
+        if ( opt.kind != wxCMD_LINE_USAGE_TEXT )
         {
-            usage << _T('[');
-        }
+            usage << wxT(' ');
+            if ( !(opt.flags & wxCMD_LINE_OPTION_MANDATORY) )
+            {
+                usage << wxT('[');
+            }
 
-        if ( !opt.shortName.empty() )
-        {
-            usage << chSwitch << opt.shortName;
-        }
-        else if ( areLongOptionsEnabled && !opt.longName.empty() )
-        {
-            usage << _T("--") << opt.longName;
-        }
-        else
-        {
-            if (!opt.longName.empty())
+            if ( opt.flags & wxCMD_LINE_SWITCH_NEGATABLE )
+                negator = wxT("[-]");
+
+            if ( !opt.shortName.empty() )
             {
-                wxFAIL_MSG( wxT("option with only a long name while long ")
-                            wxT("options are disabled") );
+                usage << chSwitch << opt.shortName << negator;
+            }
+            else if ( areLongOptionsEnabled && !opt.longName.empty() )
+            {
+                usage << wxT("--") << opt.longName << negator;
             }
             else
             {
-                wxFAIL_MSG( _T("option without neither short nor long name") );
+                if (!opt.longName.empty())
+                {
+                    wxFAIL_MSG( wxT("option with only a long name while long ")
+                                wxT("options are disabled") );
+                }
+                else
+                {
+                    wxFAIL_MSG( wxT("option without neither short nor long name") );
+                }
             }
-        }
 
-        wxString option;
-
-        if ( !opt.shortName.empty() )
-        {
-            option << _T("  ") << chSwitch << opt.shortName;
-        }
+            if ( !opt.shortName.empty() )
+            {
+                option << wxT("  ") << chSwitch << opt.shortName;
+            }
 
-        if ( areLongOptionsEnabled && !opt.longName.empty() )
-        {
-            option << (option.empty() ? _T("  ") : _T(", "))
-                   << _T("--") << opt.longName;
-        }
+            if ( areLongOptionsEnabled && !opt.longName.empty() )
+            {
+                option << (option.empty() ? wxT("  ") : wxT(", "))
+                       << wxT("--") << opt.longName;
+            }
 
-        if ( opt.kind != wxCMD_LINE_SWITCH )
-        {
-            wxString val;
-            val << _T('<') << GetTypeName(opt.type) << _T('>');
-            usage << _T(' ') << val;
-            option << (!opt.longName ? _T(':') : _T('=')) << val;
-        }
+            if ( opt.kind != wxCMD_LINE_SWITCH )
+            {
+                wxString val;
+                val << wxT('<') << GetTypeName(opt.type) << wxT('>');
+                usage << wxT(' ') << val;
+                option << (!opt.longName ? wxT(':') : wxT('=')) << val;
+            }
 
-        if ( !(opt.flags & wxCMD_LINE_OPTION_MANDATORY) )
-        {
-            usage << _T(']');
+            if ( !(opt.flags & wxCMD_LINE_OPTION_MANDATORY) )
+            {
+                usage << wxT(']');
+            }
         }
 
         namesOptions.push_back(option);
@@ -1099,26 +1232,26 @@ wxString wxCmdLineParser::GetUsageString() const
     {
         wxCmdLineParam& param = m_data->m_paramDesc[n];
 
-        usage << _T(' ');
+        usage << wxT(' ');
         if ( param.flags & wxCMD_LINE_PARAM_OPTIONAL )
         {
-            usage << _T('[');
+            usage << wxT('[');
         }
 
         usage << param.description;
 
         if ( param.flags & wxCMD_LINE_PARAM_MULTIPLE )
         {
-            usage << _T("...");
+            usage << wxT("...");
         }
 
         if ( param.flags & wxCMD_LINE_PARAM_OPTIONAL )
         {
-            usage << _T(']');
+            usage << wxT(']');
         }
     }
 
-    usage << _T('\n');
+    usage << wxT('\n');
 
     // set to number of our own options, not counting the standard ones
     count = namesOptions.size();
@@ -1141,13 +1274,21 @@ wxString wxCmdLineParser::GetUsageString() const
     for ( n = 0; n < namesOptions.size(); n++ )
     {
         if ( n == count )
-            usage << _T('\n') << stdDesc;
+            usage << wxT('\n') << stdDesc;
 
         len = namesOptions[n].length();
-        usage << namesOptions[n]
-              << wxString(_T(' '), lenMax - len) << _T('\t')
-              << descOptions[n]
-              << _T('\n');
+        // desc contains text if name is empty
+        if (len == 0)
+        {
+            usage << descOptions[n] << wxT('\n');
+        }
+        else
+        {
+            usage << namesOptions[n]
+                  << wxString(wxT(' '), lenMax - len) << wxT('\t')
+                  << descOptions[n]
+                  << wxT('\n');
+        }
     }
 
     return usage;
@@ -1163,7 +1304,7 @@ static wxString GetTypeName(wxCmdLineParamType type)
     switch ( type )
     {
         default:
-            wxFAIL_MSG( _T("unknown option type") );
+            wxFAIL_MSG( wxT("unknown option type") );
             // still fall through
 
         case wxCMD_LINE_VAL_STRING:
@@ -1243,18 +1384,35 @@ static wxString GetLongOptionName(wxString::const_iterator p,
    Windows conventions for the command line handling, not Unix ones. For
    instance, backslash is not special except when it precedes double quote when
    it does quote it.
+
+   TODO: Rewrite this to follow the even more complicated rule used by Windows
+         CommandLineToArgv():
+
+    * A string of backslashes not followed by a quotation mark has no special
+      meaning.
+    * An even number of backslashes followed by a quotation mark is treated as
+      pairs of protected backslashes, followed by a word terminator.
+    * An odd number of backslashes followed by a quotation mark is treated as
+      pairs of protected backslashes, followed by a protected quotation mark.
+
+    See http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx
+
+    It could also be useful to provide a converse function which is also
+    non-trivial, see
+
+    http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx
  */
 
 /* static */
-wxArrayString wxCmdLineParser::ConvertStringToArgs(const wxString& cmdline)
+wxArrayString
+wxCmdLineParser::ConvertStringToArgs(const wxString& cmdline,
+                                     wxCmdLineSplitType type)
 {
     wxArrayString args;
 
     wxString arg;
     arg.reserve(1024);
 
-    bool isInsideQuotes = false;
-
     const wxString::const_iterator end = cmdline.end();
     wxString::const_iterator p = cmdline.begin();
 
@@ -1269,31 +1427,76 @@ wxArrayString wxCmdLineParser::ConvertStringToArgs(const wxString& cmdline)
             break;
 
         // parse this parameter
-        bool lastBS = false;
+        bool lastBS = false,
+             isInsideQuotes = false;
+        wxChar chDelim = '\0';
         for ( arg.clear(); p != end; ++p )
         {
             const wxChar ch = *p;
-            if ( ch == '"' )
+
+            if ( type == wxCMD_LINE_SPLIT_DOS )
             {
-                if ( !lastBS )
+                if ( ch == '"' )
                 {
-                    isInsideQuotes = !isInsideQuotes;
+                    if ( !lastBS )
+                    {
+                        isInsideQuotes = !isInsideQuotes;
 
-                    // don't put quote in arg
-                    continue;
+                        // don't put quote in arg
+                        continue;
+                    }
+                    //else: quote has no special meaning but the backslash
+                    //      still remains -- makes no sense but this is what
+                    //      Windows does
+                }
+                // note that backslash does *not* quote the space, only quotes do
+                else if ( !isInsideQuotes && (ch == ' ' || ch == '\t') )
+                {
+                    ++p;    // skip this space anyhow
+                    break;
                 }
-                //else: quote has no special meaning but the backslash
-                //      still remains -- makes no sense but this is what
-                //      Windows does
+
+                lastBS = !lastBS && ch == '\\';
             }
-            // note that backslash does *not* quote the space, only quotes do
-            else if ( !isInsideQuotes && (ch == ' ' || ch == '\t') )
+            else // type == wxCMD_LINE_SPLIT_UNIX
             {
-                ++p;    // skip this space anyhow
-                break;
-            }
+                if ( !lastBS )
+                {
+                    if ( isInsideQuotes )
+                    {
+                        if ( ch == chDelim )
+                        {
+                            isInsideQuotes = false;
 
-            lastBS = ch == '\\';
+                            continue;   // don't use the quote itself
+                        }
+                    }
+                    else // not in quotes and not escaped
+                    {
+                        if ( ch == '\'' || ch == '"' )
+                        {
+                            isInsideQuotes = true;
+                            chDelim = ch;
+
+                            continue;   // don't use the quote itself
+                        }
+
+                        if ( ch == ' ' || ch == '\t' )
+                        {
+                            ++p;    // skip this space anyhow
+                            break;
+                        }
+                    }
+
+                    lastBS = ch == '\\';
+                    if ( lastBS )
+                        continue;
+                }
+                else // escaped by backslash, just use as is
+                {
+                    lastBS = false;
+                }
+            }
 
             arg += ch;
         }