| author | wenzelm | 
| Thu, 23 Nov 2023 11:40:49 +0100 | |
| changeset 79044 | 8cc1ae43e12e | 
| parent 78956 | 12abaffb0346 | 
| child 79045 | 24d04dd5bf01 | 
| permissions | -rw-r--r-- | 
| 
48411
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
1  | 
/* Title: Pure/General/file.scala  | 
| 
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
2  | 
Author: Makarius  | 
| 
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
3  | 
|
| 64698 | 4  | 
File-system operations.  | 
| 
48411
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
5  | 
*/  | 
| 
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
6  | 
|
| 
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
7  | 
package isabelle  | 
| 
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
8  | 
|
| 
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
9  | 
|
| 
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
10  | 
import java.io.{BufferedWriter, OutputStreamWriter, FileOutputStream, BufferedOutputStream,
 | 
| 51504 | 11  | 
OutputStream, InputStream, FileInputStream, BufferedInputStream, BufferedReader,  | 
12  | 
InputStreamReader, File => JFile, IOException}  | 
|
| 73317 | 13  | 
import java.nio.file.{StandardOpenOption, Path => JPath, Files, SimpleFileVisitor,
 | 
14  | 
FileVisitOption, FileVisitResult}  | 
|
| 
78169
 
5ad1ae8626de
minor performance tuning: avoid external process;
 
wenzelm 
parents: 
78161 
diff
changeset
 | 
15  | 
import java.nio.file.attribute.{BasicFileAttributes, PosixFilePermission}
 | 
| 
79044
 
8cc1ae43e12e
clarified signature: avoid deprecated URL constructors;
 
wenzelm 
parents: 
78956 
diff
changeset
 | 
16  | 
import java.net.{URI, URL, MalformedURLException, URISyntaxException}
 | 
| 
50684
 
12b7e0b4a66e
support File.read_gzip as well, in accordance to File.write_gzip;
 
wenzelm 
parents: 
50203 
diff
changeset
 | 
17  | 
import java.util.zip.{GZIPInputStream, GZIPOutputStream}
 | 
| 
69293
 
72a9860f8602
clarified find_files: follow links by default, e.g. relevant for "~/cronjob/log";
 
wenzelm 
parents: 
66693 
diff
changeset
 | 
