/*---------------------------------------------------------------------------
  Copyright 2012-2016 Microsoft Corporation.

  This is free software; you can redistribute it and/or modify it under the
  terms of the Apache License, Version 2.0. A copy of the License can be
  found in the file "license.txt" at the root of this distribution.
---------------------------------------------------------------------------*/


/* File system paths.

The path functions are system independent: backward slashes (`'\\'`)
are treated as a forward slash to separate directories. Windows style
root names like ``c:\`` or ``//server`` are also recognised.

A list of paths can be separated with either a colon (``:``) or
semi-colon (``;``).

A `:path` is created using the `path` functions. Use `string` to convert
back to a normalized path string. A path consists of a _root_ name
(``/``, ``c:\``), the directory (``foo/bar``) and finally the _base_ name.
The base name itself consists of the _stem_ and the _extension_. The
extension is always the part that follows the last occurence of a dot (`'.'`)
in the base name.

A `:path` is always normalized. For a sequence of directories, any
empty directory or ``.`` directory is ignored.
A directory followed by ``..`` is also ignored -- this is the [Plan 9](https://9p.io/sys/doc/lexnames.html)
interpretation of paths where ``..`` is considered lexically.
If parent directories should be resolved through symbolic links,
the `realpath` function should be used (which has the `:io` effect though).
*/
module path

import regex

extern include {
  cs file "path-inline.cs"
  js file "path-inline.js"
}

// A `:path` represents a file system path.\
abstract struct pathstd/os/path/path: V(
  rootroot: string : stringstd/core/string: V = "",
  partsparts: list<string>: liststd/core/optional: V -> V<stringstd/core/string: V> = [std/core/Nil: forall<a> list<a>]std/core/Nil: forall<a> list<a> // directory parts in reverse order
)

// Return the base name of a path (stem name + extension)\
// `"/foo/bar.txt".path.basename === "bar.txt"` \
// `"/foo".path.basename === "foo"`
public fun basenamestd/os/path/basename: (p : path) -> string( pp: path : pathstd/os/path/path: V ) : stringstd/core/string: V {
  pp: path.partsstd/os/path/parts: (path : path) -> list<string>.headstd/core/head.2: forall<a> (xs : list<a>, default : a) -> a("")
}

// Return the directory part of a path (including the rootname)
// `"/foo/bar.txt".path.dirname === "/foo"` \
// `"/foo".path.dirname === "/"`
public fun dirnamestd/os/path/dirname: (p : path) -> string( pp: path : pathstd/os/path/path: V ) : stringstd/core/string: V {
  pp: path.rootstd/os/path/root: (path : path) -> string +std/core/(+).3: (string, string) -> string pp: path.partsstd/os/path/parts: (path : path) -> list<string>.tailstd/core/tail.1: forall<a> (xs : list<a>) -> list<a>.reversestd/core/reverse: forall<a> (xs : list<a>) -> list<a>.joinstd/core/join.3: (xs : list<string>, sep : string) -> string("/")
}

// Return the extension of path (without the preceding dot (`'.'`))\
// `"/foo/bar.svg.txt".path.extname === "txt"`
public fun extnamestd/os/path/extname: (p : path) -> string( pp: path : pathstd/os/path/path: V ) : stringstd/core/string: V {
  pp: path.basenamestd/os/path/basename: (p : path) -> string.split-basestd/os/path/split-base: (basename : string) -> (string, string).sndstd/core/snd: forall<a,b> ((a, b)) -> b
}

// Return the stem name of path.\
// `"/foo/bar.svg.txt".path.extname === "foo.svg"`
public fun stemnamestd/os/path/stemname: (p : path) -> string( pp: path : pathstd/os/path/path: V ) : stringstd/core/string: V {
  pp: path.basenamestd/os/path/basename: (p : path) -> string.split-basestd/os/path/split-base: (basename : string) -> (string, string).fststd/core/fst: forall<a,b> ((a, b)) -> a
}

// Return the root name of path.
// `"c:\\foo".path.rootname === "c:/"`\
// `"/foo".path.rootname === "/"`
public fun rootnamestd/os/path/rootname: (p : path) -> string( pp: path : pathstd/os/path/path: V ) : stringstd/core/string: V {
  pp: path.rootstd/os/path/root: (path : path) -> string
}

