mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-06 21:17:48 +00:00
Enable macros to use certain things from the OS module when the target OS is not supported (#24639)
Essentially this PR removes the `{.error.}` pragmas littered around in
the OS module and submodules which prevents them from being imported if
the target OS is not supported. This made it impossible to use certain
supported features of the OS module in macros from a supported host OS.
Instead of the `{.error.}` pragmas the `oscommon` module now has a
constant `supportedSystem` which is false in the cases where the
`{.error.}` pragmas where generated. All procedures which can't be run
by macros is also not declared when `supportedSystem` is false.
It would be possible to create dummy versions of the omitted functions
with an `{.error.}` pragma that would trigger upon their use, but this
is currently not done.
This properly fixes #19414
This commit is contained in:
committed by
GitHub
parent
67f9bc2f4b
commit
1f9cac1f5c
@@ -263,7 +263,7 @@ proc registerAdditionalOps*(c: PCtx) =
|
||||
wrap2si(readLines, ioop)
|
||||
systemop getCurrentExceptionMsg
|
||||
systemop getCurrentException
|
||||
registerCallback c, "stdlib.osdirs.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
|
||||
registerCallback c, "stdlib.staticos.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
|
||||
setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
|
||||
registerCallback c, "stdlib.staticos.staticDirExists", proc (a: VmArgs) {.nimcall.} =
|
||||
setResult(a, dirExists(getString(a, 0)))
|
||||
|
||||
1157
lib/pure/os.nim
1157
lib/pure/os.nim
File diff suppressed because it is too large
Load Diff
@@ -19,7 +19,7 @@ include system/inclrtl
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/widestrs
|
||||
|
||||
|
||||
when defined(nodejs):
|
||||
from std/private/oscommon import ReadDirEffect
|
||||
|
||||
@@ -33,8 +33,6 @@ elif defined(windows):
|
||||
import std/winlean
|
||||
elif defined(posix):
|
||||
import std/posix
|
||||
else:
|
||||
{.error: "The cmdline module has not been implemented for the target platform.".}
|
||||
|
||||
|
||||
# Needed by windows in order to obtain the command line for targets
|
||||
|
||||
@@ -5,9 +5,13 @@ import std/[oserrors]
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[syncio, assertions, widestrs]
|
||||
|
||||
from std/staticos import PathComponent
|
||||
|
||||
## .. importdoc:: osdirs.nim, os.nim
|
||||
|
||||
const weirdTarget* = defined(nimscript) or defined(js)
|
||||
const
|
||||
weirdTarget* = defined(nimscript) or defined(js)
|
||||
supportedSystem* = weirdTarget or defined(windows) or defined(posix)
|
||||
|
||||
|
||||
type
|
||||
@@ -27,8 +31,6 @@ elif defined(posix):
|
||||
import std/posix
|
||||
proc c_rename(oldname, newname: cstring): cint {.
|
||||
importc: "rename", header: "<stdio.h>".}
|
||||
else:
|
||||
{.error: "OS module not ported to your operating system!".}
|
||||
|
||||
|
||||
when weirdTarget:
|
||||
@@ -65,122 +67,110 @@ when defined(windows) and not weirdTarget:
|
||||
result = f.cFileName[0].int == dot and (f.cFileName[1].int == 0 or
|
||||
f.cFileName[1].int == dot and f.cFileName[2].int == 0)
|
||||
|
||||
when supportedSystem:
|
||||
when defined(posix) and not weirdTarget:
|
||||
proc getSymlinkFileKind*(path: string):
|
||||
tuple[pc: PathComponent, isSpecial: bool] =
|
||||
# Helper function.
|
||||
var s: Stat
|
||||
assert(path != "")
|
||||
result = (pcLinkToFile, false)
|
||||
if stat(path, s) == 0'i32:
|
||||
if S_ISDIR(s.st_mode):
|
||||
result = (pcLinkToDir, false)
|
||||
elif not S_ISREG(s.st_mode):
|
||||
result = (pcLinkToFile, true)
|
||||
|
||||
type
|
||||
PathComponent* = enum ## Enumeration specifying a path component.
|
||||
proc tryMoveFSObject*(source, dest: string, isDir: bool): bool {.noWeirdTarget.} =
|
||||
## Moves a file (or directory if `isDir` is true) from `source` to `dest`.
|
||||
##
|
||||
## Returns false in case of `EXDEV` error or `AccessDeniedError` on Windows (if `isDir` is true).
|
||||
## In case of other errors `OSError` is raised.
|
||||
## Returns true in case of success.
|
||||
when defined(windows):
|
||||
let s = newWideCString(source)
|
||||
let d = newWideCString(dest)
|
||||
result = moveFileExW(s, d, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) != 0'i32
|
||||
else:
|
||||
result = c_rename(source, dest) == 0'i32
|
||||
|
||||
if not result:
|
||||
let err = osLastError()
|
||||
let isAccessDeniedError =
|
||||
when defined(windows):
|
||||
const AccessDeniedError = OSErrorCode(5)
|
||||
isDir and err == AccessDeniedError
|
||||
else:
|
||||
err == EXDEV.OSErrorCode
|
||||
if not isAccessDeniedError:
|
||||
raiseOSError(err, $(source, dest))
|
||||
|
||||
when not defined(windows):
|
||||
const maxSymlinkLen* = 1024
|
||||
|
||||
proc fileExists*(filename: string): bool {.rtl, extern: "nos$1",
|
||||
tags: [ReadDirEffect], noNimJs, sideEffect.} =
|
||||
## Returns true if `filename` exists and is a regular file or symlink.
|
||||
##
|
||||
## Directories, device files, named pipes and sockets return false.
|
||||
##
|
||||
## See also:
|
||||
## * `walkDirRec iterator`_
|
||||
## * `FileInfo object`_
|
||||
pcFile, ## path refers to a file
|
||||
pcLinkToFile, ## path refers to a symbolic link to a file
|
||||
pcDir, ## path refers to a directory
|
||||
pcLinkToDir ## path refers to a symbolic link to a directory
|
||||
## * `dirExists proc`_
|
||||
## * `symlinkExists proc`_
|
||||
when defined(windows):
|
||||
wrapUnary(a, getFileAttributesW, filename)
|
||||
if a != -1'i32:
|
||||
result = (a and FILE_ATTRIBUTE_DIRECTORY) == 0'i32
|
||||
else:
|
||||
var res: Stat
|
||||
return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode)
|
||||
|
||||
|
||||
when defined(posix) and not weirdTarget:
|
||||
proc getSymlinkFileKind*(path: string):
|
||||
tuple[pc: PathComponent, isSpecial: bool] =
|
||||
# Helper function.
|
||||
var s: Stat
|
||||
assert(path != "")
|
||||
result = (pcLinkToFile, false)
|
||||
if stat(path, s) == 0'i32:
|
||||
if S_ISDIR(s.st_mode):
|
||||
result = (pcLinkToDir, false)
|
||||
elif not S_ISREG(s.st_mode):
|
||||
result = (pcLinkToFile, true)
|
||||
|
||||
proc tryMoveFSObject*(source, dest: string, isDir: bool): bool {.noWeirdTarget.} =
|
||||
## Moves a file (or directory if `isDir` is true) from `source` to `dest`.
|
||||
##
|
||||
## Returns false in case of `EXDEV` error or `AccessDeniedError` on Windows (if `isDir` is true).
|
||||
## In case of other errors `OSError` is raised.
|
||||
## Returns true in case of success.
|
||||
when defined(windows):
|
||||
let s = newWideCString(source)
|
||||
let d = newWideCString(dest)
|
||||
result = moveFileExW(s, d, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) != 0'i32
|
||||
else:
|
||||
result = c_rename(source, dest) == 0'i32
|
||||
|
||||
if not result:
|
||||
let err = osLastError()
|
||||
let isAccessDeniedError =
|
||||
when defined(windows):
|
||||
const AccessDeniedError = OSErrorCode(5)
|
||||
isDir and err == AccessDeniedError
|
||||
else:
|
||||
err == EXDEV.OSErrorCode
|
||||
if not isAccessDeniedError:
|
||||
raiseOSError(err, $(source, dest))
|
||||
|
||||
when not defined(windows):
|
||||
const maxSymlinkLen* = 1024
|
||||
|
||||
proc fileExists*(filename: string): bool {.rtl, extern: "nos$1",
|
||||
tags: [ReadDirEffect], noNimJs, sideEffect.} =
|
||||
## Returns true if `filename` exists and is a regular file or symlink.
|
||||
##
|
||||
## Directories, device files, named pipes and sockets return false.
|
||||
##
|
||||
## See also:
|
||||
## * `dirExists proc`_
|
||||
## * `symlinkExists proc`_
|
||||
when defined(windows):
|
||||
wrapUnary(a, getFileAttributesW, filename)
|
||||
if a != -1'i32:
|
||||
result = (a and FILE_ATTRIBUTE_DIRECTORY) == 0'i32
|
||||
else:
|
||||
var res: Stat
|
||||
return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode)
|
||||
proc dirExists*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect],
|
||||
noNimJs, sideEffect.} =
|
||||
## Returns true if the directory `dir` exists. If `dir` is a file, false
|
||||
## is returned. Follows symlinks.
|
||||
##
|
||||
## See also:
|
||||
## * `fileExists proc`_
|
||||
## * `symlinkExists proc`_
|
||||
when defined(windows):
|
||||
wrapUnary(a, getFileAttributesW, dir)
|
||||
if a != -1'i32:
|
||||
result = (a and FILE_ATTRIBUTE_DIRECTORY) != 0'i32
|
||||
else:
|
||||
var res: Stat
|
||||
result = stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode)
|
||||
|
||||
|
||||
proc dirExists*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect],
|
||||
noNimJs, sideEffect.} =
|
||||
## Returns true if the directory `dir` exists. If `dir` is a file, false
|
||||
## is returned. Follows symlinks.
|
||||
##
|
||||
## See also:
|
||||
## * `fileExists proc`_
|
||||
## * `symlinkExists proc`_
|
||||
when defined(windows):
|
||||
wrapUnary(a, getFileAttributesW, dir)
|
||||
if a != -1'i32:
|
||||
result = (a and FILE_ATTRIBUTE_DIRECTORY) != 0'i32
|
||||
else:
|
||||
var res: Stat
|
||||
result = stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode)
|
||||
proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
|
||||
tags: [ReadDirEffect],
|
||||
noWeirdTarget, sideEffect.} =
|
||||
## Returns true if the symlink `link` exists. Will return true
|
||||
## regardless of whether the link points to a directory or file.
|
||||
##
|
||||
## See also:
|
||||
## * `fileExists proc`_
|
||||
## * `dirExists proc`_
|
||||
when defined(windows):
|
||||
wrapUnary(a, getFileAttributesW, link)
|
||||
if a != -1'i32:
|
||||
# xxx see: bug #16784 (bug9); checking `IO_REPARSE_TAG_SYMLINK`
|
||||
# may also be needed.
|
||||
result = (a and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32
|
||||
else:
|
||||
var res: Stat
|
||||
result = lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode)
|
||||
|
||||
when defined(windows) and not weirdTarget:
|
||||
proc openHandle*(path: string, followSymlink=true, writeAccess=false): Handle =
|
||||
var flags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL
|
||||
if not followSymlink:
|
||||
flags = flags or FILE_FLAG_OPEN_REPARSE_POINT
|
||||
let access = if writeAccess: GENERIC_WRITE else: 0'i32
|
||||
|
||||
proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
|
||||
tags: [ReadDirEffect],
|
||||
noWeirdTarget, sideEffect.} =
|
||||
## Returns true if the symlink `link` exists. Will return true
|
||||
## regardless of whether the link points to a directory or file.
|
||||
##
|
||||
## See also:
|
||||
## * `fileExists proc`_
|
||||
## * `dirExists proc`_
|
||||
when defined(windows):
|
||||
wrapUnary(a, getFileAttributesW, link)
|
||||
if a != -1'i32:
|
||||
# xxx see: bug #16784 (bug9); checking `IO_REPARSE_TAG_SYMLINK`
|
||||
# may also be needed.
|
||||
result = (a and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32
|
||||
else:
|
||||
var res: Stat
|
||||
result = lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode)
|
||||
|
||||
when defined(windows) and not weirdTarget:
|
||||
proc openHandle*(path: string, followSymlink=true, writeAccess=false): Handle =
|
||||
var flags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL
|
||||
if not followSymlink:
|
||||
flags = flags or FILE_FLAG_OPEN_REPARSE_POINT
|
||||
let access = if writeAccess: GENERIC_WRITE else: 0'i32
|
||||
|
||||
result = createFileW(
|
||||
newWideCString(path), access,
|
||||
FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
|
||||
nil, OPEN_EXISTING, flags, 0
|
||||
)
|
||||
result = createFileW(
|
||||
newWideCString(path), access,
|
||||
FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
|
||||
nil, OPEN_EXISTING, flags, 0
|
||||
)
|
||||
|
||||
@@ -6,7 +6,9 @@ import std/oserrors
|
||||
|
||||
import ospaths2, osfiles
|
||||
import oscommon
|
||||
export dirExists, PathComponent
|
||||
import std/staticos
|
||||
when supportedSystem:
|
||||
export dirExists, PathComponent
|
||||
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
@@ -20,9 +22,6 @@ elif defined(windows):
|
||||
elif defined(posix):
|
||||
import std/[posix, times]
|
||||
|
||||
else:
|
||||
{.error: "OS module not ported to your operating system!".}
|
||||
|
||||
|
||||
when weirdTarget:
|
||||
{.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".}
|
||||
@@ -152,10 +151,6 @@ iterator walkDirs*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarg
|
||||
assert "lib/pure/concurrency".unixToNativePath in paths
|
||||
walkCommon(pattern, isDir)
|
||||
|
||||
proc staticWalkDir(dir: string; relative: bool): seq[
|
||||
tuple[kind: PathComponent, path: string]] =
|
||||
discard
|
||||
|
||||
iterator walkDir*(dir: string; relative = false, checkDir = false,
|
||||
skipSpecial = false):
|
||||
tuple[kind: PathComponent, path: string] {.tags: [ReadDirEffect].} =
|
||||
@@ -325,7 +320,6 @@ iterator walkDirRec*(dir: string,
|
||||
# continue iteration.
|
||||
# Future work can provide a way to customize this and do error reporting.
|
||||
|
||||
|
||||
proc rawRemoveDir(dir: string) {.noWeirdTarget.} =
|
||||
when defined(windows):
|
||||
wrapUnary(res, removeDirectoryW, dir)
|
||||
|
||||
@@ -21,8 +21,6 @@ elif defined(posix):
|
||||
|
||||
proc toTime(ts: Timespec): times.Time {.inline.} =
|
||||
result = initTime(ts.tv_sec.int64, ts.tv_nsec.int)
|
||||
else:
|
||||
{.error: "OS module not ported to your operating system!".}
|
||||
|
||||
|
||||
when weirdTarget:
|
||||
|
||||
@@ -20,8 +20,6 @@ elif defined(windows):
|
||||
import std/winlean
|
||||
elif defined(posix):
|
||||
import std/posix, system/ansi_c
|
||||
else:
|
||||
{.error: "OS module not ported to your operating system!".}
|
||||
|
||||
when weirdTarget:
|
||||
{.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".}
|
||||
@@ -840,7 +838,7 @@ proc unixToNativePath*(path: string, drive=""): string {.
|
||||
inc(i)
|
||||
|
||||
|
||||
when not defined(nimscript):
|
||||
when not defined(nimscript) and supportedSystem:
|
||||
proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} =
|
||||
## Returns the `current working directory`:idx: i.e. where the built
|
||||
## binary is run.
|
||||
@@ -889,7 +887,7 @@ when not defined(nimscript):
|
||||
else:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc absolutePath*(path: string, root = getCurrentDir()): string =
|
||||
proc absolutePath*(path: string, root = when supportedSystem: getCurrentDir() else: ""): string =
|
||||
## Returns the absolute path of `path`, rooted at `root` (which must be absolute;
|
||||
## default: current directory).
|
||||
## If `path` is absolute, return it, ignoring `root`.
|
||||
@@ -907,7 +905,7 @@ proc absolutePath*(path: string, root = getCurrentDir()): string =
|
||||
joinPath(root, path)
|
||||
|
||||
proc absolutePathInternal(path: string): string =
|
||||
absolutePath(path, getCurrentDir())
|
||||
absolutePath(path)
|
||||
|
||||
|
||||
proc normalizePath*(path: var string) {.rtl, extern: "nos$1", tags: [].} =
|
||||
@@ -984,48 +982,49 @@ proc normalizeExe*(file: var string) {.since: (1, 3, 5).} =
|
||||
if file.len > 0 and DirSep notin file and file != "." and file != "..":
|
||||
file = "./" & file
|
||||
|
||||
proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
|
||||
tags: [ReadDirEffect], noWeirdTarget.} =
|
||||
## Returns true if both pathname arguments refer to the same physical
|
||||
## file or directory.
|
||||
##
|
||||
## Raises `OSError` if any of the files does not
|
||||
## exist or information about it can not be obtained.
|
||||
##
|
||||
## This proc will return true if given two alternative hard-linked or
|
||||
## sym-linked paths to the same file or directory.
|
||||
##
|
||||
## See also:
|
||||
## * `sameFileContent proc`_
|
||||
result = false
|
||||
when defined(windows):
|
||||
var success = true
|
||||
var f1 = openHandle(path1)
|
||||
var f2 = openHandle(path2)
|
||||
when supportedSystem:
|
||||
proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
|
||||
tags: [ReadDirEffect], noWeirdTarget.} =
|
||||
## Returns true if both pathname arguments refer to the same physical
|
||||
## file or directory.
|
||||
##
|
||||
## Raises `OSError` if any of the files does not
|
||||
## exist or information about it can not be obtained.
|
||||
##
|
||||
## This proc will return true if given two alternative hard-linked or
|
||||
## sym-linked paths to the same file or directory.
|
||||
##
|
||||
## See also:
|
||||
## * `sameFileContent proc`_
|
||||
result = false
|
||||
when defined(windows):
|
||||
var success = true
|
||||
var f1 = openHandle(path1)
|
||||
var f2 = openHandle(path2)
|
||||
|
||||
var lastErr: OSErrorCode
|
||||
if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE:
|
||||
var fi1, fi2: BY_HANDLE_FILE_INFORMATION
|
||||
var lastErr: OSErrorCode
|
||||
if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE:
|
||||
var fi1, fi2: BY_HANDLE_FILE_INFORMATION
|
||||
|
||||
if getFileInformationByHandle(f1, addr(fi1)) != 0 and
|
||||
getFileInformationByHandle(f2, addr(fi2)) != 0:
|
||||
result = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber and
|
||||
fi1.nFileIndexHigh == fi2.nFileIndexHigh and
|
||||
fi1.nFileIndexLow == fi2.nFileIndexLow
|
||||
if getFileInformationByHandle(f1, addr(fi1)) != 0 and
|
||||
getFileInformationByHandle(f2, addr(fi2)) != 0:
|
||||
result = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber and
|
||||
fi1.nFileIndexHigh == fi2.nFileIndexHigh and
|
||||
fi1.nFileIndexLow == fi2.nFileIndexLow
|
||||
else:
|
||||
lastErr = osLastError()
|
||||
success = false
|
||||
else:
|
||||
lastErr = osLastError()
|
||||
success = false
|
||||
else:
|
||||
lastErr = osLastError()
|
||||
success = false
|
||||
|
||||
discard closeHandle(f1)
|
||||
discard closeHandle(f2)
|
||||
discard closeHandle(f1)
|
||||
discard closeHandle(f2)
|
||||
|
||||
if not success: raiseOSError(lastErr, $(path1, path2))
|
||||
else:
|
||||
var a, b: Stat
|
||||
if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32:
|
||||
raiseOSError(osLastError(), $(path1, path2))
|
||||
if not success: raiseOSError(lastErr, $(path1, path2))
|
||||
else:
|
||||
result = a.st_dev == b.st_dev and a.st_ino == b.st_ino
|
||||
var a, b: Stat
|
||||
if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32:
|
||||
raiseOSError(osLastError(), $(path1, path2))
|
||||
else:
|
||||
result = a.st_dev == b.st_dev and a.st_ino == b.st_ino
|
||||
|
||||
@@ -2,7 +2,8 @@ include system/inclrtl
|
||||
import std/oserrors
|
||||
|
||||
import oscommon
|
||||
export symlinkExists
|
||||
when supportedSystem:
|
||||
export symlinkExists
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[syncio, assertions, widestrs]
|
||||
@@ -13,8 +14,6 @@ elif defined(windows):
|
||||
import std/[winlean, times]
|
||||
elif defined(posix):
|
||||
import std/posix
|
||||
else:
|
||||
{.error: "OS module not ported to your operating system!".}
|
||||
|
||||
|
||||
when weirdTarget:
|
||||
|
||||
@@ -14,3 +14,25 @@ proc staticDirExists*(dir: string): bool {.compileTime.} =
|
||||
## Returns true if the directory `dir` exists. If `dir` is a file, false
|
||||
## is returned. Follows symlinks.
|
||||
raiseAssert "implemented in the vmops"
|
||||
|
||||
type
|
||||
PathComponent* = enum ## Enumeration specifying a path component.
|
||||
##
|
||||
## See also:
|
||||
## * `walkDirRec iterator`_
|
||||
## * `FileInfo object`_
|
||||
pcFile, ## path refers to a file
|
||||
pcLinkToFile, ## path refers to a symbolic link to a file
|
||||
pcDir, ## path refers to a directory
|
||||
pcLinkToDir ## path refers to a symbolic link to a directory
|
||||
|
||||
proc staticWalkDir*(dir: string; relative = false): seq[
|
||||
tuple[kind: PathComponent, path: string]] {.compileTime.} =
|
||||
## Walks over the directory `dir` and returns a seq with each directory or
|
||||
## file in `dir`. The component type and full path for each item are returned.
|
||||
##
|
||||
## Walking is not recursive.
|
||||
## * If `relative` is true (default: false)
|
||||
## the resulting path is shortened to be relative to ``dir``,
|
||||
## otherwise the full path is returned.
|
||||
raiseAssert "implemented in the vmops"
|
||||
|
||||
Reference in New Issue
Block a user