18  | 
import java.util.EnumSet  | 
| 
48411
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
19  | 
|
| 76353 | 20  | 
import org.tukaani.xz  | 
21  | 
import com.github.luben.zstd  | 
|
| 76348 | 22  | 
|
| 
48411
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
23  | 
import scala.collection.mutable  | 
| 
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
24  | 
|
| 
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
25  | 
|
| 75393 | 26  | 
object File {
 | 
| 60992 | 27  | 
/* standard path (Cygwin or Posix) */  | 
| 60988 | 28  | 
|
29  | 
def standard_path(path: Path): String = path.expand.implode  | 
|
30  | 
||
| 60992 | 31  | 
def standard_path(platform_path: String): String =  | 
| 73911 | 32  | 
isabelle.setup.Environment.standard_path(platform_path)  | 
| 60992 | 33  | 
|
34  | 
def standard_path(file: JFile): String = standard_path(file.getPath)  | 
|
35  | 
||
36  | 
def standard_url(name: String): String =  | 
|
37  | 
    try {
 | 
|
| 
79044
 
8cc1ae43e12e
clarified signature: avoid deprecated URL constructors;
 
wenzelm 
parents: 
78956 
diff
changeset
 | 
38  | 
val url = new URI(name).toURL  | 
| 
 
8cc1ae43e12e
clarified signature: avoid deprecated URL constructors;
 
wenzelm 
parents: 
78956 
diff
changeset
 | 
39  | 
      if (url.getProtocol == "file" && Url.is_wellformed_file(name)) {
 | 
| 64775 | 40  | 
standard_path(Url.parse_file(name))  | 
| 
79044
 
8cc1ae43e12e
clarified signature: avoid deprecated URL constructors;
 
wenzelm 
parents: 
78956 
diff
changeset
 | 
41  | 
}  | 
| 60992 | 42  | 
else name  | 
43  | 
}  | 
|
| 
79044
 
8cc1ae43e12e
clarified signature: avoid deprecated URL constructors;
 
wenzelm 
parents: 
78956 
diff
changeset
 | 
44  | 
    catch {
 | 
| 
 
8cc1ae43e12e
clarified signature: avoid deprecated URL constructors;
 
wenzelm 
parents: 
78956 
diff
changeset
 | 
45  | 
case _: MalformedURLException | _: URISyntaxException => standard_path(name)  | 
| 
 
8cc1ae43e12e
clarified signature: avoid deprecated URL constructors;
 
wenzelm 
parents: 
78956 
diff
changeset
 | 
46  | 
}  | 
| 60992 | 47  | 
|
48  | 
||
49  | 
/* platform path (Windows or Posix) */  | 
|
50  | 
||
51  | 
def platform_path(standard_path: String): String =  | 
|
| 73911 | 52  | 
isabelle.setup.Environment.platform_path(standard_path)  | 
| 60992 | 53  | 
|
54  | 
def platform_path(path: Path): String = platform_path(standard_path(path))  | 
|
| 60988 | 55  | 
def platform_file(path: Path): JFile = new JFile(platform_path(path))  | 
56  | 
||
| 60992 | 57  | 
|
| 76884 | 58  | 
/* symbolic path representation, e.g. "~~/src/Pure/ROOT.ML" */  | 
59  | 
||
60  | 
  def symbolic_path(path: Path): String = {
 | 
|
| 77218 | 61  | 
    val directories = space_explode(':', Isabelle_System.getenv("ISABELLE_DIRECTORIES")).reverse
 | 
| 76884 | 62  | 
val full_name = standard_path(path)  | 
63  | 
directories.view.flatMap(a =>  | 
|
64  | 
      try {
 | 
|
65  | 
val b = standard_path(Path.explode(a))  | 
|
66  | 
if (full_name == b) Some(a)  | 
|
67  | 
        else {
 | 
|
68  | 
          Library.try_unprefix(b + "/", full_name) match {
 | 
|
69  | 
case Some(name) => Some(a + "/" + name)  | 
|
70  | 
case None => None  | 
|
71  | 
}  | 
|
72  | 
}  | 
|
73  | 
      } catch { case ERROR(_) => None }).headOption.getOrElse(path.implode)
 | 
|
74  | 
}  | 
|
75  | 
||
76  | 
||
| 66232 | 77  | 
/* platform files */  | 
78  | 
||
79  | 
def absolute(file: JFile): JFile = file.toPath.toAbsolutePath.normalize.toFile  | 
|
80  | 
def canonical(file: JFile): JFile = file.getCanonicalFile  | 
|
81  | 
||
82  | 
def path(file: JFile): Path = Path.explode(standard_path(file))  | 
|
| 76546 | 83  | 
def path(java_path: JPath): Path = path(java_path.toFile)  | 
84  | 
||
| 66232 | 85  | 
def pwd(): Path = path(Path.current.absolute_file)  | 
86  | 
||
| 75701 | 87  | 
def uri(file: JFile): URI = file.toURI  | 
88  | 
def uri(path: Path): URI = path.file.toURI  | 
|
89  | 
||
90  | 
def url(file: JFile): URL = uri(file).toURL  | 
|
91  | 
def url(path: Path): URL = url(path.file)  | 
|
92  | 
||
| 66232 | 93  | 
|
| 
75906
 
2167b9e3157a
clarified signature: support for adhoc file types;
 
wenzelm 
parents: 
75825 
diff
changeset
 | 
94  | 
/* adhoc file types */  | 
| 
 
2167b9e3157a
clarified signature: support for adhoc file types;
 
wenzelm 
parents: 
75825 
diff
changeset
 | 
95  | 
|
| 
 
2167b9e3157a
clarified signature: support for adhoc file types;
 
wenzelm 
parents: 
75825 
diff
changeset
 | 
96  | 
  def is_ML(s: String): Boolean = s.endsWith(".ML")
 | 
| 
 
2167b9e3157a
clarified signature: support for adhoc file types;
 
wenzelm 
parents: 
75825 
diff
changeset
 | 
97  | 
  def is_bib(s: String): Boolean = s.endsWith(".bib")
 | 
| 
 
2167b9e3157a
clarified signature: support for adhoc file types;
 
wenzelm 
parents: 
75825 
diff
changeset
 | 
98  | 
  def is_dll(s: String): Boolean = s.endsWith(".dll")
 | 
| 
 
2167b9e3157a
clarified signature: support for adhoc file types;
 
wenzelm 
parents: 
75825 
diff
changeset
 | 
99  | 
  def is_exe(s: String): Boolean = s.endsWith(".exe")
 | 
| 
 
2167b9e3157a
clarified signature: support for adhoc file types;
 
wenzelm 
parents: 
75825 
diff
changeset
 | 
100  | 
  def is_gz(s: String): Boolean = s.endsWith(".gz")
 | 
| 
 
2167b9e3157a
clarified signature: support for adhoc file types;
 
wenzelm 
parents: 
75825 
diff
changeset
 | 
101  | 
  def is_html(s: String): Boolean = s.endsWith(".html")
 | 
| 
 
2167b9e3157a
clarified signature: support for adhoc file types;
 
wenzelm 
parents: 
75825 
diff
changeset
 | 
102  | 
  def is_jar(s: String): Boolean = s.endsWith(".jar")
 | 
| 
 
2167b9e3157a
clarified signature: support for adhoc file types;
 
wenzelm 
parents: 
75825 
diff
changeset
 | 
103  | 
  def is_java(s: String): Boolean = s.endsWith(".java")
 | 
| 
 
2167b9e3157a
clarified signature: support for adhoc file types;
 
wenzelm 
parents: 
75825 
diff
changeset
 | 
104  | 
  def is_node(s: String): Boolean = s.endsWith(".node")
 | 
| 
 
2167b9e3157a
clarified signature: support for adhoc file types;
 
wenzelm 
parents: 
75825 
diff
changeset
 | 
105  | 
  def is_pdf(s: String): Boolean = s.endsWith(".pdf")
 | 
| 
 
2167b9e3157a
clarified signature: support for adhoc file types;
 
wenzelm 
parents: 
75825 
diff
changeset
 | 
106  | 
  def is_png(s: String): Boolean = s.endsWith(".png")
 | 
| 
76540
 
83de6e9ae983
clarified signature: prefer Scala functions instead of shell scripts;
 
wenzelm 
parents: 
76533 
diff
changeset
 | 
107  | 
  def is_tar_bz2(s: String): Boolean = s.endsWith(".tar.bz2")
 | 
| 76533 | 108  | 
  def is_tar_gz(s: String): Boolean = s.endsWith(".tar.gz")
 | 
| 
76540
 
83de6e9ae983
clarified signature: prefer Scala functions instead of shell scripts;
 
wenzelm 
parents: 
76533 
diff
changeset
 | 
109  | 
  def is_tgz(s: String): Boolean = s.endsWith(".tgz")
 | 
| 
75906
 
2167b9e3157a
clarified signature: support for adhoc file types;
 
wenzelm 
parents: 
75825 
diff
changeset
 | 
110  | 
  def is_thy(s: String): Boolean = s.endsWith(".thy")
 | 
| 
 
2167b9e3157a
clarified signature: support for adhoc file types;
 
wenzelm 
parents: 
75825 
diff
changeset
 | 
111  | 
  def is_xz(s: String): Boolean = s.endsWith(".xz")
 | 
| 
 
2167b9e3157a
clarified signature: support for adhoc file types;
 
wenzelm 
parents: 
75825 
diff
changeset
 | 
112  | 
  def is_zip(s: String): Boolean = s.endsWith(".zip")
 | 
| 76348 | 113  | 
  def is_zst(s: String): Boolean = s.endsWith(".zst")
 | 
| 
75906
 
2167b9e3157a
clarified signature: support for adhoc file types;
 
wenzelm 
parents: 
75825 
diff
changeset
 | 
114  | 
|
| 
 
2167b9e3157a
clarified signature: support for adhoc file types;
 
wenzelm 
parents: 
75825 
diff
changeset
 | 
115  | 
  def is_backup(s: String): Boolean = s.endsWith("~") || s.endsWith(".orig")
 | 
| 
 
2167b9e3157a
clarified signature: support for adhoc file types;
 
wenzelm 
parents: 
75825 
diff
changeset
 | 
116  | 
|
| 
 
2167b9e3157a
clarified signature: support for adhoc file types;
 
wenzelm 
parents: 
75825 
diff
changeset
 | 
117  | 
|
| 66693 | 118  | 
/* relative paths */  | 
119  | 
||
| 75393 | 120  | 
  def relative_path(base: Path, other: Path): Option[Path] = {
 | 
| 73945 | 121  | 
val base_path = base.java_path  | 
122  | 
val other_path = other.java_path  | 
|
| 66693 | 123  | 
if (other_path.startsWith(base_path))  | 
124  | 
Some(path(base_path.relativize(other_path).toFile))  | 
|
125  | 
else None  | 
|
126  | 
}  | 
|
127  | 
||
128  | 
||
| 
62545
 
8ebffdaf2ce2
Bash.process always uses a closed script instead of an open argument list, for extra robustness on Windows, where quoting is not well-defined;
 
wenzelm 
parents: 
62544 
diff
changeset
 | 
129  | 
/* bash path */  | 
| 60992 | 130  | 
|
| 64304 | 131  | 
def bash_path(path: Path): String = Bash.string(standard_path(path))  | 
132  | 
def bash_path(file: JFile): String = Bash.string(standard_path(file))  | 
|
| 60988 | 133  | 
|
| 72036 | 134  | 
def bash_platform_path(path: Path): String = Bash.string(platform_path(path))  | 
135  | 
||
| 60988 | 136  | 
|
| 
62829
 
4141c2a8458b
clarified Isabelle tool wrapper: bash, Scala, no perl, no ML;
 
wenzelm 
parents: 
62704 
diff
changeset
 | 
137  | 
/* directory content */  | 
| 
 
4141c2a8458b
clarified Isabelle tool wrapper: bash, Scala, no perl, no ML;
 
wenzelm 
parents: 
62704 
diff
changeset
 | 
138  | 
|
| 75393 | 139  | 
  def read_dir(dir: Path): List[String] = {
 | 
| 
69300
 
8b6ab9989bcd
is_file/is_dir/read_dir: more uniform treatment of errors and boundary cases, notably for symlinks in ssh;
 
wenzelm 
parents: 
69299 
diff
changeset
 | 
140  | 
    if (!dir.is_dir) error("No such directory: " + dir.toString)
 | 
| 
62829
 
4141c2a8458b
clarified Isabelle tool wrapper: bash, Scala, no perl, no ML;
 
wenzelm 
parents: 
62704 
diff
changeset
 | 
141  | 
val files = dir.file.listFiles  | 
| 
 
4141c2a8458b
clarified Isabelle tool wrapper: bash, Scala, no perl, no ML;
 
wenzelm 
parents: 
62704 
diff
changeset
 | 
142  | 
if (files == null) Nil  | 
| 
69427
 
ff2f39a221d4
clarified operations: uniform sorting of results;
 
wenzelm 
parents: 
69405 
diff
changeset
 | 
143  | 
else files.toList.map(_.getName).sorted  | 
| 
62829
 
4141c2a8458b
clarified Isabelle tool wrapper: bash, Scala, no perl, no ML;
 
wenzelm 
parents: 
62704 
diff
changeset
 | 
144  | 
}  | 
| 
48613
 
232652ac346e
clarified directory content operations (similar to ML version);
 
wenzelm 
parents: 
48550 
diff
changeset
 | 
145  | 
|
| 76529 | 146  | 
def get_entry(  | 
147  | 
dir: Path,  | 
|
148  | 
pred: Path => Boolean = _ => true,  | 
|
149  | 
title: String = ""  | 
|
150  | 
): Path =  | 
|
151  | 
    read_dir(dir).filter(name => pred(dir + Path.basic(name))) match {
 | 
|
152  | 
case List(entry) => dir + Path.basic(entry)  | 
|
153  | 
case bad =>  | 
|
154  | 
        error("Bad directory content in " + (if (title.nonEmpty) title else dir.toString) +
 | 
|
155  | 
"\nexpected a single entry, but found" +  | 
|
156  | 
(if (bad.isEmpty) " nothing"  | 
|
157  | 
           else bad.sorted.map(quote).mkString(":\n  ", "\n  ", "")))
 | 
|
| 72442 | 158  | 
}  | 
159  | 
||
| 76529 | 160  | 
def get_file(dir: Path, title: String = ""): Path =  | 
161  | 
get_entry(dir, pred = _.is_file, title = title)  | 
|
162  | 
||
163  | 
def get_dir(dir: Path, title: String = ""): Path =  | 
|
164  | 
get_entry(dir, pred = _.is_dir, title = title)  | 
|
165  | 
||
| 64932 | 166  | 
def find_files(  | 
167  | 
start: JFile,  | 
|
168  | 
pred: JFile => Boolean = _ => true,  | 
|
| 
69293
 
72a9860f8602
clarified find_files: follow links by default, e.g. relevant for "~/cronjob/log";
 
wenzelm 
parents: 
66693 
diff
changeset
 | 
169  | 
include_dirs: Boolean = false,  | 
| 75393 | 170  | 
follow_links: Boolean = false  | 
171  | 
  ): List[JFile] = {
 | 
|
| 
62443
 
133f65ac17e5
just one File.find_files, based on Java 7 Files operations;
 
wenzelm 
parents: 
62294 
diff
changeset
 | 
172  | 
val result = new mutable.ListBuffer[JFile]  | 
| 73340 | 173  | 
def check(file: JFile): Unit = if (pred(file)) result += file  | 
| 
62443
 
133f65ac17e5
just one File.find_files, based on Java 7 Files operations;
 
wenzelm 
parents: 
62294 
diff
changeset
 | 
174  | 
|
| 64932 | 175  | 
if (start.isFile) check(start)  | 
| 
62443
 
133f65ac17e5
just one File.find_files, based on Java 7 Files operations;
 
wenzelm 
parents: 
62294 
diff
changeset
 | 
176  | 
    else if (start.isDirectory) {
 | 
| 
69293
 
72a9860f8602
clarified find_files: follow links by default, e.g. relevant for "~/cronjob/log";
 
wenzelm 
parents: 
66693 
diff
changeset
 | 
177  | 
val options =  | 
| 
 
72a9860f8602
clarified find_files: follow links by default, e.g. relevant for "~/cronjob/log";
 
wenzelm 
parents: 
66693 
diff
changeset
 | 
178  | 
if (follow_links) EnumSet.of(FileVisitOption.FOLLOW_LINKS)  | 
| 
 
72a9860f8602
clarified find_files: follow links by default, e.g. relevant for "~/cronjob/log";
 
wenzelm 
parents: 
66693 
diff
changeset
 | 
179  | 
else EnumSet.noneOf(classOf[FileVisitOption])  | 
| 78243 | 180  | 
Files.walkFileTree(start.toPath, options, Int.MaxValue,  | 
| 
62443
 
133f65ac17e5
just one File.find_files, based on Java 7 Files operations;
 
wenzelm 
parents: 
62294 
diff
changeset
 | 
181  | 
        new SimpleFileVisitor[JPath] {
 | 
| 75393 | 182  | 
override def preVisitDirectory(  | 
183  | 
path: JPath,  | 
|
184  | 
attrs: BasicFileAttributes  | 
|
185  | 
          ): FileVisitResult = {
 | 
|
| 64932 | 186  | 
if (include_dirs) check(path.toFile)  | 
187  | 
FileVisitResult.CONTINUE  | 
|
188  | 
}  | 
|
| 75393 | 189  | 
override def visitFile(  | 
190  | 
path: JPath,  | 
|
191  | 
attrs: BasicFileAttributes  | 
|
192  | 
          ): FileVisitResult = {
 | 
|
| 69301 | 193  | 
val file = path.toFile  | 
194  | 
if (include_dirs || !file.isDirectory) check(file)  | 
|
| 
62443
 
133f65ac17e5
just one File.find_files, based on Java 7 Files operations;
 
wenzelm 
parents: 
62294 
diff
changeset
 | 
195  | 
FileVisitResult.CONTINUE  | 
| 
 
133f65ac17e5
just one File.find_files, based on Java 7 Files operations;
 
wenzelm 
parents: 
62294 
diff
changeset
 | 
196  | 
}  | 
| 
 
133f65ac17e5
just one File.find_files, based on Java 7 Files operations;
 
wenzelm 
parents: 
62294 
diff
changeset
 | 
197  | 
}  | 
| 
 
133f65ac17e5
just one File.find_files, based on Java 7 Files operations;
 
wenzelm 
parents: 
62294 
diff
changeset
 | 
198  | 
)  | 
| 
 
133f65ac17e5
just one File.find_files, based on Java 7 Files operations;
 
wenzelm 
parents: 
62294 
diff
changeset
 | 
199  | 
}  | 
| 
 
133f65ac17e5
just one File.find_files, based on Java 7 Files operations;
 
wenzelm 
parents: 
62294 
diff
changeset
 | 
200  | 
|
| 
 
133f65ac17e5
just one File.find_files, based on Java 7 Files operations;
 
wenzelm 
parents: 
62294 
diff
changeset
 | 
201  | 
result.toList  | 
| 
48613
 
232652ac346e
clarified directory content operations (similar to ML version);
 
wenzelm 
parents: 
48550 
diff
changeset
 | 
202  | 
}  | 
| 
 
232652ac346e
clarified directory content operations (similar to ML version);
 
wenzelm 
parents: 
48550 
diff
changeset
 | 
203  | 
|
| 
 
232652ac346e
clarified directory content operations (similar to ML version);
 
wenzelm 
parents: 
48550 
diff
changeset
 | 
204  | 
|
| 
48411
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
205  | 
/* read */  | 
| 
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
206  | 
|
| 
65589
 
f70c617e9c26
more robust treatment of non-UTF8 text files (cf. 3ed43cfc8b14), notably old log files in ISO-8859-15;
 
wenzelm 
parents: 
64934 
diff
changeset
 | 
207  | 
def read(file: JFile): String = Bytes.read(file).text  | 
| 
48913
 
f686cb016c0c
more direct File.read_bytes -- avoid cumulative copying of StringBuilder;
 
wenzelm 
parents: 
48613 
diff
changeset
 | 
208  | 
def read(path: Path): String = read(path.file)  | 
| 
 
f686cb016c0c
more direct File.read_bytes -- avoid cumulative copying of StringBuilder;
 
wenzelm 
parents: 
48613 
diff
changeset
 | 
209  | 
|
| 
50684
 
12b7e0b4a66e
support File.read_gzip as well, in accordance to File.write_gzip;
 
wenzelm 
parents: 
50203 
diff
changeset
 | 
210  | 
|
| 75393 | 211  | 
  def read_stream(reader: BufferedReader): String = {
 | 
| 
48411
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
212  | 
val output = new StringBuilder(100)  | 
| 
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
213  | 
var c = -1  | 
| 
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
214  | 
    while ({ c = reader.read; c != -1 }) output += c.toChar
 | 
| 73367 | 215  | 
reader.close()  | 
| 
48411
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
216  | 
output.toString  | 
| 
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
217  | 
}  | 
| 
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
218  | 
|
| 
50684
 
12b7e0b4a66e
support File.read_gzip as well, in accordance to File.write_gzip;
 
wenzelm 
parents: 
50203 
diff
changeset
 | 
219  | 
def read_stream(stream: InputStream): String =  | 
| 64000 | 220  | 
read_stream(new BufferedReader(new InputStreamReader(stream, UTF8.charset)))  | 
| 
50684
 
12b7e0b4a66e
support File.read_gzip as well, in accordance to File.write_gzip;
 
wenzelm 
parents: 
50203 
diff
changeset
 | 
221  | 
|
| 
 
12b7e0b4a66e
support File.read_gzip as well, in accordance to File.write_gzip;
 
wenzelm 
parents: 
50203 
diff
changeset
 | 
222  | 
def read_gzip(file: JFile): String =  | 
| 
 
12b7e0b4a66e
support File.read_gzip as well, in accordance to File.write_gzip;
 
wenzelm 
parents: 
50203 
diff
changeset
 | 
223  | 
read_stream(new GZIPInputStream(new BufferedInputStream(new FileInputStream(file))))  | 
| 64000 | 224  | 
def read_gzip(path: Path): String = read_gzip(path.file)  | 
| 51504 | 225  | 
|
| 64002 | 226  | 
def read_xz(file: JFile): String =  | 
| 76353 | 227  | 
read_stream(new xz.XZInputStream(new BufferedInputStream(new FileInputStream(file))))  | 
| 64000 | 228  | 
def read_xz(path: Path): String = read_xz(path.file)  | 
| 
48411
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
229  | 
|
| 76348 | 230  | 
  def read_zstd(file: JFile): String = {
 | 
| 
76349
 
b4daf7577ca0
clarified Zstd.init(): avoid accidential com.github.luben.zstd.util.Native.load() operation;
 
wenzelm 
parents: 
76348 
diff
changeset
 | 
231  | 
Zstd.init()  | 
| 76353 | 232  | 
read_stream(new zstd.ZstdInputStream(new BufferedInputStream(new FileInputStream(file))))  | 
| 76348 | 233  | 
}  | 
234  | 
def read_zstd(path: Path): String = read_zstd(path.file)  | 
|
235  | 
||
| 
48411
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
236  | 
|
| 50845 | 237  | 
/* read lines */  | 
238  | 
||
| 75393 | 239  | 
  def read_line(reader: BufferedReader): Option[String] = {
 | 
| 69487 | 240  | 
val line =  | 
241  | 
      try { reader.readLine}
 | 
|
242  | 
      catch { case _: IOException => null }
 | 
|
| 72698 | 243  | 
Option(line).map(Library.trim_line)  | 
| 69487 | 244  | 
}  | 
245  | 
||
| 75393 | 246  | 
  def read_lines(reader: BufferedReader, progress: String => Unit): List[String] = {
 | 
| 50845 | 247  | 
val result = new mutable.ListBuffer[String]  | 
| 69487 | 248  | 
var line: Option[String] = None  | 
249  | 
    while ({ line = read_line(reader); line.isDefined }) {
 | 
|
250  | 
progress(line.get)  | 
|
251  | 
result += line.get  | 
|
| 50845 | 252  | 
}  | 
| 73367 | 253  | 
reader.close()  | 
| 50845 | 254  | 
result.toList  | 
255  | 
}  | 
|
256  | 
||
257  | 
||
| 
48411
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
258  | 
/* write */  | 
| 
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
259  | 
|
| 71534 | 260  | 
def writer(file: JFile): BufferedWriter =  | 
261  | 
new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), UTF8.charset))  | 
|
262  | 
||
| 73340 | 263  | 
def write_file(  | 
| 75393 | 264  | 
file: JFile,  | 
265  | 
text: String,  | 
|
266  | 
make_stream: OutputStream => OutputStream  | 
|
267  | 
  ): Unit = {
 | 
|
| 51504 | 268  | 
val stream = make_stream(new FileOutputStream(file))  | 
| 
69393
 
ed0824ef337e
static type for Library.using: avoid Java 11 warnings on "Illegal reflective access";
 
wenzelm 
parents: 
69301 
diff
changeset
 | 
269  | 
using(new BufferedWriter(new OutputStreamWriter(stream, UTF8.charset)))(_.append(text))  | 
| 
48411
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
270  | 
}  | 
| 
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
271  | 
|
| 
73574
 
