[PATCH] Fix win32 command processor quoting in system() calls

Patrick Mezard pmezard at gmail.com
Fri Dec 15 18:29:57 UTC 2006


# HG changeset patch
# User Patrick Mezard <pmezard at gmail.com>
# Date 1166167774 -3600
# Node ID 0f2f187d1c69e6e2507abf1238f2f12d7f8d99ff
# Parent  c0a12e6441a5abb669fcd62cc9719220c255fcd5
Fix win32 command processor quoting in system() calls.

Win32 command processor does strange things regarding quotes. Sometimes, it
just strips the leading and ending command quotes. Try to anticipate this
behaviour documented in CMD.EXE man page, and add quotes preventively.

diff -r c0a12e6441a5 -r 0f2f187d1c69 mercurial/util.py
--- a/mercurial/util.py	Tue Dec 12 17:52:33 2006 -0600
+++ b/mercurial/util.py	Fri Dec 15 08:29:34 2006 +0100
@@ -506,7 +506,7 @@ def system(cmd, environ={}, cwd=None, on
             os.environ[k] = py2shell(v)
         if cwd is not None and oldcwd != cwd:
             os.chdir(cwd)
-        rc = os.system(cmd)
+        rc = os_system(cmd)
         if rc and onerr:
             errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
                                 explain_exit(rc)[0])
@@ -904,6 +904,9 @@ else:
         if st is None:
             st = fstat(fp)
         return st.st_uid == os.getuid()
+        
+    def os_system(cmd):
+        return os.system(cmd)
 
 def _buildencodefun():
     e = '_'
diff -r c0a12e6441a5 -r 0f2f187d1c69 mercurial/util_win32.py
--- a/mercurial/util_win32.py	Tue Dec 12 17:52:33 2006 -0600
+++ b/mercurial/util_win32.py	Fri Dec 15 08:29:34 2006 +0100
@@ -197,6 +197,48 @@ def user_rcpath():
             shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA))
         userdir = os.path.dirname(appdir)
     return os.path.join(userdir, 'mercurial.ini')
+    
+    
+cmd_special_chars = '&<>()@^|'
+    
+def preserve_quotes(cmd):
+    '''Test if quotes in cmd are preserved or not by the command processor (see
+    cmd.exe man page for details.
+    '''
+    #Exactly two quotes characters
+    if cmd.count('"')!=2:
+        return False
+    # String within quotes must have at least one whitespace character and no
+    # special ones.
+    quote1 = cmd.find('"')
+    quote2 = cmd[quote1+1:].find('"')
+    quoted = cmd[quote1+1:quote2]
+    whitespaces = 0
+    for c in quoted:
+        if c in cmd_special_chars:
+            return False
+        if c == ' ':
+            whitespaces += 1
+    if whitespaces==0:
+        return False
+    # String within quotes must be the name of an executable file
+    if not os.path.isfile(quoted):
+        return False
+    pathext = os.environ.get('PATHEXT', '').lower().split(';')
+    if os.path.splitext()[1].lower() not in pathext:
+        return False
+    return True
+    
+def os_system(cmd):
+    '''Win32 command processor has a convoluted behaviour regarding quotes. If
+    some conditions are fulfilled it left them unchanged, otherwise if the 
+    command starts with a quote it will remove it and the trailing one. In this,
+    case, wrap the command with quotes, preventively.   
+    '''
+    if not preserve_quotes(cmd):
+        #Add fake quotes which will be removed by the command processor
+        cmd = '"' + cmd + '"'
+    return os.system(cmd)
 
 class posixfile_nt(object):
     '''file object with posix-like semantics.  on windows, normal
@@ -297,5 +339,6 @@ class posixfile_nt(object):
             win32file.SetEndOfFile(self.handle)
         except pywintypes.error, err:
             raise WinIOError(err)
+            
 
 getuser_fallback = win32api.GetUserName



More information about the Mercurial-devel mailing list