| author | haftmann | 
| Mon, 19 Jul 2010 11:55:44 +0200 | |
| changeset 37881 | 096c8397c989 | 
| parent 37739 | 312fe7201f88 | 
| child 40741 | 17d6293a1e26 | 
| permissions | -rw-r--r-- | 
| 6118 | 1 | (* Title: Pure/General/file.ML | 
| 5009 | 2 | Author: Markus Wenzel, TU Muenchen | 
| 3 | ||
| 4 | File system operations. | |
| 5 | *) | |
| 6 | ||
| 7 | signature FILE = | |
| 8 | sig | |
| 16261 | 9 | val platform_path: Path.T -> string | 
| 32943 | 10 | val shell_quote: string -> string | 
| 16261 | 11 | val shell_path: Path.T -> string | 
| 6224 | 12 | val cd: Path.T -> unit | 
| 13 | val pwd: unit -> Path.T | |
| 14 | val full_path: Path.T -> Path.T | |
| 6182 | 15 | val tmp_path: Path.T -> Path.T | 
| 31818 
f698f76a3713
builtin isabelle_tool for ML and Scala -- avoids excessive shell script (especially important for Cygwin);
 wenzelm parents: 
29606diff
changeset | 16 | val isabelle_tool: string -> string -> int | 
| 23861 
72bb3494746f
replaced info by ident (for full identification, potentially content-based);
 wenzelm parents: 
23826diff
changeset | 17 | eqtype ident | 
| 23874 | 18 | val rep_ident: ident -> string | 
| 23861 
72bb3494746f
replaced info by ident (for full identification, potentially content-based);
 wenzelm parents: 
23826diff
changeset | 19 | val ident: Path.T -> ident option | 
| 17826 | 20 | val exists: Path.T -> bool | 
| 21858 
05f57309170c
avoid conflict with Alice keywords: renamed pack -> implode, unpack -> explode, any -> many, avoided assert;
 wenzelm parents: 
19960diff
changeset | 21 | val check: Path.T -> unit | 
| 17826 | 22 | val rm: Path.T -> unit | 
| 37739 | 23 | val rm_tree: Path.T -> unit | 
| 17826 | 24 | val mkdir: Path.T -> unit | 
| 37651 
62fc16341922
mkdir_leaf -- avoiding surprises with typos in user-given paths
 haftmann parents: 
35010diff
changeset | 25 | val mkdir_leaf: Path.T -> unit | 
| 26503 
4dec4460244f
discontinued unused hooks explode_platform_path_fn, platform_path_fn, shell_path_fn;
 wenzelm parents: 