12b3f78dde61
clarified signature: avoid overlap of String vs. Bytes (both are CharSequence);
 
wenzelm 
parents: 
73367 
diff
changeset
 | 
272  | 
def write(file: JFile, text: String): Unit = write_file(file, text, s => s)  | 
| 
 
12b3f78dde61
clarified signature: avoid overlap of String vs. Bytes (both are CharSequence);
 
wenzelm 
parents: 
73367 
diff
changeset
 | 
273  | 
def write(path: Path, text: String): Unit = write(path.file, text)  | 
| 
48411
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
274  | 
|
| 
73574
 
12b3f78dde61
clarified signature: avoid overlap of String vs. Bytes (both are CharSequence);
 
wenzelm 
parents: 
73367 
diff
changeset
 | 
275  | 
def write_gzip(file: JFile, text: String): Unit =  | 
| 51504 | 276  | 
write_file(file, text, (s: OutputStream) => new GZIPOutputStream(new BufferedOutputStream(s)))  | 
| 
73574
 
12b3f78dde61
clarified signature: avoid overlap of String vs. Bytes (both are CharSequence);
 
wenzelm 
parents: 
73367 
diff
changeset
 | 
277  | 
def write_gzip(path: Path, text: String): Unit = write_gzip(path.file, text)  | 
| 
48411
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
278  | 
|
| 
76351
 