fun split-basestd/os/path/split-base: (basename : string) -> (string, string)( basenamebasename: string : stringstd/core/string: V ) : (std/core/(,): (V, V) -> Vstringstd/core/string: V,stringstd/core/string: V)std/core/(,): (V, V) -> V {
  match(basenamebasename: string.find-laststd/core/find-last: (s : string, sub : string) -> maybe<sslice>(".")) {
    Juststd/core/Just: forall<a> (value : a) -> maybe<a>(sliceslice: sslice) -> (std/core/(,): forall<a,b> (fst : a, snd : b) -> (a, b)sliceslice: sslice.beforestd/core/before: (slice : sslice) -> sslice.stringstd/core/string.3: (slice : sslice) -> string,sliceslice: sslice.afterstd/core/after: (slice : sslice) -> sslice.stringstd/core/string.3: (slice : sslice) -> string)std/core/(,): forall<a,b> (fst : a, snd : b) -> (a, b)
    Nothingstd/core/Nothing: forall<a> maybe<a>     -> (std/core/(,): forall<a,b> (fst : a, snd : b) -> (a, b)basenamebasename: string,"")std/core/(,): forall<a,b> (fst : a, snd : b) -> (a, b)
  }
}

fun split-partsstd/os/path/split-parts: (parts : list<string>) -> (string, list<string>)( partsparts: list<string> : liststd/core/list: V -> V<stringstd/core/string: V> ) : (std/core/(,): (V, V) -> Vstringstd/core/string: V,liststd/core/list: V -> V<stringstd/core/string: V>)std/core/(,): (V, V) -> V {
  (std/core/(,): forall<a,b> (fst : a, snd : b) -> (a, b)partsparts: list<string>.headstd/core/head.2: forall<a> (xs : list<a>, default : a) -> a(""), partsparts: list<string>.tailstd/core/tail.1: forall<a> (xs : list<a>) -> list<a>)std/core/(,): forall<a,b> (fst : a, snd : b) -> (a, b)
}

// Convert a `:path` to a normalized `:string` path.\
// If this results in an empty string, the current directory path `"."` is returned.
// `"c:/foo/test.txt".path.string -> "c:/foo/test.txt"`\
// `"c:\\foo\\test.txt".path.string -> "c:/foo/test.txt"`\
// `"/foo//./bar/../test.txt".path.string -> "/foo/test.txt"`
public fun stringstd/os/path/string: (p : path) -> string( pp: path : pathstd/os/path/path: V ) : stringstd/core/string: V {
  val ss: string = pp: path.rootstd/os/path/root: (path : path) -> string +std/core/(+).3: (string, string) -> string pp: path.partsstd/os/path/parts: (path : path) -> list<string>.reversestd/core/reverse: forall<a> (xs : list<a>) -> list<a>.joinstd/core/join.3: (xs : list<string>, sep : string) -> string("/")
  if (sstd/core/True: bool.empty?std/core/empty?.1: (s : string) -> bool) then "." else ss: string
}

// Show a path as a string.
public fun showstd/os/path/show: (p : path) -> string( pp: path : pathstd/os/path/path: V ) : stringstd/core/string: V {
  pp: path.stringstd/os/path/string: (p : path) -> string.showstd/core/show.4: (s : string) -> string
}


// Is a path empty?
public fun empty?std/os/path/empty?: (p : path) -> bool( pp: path : pathstd/os/path/path: V ) : boolstd/core/bool: V {
  pp: path.rootstd/os/path/root: (path : path) -> string.empty?std/core/empty?.1: (s : string) -> bool &&std/core/(&&): (bool, bool) -> bool pp: path.partsstd/os/path/parts: (path : path) -> list<string>.nil?std/core/nil?: forall<a> (list : list<a>) -> bool
}

// Is a path relative?
public fun relative?std/os/path/relative?: (p : path) -> bool( pp: path : pathstd/os/path/path: V ) : boolstd/core/bool: V {
  pp: path.rootstd/os/path/root: (path : path) -> string.empty?std/core/empty?.1: (s : string) -> bool
}

// Is a path absolute?
public fun absolute?std/os/path/absolute?: (p : path) -> bool( pp: path : pathstd/os/path/path: V ) : boolstd/core/bool: V {
  !std/core/(!): (bool) -> boolpp: path.relative?std/os/path/relative?: (p : path) -> bool
}

