Bash.process always uses a closed script instead of an open argument list, for extra robustness on Windows, where quoting is not well-defined;
more robust File.bash_escape;
more robust treatment of ML_OPTIONS;
clarified prover args (again);
--- a/src/Pure/Concurrent/bash.scala Mon Mar 07 15:21:50 2016 +0100
+++ b/src/Pure/Concurrent/bash.scala Mon Mar 07 18:20:22 2016 +0100
@@ -26,20 +26,21 @@
}
}
- def process(cwd: JFile, env: Map[String, String], redirect: Boolean, args: String*): Process =
- new Process(cwd, env, redirect, args:_*)
+ def process(script: String,
+ cwd: JFile = null, env: Map[String, String] = null, redirect: Boolean = false): Process =
+ new Process(script, cwd, env, redirect)
class Process private [Bash](
- cwd: JFile, env: Map[String, String], redirect: Boolean, args: String*)
+ script: String, cwd: JFile, env: Map[String, String], redirect: Boolean)
extends Prover.System_Process
{
+ val script_file = Isabelle_System.tmp_file("bash_script")
+ File.write(script_file, script)
+
private val proc =
- {
- val params =
- List(File.platform_path(Path.variable("ISABELLE_BASH_PROCESS")), "-", "bash")
- Isabelle_System.process(
- cwd, Isabelle_System.settings(env), redirect, (params ::: args.toList):_*)
- }
+ Isabelle_System.process(cwd, Isabelle_System.settings(env), redirect,
+ File.platform_path(Path.variable("ISABELLE_BASH_PROCESS")), "-",
+ "bash", File.standard_path(script_file))
// channels
@@ -90,14 +91,20 @@
try { Runtime.getRuntime.addShutdownHook(shutdown_hook) }
catch { case _: IllegalStateException => }
- private def cleanup() =
+
+ /* join */
+
+ def join: Int =
+ {
+ val rc = proc.waitFor
+
try { Runtime.getRuntime.removeShutdownHook(shutdown_hook) }
catch { case _: IllegalStateException => }
+ script_file.delete
- /* join */
-
- def join: Int = { val rc = proc.waitFor; cleanup(); rc }
+ rc
+ }
/* result */
--- a/src/Pure/General/file.scala Mon Mar 07 15:21:50 2016 +0100
+++ b/src/Pure/General/file.scala Mon Mar 07 18:20:22 2016 +0100
@@ -101,11 +101,34 @@
}
- /* shell path (bash) */
+ /* bash path */
- def shell_quote(s: String): String = "'" + s + "'"
- def shell_path(path: Path): String = shell_quote(standard_path(path))
- def shell_path(file: JFile): String = shell_quote(standard_path(file))
+ private def bash_escape(c: Byte): String =
+ {
+ val ch = c.toChar
+ ch match {
+ case '\t' => "$'\\t'"
+ case '\n' => "$'\\n'"
+ case '\f' => "$'\\f'"
+ case '\r' => "$'\\r'"
+ case _ =>
+ if (Symbol.is_ascii_letter(ch) || Symbol.is_ascii_digit(ch) || "-./:_".contains(ch))
+ Symbol.ascii(ch)
+ else if (c < 0) "$'\\x" + Integer.toHexString(256 + c) + "'"
+ else if (c < 16) "$'\\x0" + Integer.toHexString(c) + "'"
+ else if (c < 32) "$'\\x" + Integer.toHexString(c) + "'"
+ else "\\" + ch
+ }
+ }
+
+ def bash_escape(s: String): String =
+ UTF8.bytes(s).iterator.map(bash_escape(_)).mkString
+
+ def bash_escape(args: List[String]): String =
+ args.iterator.map(bash_escape(_)).mkString(" ")
+
+ def bash_path(path: Path): String = bash_escape(standard_path(path))
+ def bash_path(file: JFile): String = bash_escape(standard_path(file))
/* directory entries */
--- a/src/Pure/PIDE/batch_session.scala Mon Mar 07 15:21:50 2016 +0100
+++ b/src/Pure/PIDE/batch_session.scala Mon Mar 07 18:20:22 2016 +0100
@@ -58,7 +58,7 @@
case _ =>
}
- prover_session.start("Isabelle", "-q " + quote(parent_session))
+ prover_session.start("Isabelle", List("-q", parent_session))
batch_session
}
--- a/src/Pure/PIDE/resources.scala Mon Mar 07 15:21:50 2016 +0100
+++ b/src/Pure/PIDE/resources.scala Mon Mar 07 18:20:22 2016 +0100
@@ -136,7 +136,7 @@
/* prover process */
- def start_prover(receiver: Prover.Message => Unit, name: String, args: String): Prover =
+ def start_prover(receiver: Prover.Message => Unit, name: String, args: List[String]): Prover =
Isabelle_Process(receiver, args)
}
--- a/src/Pure/PIDE/session.scala Mon Mar 07 15:21:50 2016 +0100
+++ b/src/Pure/PIDE/session.scala Mon Mar 07 18:20:22 2016 +0100
@@ -212,7 +212,7 @@
/* internal messages */
- private case class Start(name: String, args: String)
+ private case class Start(name: String, args: List[String])
private case object Stop
private case class Cancel_Exec(exec_id: Document_ID.Exec)
private case class Protocol_Command(name: String, args: List[String])
@@ -601,7 +601,7 @@
pending_edits: List[Text.Edit] = Nil): Document.Snapshot =
global_state.value.snapshot(name, pending_edits)
- def start(name: String, args: String)
+ def start(name: String, args: List[String])
{ manager.send(Start(name, args)) }
def stop()
--- a/src/Pure/System/isabelle_process.scala Mon Mar 07 15:21:50 2016 +0100
+++ b/src/Pure/System/isabelle_process.scala Mon Mar 07 18:20:22 2016 +0100
@@ -11,16 +11,14 @@
{
def apply(
receiver: Prover.Message => Unit = Console.println(_),
- prover_args: String = ""): Isabelle_Process =
+ prover_args: List[String] = Nil): Isabelle_Process =
{
val system_channel = System_Channel()
val system_process =
try {
- val script =
- File.shell_quote(Isabelle_System.getenv_strict("ISABELLE_PROCESS")) +
- " -P " + system_channel.server_name +
- (if (prover_args == "") "" else " " + prover_args)
- val process = Bash.process(null, null, false, "-c", script)
+ val process =
+ Bash.process("\"$ISABELLE_PROCESS\" -P " + File.bash_escape(system_channel.server_name) +
+ " " + File.bash_escape(prover_args))
process.stdin.close
process
}
--- a/src/Pure/System/isabelle_system.scala Mon Mar 07 15:21:50 2016 +0100
+++ b/src/Pure/System/isabelle_system.scala Mon Mar 07 18:20:22 2016 +0100
@@ -167,7 +167,7 @@
def mkdirs(path: Path): Unit =
if (!path.is_dir) {
- bash("perl -e \"use File::Path make_path; make_path(" + File.shell_path(path) + ");\"")
+ bash("perl -e \"use File::Path make_path; make_path('" + File.standard_path(path) + "');\"")
if (!path.is_dir) error("Failed to create directory: " + quote(File.platform_path(path)))
}
@@ -308,11 +308,7 @@
progress_limit: Option[Long] = None,
strict: Boolean = true): Process_Result =
{
- with_tmp_file("isabelle_script") { script_file =>
- File.write(script_file, script)
- Bash.process(cwd, env, false, File.standard_path(script_file)).
- result(progress_stdout, progress_stderr, progress_limit, strict)
- }
+ Bash.process(script, cwd, env).result(progress_stdout, progress_stderr, progress_limit, strict)
}
@@ -342,7 +338,7 @@
bash("exec \"$PDF_VIEWER\" '" + File.standard_path(arg) + "' >/dev/null 2>/dev/null &")
def hg(cmd_line: String, cwd: Path = Path.current): Process_Result =
- bash("cd " + File.shell_path(cwd) + " && \"${HG:-hg}\" " + cmd_line)
+ bash("cd " + File.bash_path(cwd) + " && \"${HG:-hg}\" " + cmd_line)
--- a/src/Pure/System/ml_process.scala Mon Mar 07 15:21:50 2016 +0100
+++ b/src/Pure/System/ml_process.scala Mon Mar 07 18:20:22 2016 +0100
@@ -66,13 +66,18 @@
// options
val isabelle_process_options = Isabelle_System.tmp_file("options")
File.write(isabelle_process_options, YXML.string_of_body(options.encode))
- val bash_env = Map("ISABELLE_PROCESS_OPTIONS" -> File.standard_path(isabelle_process_options))
+ val env = Map("ISABELLE_PROCESS_OPTIONS" -> File.standard_path(isabelle_process_options))
val eval_options = List("Options.load_default ()")
val eval_process =
if (process_socket == "") Nil
else List("Isabelle_Process.init " + ML_Syntax.print_string_raw(process_socket))
+ // bash
+ val bash_args =
+ Word.explode(Isabelle_System.getenv("ML_OPTIONS")) :::
+ (eval_heaps ::: eval_exit ::: eval_secure ::: eval_modes ::: eval_options ::: eval_process).
+ map(eval => List("--eval", eval)).flatten ::: args
val bash_script =
"""
[ -z "$ISABELLE_TMP_PREFIX" ] && ISABELLE_TMP_PREFIX=/tmp/isabelle
@@ -83,7 +88,7 @@
chmod $(umask -S) "$ISABELLE_TMP"
librarypath "$ML_HOME"
- "$ML_HOME/poly" -q -i $ML_OPTIONS "$@"
+ "$ML_HOME/poly" -q -i """ + File.bash_escape(bash_args) + """
RC="$?"
rm -f "$ISABELLE_PROCESS_OPTIONS"
@@ -92,11 +97,6 @@
exit "$RC"
"""
- val bash_args =
- List("-c", bash_script, "ml_process") :::
- (eval_heaps ::: eval_exit ::: eval_secure ::: eval_modes ::: eval_options ::: eval_process).
- map(eval => List("--eval", eval)).flatten ::: args
-
- Bash.process(null, bash_env, false, bash_args:_*)
+ Bash.process(bash_script, env = env)
}
}
--- a/src/Pure/Tools/check_sources.scala Mon Mar 07 15:21:50 2016 +0100
+++ b/src/Pure/Tools/check_sources.scala Mon Mar 07 18:20:22 2016 +0100
@@ -41,7 +41,7 @@
def check_hg(root: Path)
{
Output.writeln("Checking " + root + " ...")
- Isabelle_System.hg("--repository " + File.shell_path(root) + " root").check
+ Isabelle_System.hg("--repository " + File.bash_path(root) + " root").check
for {
file <- Isabelle_System.hg("manifest", root).check.out_lines
if file.endsWith(".thy") || file.endsWith(".ML") || file.endsWith("/ROOT")
--- a/src/Tools/jEdit/src/isabelle_logic.scala Mon Mar 07 15:21:50 2016 +0100
+++ b/src/Tools/jEdit/src/isabelle_logic.scala Mon Mar 07 18:20:22 2016 +0100
@@ -69,12 +69,11 @@
dirs = session_dirs(), sessions = List(session_name()))
}
- def session_args(): String =
+ def session_args(): List[String] =
{
- val print_modes =
- (space_explode(',', PIDE.options.string("jedit_print_mode")) :::
- space_explode(',', Isabelle_System.getenv("JEDIT_PRINT_MODE"))).map("-m " + _)
- (print_modes ::: List("-q", File.shell_quote(session_name()))).mkString(" ")
+ (space_explode(',', PIDE.options.string("jedit_print_mode")) :::
+ space_explode(',', Isabelle_System.getenv("JEDIT_PRINT_MODE"))).
+ map(m => List("-m", m)).flatten ::: List("-q", session_name())
}
def session_start(): Unit = PIDE.session.start("Isabelle", session_args())