2cee31cd92f0
generic support for XZ and Zstd compression in Isabelle/Scala;
 
wenzelm 
parents: 
76349 
diff
changeset
 | 
279  | 
def write_xz(file: JFile, text: String, options: Compress.Options_XZ): Unit =  | 
| 76353 | 280  | 
File.write_file(file, text,  | 
281  | 
s => new xz.XZOutputStream(new BufferedOutputStream(s), options.make))  | 
|
| 
76351
 
2cee31cd92f0
generic support for XZ and Zstd compression in Isabelle/Scala;
 
wenzelm 
parents: 
76349 
diff
changeset
 | 
282  | 
def write_xz(file: JFile, text: String): Unit = write_xz(file, text, Compress.Options_XZ())  | 
| 
 
2cee31cd92f0
generic support for XZ and Zstd compression in Isabelle/Scala;
 
wenzelm 
parents: 
76349 
diff
changeset
 | 
283  | 
def write_xz(path: Path, text: String, options: Compress.Options_XZ): Unit =  | 
| 64002 | 284  | 
write_xz(path.file, text, options)  | 
| 
76351
 
2cee31cd92f0
generic support for XZ and Zstd compression in Isabelle/Scala;
 
wenzelm 
parents: 
76349 
diff
changeset
 | 
285  | 
def write_xz(path: Path, text: String): Unit = write_xz(path, text, Compress.Options_XZ())  | 
| 64000 | 286  | 
|
| 
76351
 