// Create a normalized `:path` from a path string.
public fun pathstd/os/path/path: (s : string) -> path( ss: string : stringstd/core/string: V ) : pathstd/os/path/path: V {
  if (sstd/core/True: bool.empty?std/core/empty?.1: (s : string) -> bool) returnreturn: path Pathstd/os/path/Path: (root : string, parts : list<string>) -> path("",[std/core/Nil: forall<a> list<a>]std/core/Nil: forall<a> list<a>)std/core/(): ()
  match(ss: string.findstd/text/regex/find: (s : string, regex : regex) -> maybe<matched>(rx-rootstd/os/path/rx-root: regex)) {
    Nothingstd/core/Nothing: forall<a> maybe<a>    -> path-partsstd/os/path/path-parts: (root : string, s : string, dirs : ?list<string>) -> path("",ss: string)
    Juststd/core/Just: forall<a> (value : a) -> maybe<a>(caprcapr: matched) -> path-partsstd/os/path/path-parts: (root : string, s : string, dirs : ?list<string>) -> path(caprcapr: matched.groupsstd/text/regex/groups: (matched : matched) -> groups[std/text/regex/[]: (groups : groups, index : int) -> string1] +std/core/(+).3: (string, string) -> string "/",caprcapr: matched.slicestd/text/regex/slice: (matched : matched) -> sslice.afterstd/core/after: (slice : sslice) -> sslice.stringstd/core/string.3: (slice : sslice) -> string)
  }
}
fun path-partsstd/os/path/path-parts: (root : string, s : string, dirs : ?list<string>) -> path( rootroot: string : stringstd/core/string: V, ss: string : stringstd/core/string: V, dirsdirs: ?list<string> : liststd/core/optional: V -> V<stringstd/core/string: V> = [std/core/Nil: forall<a> list<a>]std/core/Nil: forall<a> list<a> ) : pathstd/os/path/path: V {
  val partsparts: list<string> = ss: string.splitstd/text/regex/split: (s : string, r : regex, n : ?int) -> list<string>(rx-partsepstd/os/path/rx-partsep: regex).path-splitstd/os/path/path-split: (parts : list<string>, dirs : list<string>) -> list<string>(dirsdirs: list<string>)
  Pathstd/os/path/Path: (root : string, parts : list<string>) -> path(rootroot: string,partsparts: list<string>)
}
fun path-splitstd/os/path/path-split: (parts : list<string>, dirs : list<string>) -> list<string>( partsparts: list<string> : liststd/core/list: V -> V<stringstd/core/string: V>, dirsdirs: list<string> : liststd/core/list: V -> V<stringstd/core/string: V> ) : liststd/core/list: V -> V<stringstd/core/string: V>  {
  match(partsparts: list<string>) {
    Consstd/core/Cons: forall<a> (head : a, tail : list<a>) -> list<a>(partpart: string,restrest: list<string>) -> path-splitstd/os/path/path-split: (parts : list<string>, dirs : list<string>) -> list<string>(restrest: list<string>, push-dirstd/os/path/push-dir: (dir : string, dirs : list<string>) -> list<string>(partpart: string,dirsdirs: list<string>))
    Nilstd/core/Nil: forall<a> list<a> -> dirsdirs: list<string>
  }
}
fun push-dirstd/os/path/push-dir: (dir : string, dirs : list<string>) -> list<string>( dirdir: string : stringstd/core/string: V, dirsdirs: list<string> : liststd/core/list: V -> V<stringstd/core/string: V> ) : liststd/core/list: V -> V<stringstd/core/string: V> {
  if (dirstd/core/True: bool==std/core/(==).3: (string, string) -> bool"." ||std/core/(||): (bool, bool) -> bool dirdir: string==std/core/(==).3: (string, string) -> bool"")       then dirsdirs: list<string>
  elif (dirstd/core/True: bool==std/core/(==).3: (string, string) -> bool".." &&std/core/(&&): (bool, bool) -> bool dirsdirs: list<string>.cons?std/core/cons?: forall<a> (list : list<a>) -> bool) then dirsdirs: list<string>.tailstd/core/tail.1: forall<a> (xs : list<a>) -> list<a>
  else Consstd/core/Cons: forall<a> (head : a, tail : list<a>) -> list<a>(dirdir: string,dirsdirs: list<string>)
}