26220diff
changeset | 26 | val open_input: (TextIO.instream -> 'a) -> Path.T -> 'a | 
| 
4dec4460244f
discontinued unused hooks explode_platform_path_fn, platform_path_fn, shell_path_fn;
 wenzelm parents: 
26220diff
changeset | 27 | val open_output: (TextIO.outstream -> 'a) -> Path.T -> 'a | 
| 
4dec4460244f
discontinued unused hooks explode_platform_path_fn, platform_path_fn, shell_path_fn;
 wenzelm parents: 
26220diff
changeset | 28 | val open_append: (TextIO.outstream -> 'a) -> Path.T -> 'a | 
| 28028 | 29 | val fold_lines: (string -> 'a -> 'a) -> Path.T -> 'a -> 'a | 
| 6182 | 30 | val read: Path.T -> string | 
| 31 | val write: Path.T -> string -> unit | |
| 32 | val append: Path.T -> string -> unit | |
| 16713 | 33 | val write_list: Path.T -> string list -> unit | 
| 34 | val append_list: Path.T -> string list -> unit | |
| 28028 | 35 | val write_buffer: Path.T -> Buffer.T -> unit | 
| 16603 | 36 | val eq: Path.T * Path.T -> bool | 
| 6182 | 37 | val copy: Path.T -> Path.T -> unit | 
| 16261 | 38 | val copy_dir: Path.T -> Path.T -> unit | 
| 5009 | 39 | end; | 
| 40 | ||
| 41 | structure File: FILE = | |
| 42 | struct | |
| 43 | ||
| 26503 
4dec4460244f
discontinued unused hooks explode_platform_path_fn, platform_path_fn, shell_path_fn;
 wenzelm parents: 
26220diff
changeset | 44 | (* system path representations *) | 
| 6224 | 45 | |
| 26503 
4dec4460244f
discontinued unused hooks explode_platform_path_fn, platform_path_fn, shell_path_fn;
 wenzelm parents: 
26220diff
changeset | 46 | val platform_path = Path.implode o Path.expand; | 
| 26980 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 47 | |
| 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 48 | val shell_quote = enclose "'" "'"; | 
| 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 49 | val shell_path = shell_quote o platform_path; | 
| 6224 | 50 | |
| 51 | ||
| 26503 
4dec4460244f
discontinued unused hooks explode_platform_path_fn, platform_path_fn, shell_path_fn;
 wenzelm parents: 
26220diff
changeset | 52 | (* current working directory *) | 
| 6224 | 53 | |
| 23826 
463903573934
moved cd/pwd to ML compatibility layer (simplifies bootstrapping with Alice);
 wenzelm parents: 
22145diff
changeset | 54 | val cd = cd o platform_path; | 
| 26503 
4dec4460244f
discontinued unused hooks explode_platform_path_fn, platform_path_fn, shell_path_fn;
 wenzelm parents: 
26220diff
changeset | 55 | val pwd = Path.explode o pwd; | 
| 6224 | 56 | |
| 16261 | 57 | fun full_path path = | 
| 26946 
0b6eff8c088d
removed norm_absolute (not thread safe; chdir does not guarantee normalization anyway);
 wenzelm parents: 
26656diff
changeset | 58 | if Path.is_absolute path then path | 
| 
0b6eff8c088d
removed norm_absolute (not thread safe; chdir does not guarantee normalization anyway);
 wenzelm parents: 
26656diff
changeset | 59 | else Path.append (pwd ()) path; | 
| 5009 | 60 | |
| 6182 | 61 | |
| 62 | (* tmp_path *) | |
| 63 | ||
| 64 | fun tmp_path path = | |
| 65 | Path.append (Path.variable "ISABELLE_TMP") (Path.base path); | |
| 5009 | 66 | |
| 67 | ||
| 16261 | 68 | (* system commands *) | 
| 69 | ||
| 31818 
f698f76a3713
builtin isabelle_tool for ML and Scala -- avoids excessive shell script (especially important for Cygwin);
 wenzelm parents: 
29606diff
changeset | 70 | fun isabelle_tool name args = | 
| 
f698f76a3713
builtin isabelle_tool for ML and Scala -- avoids excessive shell script (especially important for Cygwin);
 wenzelm parents: 
29606diff
changeset | 71 | (case space_explode ":" (getenv "ISABELLE_TOOLS") |> get_first (fn dir => | 
| 
f698f76a3713
builtin isabelle_tool for ML and Scala -- avoids excessive shell script (especially important for Cygwin);
 wenzelm parents: 
29606diff
changeset | 72 | let val path = dir ^ "/" ^ name in | 
| 
f698f76a3713
builtin isabelle_tool for ML and Scala -- avoids excessive shell script (especially important for Cygwin);
 wenzelm parents: 
29606diff
changeset | 73 | if can OS.FileSys.modTime path andalso | 
| 
f698f76a3713
builtin isabelle_tool for ML and Scala -- avoids excessive shell script (especially important for Cygwin);
 wenzelm parents: 
29606diff
changeset | 74 | not (OS.FileSys.isDir path) andalso | 
| 
f698f76a3713
builtin isabelle_tool for ML and Scala -- avoids excessive shell script (especially important for Cygwin);
 wenzelm parents: 
29606diff
changeset | 75 | OS.FileSys.access (path, [OS.FileSys.A_READ, OS.FileSys.A_EXEC]) | 
| 
f698f76a3713
builtin isabelle_tool for ML and Scala -- avoids excessive shell script (especially important for Cygwin);
 wenzelm parents: 
29606diff
changeset | 76 | then SOME path | 
| 
f698f76a3713
builtin isabelle_tool for ML and Scala -- avoids excessive shell script (especially important for Cygwin);
 wenzelm parents: 
29606diff
changeset | 77 | else NONE | 
| 
f698f76a3713
builtin isabelle_tool for ML and Scala -- avoids excessive shell script (especially important for Cygwin);
 wenzelm parents: 
29606diff
changeset | 78 | end handle OS.SysErr _ => NONE) of | 
| 35010 
d6e492cea6e4
renamed system/system_out to bash/bash_output -- to emphasized that this is really GNU bash, not some undefined POSIX sh;
 wenzelm parents: 
33223diff
changeset | 79 | SOME path => bash (shell_quote path ^ " " ^ args) | 
| 31818 
f698f76a3713
builtin isabelle_tool for ML and Scala -- avoids excessive shell script (especially important for Cygwin);
 wenzelm parents: 
29606diff
changeset | 80 |   | NONE => (writeln ("Unknown Isabelle tool: " ^ name); 2));
 | 
| 16261 | 81 | |
| 82 | fun system_command cmd = | |
| 35010 
d6e492cea6e4
renamed system/system_out to bash/bash_output -- to emphasized that this is really GNU bash, not some undefined POSIX sh;
 wenzelm parents: 
33223diff
changeset | 83 |   if bash cmd <> 0 then error ("System command failed: " ^ cmd)
 | 
| 16261 | 84 | else (); | 
| 85 | ||
| 86 | ||
| 26980 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 87 | (* file identification *) | 
| 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 88 | |
| 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 89 | local | 
| 32738 | 90 | val ident_cache = | 
| 91 |     Unsynchronized.ref (Symtab.empty: {time_stamp: string, id: string} Symtab.table);
 | |
| 26980 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 92 | in | 
| 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 93 | |
| 33223 | 94 | fun check_cache (path, time) = | 
| 26980 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 95 | (case Symtab.lookup (! ident_cache) path of | 
| 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 96 | NONE => NONE | 
| 33223 | 97 |   | SOME {time_stamp, id} => if time_stamp = time then SOME id else NONE);
 | 
| 26980 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 98 | |
| 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 99 | fun update_cache (path, (time, id)) = CRITICAL (fn () => | 
| 32738 | 100 | Unsynchronized.change ident_cache | 
| 101 |     (Symtab.update (path, {time_stamp = time, id = id})));
 | |
| 26980 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 102 | |
| 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 103 | end; | 
| 16261 | 104 | |
| 23861 
72bb3494746f
replaced info by ident (for full identification, potentially content-based);
 wenzelm parents: 
23826diff
changeset | 105 | datatype ident = Ident of string; | 
| 23874 | 106 | fun rep_ident (Ident s) = s; | 
| 107 | ||
| 23861 
72bb3494746f
replaced info by ident (for full identification, potentially content-based);
 wenzelm parents: 
23826diff
changeset | 108 | fun ident path = | 
| 26980 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 109 | let val physical_path = perhaps (try OS.FileSys.fullPath) (platform_path path) in | 
| 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 110 | (case try (Time.toString o OS.FileSys.modTime) physical_path of | 
| 26946 
0b6eff8c088d
removed norm_absolute (not thread safe; chdir does not guarantee normalization anyway);
 wenzelm parents: 
26656diff
changeset | 111 | NONE => NONE | 
| 26980 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 112 | | SOME mod_time => SOME (Ident | 
| 26946 
0b6eff8c088d
removed norm_absolute (not thread safe; chdir does not guarantee normalization anyway);
 wenzelm parents: 
26656diff
changeset | 113 | (case getenv "ISABELLE_FILE_IDENT" of | 
| 26980 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 114 | "" => physical_path ^ ": " ^ mod_time | 
| 26946 
0b6eff8c088d
removed norm_absolute (not thread safe; chdir does not guarantee normalization anyway);
 wenzelm parents: 
26656diff
changeset | 115 | | cmd => | 
| 26980 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 116 | (case check_cache (physical_path, mod_time) of | 
| 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 117 | SOME id => id | 
| 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 118 | | NONE => | 
| 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 119 | let val (id, rc) = (*potentially slow*) | 
| 35010 
d6e492cea6e4
renamed system/system_out to bash/bash_output -- to emphasized that this is really GNU bash, not some undefined POSIX sh;
 wenzelm parents: 
33223diff
changeset | 120 |                   bash_output ("\"$ISABELLE_HOME/lib/scripts/fileident\" " ^ shell_quote physical_path)
 | 
| 26980 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 121 | in | 
| 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 122 | if id <> "" andalso rc = 0 then (update_cache (physical_path, (mod_time, id)); id) | 
| 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 123 |                   else error ("Failed to identify file " ^ quote (Path.implode path) ^ " by " ^ cmd)
 | 
| 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 124 | end)))) | 
| 26946 
0b6eff8c088d
removed norm_absolute (not thread safe; chdir does not guarantee normalization anyway);
 wenzelm parents: 
26656diff
changeset | 125 | end; | 
| 16261 | 126 | |
| 26980 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 127 | |
| 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 128 | (* directory entries *) | 
| 
f7f48bb9a025
ident: naive caching prevents potentially slow external invocations;
 wenzelm parents: 
26946diff
changeset | 129 | |
| 23861 
72bb3494746f
replaced info by ident (for full identification, potentially content-based);
 wenzelm parents: 
23826diff
changeset | 130 | val exists = can OS.FileSys.modTime o platform_path; | 
| 16261 | 131 | |
| 21858 
05f57309170c
avoid conflict with Alice keywords: renamed pack -> implode, unpack -> explode, any -> many, avoided assert;
 wenzelm parents: 
19960diff
changeset | 132 | fun check path = | 
| 17826 | 133 | if exists path then () | 
| 21858 
05f57309170c
avoid conflict with Alice keywords: renamed pack -> implode, unpack -> explode, any -> many, avoided assert;
 wenzelm parents: 
19960diff
changeset | 134 |   else error ("No such file or directory: " ^ quote (Path.implode path));
 | 
| 17826 | 135 | |
| 16261 | 136 | val rm = OS.FileSys.remove o platform_path; | 
| 137 | ||
| 37739 | 138 | fun rm_tree path = system_command ("rm -r " ^ shell_path path);
 | 
| 139 | ||
| 16261 | 140 | fun mkdir path = system_command ("mkdir -p " ^ shell_path path);
 | 
| 141 | ||
| 37668 
892f8d00426c
refined semantics of mkdir_leaf: do not fail if directory already exists
 haftmann parents: 
37651diff
changeset | 142 | fun mkdir_leaf path = (check (Path.dir path); mkdir path); | 
| 37651 
62fc16341922
mkdir_leaf -- avoiding surprises with typos in user-given paths
 haftmann parents: 
35010diff
changeset | 143 | |
| 16261 | 144 | fun is_dir path = | 
| 19473 | 145 | the_default false (try OS.FileSys.isDir (platform_path path)); | 
| 16261 | 146 | |
| 147 | ||
| 28028 | 148 | (* open files *) | 
| 6224 | 149 | |
| 16261 | 150 | local | 
| 151 | ||
| 26503 
4dec4460244f
discontinued unused hooks explode_platform_path_fn, platform_path_fn, shell_path_fn;
 wenzelm parents: 
26220diff
changeset | 152 | fun with_file open_file close_file f path = | 
| 
4dec4460244f
discontinued unused hooks explode_platform_path_fn, platform_path_fn, shell_path_fn;
 wenzelm parents: 
26220diff
changeset | 153 | let val file = open_file path | 
| 
4dec4460244f
discontinued unused hooks explode_platform_path_fn, platform_path_fn, shell_path_fn;
 wenzelm parents: 
26220diff
changeset | 154 | in Exn.release (Exn.capture f file before close_file file) end; | 
| 6224 | 155 | |
| 16261 | 156 | in | 
| 6218 | 157 | |
| 26503 
4dec4460244f
discontinued unused hooks explode_platform_path_fn, platform_path_fn, shell_path_fn;
 wenzelm parents: 
26220diff
changeset | 158 | fun open_input f = with_file TextIO.openIn TextIO.closeIn f o platform_path; | 
| 
4dec4460244f
discontinued unused hooks explode_platform_path_fn, platform_path_fn, shell_path_fn;
 wenzelm parents: 
26220diff
changeset | 159 | fun open_output f = with_file TextIO.openOut TextIO.closeOut f o platform_path; | 
| 
4dec4460244f
discontinued unused hooks explode_platform_path_fn, platform_path_fn, shell_path_fn;
 wenzelm parents: 
26220diff
changeset | 160 | fun open_append f = with_file TextIO.openAppend TextIO.closeOut f o platform_path; | 
| 6224 | 161 | |
| 28028 | 162 | end; | 
| 163 | ||
| 164 | ||
| 165 | (* input *) | |
| 166 | ||
| 167 | (*scalable iterator -- avoid size limit of TextIO.inputAll, and overhead of many TextIO.inputLine*) | |
| 168 | fun fold_lines f path a = open_input (fn file => | |
| 169 | let | |
| 28510 
66b95e857bde
fold_lines: more tuning, avoiding extra split_last;
 wenzelm parents: 
28500diff
changeset | 170 | fun read buf x = | 
| 
66b95e857bde
fold_lines: more tuning, avoiding extra split_last;
 wenzelm parents: 
28500diff
changeset | 171 | (case TextIO.input file of | 
| 
66b95e857bde
fold_lines: more tuning, avoiding extra split_last;
 wenzelm parents: 
28500diff
changeset | 172 | "" => (case Buffer.content buf of "" => x | line => f line x) | 
| 
66b95e857bde
fold_lines: more tuning, avoiding extra split_last;
 wenzelm parents: 
28500diff
changeset | 173 | | input => | 
| 
66b95e857bde
fold_lines: more tuning, avoiding extra split_last;
 wenzelm parents: 
28500diff
changeset | 174 | (case String.fields (fn c => c = #"\n") input of | 
| 
66b95e857bde
fold_lines: more tuning, avoiding extra split_last;
 wenzelm parents: 
28500diff
changeset | 175 | [rest] => read (Buffer.add rest buf) x | 
| 
66b95e857bde
fold_lines: more tuning, avoiding extra split_last;
 wenzelm parents: 
28500diff
changeset | 176 | | line :: more => read_lines more (f (Buffer.content (Buffer.add line buf)) x))) | 
| 
66b95e857bde
fold_lines: more tuning, avoiding extra split_last;
 wenzelm parents: 
28500diff
changeset | 177 | and read_lines [rest] x = read (Buffer.add rest Buffer.empty) x | 
| 
66b95e857bde
fold_lines: more tuning, avoiding extra split_last;
 wenzelm parents: 
28500diff
changeset | 178 | | read_lines (line :: more) x = read_lines more (f line x); | 
| 28028 | 179 | in read Buffer.empty a end) path; | 
| 180 | ||
| 26503 
4dec4460244f
discontinued unused hooks explode_platform_path_fn, platform_path_fn, shell_path_fn;
 wenzelm parents: 
26220diff
changeset | 181 | val read = open_input TextIO.inputAll; | 
| 5009 | 182 | |
| 28028 | 183 | |
| 184 | (* output *) | |
| 185 | ||
| 186 | fun output txts file = List.app (fn txt => TextIO.output (file, txt)) txts; | |
| 187 | ||
| 26503 
4dec4460244f
discontinued unused hooks explode_platform_path_fn, platform_path_fn, shell_path_fn;
 wenzelm parents: 
26220diff
changeset | 188 | fun write_list path txts = open_output (output txts) path; | 
| 
4dec4460244f
discontinued unused hooks explode_platform_path_fn, platform_path_fn, shell_path_fn;
 wenzelm parents: 
26220diff
changeset | 189 | fun append_list path txts = open_append (output txts) path; | 
| 16713 | 190 | |
| 191 | fun write path txt = write_list path [txt]; | |
| 192 | fun append path txt = append_list path [txt]; | |
| 6182 | 193 | |
| 28028 | 194 | fun write_buffer path buf = open_output (Buffer.output buf) path; | 
| 195 | ||
| 196 | ||
| 197 | (* copy *) | |
| 5009 | 198 | |
| 16603 | 199 | fun eq paths = | 
| 200 | (case try (pairself (OS.FileSys.fileId o platform_path)) paths of | |
| 26656 | 201 | SOME ids => is_equal (OS.FileSys.compare ids) | 
| 16603 | 202 | | NONE => false); | 
| 203 | ||
| 21962 | 204 | fun copy src dst = | 
| 205 | if eq (src, dst) then () | |
| 206 | else | |
| 207 | let val target = if is_dir dst then Path.append dst (Path.base src) else dst | |
| 208 | in write target (read src) end; | |
| 6318 | 209 | |
| 21962 | 210 | fun copy_dir src dst = | 
| 211 | if eq (src, dst) then () | |
| 212 |   else (system_command ("cp -r -f " ^ shell_path src ^ "/. " ^ shell_path dst); ());
 | |
| 6640 | 213 | |
| 5009 | 214 | end; |