@@ -19,7 +19,7 @@ import error, osutil, encoding
import errno, shutil, sys, tempfile, traceback
import re as remod
import os, time, datetime, calendar, textwrap, signal, collections
-import imp, socket, urllib
+import imp, socket, urllib, shlex, cStringIO
if os.name == 'nt':
import windows as platform
@@ -609,6 +609,49 @@ def _sethgexecutable(path):
global _hgexecutable
_hgexecutable = path
+def shellsplit(cmdline, all=True):
+ '''Split a command line into components
+
+ If ``all`` is true (default), this splits ``cmdline`` into
+ components, and returns list of them.
+
+ Otherwise, this splits ``cmdline`` only at the first shell
+ delimiter character, and returns ``(command, rest)`` tuple.
+
+ Code paths splitting the string from configuration files into each
+ components should use this instead of ``shlex.split``, because the
+ latter loses whether users really want to quote each components or
+ not (see issue4463 for detail).
+
+ >>> shellsplit('foo bar baz')
+ ['foo', 'bar', 'baz']
+ >>> shellsplit('foo', all=False)
+ ('foo', '')
+ >>> shellsplit('foo bar baz', all=False)
+ ('foo', 'bar baz')
+ >>> shellsplit('"foo foo"', all=False)
+ ('foo foo', '')
+ >>> shellsplit('"foo foo" bar baz', all=False)
+ ('foo foo', 'bar baz')
+ >>> shellsplit('"foo foo" "bar" baz', all=False)
+ ('foo foo', '"bar" baz')
+ >>> shellsplit('foo "bar" baz', all=False)
+ ('foo', '"bar" baz')
+ >>> shellsplit('"foo"/"foo" "bar" baz', all=False)
+ ('foo/foo', '"bar" baz')
+ '''
+ if all:
+ return shlex.split(cmdline)
+
+ stream = cStringIO.StringIO(cmdline)
+ # According to "shlex.split" implementation, ``posix`` is True
+ # even on Windows
+ lex = shlex.shlex(stream, posix=True)
+ lex.whitespace_split = True
+ lex.commenters = ''
+
+ return (lex.next(), cmdline[stream.tell():].lstrip())
+
def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
'''enhanced shell command execution.
run with environment maybe modified, maybe in different dir.