src/Pure/System/standard_system.scala
changeset 34201 c95dcd12f48a
child 34202 99241daf807d
equal deleted inserted replaced
34200:ca5f522fa233 34201:c95dcd12f48a
       
     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 }