val rx-partsepstd/os/path/rx-partsep: regex = regexstd/text/regex/regex: (regex : string, ignorecase : ?bool, multiline : ?bool) -> regex("[/\\\\]")
val rx-rootstd/os/path/rx-root: regex= regexstd/text/regex/regex: (regex : string, ignorecase : ?bool, multiline : ?bool) -> regex("^([a-zA-Z]:|(?:\\\\\\\\|//)[^/\\\\]+)?(?:[/\\\\]|$)")

// Parse a list of paths seperated by colon (`':'`) or semi-colon (`';'`)
//
// Colon separated paths can be ambiguous with Windows style root names (`c:\\`)
// In particular, a single letter path followed by an absolute path, e.g. ``c:/foo:/bar`` is
// parsed as ``c:/foo`` and ``/bar``.
public fun pathsstd/os/path/paths: (s : string) -> list<path>( ss: string : stringstd/core/string: V ) : liststd/core/list: V -> V<pathstd/os/path/path: V> {
  ss: string.splitstd/text/regex/split: (s : string, r : regex, n : ?int) -> list<string>(rx-pathsepstd/os/path/rx-pathsep: regex).paths-splitstd/os/path/paths-split: (ps : list<string>) -> list<path>
}
fun paths-splitstd/os/path/paths-split: (ps : list<string>) -> list<path>( psps: list<string> : liststd/core/list: V -> V<stringstd/core/string: V> ) : liststd/core/list: V -> V<pathstd/os/path/path: V> {
  match(psps: list<string>) {
    Consstd/core/Cons: forall<a> (head : a, tail : list<a>) -> list<a>(rootroot: string,Consstd/core/Cons: forall<a> (head : a, tail : list<a>) -> list<a>(coloncolon: string,Consstd/core/Cons: forall<a> (head : a, tail : list<a>) -> list<a>(partpart: string,restrest: list<string>))) // match on windows 'c:\' parts
      | coloncolon: string==std/core/(==).3: (string, string) -> bool":" &&std/core/(&&): (bool, bool) -> bool rootroot: string.countstd/core/count.1: (s : string) -> int==std/core/(==).1: (int, int) -> bool1 &&std/core/(&&): (bool, bool) -> bool rootroot: string.head-charstd/core/head-char: (s : string) -> maybe<char>.defaultstd/core/default: forall<a> (m : maybe<a>, nothing : a) -> a(' ').alpha?std/core/alpha?: (c : char) -> bool &&std/core/(&&): (bool, bool) -> bool
        partpart: string.notempty?std/core/notempty?.1: (s : string) -> bool &&std/core/(&&): (bool, bool) -> bool "/\\".containsstd/core/contains: (s : string, sub : string) -> bool(partpart: string.headstd/core/head.3: (s : string) -> string)
      -> Consstd/core/Cons: forall<a> (head : a, tail : list<a>) -> list<a>( pathstd/os/path/path: (s : string) -> path(rootroot: string+std/core/(+).3: (string, string) -> string":"+std/core/(+).3: (string, string) -> stringpartpart: string), paths-splitstd/os/path/paths-split: (ps : list<string>) -> list<path>(restrest: list<string>))
    Consstd/core/Cons: forall<a> (head : a, tail : list<a>) -> list<a>(partpart: string,restrest: list<string>)
      | partpart: string==std/core/(==).3: (string, string) -> bool";" ||std/core/(||): (bool, bool) -> bool partpart: string==std/core/(==).3: (string, string) -> bool":" ||std/core/(||): (bool, bool) -> bool partpart: string==std/core/(==).3: (string, string) -> bool""
      -> paths-splitstd/os/path/paths-split: (ps : list<string>) -> list<path>(restrest: list<string>)
    Consstd/core/Cons: forall<a> (head : a, tail : list<a>) -> list<a>(partpart: string,restrest: list<string>) -> Consstd/core/Cons: forall<a> (head : a, tail : list<a>) -> list<a>( pathstd/os/path/path: (s : string) -> path(partpart: string), paths-splitstd/os/path/paths-split: (ps : list<string>) -> list<path>(restrest: list<string>))
    Nilstd/core/Nil: forall<a> list<a>             -> Nilstd/core/Nil: forall<a> list<a>
  }
}
val rx-pathsepstd/os/path/rx-pathsep: regex = regexstd/text/regex/regex: (regex : string, ignorecase : ?bool, multiline : ?bool) -> regex("([;:])")

