Add functions to quote arguments for cmd.exe. This was needed for the source_cmd function.

This commit is contained in:
Morten Enemark Lund 2016-04-22 13:16:46 +02:00
parent 17bf2a9a86
commit 4863a94b1c
4 changed files with 63 additions and 15 deletions

View file

@ -7,10 +7,11 @@ import nose
from nose.tools import assert_equal, assert_true, assert_false
from xonsh.lexer import Lexer
from xonsh.tools import subproc_toks, subexpr_from_unbalanced, is_int, \
always_true, always_false, ensure_string, is_env_path, str_to_env_path, \
env_path_to_str, escape_windows_title_string, is_bool, to_bool, bool_to_str, \
ensure_int_or_slice, is_float, is_string, check_for_partial_string
from xonsh.tools import ( subproc_toks, subexpr_from_unbalanced, is_int,
always_true, always_false, ensure_string, is_env_path, str_to_env_path,
env_path_to_str, escape_windows_cmd_string, is_bool, to_bool, bool_to_str,
ensure_int_or_slice, is_float, is_string, check_for_partial_string,
argvquote )
LEXER = Lexer()
LEXER.build()
@ -273,19 +274,37 @@ def test_ensure_int_or_slice():
obs = ensure_int_or_slice(inp)
yield assert_equal, exp, obs
def test_escape_windows_title_string():
def test_escape_windows_cmd_string():
cases = [
('', ''),
('foo', 'foo'),
('foo&bar', 'foo^&bar'),
('foo$?-/_"\\', 'foo$?-/_"\\'),
('foo$?-/_"\\', 'foo$?-/_^"\\'),
('^&<>|', '^^^&^<^>^|'),
('this /?', 'this /.')
]
for st, esc in cases:
obs = escape_windows_title_string(st)
obs = escape_windows_cmd_string(st)
yield assert_equal, esc, obs
def test_argvquote():
cases = [
('', '""'),
('foo', 'foo'),
(r'arg1 "hallo, "world"" "\some\path with\spaces")',
r'"arg1 \"hallo, \"world\"\" \"\some\path with\spaces\")"'),
(r'"argument"2" argument3 argument4',
r'"\"argument\"2\" argument3 argument4"'),
(r'"\foo\bar bar\foo\" arg',
r'"\"\foo\bar bar\foo\\\" arg"')
]
for st, esc in cases:
obs = argvquote(st)
yield assert_equal, esc, obs
_leaders = ('', 'not empty')
_r = ('r', '')
_b = ('b', '')

View file

@ -18,6 +18,7 @@ from xonsh.replay import main as replay_main
from xonsh.environ import locate_binary
from xonsh.foreign_shells import foreign_shell_data
from xonsh.vox import Vox
from xonsh.tools import argvquote, escape_windows_cmd_string
class Aliases(MutableMapping):
@ -254,8 +255,10 @@ def source_cmd(args, stdin=None):
args[0] = fpath if fpath else args[0]
if not os.path.isfile(args[0]):
raise FileNotFoundError(args[0])
prevcmd = 'call {}'.format(' '.join(args))
prevcmd += '\necho off'
prevcmd = 'call '
prevcmd += ' '.join([argvquote(arg,force=True) for arg in args])
prevcmd = escape_windows_cmd_string(prevcmd)
prevcmd += '\n@echo off'
args.append('--prevcmd={}'.format(prevcmd))
args.insert(0, 'cmd')
args.append('--interactive=0')

View file

@ -6,7 +6,7 @@ import sys
import time
import builtins
from xonsh.tools import XonshError, escape_windows_title_string, ON_WINDOWS, \
from xonsh.tools import XonshError, escape_windows_cmd_string, ON_WINDOWS, \
print_exception, HAVE_PYGMENTS
from xonsh.completer import Completer
from xonsh.environ import multiline_prompt, format_prompt
@ -214,7 +214,7 @@ class BaseShell(object):
return
t = format_prompt(t)
if ON_WINDOWS and 'ANSICON' not in env:
t = escape_windows_title_string(t)
t = escape_windows_cmd_string(t)
os.system('title {}'.format(t))
else:
os.write(1, "\x1b]2;{0}\x07".format(t).encode())

View file

@ -410,17 +410,43 @@ def suggestion_sort_helper(x, y):
return lendiff + inx + iny
def escape_windows_title_string(s):
"""Returns a string that is usable by the Windows cmd.exe title
builtin. The escaping is based on details here and emperical testing:
def escape_windows_cmd_string(s):
"""Returns a string that is usable by the Windows cmd.exe.
The escaping is based on details here and emperical testing:
http://www.robvanderwoude.com/escapechars.php
"""
for c in '^&<>|':
for c in '()%!^<>&|"':
s = s.replace(c, '^' + c)
s = s.replace('/?', '/.')
return s
def argvquote(arg, force=False):
""" It Converts arguments to a command line such
that CommandLineToArgvW will return the argument string unchanged.
Arguments in a command line should be separated by spaces; this
function does not add these spaces. This follows the suggestions outlined
here: https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
"""
if not force and len(arg) != 0 and not any([c in arg for c in ' \t\n\v"']):
cmdline = arg
else:
n_backslashes = 0
cmdline = '"'
for c in arg:
if c == '"':
cmdline += (n_backslashes * 2 + 1) * '\\'
else:
cmdline += n_backslashes * '\\'
if c != '\\':
cmdline += c
n_backslashes = 0
else:
n_backslashes += 1
cmdline += n_backslashes * 2 * '\\' + '"'
return cmdline
def on_main_thread():
"""Checks if we are on the main thread or not."""
return threading.current_thread() is threading.main_thread()