src/Pure/General/file.ML
author wenzelm
Sat Oct 04 16:05:09 2008 +0200 (2008-10-04 ago)
changeset 28500 4b79e5d3d0aa
parent 28496 4cff10648928
child 28510 66b95e857bde
permissions -rw-r--r--
replaced ISATOOL by ISABELLE_TOOL;
     1 (*  Title:      Pure/General/file.ML
     2     ID:         $Id$
     3     Author:     Markus Wenzel, TU Muenchen
     4 
     5 File system operations.
     6 *)
     7 
     8 signature FILE =
     9 sig
    10   val platform_path: Path.T -> string
    11   val shell_path: Path.T -> string
    12   val cd: Path.T -> unit
    13   val pwd: unit -> Path.T
    14   val full_path: Path.T -> Path.T
    15   val tmp_path: Path.T -> Path.T
    16   val isabelle_tool: string -> int
    17   val system_command: string -> unit
    18   eqtype ident
    19   val rep_ident: ident -> string
    20   val ident: Path.T -> ident option
    21   val exists: Path.T -> bool
    22   val check: Path.T -> unit
    23   val rm: Path.T -> unit
    24   val mkdir: Path.T -> unit
    25   val open_input: (TextIO.instream -> 'a) -> Path.T -> 'a
    26   val open_output: (TextIO.outstream -> 'a) -> Path.T -> 'a
    27   val open_append: (TextIO.outstream -> 'a) -> Path.T -> 'a
    28   val fold_lines: (string -> 'a -> 'a) -> Path.T -> 'a -> 'a
    29   val read: Path.T -> string
    30   val write: Path.T -> string -> unit
    31   val append: Path.T -> string -> unit
    32   val write_list: Path.T -> string list -> unit
    33   val append_list: Path.T -> string list -> unit
    34   val write_buffer: Path.T -> Buffer.T -> unit
    35   val eq: Path.T * Path.T -> bool
    36   val copy: Path.T -> Path.T -> unit
    37   val copy_dir: Path.T -> Path.T -> unit
    38 end;
    39 
    40 structure File: FILE =
    41 struct
    42 
    43 (* system path representations *)
    44 
    45 val platform_path = Path.implode o Path.expand;
    46 
    47 val shell_quote = enclose "'" "'";
    48 val shell_path = shell_quote o platform_path;
    49 
    50 
    51 (* current working directory *)
    52 
    53 val cd = cd o platform_path;
    54 val pwd = Path.explode o pwd;
    55 
    56 fun full_path path =
    57   if Path.is_absolute path then path
    58   else Path.append (pwd ()) path;
    59 
    60 
    61 (* tmp_path *)
    62 
    63 fun tmp_path path =
    64   Path.append (Path.variable "ISABELLE_TMP") (Path.base path);
    65 
    66 
    67 (* system commands *)
    68 
    69 fun isabelle_tool cmd = system ("\"$ISABELLE_TOOL\" " ^ cmd);
    70 
    71 fun system_command cmd =
    72   if system cmd <> 0 then error ("System command failed: " ^ cmd)
    73   else ();
    74 
    75 
    76 (* file identification *)
    77 
    78 local
    79   val ident_cache = ref (Symtab.empty: {time_stamp: string, id: string} Symtab.table);
    80 in
    81 
    82 fun check_cache (path, time) = CRITICAL (fn () =>
    83   (case Symtab.lookup (! ident_cache) path of
    84     NONE => NONE
    85   | SOME {time_stamp, id} => if time_stamp = time then SOME id else NONE));
    86 
    87 fun update_cache (path, (time, id)) = CRITICAL (fn () =>
    88   change ident_cache (Symtab.update (path, {time_stamp = time, id = id})));
    89 
    90 end;
    91 
    92 datatype ident = Ident of string;
    93 fun rep_ident (Ident s) = s;
    94 
    95 fun ident path =
    96   let val physical_path = perhaps (try OS.FileSys.fullPath) (platform_path path) in
    97     (case try (Time.toString o OS.FileSys.modTime) physical_path of
    98       NONE => NONE
    99     | SOME mod_time => SOME (Ident
   100         (case getenv "ISABELLE_FILE_IDENT" of
   101           "" => physical_path ^ ": " ^ mod_time
   102         | cmd =>
   103             (case check_cache (physical_path, mod_time) of
   104               SOME id => id
   105             | NONE =>
   106                 let val (id, rc) =  (*potentially slow*)
   107                   system_out ("\"$ISABELLE_HOME/lib/scripts/fileident\" " ^ shell_quote physical_path)
   108                 in
   109                   if id <> "" andalso rc = 0 then (update_cache (physical_path, (mod_time, id)); id)
   110                   else error ("Failed to identify file " ^ quote (Path.implode path) ^ " by " ^ cmd)
   111                 end))))
   112   end;
   113 
   114 
   115 (* directory entries *)
   116 
   117 val exists = can OS.FileSys.modTime o platform_path;
   118 
   119 fun check path =
   120   if exists path then ()
   121   else error ("No such file or directory: " ^ quote (Path.implode path));
   122 
   123 val rm = OS.FileSys.remove o platform_path;
   124 
   125 fun mkdir path = system_command ("mkdir -p " ^ shell_path path);
   126 
   127 fun is_dir path =
   128   the_default false (try OS.FileSys.isDir (platform_path path));
   129 
   130 
   131 (* open files *)
   132 
   133 local
   134 
   135 fun with_file open_file close_file f path =
   136   let val file = open_file path
   137   in Exn.release (Exn.capture f file before close_file file) end;
   138 
   139 in
   140 
   141 fun open_input f = with_file TextIO.openIn TextIO.closeIn f o platform_path;
   142 fun open_output f = with_file TextIO.openOut TextIO.closeOut f o platform_path;
   143 fun open_append f = with_file TextIO.openAppend TextIO.closeOut f o platform_path;
   144 
   145 end;
   146 
   147 
   148 (* input *)
   149 
   150 (*scalable iterator -- avoid size limit of TextIO.inputAll, and overhead of many TextIO.inputLine*)
   151 fun fold_lines f path a = open_input (fn file =>
   152   let
   153     val split = split_last o String.fields (fn c => str c = "\n");
   154     fun read buf =
   155       (case split (TextIO.input file) of
   156         ([], "") => (case Buffer.content buf of "" => I | line => f line)
   157       | ([], rest) => read (Buffer.add rest buf)
   158       | (line :: lines, rest) =>
   159           f (Buffer.content (Buffer.add line buf))
   160           #> fold f lines
   161           #> read (Buffer.add rest Buffer.empty));
   162   in read Buffer.empty a end) path;
   163 
   164 val read = open_input TextIO.inputAll;
   165 
   166 
   167 (* output *)
   168 
   169 fun output txts file = List.app (fn txt => TextIO.output (file, txt)) txts;
   170 
   171 fun write_list path txts = open_output (output txts) path;
   172 fun append_list path txts = open_append (output txts) path;
   173 
   174 fun write path txt = write_list path [txt];
   175 fun append path txt = append_list path [txt];
   176 
   177 fun write_buffer path buf = open_output (Buffer.output buf) path;
   178 
   179 
   180 (* copy *)
   181 
   182 fun eq paths =
   183   (case try (pairself (OS.FileSys.fileId o platform_path)) paths of
   184     SOME ids => is_equal (OS.FileSys.compare ids)
   185   | NONE => false);
   186 
   187 fun copy src dst =
   188   if eq (src, dst) then ()
   189   else
   190     let val target = if is_dir dst then Path.append dst (Path.base src) else dst
   191     in write target (read src) end;
   192 
   193 fun copy_dir src dst =
   194   if eq (src, dst) then ()
   195   else (system_command ("cp -r -f " ^ shell_path src ^ "/. " ^ shell_path dst); ());
   196 
   197 end;