2cee31cd92f0
generic support for XZ and Zstd compression in Isabelle/Scala;
 
wenzelm 
parents: 
76349 
diff
changeset
 | 
287  | 
  def write_zstd(file: JFile, text: String, options: Compress.Options_Zstd): Unit = {
 | 
| 
76349
 
b4daf7577ca0
clarified Zstd.init(): avoid accidential com.github.luben.zstd.util.Native.load() operation;
 
wenzelm 
parents: 
76348 
diff
changeset
 | 
288  | 
Zstd.init()  | 
| 76353 | 289  | 
File.write_file(file, text,  | 
290  | 
s => new zstd.ZstdOutputStream(new BufferedOutputStream(s), options.level))  | 
|
| 76348 | 291  | 
}  | 
| 
76351
 
2cee31cd92f0
generic support for XZ and Zstd compression in Isabelle/Scala;
 
wenzelm 
parents: 
76349 
diff
changeset
 | 
292  | 
def write_zstd(file: JFile, text: String): Unit =  | 
| 
 
2cee31cd92f0
generic support for XZ and Zstd compression in Isabelle/Scala;
 
wenzelm 
parents: 
76349 
diff
changeset
 | 
293  | 
write_zstd(file, text, Compress.Options_Zstd())  | 
| 
 
2cee31cd92f0
generic support for XZ and Zstd compression in Isabelle/Scala;
 
