mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-04 08:24:40 +01:00
Add functions to quote arguments for cmd.exe. This was needed for the source_cmd function.
This commit is contained in:
parent
17bf2a9a86
commit
4863a94b1c
4 changed files with 63 additions and 15 deletions
|
@ -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', '')
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Add table
Reference in a new issue