]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/cmdline.cpp
Workaround for #15404: wxRichTextCtrl: caret does not disappear when focus is lost...
[wxWidgets.git] / src / common / cmdline.cpp
index 1a143d476c342b6e2f7f53d05c96e8ba21fef375..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
@@ -104,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
@@ -139,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,
@@ -152,6 +163,7 @@ public:
 
 private:
     bool m_hasVal;
+    bool m_isNegated;
 
     double m_doubleVal;
     long m_longVal;
@@ -230,13 +242,45 @@ wxCmdLineParserData::wxCmdLineParserData()
 #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);
     }
 }
 
@@ -484,18 +528,23 @@ void wxCmdLineParser::AddUsageText(const wxString& text)
 // ----------------------------------------------------------------------------
 
 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, wxT("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
@@ -593,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();
     }
 }
 
@@ -633,7 +681,15 @@ int wxCmdLineParser::Parse(bool showUsage)
 
             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
@@ -658,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())
-                                 << wxT('\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
@@ -721,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];
@@ -758,7 +856,10 @@ 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())
                              << wxT('\n');
@@ -768,6 +869,8 @@ int wxCmdLineParser::Parse(bool showUsage)
                 {
                     // nothing more to do
                     opt.SetHasValue();
+                    if ( negated )
+                        opt.SetNegated();
 
                     if ( opt.flags & wxCMD_LINE_OPTION_HELP )
                     {
@@ -873,8 +976,8 @@ int wxCmdLineParser::Parse(bool showUsage)
                         case wxCMD_LINE_VAL_DATE:
                             {
                                 wxDateTime dt;
-                                wxString::const_iterator end;
-                                if ( !dt.ParseDate(value, &end) || end != value.end() )
+                                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())
@@ -1061,7 +1164,7 @@ wxString wxCmdLineParser::GetUsageString() const
     for ( n = 0; n < count; n++ )
     {
         wxCmdLineOption& opt = m_data->m_options[n];
-        wxString option;
+        wxString option, negator;
 
         if ( opt.kind != wxCMD_LINE_USAGE_TEXT )
         {
@@ -1071,13 +1174,16 @@ wxString wxCmdLineParser::GetUsageString() const
                 usage << wxT('[');
             }
 
+            if ( opt.flags & wxCMD_LINE_SWITCH_NEGATABLE )
+                negator = wxT("[-]");
+
             if ( !opt.shortName.empty() )
             {
-                usage << chSwitch << opt.shortName;
+                usage << chSwitch << opt.shortName << negator;
             }
             else if ( areLongOptionsEnabled && !opt.longName.empty() )
             {
-                usage << wxT("--") << opt.longName;
+                usage << wxT("--") << opt.longName << negator;
             }
             else
             {
@@ -1278,6 +1384,23 @@ 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 */