src/Pure/System/isabelle_tool.scala
changeset 62829 4141c2a8458b
child 62830 85024c0e953d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Pure/System/isabelle_tool.scala	Sun Apr 03 21:32:57 2016 +0200
@@ -0,0 +1,75 @@
+/*  Title:      Pure/System/isabelle_tool.scala
+    Author:     Makarius
+
+Isabelle system tools: external executables or internal Scala functions.
+*/
+
+package isabelle
+
+
+object Isabelle_Tool
+{
+  /* external tools */
+
+  private def dirs(): List[Path] = Path.split(Isabelle_System.getenv_strict("ISABELLE_TOOLS"))
+
+  private def is_external(dir: Path, name: String): Boolean =
+  {
+    val file = (dir + Path.basic(name)).file
+    try {
+      file.isFile && file.canRead && file.canExecute &&
+        !name.endsWith("~") && !name.endsWith(".orig")
+    }
+    catch { case _: SecurityException => false }
+  }
+
+  private def run_external(dir: Path, name: String)(args: List[String]): Nothing =
+  {
+    val tool = dir + Path.basic(name)
+    val result = Isabelle_System.bash(File.bash_path(tool) + " " + File.bash_args(args))
+    sys.exit(result.print_stdout.rc)
+  }
+
+  private def find_external(name: String): Option[List[String] => Unit] =
+    dirs.collectFirst({ case dir if is_external(dir, name) => run_external(dir, name) })
+
+
+  /* command line entry point */
+
+  private def tool_descriptions(): List[String] =
+  {
+    val Description = """.*\bDESCRIPTION: *(.*)""".r
+    val entries =
+      for {
+        dir <- dirs() if dir.is_dir
+        name <- File.read_dir(dir) if is_external(dir, name)
+      } yield {
+        val source = File.read(dir + Path.basic(name))
+        split_lines(source).collectFirst({ case Description(s) => s }) match {
+          case None => (name, "")
+          case Some(description) => (name, " - " + description)
+        }
+      }
+    entries.sortBy(_._1).map({ case (a, b) => a + b })
+  }
+
+  def main(args: Array[String])
+  {
+    Command_Line.tool0 {
+      args.toList match {
+        case Nil | List("-?") =>
+          Getopts("""
+Usage: isabelle TOOL [ARGS ...]
+
+  Start Isabelle TOOL with ARGS; pass "-?" for tool-specific help.
+
+Available tools:""" + tool_descriptions.mkString("\n  ", "\n  ", "\n")).usage
+        case tool_name :: tool_args =>
+          find_external(tool_name) match {
+            case Some(run) => run(tool_args)
+            case None => error("Unknown Isabelle tool: " + quote(tool_name))
+          }
+      }
+    }
+  }
+}