// Add two paths together. \
// Keeps the root of `p1` and discards the root name of `p2`.\
// `"/a/" + "b/foo.txt"          === "/a/b/foo.txt"`\
// `"/a/foo.txt" + "/b/bar.txt"  === "/a/foo.txt/b/bar.txt"`\
// `"c:/foo" + "d:/bar"          === "c:/foo/bar"`
public fun (+)std/os/path/(+): (p1 : path, p2 : path) -> path(p1p1: path : pathstd/os/path/path: V, p2p2: path: pathstd/os/path/path: V) : pathstd/os/path/path: V {
  Pathstd/os/path/Path: (root : string, parts : list<string>) -> path(p1p1: path.rootstd/os/path/root: (path : path) -> string,p2p2: path.partsstd/os/path/parts: (path : path) -> list<string> +std/core/(+).4: forall<a> (xs : list<a>, ys : list<a>) -> list<a> p1p1: path.partsstd/os/path/parts: (path : path) -> list<string>)
}

// Return the first path if it is not empty, otherwise return the second one.
public fun (||)std/os/path/(||): (p1 : path, p2 : path) -> path(p1p1: path : pathstd/os/path/path: V, p2p2: path: pathstd/os/path/path: V) : pathstd/os/path/path: V {
  if (p1std/core/True: bool.empty?std/os/path/empty?: (p : path) -> bool) then p2p2: path else p1p1: path
}

// Combine multiple paths using `(+)`.
public fun combinestd/os/path/combine: (ps : list<path>) -> path( psps: list<path> : liststd/core/list: V -> V<pathstd/os/path/path: V> ) : pathstd/os/path/path: V {
  match(psps: list<path>) {
    Nilstd/core/Nil: forall<a> list<a> -> Pathstd/os/path/Path: (root : ?string, parts : ?list<string>) -> path()
    Consstd/core/Cons: forall<a> (head : a, tail : list<a>) -> list<a>(pp: path,pppp: list<path>) -> pppp: list<path>.foldlstd/core/foldl: forall<a,b,e> (list<a>, b, (b, a) -> e b) -> e b(pp: path,(+)std/os/path/(+): (p1 : path, p2 : path) -> path)
  }
}

// Remove the directory and root and only keep the base name (file name) portion of the path.\
// `nodir("foo/bar.ext".path) === "bar.ext"`
public fun nodirstd/os/path/nodir: (p : path) -> path( pp: path : pathstd/os/path/path: V ) : pathstd/os/path/path: V {
  pp: path(root="",parts=pp: path.partsstd/os/path/parts: (path : path) -> list<string>.takestd/core/take: forall<a> (xs : list<a>, n : int) -> list<a>(1))
}

// Remove the basename and only keep the root and directory name portion of the path.\
// `nobase("foo/bar.ext".path) == "foo")`
public fun nobasestd/os/path/nobase: (p : path) -> path( pp: path : pathstd/os/path/path: V ) : pathstd/os/path/path: V {
  pp: path( parts = pp: path.partsstd/os/path/parts: (path : path) -> list<string>.tailstd/core/tail.1: forall<a> (xs : list<a>) -> list<a>)
}

// Remove the extension from a path.
public fun noextstd/os/path/noext: (p : path) -> path( pp: path : pathstd/os/path/path: V ) : pathstd/os/path/path: V {
  pp: path.change-extstd/os/path/change-ext: (p : path, extname : string) -> path("")
}

