From bf8ecc9de563bd9297508143f3c8da8ed4667419 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Fri, 18 Sep 2015 01:50:21 -0400 Subject: [PATCH] Don't shell-parse REPL shell commands to make redirection etc. work Julia's "shell parsing" (as used for backticks) excplicitly states that output redirection, pipes etc. are not supported via their shell metacharacters. Thus parsing a REPL shell command via the backtick mechanism makes it impossible to redirect output, unless shell quoting has loopholes and lets these metacharacters through (which leads to severe problems if one e.g. wants to quote an already-quoted string). For example, Julia's shell parsing returns the same result for these two different commands: echo '>a' echo >a In both cases, the command ["echo", ">a"] is generated, assuming that the character ">" is not special. It is then impossible to extract the original REPL user's intention from this command. Thus the REPL needs to parse shell commands via `split`, which suffices to determine whether the command is `cd`. Everything else is left to the shell. --- base/REPL.jl | 6 ++---- base/client.jl | 22 ++++++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/base/REPL.jl b/base/REPL.jl index 1616ca8f85e243..4d4349b4814078 100644 --- a/base/REPL.jl +++ b/base/REPL.jl @@ -727,11 +727,9 @@ function setup_interface(repl::LineEditREPL; hascolor = repl.hascolor, extra_rep (repl.envcolors ? Base.input_color : repl.input_color) : "", keymap_func_data = repl, complete = ShellCompletionProvider(repl), - # Transform "foo bar baz" into `foo bar baz` (shell quoting) - # and pass into Base.repl_cmd for processing (handles `ls` and `cd` - # special) + # Pass into Base.repl_cmd for processing (handles `cd` special) on_done = respond(repl, julia_prompt) do line - Expr(:call, :(Base.repl_cmd), macroexpand(Expr(:macrocall, symbol("@cmd"),line)), outstream(repl)) + Base.repl_cmd(line, outstream(repl)) end) ################################# Stage II ############################# diff --git a/base/client.jl b/base/client.jl index 1530242c9787d2..e82968db734d9d 100644 --- a/base/client.jl +++ b/base/client.jl @@ -39,7 +39,7 @@ exit(n) = ccall(:jl_exit, Void, (Int32,), n) exit() = exit(0) quit() = exit() -function repl_cmd(cmd, out) +function repl_cmd(line, out) shell = shell_split(get(ENV,"JULIA_SHELL",get(ENV,"SHELL","/bin/sh"))) # Note that we can't support the fish shell due to its lack of subshells # See this for details: https://github.com/JuliaLang/julia/issues/4918 @@ -49,21 +49,24 @@ function repl_cmd(cmd, out) shell = "/bin/sh" end - if isempty(cmd.exec) + cmds = split(line) + if isempty(cmds) throw(ArgumentError("no cmd to execute")) - elseif cmd.exec[1] == "cd" + elseif cmds[1] == "cd" new_oldpwd = pwd() - if length(cmd.exec) > 2 + if length(cmds) > 2 throw(ArgumentError("cd method only takes one argument")) - elseif length(cmd.exec) == 2 - dir = cmd.exec[2] + elseif length(cmds) == 2 + dir = cmds[2] if dir == "-" if !haskey(ENV, "OLDPWD") error("cd: OLDPWD not set") end cd(ENV["OLDPWD"]) else - cd(@windows? dir : readchomp(`$shell -c "echo $(shell_escape(dir))"`)) + cmd = "echo $dir" + unixcmd = `$shell -c $cmd` + cd(@windows? dir : readchomp(unixcmd)) end else cd() @@ -71,7 +74,10 @@ function repl_cmd(cmd, out) ENV["OLDPWD"] = new_oldpwd println(out, pwd()) else - run(ignorestatus(@windows? cmd : (isa(STDIN, TTY) ? `$shell -i -c "($(shell_escape(cmd))) && true"` : `$shell -c "($(shell_escape(cmd))) && true"`))) + ttyflag = isa(STDIN, TTY) ? "-i" : "" + unixcmd = `$shell $ttyflag -c $line` + wincmd = Cmd(Vector{ByteString}(shell_split(line))) + run(ignorestatus(@windows? wincmd : unixcmd)) end nothing end