| author | wenzelm | 
| Fri, 18 Oct 2024 14:20:09 +0200 | |
| changeset 81182 | fc5066122e68 | 
| parent 78957 | 932b2a7139e2 | 
| child 82142 | 508a673c87ac | 
| permissions | -rw-r--r-- | 
| 43600 | 1 | /* Title: Pure/General/path.scala | 
| 2 | Author: Makarius | |
| 3 | ||
| 43601 | 4 | Algebra of file-system paths: basic POSIX notation, extended by named | 
| 5 | roots (e.g. //foo) and variables (e.g. $BAR). | |
| 43600 | 6 | */ | 
| 7 | ||
| 8 | package isabelle | |
| 9 | ||
| 10 | ||
| 73897 | 11 | import java.util.{Map => JMap}
 | 
| 48409 | 12 | import java.io.{File => JFile}
 | 
| 73945 | 13 | import java.nio.file.{Path => JPath}
 | 
| 48373 | 14 | |
| 43697 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 15 | import scala.util.matching.Regex | 
| 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 16 | |
| 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 17 | |
| 75393 | 18 | object Path {
 | 
| 43600 | 19 | /* path elements */ | 
| 20 | ||
| 45244 | 21 | sealed abstract class Elem | 
| 60215 | 22 | private case class Root(name: String) extends Elem | 
| 23 | private case class Basic(name: String) extends Elem | |
| 24 | private case class Variable(name: String) extends Elem | |
| 43600 | 25 | private case object Parent extends Elem | 
| 26 | ||
| 27 | private def err_elem(msg: String, s: String): Nothing = | |
| 69547 | 28 | error(msg + " path element " + quote(s)) | 
| 29 | ||
| 69548 
415dc92050a6
more strict check: avoid confusion of Path.basic with Path.current / Path.parent;
 wenzelm parents: 
69547diff
changeset | 30 |   private val illegal_elem = Set("", "~", "~~", ".", "..")
 | 
| 69550 
57ff523d9008
reject further illegal chars according to https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
 wenzelm parents: 
69549diff
changeset | 31 | private val illegal_char = "/\\$:\"'<>|?*" | 
| 43600 | 32 | |
| 33 | private def check_elem(s: String): String = | |
| 69547 | 34 |     if (illegal_elem.contains(s)) err_elem("Illegal", s)
 | 
| 52106 | 35 |     else {
 | 
| 69550 
57ff523d9008
reject further illegal chars according to https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
 wenzelm parents: 
69549diff
changeset | 36 |       for (c <- s) {
 | 
| 
57ff523d9008
reject further illegal chars according to https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
 wenzelm parents: 
69549diff
changeset | 37 | if (c.toInt < 32) | 
| 
57ff523d9008
reject further illegal chars according to https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
 wenzelm parents: 
69549diff
changeset | 38 |           err_elem("Illegal control character " + c.toInt + " in", s)
 | 
| 
57ff523d9008
reject further illegal chars according to https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
 wenzelm parents: 
69549diff
changeset | 39 | if (illegal_char.contains(c)) | 
| 
57ff523d9008
reject further illegal chars according to https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
 wenzelm parents: 
69549diff
changeset | 40 |           err_elem("Illegal character " + quote(c.toString) + " in", s)
 | 
| 69547 | 41 | } | 
| 52106 | 42 | s | 
| 43 | } | |
| 43600 | 44 | |
| 45 | private def root_elem(s: String): Elem = Root(check_elem(s)) | |
| 46 | private def basic_elem(s: String): Elem = Basic(check_elem(s)) | |
| 47 | private def variable_elem(s: String): Elem = Variable(check_elem(s)) | |
| 48 | ||
| 49 | private def apply_elem(y: Elem, xs: List[Elem]): List[Elem] = | |
| 50 |     (y, xs) match {
 | |
| 51 | case (Root(_), _) => List(y) | |
| 52 | case (Parent, Root(_) :: _) => xs | |
| 53 | case (Parent, Basic(_) :: rest) => rest | |
| 54 | case _ => y :: xs | |
| 55 | } | |
| 56 | ||
| 57 | private def norm_elems(elems: List[Elem]): List[Elem] = | |
| 73361 | 58 | elems.foldRight(List.empty[Elem])(apply_elem) | 
| 43600 | 59 | |
| 56844 | 60 | private def implode_elem(elem: Elem, short: Boolean): String = | 
| 43600 | 61 |     elem match {
 | 
| 62 |       case Root("") => ""
 | |
| 63 | case Root(s) => "//" + s | |
| 64 | case Basic(s) => s | |
| 56844 | 65 |       case Variable("USER_HOME") if short => "~"
 | 
| 66 |       case Variable("ISABELLE_HOME") if short => "~~"
 | |
| 43600 | 67 | case Variable(s) => "$" + s | 
| 68 | case Parent => ".." | |
| 69 | } | |
| 70 | ||
| 72962 | 71 | private def squash_elem(elem: Elem): String = | 
| 72 |     elem match {
 | |
| 73 |       case Root("") => "ROOT"
 | |
| 74 | case Root(s) => "SERVER_" + s | |
| 75 | case Basic(s) => s | |
| 76 | case Variable(s) => s | |
| 77 | case Parent => "PARENT" | |
| 78 | } | |
| 79 | ||
| 43600 | 80 | |
| 81 | /* path constructors */ | |
| 82 | ||
| 45244 | 83 | val current: Path = new Path(Nil) | 
| 84 |   val root: Path = new Path(List(Root("")))
 | |
| 85 | def named_root(s: String): Path = new Path(List(root_elem(s))) | |
| 71601 | 86 | def make(elems: List[String]): Path = new Path(elems.reverse.map(basic_elem)) | 
| 45244 | 87 | def basic(s: String): Path = new Path(List(basic_elem(s))) | 
| 88 | def variable(s: String): Path = new Path(List(variable_elem(s))) | |
| 89 | val parent: Path = new Path(List(Parent)) | |
| 43600 | 90 | |
| 73522 | 91 |   val USER_HOME: Path = variable("USER_HOME")
 | 
| 92 |   val ISABELLE_HOME: Path = variable("ISABELLE_HOME")
 | |
| 93 | ||
| 75926 
b8ee1ef948c2
more thorough checks of browser_info file conflicts;
 wenzelm parents: 
75701diff
changeset | 94 |   val index_html: Path = basic("index.html")
 | 
| 
b8ee1ef948c2
more thorough checks of browser_info file conflicts;
 wenzelm parents: 
75701diff
changeset | 95 | |
| 43600 | 96 | |
| 97 | /* explode */ | |
| 98 | ||
| 75393 | 99 |   def explode(str: String): Path = {
 | 
| 52106 | 100 | def explode_elem(s: String): Elem = | 
| 101 |       try {
 | |
| 102 | if (s == "..") Parent | |
| 103 |         else if (s == "~") Variable("USER_HOME")
 | |
| 104 |         else if (s == "~~") Variable("ISABELLE_HOME")
 | |
| 105 |         else if (s.startsWith("$")) variable_elem(s.substring(1))
 | |
| 106 | else basic_elem(s) | |
| 107 | } | |
| 108 |       catch { case ERROR(msg) => cat_error(msg, "The error(s) above occurred in " + quote(str)) }
 | |
| 109 | ||
| 43670 
7f933761764b
prefer space_explode/split_lines as in Isabelle/ML;
 wenzelm parents: 
43669diff
changeset | 110 |     val ss = space_explode('/', str)
 | 
| 43600 | 111 | val r = ss.takeWhile(_.isEmpty).length | 
| 112 | val es = ss.dropWhile(_.isEmpty) | |
| 113 | val (roots, raw_elems) = | |
| 114 | if (r == 0) (Nil, es) | |
| 115 |       else if (r == 1) (List(Root("")), es)
 | |
| 116 |       else if (es.isEmpty) (List(Root("")), Nil)
 | |
| 117 | else (List(root_elem(es.head)), es.tail) | |
| 52106 | 118 | val elems = raw_elems.filterNot(s => s.isEmpty || s == ".").map(explode_elem) | 
| 119 | ||
| 63866 | 120 | new Path(norm_elems(elems reverse_::: roots)) | 
| 43600 | 121 | } | 
| 43669 | 122 | |
| 55879 
ac979f750c1a
clarified path checks: avoid crash of rendering due to spurious errors;
 wenzelm parents: 
55555diff
changeset | 123 | def is_wellformed(str: String): Boolean = | 
| 48484 | 124 |     try { explode(str); true } catch { case ERROR(_) => false }
 | 
| 125 | ||
| 55879 
ac979f750c1a
clarified path checks: avoid crash of rendering due to spurious errors;
 wenzelm parents: 
55555diff
changeset | 126 | def is_valid(str: String): Boolean = | 
| 
ac979f750c1a
clarified path checks: avoid crash of rendering due to spurious errors;
 wenzelm parents: 
55555diff
changeset | 127 |     try { explode(str).expand; true } catch { case ERROR(_) => false }
 | 
| 
ac979f750c1a
clarified path checks: avoid crash of rendering due to spurious errors;
 wenzelm parents: 
55555diff
changeset | 128 | |
| 43669 | 129 | def split(str: String): List[Path] = | 
| 43670 
7f933761764b
prefer space_explode/split_lines as in Isabelle/ML;
 wenzelm parents: 
43669diff
changeset | 130 |     space_explode(':', str).filterNot(_.isEmpty).map(explode)
 | 
| 48457 | 131 | |
| 78938 
7774e1372476
clarified loading of symbols: permissive entries in $ISABELLE_SYMBOLS require explicit "?";
 wenzelm parents: 
77185diff
changeset | 132 | def split_permissive(str: String): List[(Path, Boolean)] = | 
| 
7774e1372476
clarified loading of symbols: permissive entries in $ISABELLE_SYMBOLS require explicit "?";
 wenzelm parents: 
77185diff
changeset | 133 |     space_explode(':', str).flatMap(
 | 
| 
7774e1372476
clarified loading of symbols: permissive entries in $ISABELLE_SYMBOLS require explicit "?";
 wenzelm parents: 
77185diff
changeset | 134 |       {
 | 
| 
7774e1372476
clarified loading of symbols: permissive entries in $ISABELLE_SYMBOLS require explicit "?";
 wenzelm parents: 
77185diff
changeset | 135 | case "" | "?" => None | 
| 
7774e1372476
clarified loading of symbols: permissive entries in $ISABELLE_SYMBOLS require explicit "?";
 wenzelm parents: 
77185diff
changeset | 136 | case s => | 
| 
7774e1372476
clarified loading of symbols: permissive entries in $ISABELLE_SYMBOLS require explicit "?";
 wenzelm parents: 
77185diff
changeset | 137 |           Library.try_unsuffix("?", s) match {
 | 
| 
7774e1372476
clarified loading of symbols: permissive entries in $ISABELLE_SYMBOLS require explicit "?";
 wenzelm parents: 
77185diff
changeset | 138 | case None => Some(explode(s) -> false) | 
| 
7774e1372476
clarified loading of symbols: permissive entries in $ISABELLE_SYMBOLS require explicit "?";
 wenzelm parents: 
77185diff
changeset | 139 | case Some(p) => Some(explode(p) -> true) | 
| 
7774e1372476
clarified loading of symbols: permissive entries in $ISABELLE_SYMBOLS require explicit "?";
 wenzelm parents: 
77185diff
changeset | 140 | } | 
| 
7774e1372476
clarified loading of symbols: permissive entries in $ISABELLE_SYMBOLS require explicit "?";
 wenzelm parents: 
77185diff
changeset | 141 | }) | 
| 
7774e1372476
clarified loading of symbols: permissive entries in $ISABELLE_SYMBOLS require explicit "?";
 wenzelm parents: 
77185diff
changeset | 142 | |
| 
7774e1372476
clarified loading of symbols: permissive entries in $ISABELLE_SYMBOLS require explicit "?";
 wenzelm parents: 
77185diff
changeset | 143 | def split_permissive_files(str: String): List[Path] = | 
| 
7774e1372476
clarified loading of symbols: permissive entries in $ISABELLE_SYMBOLS require explicit "?";
 wenzelm parents: 
77185diff
changeset | 144 |     for {
 | 
| 
7774e1372476
clarified loading of symbols: permissive entries in $ISABELLE_SYMBOLS require explicit "?";
 wenzelm parents: 
77185diff
changeset | 145 | (path, permissive) <- split_permissive(str) | 
| 
7774e1372476
clarified loading of symbols: permissive entries in $ISABELLE_SYMBOLS require explicit "?";
 wenzelm parents: 
77185diff
changeset | 146 | if !permissive || path.is_file | 
| 
7774e1372476
clarified loading of symbols: permissive entries in $ISABELLE_SYMBOLS require explicit "?";
 wenzelm parents: 
77185diff
changeset | 147 | } yield path | 
| 
7774e1372476
clarified loading of symbols: permissive entries in $ISABELLE_SYMBOLS require explicit "?";
 wenzelm parents: 
77185diff
changeset | 148 | |
| 48457 | 149 | |
| 150 | /* encode */ | |
| 151 | ||
| 152 | val encode: XML.Encode.T[Path] = (path => XML.Encode.string(path.implode)) | |
| 69551 
adb52af5ba55
exclude file name components that are special on Windows;
 wenzelm parents: 
69550diff
changeset | 153 | |
| 
adb52af5ba55
exclude file name components that are special on Windows;
 wenzelm parents: 
69550diff
changeset | 154 | |
| 
adb52af5ba55
exclude file name components that are special on Windows;
 wenzelm parents: 
69550diff
changeset | 155 | /* reserved names */ | 
| 
adb52af5ba55
exclude file name components that are special on Windows;
 wenzelm parents: 
69550diff
changeset | 156 | |
| 
adb52af5ba55
exclude file name components that are special on Windows;
 wenzelm parents: 
69550diff
changeset | 157 | private val reserved_windows: Set[String] = | 
| 
adb52af5ba55
exclude file name components that are special on Windows;
 wenzelm parents: 
69550diff
changeset | 158 |     Set("CON", "PRN", "AUX", "NUL",
 | 
| 
adb52af5ba55
exclude file name components that are special on Windows;
 wenzelm parents: 
69550diff
changeset | 159 | "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", | 
| 
adb52af5ba55
exclude file name components that are special on Windows;
 wenzelm parents: 
69550diff
changeset | 160 | "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9") | 
| 
adb52af5ba55
exclude file name components that are special on Windows;
 wenzelm parents: 
69550diff
changeset | 161 | |
| 
adb52af5ba55
exclude file name components that are special on Windows;
 wenzelm parents: 
69550diff
changeset | 162 | def is_reserved(name: String): Boolean = | 
| 
adb52af5ba55
exclude file name components that are special on Windows;
 wenzelm parents: 
69550diff
changeset | 163 | Long_Name.explode(name).exists(a => reserved_windows.contains(Word.uppercase(a))) | 
| 69904 | 164 | |
| 165 | ||
| 166 | /* case-insensitive names */ | |
| 167 | ||
| 75393 | 168 |   def check_case_insensitive(paths: List[Path]): Unit = {
 | 
| 69904 | 169 | val table = | 
| 73359 | 170 |       paths.foldLeft(Multi_Map.empty[String, String]) { case (tab, path) =>
 | 
| 69904 | 171 | val name = path.expand.implode | 
| 172 | tab.insert(Word.lowercase(name), name) | |
| 73359 | 173 | } | 
| 69904 | 174 | val collisions = | 
| 175 |       (for { (_, coll) <- table.iterator_list if coll.length > 1 } yield coll).toList.flatten
 | |
| 176 |     if (collisions.nonEmpty) {
 | |
| 177 |       error(("Collision of file names due case-insensitivity:" :: collisions).mkString("\n  "))
 | |
| 178 | } | |
| 179 | } | |
| 75926 
b8ee1ef948c2
more thorough checks of browser_info file conflicts;
 wenzelm parents: 
75701diff
changeset | 180 | |
| 
b8ee1ef948c2
more thorough checks of browser_info file conflicts;
 wenzelm parents: 
75701diff
changeset | 181 | def eq_case_insensitive(path1: Path, path2: Path): Boolean = | 
| 
b8ee1ef948c2
more thorough checks of browser_info file conflicts;
 wenzelm parents: 
75701diff
changeset | 182 | path1 == path2 || | 
| 
b8ee1ef948c2
more thorough checks of browser_info file conflicts;
 wenzelm parents: 
75701diff
changeset | 183 | Word.lowercase(path1.expand.implode) == Word.lowercase(path2.expand.implode) | 
| 43600 | 184 | } | 
| 185 | ||
| 43669 | 186 | |
| 75393 | 187 | final class Path private( | 
| 188 | protected val elems: List[Path.Elem] // reversed elements | |
| 189 | ) {
 | |
| 72746 | 190 | override def hashCode: Int = elems.hashCode | 
| 191 | override def equals(that: Any): Boolean = | |
| 192 |     that match {
 | |
| 193 | case other: Path => elems == other.elems | |
| 194 | case _ => false | |
| 195 | } | |
| 196 | ||
| 43600 | 197 | def is_current: Boolean = elems.isEmpty | 
| 59319 | 198 | def is_absolute: Boolean = elems.nonEmpty && elems.last.isInstanceOf[Path.Root] | 
| 65559 | 199 |   def is_root: Boolean = elems match { case List(Path.Root(_)) => true case _ => false }
 | 
| 43600 | 200 |   def is_basic: Boolean = elems match { case List(Path.Basic(_)) => true case _ => false }
 | 
| 75107 | 201 | def all_basic: Boolean = elems.forall(_.isInstanceOf[Path.Basic]) | 
| 72572 | 202 | def starts_basic: Boolean = elems.nonEmpty && elems.last.isInstanceOf[Path.Basic] | 
| 43600 | 203 | |
| 73360 | 204 | def +(other: Path): Path = new Path(other.elems.foldRight(elems)(Path.apply_elem)) | 
| 43600 | 205 | |
| 206 | ||
| 43604 | 207 | /* implode */ | 
| 43600 | 208 | |
| 56844 | 209 | private def gen_implode(short: Boolean): String = | 
| 43600 | 210 |     elems match {
 | 
| 211 | case Nil => "." | |
| 212 |       case List(Path.Root("")) => "/"
 | |
| 56844 | 213 |       case _ => elems.map(Path.implode_elem(_, short)).reverse.mkString("/")
 | 
| 43600 | 214 | } | 
| 56844 | 215 | def implode: String = gen_implode(false) | 
| 216 | def implode_short: String = gen_implode(true) | |
| 43600 | 217 | |
| 43652 | 218 | override def toString: String = quote(implode) | 
| 43600 | 219 | |
| 220 | ||
| 221 | /* base element */ | |
| 222 | ||
| 223 | private def split_path: (Path, String) = | |
| 224 |     elems match {
 | |
| 45244 | 225 | case Path.Basic(s) :: xs => (new Path(xs), s) | 
| 43604 | 226 |       case _ => error("Cannot split path into dir/base: " + toString)
 | 
| 43600 | 227 | } | 
| 228 | ||
| 229 | def dir: Path = split_path._1 | |
| 45244 | 230 | def base: Path = new Path(List(Path.Basic(split_path._2))) | 
| 43600 | 231 | |
| 74056 | 232 | def ends_with(a: String): Boolean = | 
| 233 |     elems match {
 | |
| 234 | case Path.Basic(b) :: _ => b.endsWith(a) | |
| 235 | case _ => false | |
| 236 | } | |
| 237 |   def is_java: Boolean = ends_with(".java")
 | |
| 238 |   def is_scala: Boolean = ends_with(".scala")
 | |
| 75119 | 239 |   def is_pdf: Boolean = ends_with(".pdf")
 | 
| 77000 
ffc0774e0efe
clarified file positions: retain original source path;
 wenzelm parents: 
76884diff
changeset | 240 | def is_latex: Boolean = | 
| 
ffc0774e0efe
clarified file positions: retain original source path;
 wenzelm parents: 
76884diff
changeset | 241 |     ends_with(".tex") ||
 | 
| 
ffc0774e0efe
clarified file positions: retain original source path;
 wenzelm parents: 
76884diff
changeset | 242 |     ends_with(".sty") ||
 | 
| 
ffc0774e0efe
clarified file positions: retain original source path;
 wenzelm parents: 
76884diff
changeset | 243 |     ends_with(".cls") ||
 | 
| 
ffc0774e0efe
clarified file positions: retain original source path;
 wenzelm parents: 
76884diff
changeset | 244 |     ends_with(".clo")
 | 
| 74056 | 245 | |
| 43600 | 246 | def ext(e: String): Path = | 
| 247 | if (e == "") this | |
| 248 |     else {
 | |
| 249 | val (prfx, s) = split_path | |
| 43604 | 250 | prfx + Path.basic(s + "." + e) | 
| 43600 | 251 | } | 
| 43604 | 252 | |
| 77035 | 253 |   def bib: Path = ext("bib")
 | 
| 254 |   def blg: Path = ext("blg")
 | |
| 255 |   def db: Path = ext("db")
 | |
| 77034 | 256 |   def gz: Path = ext("gz")
 | 
| 72962 | 257 |   def html: Path = ext("html")
 | 
| 77035 | 258 |   def jar: Path = ext("jar")
 | 
| 77185 | 259 |   def json: Path = ext("json")
 | 
| 73691 
2f9877db82a1
reimplemented Mirabelle as Isabelle/ML presentation hook + Isabelle/Scala tool, but sledgehammer is still inactive;
 wenzelm parents: 
73660diff
changeset | 260 |   def log: Path = ext("log")
 | 
| 75230 
bbbee54b1198
prepare patched version more thoroughly, with explicit patches;
 wenzelm parents: 
75220diff
changeset | 261 |   def orig: Path = ext("orig")
 | 
| 75220 | 262 |   def patch: Path = ext("patch")
 | 
| 77034 | 263 |   def pdf: Path = ext("pdf")
 | 
| 75312 
e641ac92b489
more formal extension_manifest, with shasum for sources;
 wenzelm parents: 
75273diff
changeset | 264 |   def shasum: Path = ext("shasum")
 | 
| 77034 | 265 |   def tar: Path = ext("tar")
 | 
| 266 |   def tex: Path = ext("tex")
 | |
| 267 |   def thy: Path = ext("thy")
 | |
| 268 |   def xml: Path = ext("xml")
 | |
| 269 |   def xz: Path = ext("xz")
 | |
| 76348 | 270 |   def zst: Path = ext("zst")
 | 
| 72574 
d892f6d66402
build documents in Isabelle/Scala, based on generated tex files as session exports;
 wenzelm parents: 
72572diff
changeset | 271 | |
| 75393 | 272 |   def backup: Path = {
 | 
| 53336 | 273 | val (prfx, s) = split_path | 
| 274 | prfx + Path.basic(s + "~") | |
| 275 | } | |
| 276 | ||
| 75393 | 277 |   def backup2: Path = {
 | 
| 58610 | 278 | val (prfx, s) = split_path | 
| 279 | prfx + Path.basic(s + "~~") | |
| 280 | } | |
| 281 | ||
| 75273 | 282 |   def exe: Path = ext("exe")
 | 
| 76017 | 283 | def exe_if(b: Boolean): Path = if (b) exe else this | 
| 284 | def platform_exe: Path = exe_if(Platform.is_windows) | |
| 72464 | 285 | |
| 43697 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 286 |   private val Ext = new Regex("(.*)\\.([^.]*)")
 | 
| 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 287 | |
| 75393 | 288 |   def split_ext: (Path, String) = {
 | 
| 43697 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 289 | val (prefix, base) = split_path | 
| 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 290 |     base match {
 | 
| 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 291 | case Ext(b, e) => (prefix + Path.basic(b), e) | 
| 56556 | 292 | case _ => (prefix + Path.basic(base), "") | 
| 43697 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 293 | } | 
| 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 294 | } | 
| 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 295 | |
| 69367 | 296 | def drop_ext: Path = split_ext._1 | 
| 297 | def get_ext: String = split_ext._2 | |
| 298 | ||
| 72962 | 299 | def squash: Path = new Path(elems.map(elem => Path.Basic(Path.squash_elem(elem)))) | 
| 300 | ||
| 43604 | 301 | |
| 302 | /* expand */ | |
| 303 | ||
| 77079 
395a0701a125
clarified signature: minimal interface for getenv/expand_env, instead of bulky java.util.Map;
 wenzelm parents: 
77035diff
changeset | 304 |   def expand_env(env: Isabelle_System.Settings): Path = {
 | 
| 43604 | 305 | def eval(elem: Path.Elem): List[Path.Elem] = | 
| 306 |       elem match {
 | |
| 43664 | 307 | case Path.Variable(s) => | 
| 64228 
b46969a851a9
expand relatively to given environment, notably remote HOME;
 wenzelm parents: 
63866diff
changeset | 308 | val path = Path.explode(Isabelle_System.getenv_strict(s, env)) | 
| 48658 | 309 | if (path.elems.exists(_.isInstanceOf[Path.Variable])) | 
| 73715 
bf51c23f3f99
clarified signature -- avoid odd warning about scala/bug#6675;
 wenzelm parents: 
73712diff
changeset | 310 |             error("Illegal path variable nesting: " + Properties.Eq(s, path.toString))
 | 
| 48658 | 311 | else path.elems | 
| 43604 | 312 | case x => List(x) | 
| 313 | } | |
| 314 | ||
| 71383 | 315 | new Path(Path.norm_elems(elems.flatMap(eval))) | 
| 43604 | 316 | } | 
| 48373 | 317 | |
| 77079 
395a0701a125
clarified signature: minimal interface for getenv/expand_env, instead of bulky java.util.Map;
 wenzelm parents: 
77035diff
changeset | 318 | def expand: Path = expand_env(Isabelle_System.settings_env()) | 
| 64228 
b46969a851a9
expand relatively to given environment, notably remote HOME;
 wenzelm parents: 
63866diff
changeset | 319 | |
| 69366 | 320 | def file_name: String = expand.base.implode | 
| 321 | ||
| 48373 | 322 | |
| 66232 | 323 | /* platform files */ | 
| 48373 | 324 | |
| 60988 | 325 | def file: JFile = File.platform_file(this) | 
| 78952 | 326 | |
| 48548 | 327 | def is_file: Boolean = file.isFile | 
| 328 | def is_dir: Boolean = file.isDirectory | |
| 65833 | 329 | |
| 78957 | 330 |   def check_file: Path = if (is_file) this else error("No such file: " + this.expand)
 | 
| 331 |   def check_dir: Path = if (is_dir) this else error("No such directory: " + this.expand)
 | |
| 78952 | 332 | |
| 73945 | 333 | def java_path: JPath = file.toPath | 
| 334 | ||
| 66232 | 335 | def absolute_file: JFile = File.absolute(file) | 
| 336 | def canonical_file: JFile = File.canonical(file) | |
| 67181 | 337 | |
| 338 | def absolute: Path = File.path(absolute_file) | |
| 339 | def canonical: Path = File.path(canonical_file) | |
| 43600 | 340 | } |