src/Pure/System/standard_system.scala
author wenzelm
Mon, 28 Dec 2009 22:03:14 +0100
changeset 34201 c95dcd12f48a
child 34202 99241daf807d
permissions -rw-r--r--
separate Standard_System (Cygwin/Posix compatibility) vs. Isabelle_System (settings environment etc.);
     1 /*  Title:      Pure/System/standard_system.scala
     2     Author:     Makarius
     3 
     4 Standard system operations, with basic Cygwin/Posix compatibility.
     5 */
     6 
     7 package isabelle
     8 
     9 import java.util.regex.Pattern
    10 import java.util.Locale
    11 import java.io.{BufferedWriter, OutputStreamWriter, FileOutputStream,
    12   BufferedInputStream, FileInputStream, BufferedReader, InputStreamReader,
    13   File, IOException}
    14 
    15 import scala.io.Source
    16 import scala.util.matching.Regex
    17 import scala.collection.mutable
    18 
    19 
    20 object Standard_System
    21 {
    22   val charset = "UTF-8"
    23 
    24 
    25   /* permissive UTF-8 decoding */
    26 
    27   // see also http://en.wikipedia.org/wiki/UTF-8#Description
    28   // overlong encodings enable byte-stuffing
    29 
    30   def decode_permissive_utf8(text: CharSequence): String =
    31   {
    32     val buf = new java.lang.StringBuilder(text.length)
    33     var code = -1
    34     var rest = 0
    35     def flush()
    36     {
    37       if (code != -1) {
    38         if (rest == 0 && Character.isValidCodePoint(code))
    39           buf.appendCodePoint(code)
    40         else buf.append('\uFFFD')
    41         code = -1
    42         rest = 0
    43       }
    44     }
    45     def init(x: Int, n: Int)
    46     {
    47       flush()
    48       code = x
    49       rest = n
    50     }
    51     def push(x: Int)
    52     {
    53       if (rest <= 0) init(x, -1)
    54       else {
    55         code <<= 6
    56         code += x
    57         rest -= 1
    58       }
    59     }
    60     for (i <- 0 until text.length) {
    61       val c = text.charAt(i)
    62       if (c < 128) { flush(); buf.append(c) }
    63       else if ((c & 0xC0) == 0x80) push(c & 0x3F)
    64       else if ((c & 0xE0) == 0xC0) init(c & 0x1F, 1)
    65       else if ((c & 0xF0) == 0xE0) init(c & 0x0F, 2)
    66       else if ((c & 0xF8) == 0xF0) init(c & 0x07, 3)
    67     }
    68     flush()
    69     buf.toString
    70   }
    71 
    72 
    73   /* basic file operations */
    74 
    75   def with_tmp_file[A](prefix: String)(body: File => A): A =
    76   {
    77     val file = File.createTempFile(prefix, null)
    78     try { body(file) } finally { file.delete }
    79   }
    80 
    81   def read_file(file: File): String =
    82   {
    83     val buf = new StringBuilder(file.length.toInt)
    84     val reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), charset))
    85     var c = reader.read
    86     while (c != -1) {
    87       buf.append(c.toChar)
    88       c = reader.read
    89     }
    90     reader.close
    91     buf.toString
    92   }
    93 
    94   def write_file(file: File, text: CharSequence)
    95   {
    96     val writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), charset))
    97     try { writer.append(text) }
    98     finally { writer.close }
    99   }
   100 
   101 
   102   /* shell processes */
   103 
   104   def raw_execute(env: Map[String, String], redirect: Boolean, args: String*): Process =
   105   {
   106     val cmdline = new java.util.LinkedList[String]
   107     for (s <- args) cmdline.add(s)
   108 
   109     val proc = new ProcessBuilder(cmdline)
   110     proc.environment.clear
   111     for ((x, y) <- env) proc.environment.put(x, y)
   112     proc.redirectErrorStream(redirect)
   113 
   114     try { proc.start }
   115     catch { case e: IOException => error(e.getMessage) }
   116   }
   117 
   118   def process_output(proc: Process): (String, Int) =
   119   {
   120     proc.getOutputStream.close
   121     val output = Source.fromInputStream(proc.getInputStream, charset).mkString  // FIXME
   122     val rc =
   123       try { proc.waitFor }
   124       finally {
   125         proc.getInputStream.close
   126         proc.getErrorStream.close
   127         proc.destroy
   128         Thread.interrupted
   129       }
   130     (output, rc)
   131   }
   132 }
   133 
   134 
   135 class Standard_System
   136 {
   137   val platform_root = if (Platform.is_windows) Cygwin.check_root() else "/"
   138   override def toString = platform_root
   139 
   140 
   141   /* jvm_path */
   142 
   143   private val Cygdrive = new Regex("/cygdrive/([a-zA-Z])($|/.*)")
   144 
   145   def jvm_path(posix_path: String): String =
   146     if (Platform.is_windows) {
   147       val result_path = new StringBuilder
   148       val rest =
   149         posix_path match {
   150           case Cygdrive(drive, rest) =>
   151             result_path ++= (drive + ":" + File.separator)
   152             rest
   153           case path if path.startsWith("/") =>
   154             result_path ++= platform_root
   155             path
   156           case path => path
   157         }
   158       for (p <- rest.split("/") if p != "") {
   159         val len = result_path.length
   160         if (len > 0 && result_path(len - 1) != File.separatorChar)
   161           result_path += File.separatorChar
   162         result_path ++= p
   163       }
   164       result_path.toString
   165     }
   166     else posix_path
   167 
   168 
   169   /* posix_path */
   170 
   171   private val Platform_Root = new Regex("(?i)" +
   172     Pattern.quote(platform_root) + """(?:\\+|\z)(.*)""")
   173 
   174   private val Drive = new Regex("""([a-zA-Z]):\\*(.*)""")
   175 
   176   def posix_path(jvm_path: String): String =
   177     if (Platform.is_windows) {
   178       jvm_path.replace('/', '\\') match {
   179         case Platform_Root(rest) => "/" + rest.replace('\\', '/')
   180         case Drive(letter, rest) =>
   181           "/cygdrive/" + letter.toLowerCase(Locale.ENGLISH) +
   182             (if (rest == "") "" else "/" + rest.replace('\\', '/'))
   183         case path => path.replace('\\', '/')
   184       }
   185     }
   186     else jvm_path
   187 }