// Change the extension of a path.
public fun change-extstd/os/path/change-ext: (p : path, extname : string) -> path( pp: path : pathstd/os/path/path: V, extnameextname: string : stringstd/core/string: V ) : pathstd/os/path/path: V {
  val (std/core/(,): forall<a,b> (fst : a, snd : b) -> (a, b)basenamebasename: string,dirsdirs: list<string>)std/core/(,): forall<a,b> (fst : a, snd : b) -> (a, b) = pp: path.partsstd/os/path/parts: (path : path) -> list<string>.split-partsstd/os/path/split-parts: (parts : list<string>) -> (string, list<string>)
  val (std/core/(,): forall<a,b> (fst : a, snd : b) -> (a, b)stemnamestemname: string,_)std/core/(,): forall<a,b> (fst : a, snd : b) -> (a, b)    = basenamebasename: string.split-basestd/os/path/split-base: (basename : string) -> (string, string)
  val newextnewext: string = if (extnamestd/core/True: bool.containsstd/text/regex/contains: (s : string, r : regex) -> bool(rx-nodotstd/os/path/rx-nodot: regex)) then extnameextname: string else "." +std/core/(+).3: (string, string) -> string extnameextname: string
  path-partsstd/os/path/path-parts: (root : string, s : string, dirs : ?list<string>) -> path(pp: path.rootstd/os/path/root: (path : path) -> string, stemnamestemname: string+std/core/(+).3: (string, string) -> stringnewextnewext: string, dirsdirs: list<string>)
}
val rx-nodotstd/os/path/rx-nodot: regex = regexstd/text/regex/regex: (regex : string, ignorecase : ?bool, multiline : ?bool) -> regex("^(?:$|[\\./\\\\])")

// If a path has no extension, set it to the provided one.
public fun default-extstd/os/path/default-ext: (p : path, newext : string) -> path( pp: path : pathstd/os/path/path: V, newextnewext: string : stringstd/core/string: V ) : pathstd/os/path/path: V {
  if (pstd/core/True: bool.extnamestd/os/path/extname: (p : path) -> string.empty?std/core/empty?.1: (s : string) -> bool) then pp: path.change-extstd/os/path/change-ext: (p : path, extname : string) -> path(newextnewext: string) else pp: path
}

// Change the stem name of a path
public fun change-stemstd/os/path/change-stem: (p : path, stemname : string) -> path( pp: path : pathstd/os/path/path: V, stemnamestemname: string : stringstd/core/string: V ) : pathstd/os/path/path: V {
  val extext: string = pp: path.extnamestd/os/path/extname: (p : path) -> string
  pp: path.change-basestd/os/path/change-base: (p : path, basename : string) -> path( stemnamestemname: string +std/core/(+).3: (string, string) -> string (if (extstd/core/True: bool.empty?std/core/empty?.1: (s : string) -> bool) then "" else "." +std/core/(+).3: (string, string) -> string extext: string) )
}

// Change the base name of a path
public fun change-basestd/os/path/change-base: (p : path, basename : string) -> path( pp: path : pathstd/os/path/path: V, basenamebasename: string : stringstd/core/string: V ) : pathstd/os/path/path: V {
  val qq: path = pp: path.nobasestd/os/path/nobase: (p : path) -> path
  path-partsstd/os/path/path-parts: (root : string, s : string, dirs : ?list<string>) -> path(qq: path.rootstd/os/path/root: (path : path) -> string, basenamebasename: string, qq: path.partsstd/os/path/parts: (path : path) -> list<string>)
}


// Return a list of all directory components (excluding the root but including the basename).\
// `"/foo/bar/test.txt".path.dirparts === ["foo","bar","test.txt"]`
public fun dirpartsstd/os/path/dirparts: (p : path) -> list<string>(pp: path : pathstd/os/path/path: V) : liststd/core/list: V -> V<stringstd/core/string: V> {
  pp: path.partsstd/os/path/parts: (path : path) -> list<string>.reversestd/core/reverse: forall<a> (xs : list<a>) -> list<a>
}

// Return the last directory component name (or the empty string).\
// `"c:/foo/bar/tst.txt".path.parentname === "bar"
public fun parentnamestd/os/path/parentname: (p : path) -> string( pp: path : pathstd/os/path/path: V ) : stringstd/core/string: V {
  pp: path.partsstd/os/path/parts: (path : path) -> list<string>.tailstd/core/tail.1: forall<a> (xs : list<a>) -> list<a>.headstd/core/head.2: forall<a> (xs : list<a>, default : a) -> a("")
}