wenzelm 
parents: 
76349 
diff
changeset
 | 
294  | 
def write_zstd(path: Path, text: String, options: Compress.Options_Zstd): Unit =  | 
| 
 
2cee31cd92f0
generic support for XZ and Zstd compression in Isabelle/Scala;
 
wenzelm 
parents: 
76349 
diff
changeset
 | 
295  | 
write_zstd(path.file, text, options)  | 
| 
 
2cee31cd92f0
generic support for XZ and Zstd compression in Isabelle/Scala;
 
wenzelm 
parents: 
76349 
diff
changeset
 | 
296  | 
def write_zstd(path: Path, text: String): Unit =  | 
| 
 
2cee31cd92f0
generic support for XZ and Zstd compression in Isabelle/Scala;
 
wenzelm 
parents: 
76349 
diff
changeset
 | 
297  | 
write_zstd(path, text, Compress.Options_Zstd())  | 
| 76348 | 298  | 
|
| 75393 | 299  | 
  def write_backup(path: Path, text: String): Unit = {
 | 
| 73317 | 300  | 
if (path.is_file) Isabelle_System.move_file(path, path.backup)  | 
| 62444 | 301  | 
write(path, text)  | 
| 53336 | 302  | 
}  | 
303  | 
||
| 75393 | 304  | 
  def write_backup2(path: Path, text: String): Unit = {
 | 
| 73317 | 305  | 
if (path.is_file) Isabelle_System.move_file(path, path.backup2)  | 
| 62444 | 306  | 
write(path, text)  | 
| 58610 | 307  | 
}  | 
308  | 
||
| 
48411
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
309  | 
|
| 62703 | 310  | 
/* append */  | 
311  | 
||
| 
73574
 
12b3f78dde61
clarified signature: avoid overlap of String vs. Bytes (both are CharSequence);
 
wenzelm 
parents: 
73367 
diff
changeset
 | 
312  | 
def append(file: JFile, text: String): Unit =  | 
| 73627 | 313  | 
Files.write(file.toPath, UTF8.bytes(text),  | 
| 62703 | 314  | 
StandardOpenOption.APPEND, StandardOpenOption.CREATE)  | 
315  | 
||
| 
73574
 
12b3f78dde61
clarified signature: avoid overlap of String vs. Bytes (both are CharSequence);
 
wenzelm 
parents: 
73367 
diff
changeset
 | 
