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

Patrick Mezard pmezard at gmail.com
Wed Dec 13 15:37:21 CST 2006


# HG changeset patch
# User Patrick Mezard <pmezard at gmail.com>
# Date 1166049119 -3600
# Node ID 5613e100ead767e6f12c40a04ea1443043b15b1d
# 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 5613e100ead7 mercurial/util.py
--- a/mercurial/util.py	Tue Dec 12 17:52:33 2006 -0600
+++ b/mercurial/util.py	Wed Dec 13 23:31:59 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 5613e100ead7 mercurial/util_win32.py
--- a/mercurial/util_win32.py	Tue Dec 12 17:52:33 2006 -0600
+++ b/mercurial/util_win32.py	Wed Dec 13 23:31:59 2006 +0100
@@ -197,6 +197,49 @@ 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 preserve_quotes(cmd):
+        return os.system(cmd)
+    else:
+        #Add fake quotes which will be removed by the command processor
+        return os.system('"' + cmd + '"')
 
 class posixfile_nt(object):
     '''file object with posix-like semantics.  on windows, normal
@@ -297,5 +340,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