// Convert a path to the absolute path on the file system.
// The path is not required to exist on disk. However, if it
// exists any permissions and symbolic links are resolved fully.\
// `".".realpath` (to get the current working directory)\
// `"/foo".realpath` (to resolve the full root, like `"c:/foo"` on windows)
public fun realpathstd/os/path/realpath: (p : path) -> io path( pp: path : pathstd/os/path/path: V ) : iostd/core/io: E pathstd/os/path/path: V {
  realpathstd/os/path/realpath.1: (s : string) -> io path(pp: path.stringstd/os/path/string: (p : path) -> string)
}

// Returns the current working directory.\
// Equal to `".".realpath`.
public fun cwdstd/os/path/cwd: () -> io path() {
  ".".realpathstd/os/path/realpath.1: (s : string) -> io path
}

// Convert a path to the absolute path on the file system.\
// The overload on a plain string is necessary as it allows
// for unnormalized paths with `".."` parts. For example
// `"/foo/symlink/../test.txt"` may resolve to `"/bar/test.txt"` if
// ``symlink`` is a symbolic link to a sub directory of `"/bar"`.
public fun realpathstd/os/path/realpath.1: (s : string) -> io path( ss: string : stringstd/core/string: V ) : iostd/core/io: E pathstd/os/path/path: V {
  xrealpathstd/os/path/xrealpath: (p : string) -> io string(ss: string).pathstd/os/path/path: (s : string) -> path
}

extern xrealpathstd/os/path/xrealpath: (p : string) -> io string( pp: string : stringstd/core/string: V ) : iostd/core/io: E stringstd/core/string: V {
  cs "System.IO.Path.GetFullPath"
  js "_get_realpath"
}


// Return the OS specific directory separator (`"/"` or `"\\"`)
public extern partsepstd/os/path/partsep: () -> ndet string() : ndetstd/core/ndet: X stringstd/core/string: V {
  cs "System.IO.Path.DirectorySeparatorChar.ToString"
  js "_get_partsep"
}

// Return the OS specific path separator (`';'` or `':'`)
public extern pathsepstd/os/path/pathsep: () -> ndet string() : ndetstd/core/ndet: X stringstd/core/string: V {
  cs "System.IO.Path.PathSeparator.ToString"
  js "_get_pathsep"
}

// Return the path to the currently executing application.
public fun app-pathstd/os/path/app-path: () -> io path() : iostd/core/io: E pathstd/os/path/path: V {
  xapp-pathstd/os/path/xapp-path: () -> io string().pathstd/os/path/path: (s : string) -> path
}

extern xapp-pathstd/os/path/xapp-path: () -> io string() : iostd/core/io: E stringstd/core/string: V {
  cs inline "System.Reflection.Assembly.GetEntryAssembly().Location"
  js "_get_apppath"
}

// Return the base directory that contains the currently running application.
// First tries `app-path().nobase`; if that ends in the ``bin`` or ``exe`` directory it
// returns the parent of that directory.
public fun appdirstd/os/path/appdir: () -> io path() : iostd/core/io: E pathstd/os/path/path: V {
  val pp: path = app-pathstd/os/path/app-path: () -> io path().nobasestd/os/path/nobase: (p : path) -> path
  if (pstd/core/True: bool.basenamestd/os/path/basename: (p : path) -> string==std/core/(==).3: (string, string) -> bool"bin" ||std/core/(||): (bool, bool) -> bool pp: path.basenamestd/os/path/basename: (p : path) -> string==std/core/(==).3: (string, string) -> bool"exe") then pp: path.nobasestd/os/path/nobase: (p : path) -> path else pp: path
}


// Return the home directory of the current user.
public fun homedirstd/os/path/homedir: () -> io path() : iostd/core/io: E pathstd/os/path/path: V {
  xhomedirstd/os/path/xhomedir: () -> io string().pathstd/os/path/path: (s : string) -> path
}

extern xhomedirstd/os/path/xhomedir: () -> io string() : iostd/core/io: E stringstd/core/string: V {
  cs "_Path.GetHomeDir"
  js "_get_homedir"
}

// Return the temporary directory for the current user.
public fun tempdirstd/os/path/tempdir: () -> io path() : iostd/core/io: E pathstd/os/path/path: V {
  xtempdirstd/os/path/xtempdir: () -> io string().pathstd/os/path/path: (s : string) -> path
}

extern xtempdirstd/os/path/xtempdir: () -> io string() : iostd/core/io: E stringstd/core/string: V {
  cs "System.IO.Path.GetTempPath"
  js "_get_tempdir"
}