316  | 
def append(path: Path, text: String): Unit = append(path.file, text)  | 
| 62703 | 317  | 
|
318  | 
||
| 75206 | 319  | 
/* change */  | 
320  | 
||
| 75393 | 321  | 
def change(  | 
322  | 
path: Path,  | 
|
323  | 
init: Boolean = false,  | 
|
324  | 
strict: Boolean = false  | 
|
325  | 
  )(f: String => String): Unit = {
 | 
|
| 75208 | 326  | 
if (!path.is_file && init) write(path, "")  | 
| 75206 | 327  | 
val x = read(path)  | 
328  | 
val y = f(x)  | 
|
329  | 
if (x != y) write(path, y)  | 
|
| 75213 | 330  | 
    else if (strict) error("Unchanged file: " + path)
 | 
| 75206 | 331  | 
}  | 
332  | 
||
| 75213 | 333  | 
def change_lines(path: Path, init: Boolean = false, strict: Boolean = false)(  | 
334  | 
f: List[String] => List[String]): Unit =  | 
|
335  | 
change(path, init = init, strict = strict)(text => cat_lines(f(split_lines(text))))  | 
|
| 75206 | 336  | 
|
337  | 
||
| 64213 | 338  | 
/* eq */  | 
| 
48411
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
339  | 
|
| 
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
340  | 
def eq(file1: JFile, file2: JFile): Boolean =  | 
| 73318 | 341  | 
    try { Files.isSameFile(file1.toPath, file2.toPath) }
 | 
| 
49673
 
2a088cff1e7b
more robust File.eq, and thus File.copy of "~~/lib/logo/isabelle.gif";
 
wenzelm 
parents: 
49610 
diff
changeset
 | 
342  | 
    catch { case ERROR(_) => false }
 | 
| 
48411
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
343  | 
|
| 64213 | 344  | 
def eq(path1: Path, path2: Path): Boolean = eq(path1.file, path2.file)  | 
345  | 
||
346  | 
||
| 64934 | 347  | 
/* eq_content */  | 
348  | 
||
349  | 
def eq_content(file1: JFile, file2: JFile): Boolean =  | 
|
350  | 
if (eq(file1, file2)) true  | 
|
351  | 
else if (file1.length != file2.length) false  | 
|
352  | 
else Bytes.read(file1) == Bytes.read(file2)  | 
|
353  | 
||
354  | 
def eq_content(path1: Path, path2: Path): Boolean = eq_content(path1.file, path2.file)  | 
|
355  | 
||
356  | 
||
| 
69405
 
22428643351f
more direct File.executable operation: avoid external process (on Unix);
 
wenzelm 
parents: 
69402 
diff
changeset
 | 
357  | 
/* permissions */  | 
| 
 
22428643351f
more direct File.executable operation: avoid external process (on Unix);
 
wenzelm 
parents: 
69402 
diff
changeset
 | 
358  | 
|
| 
78169
 
5ad1ae8626de
minor performance tuning: avoid external process;
 
wenzelm 
parents: 
78161 
diff
changeset
 | 
359  | 
private val restrict_perms: List[PosixFilePermission] =  | 
| 
 
5ad1ae8626de
minor performance tuning: avoid external process;
 
wenzelm 
parents: 
78161 
diff
changeset
 | 
360  | 
List(  | 
| 
 
5ad1ae8626de
minor performance tuning: avoid external process;
 
wenzelm 
parents: 
78161 
diff
changeset
 | 
361  | 
PosixFilePermission.GROUP_READ,  | 
| 
 
5ad1ae8626de
minor performance tuning: avoid external process;
 
wenzelm 
parents: 
78161 
diff
changeset
 | 
362  | 
PosixFilePermission.GROUP_WRITE,  | 
| 
 
5ad1ae8626de
minor performance tuning: avoid external process;
 
wenzelm 
parents: 
78161 
diff
changeset
 | 
363  | 
PosixFilePermission.GROUP_EXECUTE,  | 
| 
 
5ad1ae8626de
minor performance tuning: avoid external process;
 
wenzelm 
parents: 
78161 
diff
changeset
 | 
364  | 
PosixFilePermission.OTHERS_READ,  | 
| 
 
5ad1ae8626de
minor performance tuning: avoid external process;
 
wenzelm 
parents: 
78161 
diff
changeset
 | 
365  | 
PosixFilePermission.OTHERS_WRITE,  | 
| 
 
5ad1ae8626de
minor performance tuning: avoid external process;
 
wenzelm 
parents: 
78161 
diff
changeset
 | 
366  | 
PosixFilePermission.OTHERS_EXECUTE)  | 
| 
 
5ad1ae8626de
minor performance tuning: avoid external process;
 
wenzelm 
parents: 
78161 
diff
changeset
 | 
367  | 
|
| 
 
5ad1ae8626de
minor performance tuning: avoid external process;
 
wenzelm 
parents: 
78161 
diff
changeset
 | 
368  | 
def restrict(path: Path): Unit =  | 
| 
 
5ad1ae8626de
minor performance tuning: avoid external process;
 
wenzelm 
parents: 
78161 
diff
changeset
 | 
369  | 
    if (Platform.is_windows) Isabelle_System.chmod("g-rwx,o-rwx", path)
 | 
| 
 
5ad1ae8626de
minor performance tuning: avoid external process;
 
wenzelm 
parents: 
78161 
diff
changeset
 | 
370  | 
    else {
 | 
| 
 
5ad1ae8626de
minor performance tuning: avoid external process;
 
wenzelm 
parents: 
78161 
diff
changeset
 | 
371  | 
val perms = Files.getPosixFilePermissions(path.java_path)  | 
| 
 
5ad1ae8626de
minor performance tuning: avoid external process;
 
wenzelm 
parents: 
78161 
diff
changeset
 | 
372  | 
var perms_changed = false  | 
| 
 
5ad1ae8626de
minor performance tuning: avoid external process;
 
wenzelm 
parents: 
78161 
diff
changeset
 | 
373  | 
      for (p <- restrict_perms if perms.contains(p)) {
 | 
| 
 
5ad1ae8626de
minor performance tuning: avoid external process;
 
wenzelm 
parents: 
78161 
diff
changeset
 | 
374  | 
perms.remove(p)  | 
| 
 
5ad1ae8626de
minor performance tuning: avoid external process;
 
wenzelm 
parents: 
78161 
diff
changeset
 | 
375  | 
perms_changed = true  | 
| 
 
5ad1ae8626de
minor performance tuning: avoid external process;
 
wenzelm 
parents: 
78161 
diff
changeset
 | 
376  | 
}  | 
| 
 
5ad1ae8626de
minor performance tuning: avoid external process;
 
wenzelm 
parents: 
78161 
diff
changeset
 | 
377  | 
if (perms_changed) Files.setPosixFilePermissions(path.java_path, perms)  | 
| 
 
5ad1ae8626de
minor performance tuning: avoid external process;
 
wenzelm 
parents: 
78161 
diff
changeset
 | 
378  | 
}  | 
| 78161 | 379  | 
|
| 75393 | 380  | 
  def is_executable(path: Path): Boolean = {
 | 
| 69788 | 381  | 
    if (Platform.is_windows) Isabelle_System.bash("test -x " + bash_path(path)).check.ok
 | 
382  | 
else path.file.canExecute  | 
|
383  | 
}  | 
|
384  | 
||
| 
78298
 
3b0f8f1010f2
clarified signature, with subtle change of semantics (amending 8b5a2e4b16d4);
 
wenzelm 
parents: 
78243 
diff
changeset
 | 
385  | 
  def set_executable(path: Path, reset: Boolean = false): Unit = {
 | 
| 
 
3b0f8f1010f2
clarified signature, with subtle change of semantics (amending 8b5a2e4b16d4);
 
wenzelm 
parents: 
78243 
diff
changeset
 | 
386  | 
if (Platform.is_windows) Isabelle_System.chmod(if (reset) "a-x" else "a+x", path)  | 
| 
 
3b0f8f1010f2
clarified signature, with subtle change of semantics (amending 8b5a2e4b16d4);
 
wenzelm 
parents: 
78243 
diff
changeset
 | 
387  | 
else path.file.setExecutable(!reset, false)  | 
| 
69405
 
22428643351f
more direct File.executable operation: avoid external process (on Unix);
 
wenzelm 
parents: 
69402 
diff
changeset
 | 
388  | 
}  | 
| 74811 | 389  | 
|
390  | 
||
391  | 
/* content */  | 
|
392  | 
||
| 75825 | 393  | 
def content(path: Path, content: Bytes): Content = new Content(path, content)  | 
394  | 
def content(path: Path, content: String): Content = new Content(path, Bytes(content))  | 
|
| 75824 | 395  | 
def content(path: Path, content: XML.Body): Content_XML = new Content_XML(path, content)  | 
| 74811 | 396  | 
|
| 75825 | 397  | 
  final class Content private[File](val path: Path, val content: Bytes) {
 | 
| 75677 | 398  | 
override def toString: String = path.toString  | 
| 74811 | 399  | 
|
| 
77852
 
df35b5b7b6a4
more direct hg_sync init via ssh (see also 721b3278c8e4);
 
wenzelm 
parents: 
77218 
diff
changeset
 | 
400  | 
    def write(dir: Path, ssh: SSH.System = SSH.Local): Unit = {
 | 
| 75508 | 401  | 
val full_path = dir + path  | 
| 
77852
 
df35b5b7b6a4
more direct hg_sync init via ssh (see also 721b3278c8e4);
 
wenzelm 
parents: 
77218 
diff
changeset
 | 
402  | 
ssh.make_directory(ssh.expand_path(full_path).dir)  | 
| 
 
df35b5b7b6a4
more direct hg_sync init via ssh (see also 721b3278c8e4);
 
wenzelm 
parents: 
77218 
diff
changeset
 | 
403  | 
ssh.write_bytes(full_path, content)  | 
| 75508 | 404  | 
}  | 
| 74811 | 405  | 
}  | 
406  | 
||
| 75676 | 407  | 
  final class Content_XML private[File](val path: Path, val content: XML.Body) {
 | 
| 
75823
 
6eb8d6cdb686
proper toString for Content_XML, which is not covered by trait Content;
 
wenzelm 
parents: 
75701 
diff
changeset
 | 
408  | 
override def toString: String = path.toString  | 
| 
 
6eb8d6cdb686
proper toString for Content_XML, which is not covered by trait Content;
 
wenzelm 
parents: 
75701 
diff
changeset
 | 
409  | 
|
| 75825 | 410  | 
def output(out: XML.Body => String): Content = new Content(path, Bytes(out(content)))  | 
| 74811 | 411  | 
}  | 
| 
77109
 
e3a2b3536030
prefer typed bytes count, but retain toString of original Long for robustness of Java/Scala string composition;
 
wenzelm 
parents: 
76884 
diff
changeset
 | 
412  | 
|
| 
 
e3a2b3536030
prefer typed bytes count, but retain toString of original Long for robustness of Java/Scala string composition;
 
wenzelm 
parents: 
76884 
diff
changeset
 | 
413  | 
|
| 78956 | 414  | 
/* strict file size */  | 
| 
77109
 
e3a2b3536030
prefer typed bytes count, but retain toString of original Long for robustness of Java/Scala string composition;
 
wenzelm 
parents: 
76884 
diff
changeset
 | 
415  | 
|
| 78956 | 416  | 
def size(path: Path): Long = path.check_file.file.length  | 
417  | 
def space(path: Path): Space = Space.bytes(size(path))  | 
|
| 
48411
 
5b3440850d36
more abstract file system operations in Scala, corresponding to ML version;
 
wenzelm 
parents:  
diff
changeset
 | 
418  | 
}  |