refactor os.nim and ospaths.nim
This commit is contained in:
parent
8ce4273864
commit
50666a1f8b
4 changed files with 852 additions and 847 deletions
159
lib/pure/includes/osenv.nim
Normal file
159
lib/pure/includes/osenv.nim
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
## Include file that implements 'getEnv' and friends. Do not import it!
|
||||||
|
|
||||||
|
when not declared(ospaths):
|
||||||
|
{.error: "This is an include file for ospaths.nim!".}
|
||||||
|
|
||||||
|
proc c_getenv(env: cstring): cstring {.
|
||||||
|
importc: "getenv", header: "<stdlib.h>".}
|
||||||
|
proc c_putenv(env: cstring): cint {.
|
||||||
|
importc: "putenv", header: "<stdlib.h>".}
|
||||||
|
|
||||||
|
# Environment handling cannot be put into RTL, because the ``envPairs``
|
||||||
|
# iterator depends on ``environment``.
|
||||||
|
|
||||||
|
var
|
||||||
|
envComputed {.threadvar.}: bool
|
||||||
|
environment {.threadvar.}: seq[string]
|
||||||
|
|
||||||
|
when defined(windows):
|
||||||
|
# because we support Windows GUI applications, things get really
|
||||||
|
# messy here...
|
||||||
|
when useWinUnicode:
|
||||||
|
when defined(cpp):
|
||||||
|
proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.
|
||||||
|
importcpp: "(NI16*)wcschr((const wchar_t *)#, #)", header: "<string.h>".}
|
||||||
|
else:
|
||||||
|
proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.
|
||||||
|
importc: "wcschr", header: "<string.h>".}
|
||||||
|
else:
|
||||||
|
proc strEnd(cstr: cstring, c = 0'i32): cstring {.
|
||||||
|
importc: "strchr", header: "<string.h>".}
|
||||||
|
|
||||||
|
proc getEnvVarsC() =
|
||||||
|
if not envComputed:
|
||||||
|
environment = @[]
|
||||||
|
when useWinUnicode:
|
||||||
|
var
|
||||||
|
env = getEnvironmentStringsW()
|
||||||
|
e = env
|
||||||
|
if e == nil: return # an error occurred
|
||||||
|
while true:
|
||||||
|
var eend = strEnd(e)
|
||||||
|
add(environment, $e)
|
||||||
|
e = cast[WideCString](cast[ByteAddress](eend)+2)
|
||||||
|
if eend[1].int == 0: break
|
||||||
|
discard freeEnvironmentStringsW(env)
|
||||||
|
else:
|
||||||
|
var
|
||||||
|
env = getEnvironmentStringsA()
|
||||||
|
e = env
|
||||||
|
if e == nil: return # an error occurred
|
||||||
|
while true:
|
||||||
|
var eend = strEnd(e)
|
||||||
|
add(environment, $e)
|
||||||
|
e = cast[cstring](cast[ByteAddress](eend)+1)
|
||||||
|
if eend[1] == '\0': break
|
||||||
|
discard freeEnvironmentStringsA(env)
|
||||||
|
envComputed = true
|
||||||
|
|
||||||
|
else:
|
||||||
|
const
|
||||||
|
useNSGetEnviron = defined(macosx) and not defined(ios)
|
||||||
|
|
||||||
|
when useNSGetEnviron:
|
||||||
|
# From the manual:
|
||||||
|
# Shared libraries and bundles don't have direct access to environ,
|
||||||
|
# which is only available to the loader ld(1) when a complete program
|
||||||
|
# is being linked.
|
||||||
|
# The environment routines can still be used, but if direct access to
|
||||||
|
# environ is needed, the _NSGetEnviron() routine, defined in
|
||||||
|
# <crt_externs.h>, can be used to retrieve the address of environ
|
||||||
|
# at runtime.
|
||||||
|
proc NSGetEnviron(): ptr cstringArray {.
|
||||||
|
importc: "_NSGetEnviron", header: "<crt_externs.h>".}
|
||||||
|
else:
|
||||||
|
var gEnv {.importc: "environ".}: cstringArray
|
||||||
|
|
||||||
|
proc getEnvVarsC() =
|
||||||
|
# retrieves the variables of char** env of C's main proc
|
||||||
|
if not envComputed:
|
||||||
|
environment = @[]
|
||||||
|
when useNSGetEnviron:
|
||||||
|
var gEnv = NSGetEnviron()[]
|
||||||
|
var i = 0
|
||||||
|
while true:
|
||||||
|
if gEnv[i] == nil: break
|
||||||
|
add environment, $gEnv[i]
|
||||||
|
inc(i)
|
||||||
|
envComputed = true
|
||||||
|
|
||||||
|
proc findEnvVar(key: string): int =
|
||||||
|
getEnvVarsC()
|
||||||
|
var temp = key & '='
|
||||||
|
for i in 0..high(environment):
|
||||||
|
if startsWith(environment[i], temp): return i
|
||||||
|
return -1
|
||||||
|
|
||||||
|
proc getEnv*(key: string): TaintedString {.tags: [ReadEnvEffect].} =
|
||||||
|
## Returns the value of the `environment variable`:idx: named `key`.
|
||||||
|
##
|
||||||
|
## If the variable does not exist, "" is returned. To distinguish
|
||||||
|
## whether a variable exists or it's value is just "", call
|
||||||
|
## `existsEnv(key)`.
|
||||||
|
when nimvm:
|
||||||
|
discard "built into the compiler"
|
||||||
|
else:
|
||||||
|
var i = findEnvVar(key)
|
||||||
|
if i >= 0:
|
||||||
|
return TaintedString(substr(environment[i], find(environment[i], '=')+1))
|
||||||
|
else:
|
||||||
|
var env = c_getenv(key)
|
||||||
|
if env == nil: return TaintedString("")
|
||||||
|
result = TaintedString($env)
|
||||||
|
|
||||||
|
proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
|
||||||
|
## Checks whether the environment variable named `key` exists.
|
||||||
|
## Returns true if it exists, false otherwise.
|
||||||
|
when nimvm:
|
||||||
|
discard "built into the compiler"
|
||||||
|
else:
|
||||||
|
if c_getenv(key) != nil: return true
|
||||||
|
else: return findEnvVar(key) >= 0
|
||||||
|
|
||||||
|
proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
|
||||||
|
## Sets the value of the `environment variable`:idx: named `key` to `val`.
|
||||||
|
## If an error occurs, `EInvalidEnvVar` is raised.
|
||||||
|
|
||||||
|
# Note: by storing the string in the environment sequence,
|
||||||
|
# we guarantee that we don't free the memory before the program
|
||||||
|
# ends (this is needed for POSIX compliance). It is also needed so that
|
||||||
|
# the process itself may access its modified environment variables!
|
||||||
|
when nimvm:
|
||||||
|
discard "built into the compiler"
|
||||||
|
else:
|
||||||
|
var indx = findEnvVar(key)
|
||||||
|
if indx >= 0:
|
||||||
|
environment[indx] = key & '=' & val
|
||||||
|
else:
|
||||||
|
add environment, (key & '=' & val)
|
||||||
|
indx = high(environment)
|
||||||
|
when defined(windows):
|
||||||
|
when useWinUnicode:
|
||||||
|
var k = newWideCString(key)
|
||||||
|
var v = newWideCString(val)
|
||||||
|
if setEnvironmentVariableW(k, v) == 0'i32: raiseOSError(osLastError())
|
||||||
|
else:
|
||||||
|
if setEnvironmentVariableA(key, val) == 0'i32: raiseOSError(osLastError())
|
||||||
|
else:
|
||||||
|
if c_putenv(environment[indx]) != 0'i32:
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
|
iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} =
|
||||||
|
## Iterate over all `environments variables`:idx:. In the first component
|
||||||
|
## of the tuple is the name of the current variable stored, in the second
|
||||||
|
## its value.
|
||||||
|
getEnvVarsC()
|
||||||
|
for i in 0..high(environment):
|
||||||
|
var p = find(environment[i], '=')
|
||||||
|
yield (TaintedString(substr(environment[i], 0, p-1)),
|
||||||
|
TaintedString(substr(environment[i], p+1)))
|
||||||
132
lib/pure/includes/oserr.nim
Normal file
132
lib/pure/includes/oserr.nim
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
## Include file that implements 'osErrorMsg' and friends. Do not import it!
|
||||||
|
|
||||||
|
when not declared(ospaths):
|
||||||
|
{.error: "This is an include file for ospaths.nim!".}
|
||||||
|
|
||||||
|
when not defined(nimscript):
|
||||||
|
var errno {.importc, header: "<errno.h>".}: cint
|
||||||
|
|
||||||
|
proc c_strerror(errnum: cint): cstring {.
|
||||||
|
importc: "strerror", header: "<string.h>".}
|
||||||
|
|
||||||
|
proc osErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} =
|
||||||
|
## Retrieves the operating system's error flag, ``errno``.
|
||||||
|
## On Windows ``GetLastError`` is checked before ``errno``.
|
||||||
|
## Returns "" if no error occurred.
|
||||||
|
##
|
||||||
|
## **Deprecated since version 0.9.4**: use the other ``osErrorMsg`` proc.
|
||||||
|
|
||||||
|
result = ""
|
||||||
|
when defined(Windows) and not defined(nimscript):
|
||||||
|
var err = getLastError()
|
||||||
|
if err != 0'i32:
|
||||||
|
when useWinUnicode:
|
||||||
|
var msgbuf: WideCString
|
||||||
|
if formatMessageW(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF,
|
||||||
|
nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
|
||||||
|
result = $msgbuf
|
||||||
|
if msgbuf != nil: localFree(cast[pointer](msgbuf))
|
||||||
|
else:
|
||||||
|
var msgbuf: cstring
|
||||||
|
if formatMessageA(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF,
|
||||||
|
nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
|
||||||
|
result = $msgbuf
|
||||||
|
if msgbuf != nil: localFree(msgbuf)
|
||||||
|
when not defined(nimscript):
|
||||||
|
if errno != 0'i32:
|
||||||
|
result = $c_strerror(errno)
|
||||||
|
|
||||||
|
{.push warning[deprecated]: off.}
|
||||||
|
proc raiseOSError*(msg: string = "") {.noinline, rtl, extern: "nos$1",
|
||||||
|
deprecated.} =
|
||||||
|
## raises an OSError exception with the given message ``msg``.
|
||||||
|
## If ``msg == ""``, the operating system's error flag
|
||||||
|
## (``errno``) is converted to a readable error message. On Windows
|
||||||
|
## ``GetLastError`` is checked before ``errno``.
|
||||||
|
## If no error flag is set, the message ``unknown OS error`` is used.
|
||||||
|
##
|
||||||
|
## **Deprecated since version 0.9.4**: use the other ``raiseOSError`` proc.
|
||||||
|
if len(msg) == 0:
|
||||||
|
var m = osErrorMsg()
|
||||||
|
raise newException(OSError, if m.len > 0: m else: "unknown OS error")
|
||||||
|
else:
|
||||||
|
raise newException(OSError, msg)
|
||||||
|
{.pop.}
|
||||||
|
|
||||||
|
when not defined(nimfix):
|
||||||
|
{.deprecated: [osError: raiseOSError].}
|
||||||
|
|
||||||
|
proc `==`*(err1, err2: OSErrorCode): bool {.borrow.}
|
||||||
|
proc `$`*(err: OSErrorCode): string {.borrow.}
|
||||||
|
|
||||||
|
proc osErrorMsg*(errorCode: OSErrorCode): string =
|
||||||
|
## Converts an OS error code into a human readable string.
|
||||||
|
##
|
||||||
|
## The error code can be retrieved using the ``osLastError`` proc.
|
||||||
|
##
|
||||||
|
## If conversion fails, or ``errorCode`` is ``0`` then ``""`` will be
|
||||||
|
## returned.
|
||||||
|
##
|
||||||
|
## On Windows, the ``-d:useWinAnsi`` compilation flag can be used to
|
||||||
|
## make this procedure use the non-unicode Win API calls to retrieve the
|
||||||
|
## message.
|
||||||
|
result = ""
|
||||||
|
when defined(nimscript):
|
||||||
|
discard
|
||||||
|
elif defined(Windows):
|
||||||
|
if errorCode != OSErrorCode(0'i32):
|
||||||
|
when useWinUnicode:
|
||||||
|
var msgbuf: WideCString
|
||||||
|
if formatMessageW(0x00000100 or 0x00001000 or 0x00000200,
|
||||||
|
nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32:
|
||||||
|
result = $msgbuf
|
||||||
|
if msgbuf != nil: localFree(cast[pointer](msgbuf))
|
||||||
|
else:
|
||||||
|
var msgbuf: cstring
|
||||||
|
if formatMessageA(0x00000100 or 0x00001000 or 0x00000200,
|
||||||
|
nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32:
|
||||||
|
result = $msgbuf
|
||||||
|
if msgbuf != nil: localFree(msgbuf)
|
||||||
|
else:
|
||||||
|
if errorCode != OSErrorCode(0'i32):
|
||||||
|
result = $c_strerror(errorCode.int32)
|
||||||
|
|
||||||
|
proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} =
|
||||||
|
## Raises an ``OSError`` exception. The ``errorCode`` will determine the
|
||||||
|
## message, ``osErrorMsg`` will be used to get this message.
|
||||||
|
##
|
||||||
|
## The error code can be retrieved using the ``osLastError`` proc.
|
||||||
|
##
|
||||||
|
## If the error code is ``0`` or an error message could not be retrieved,
|
||||||
|
## the message ``unknown OS error`` will be used.
|
||||||
|
var e: ref OSError; new(e)
|
||||||
|
e.errorCode = errorCode.int32
|
||||||
|
if additionalInfo.len == 0:
|
||||||
|
e.msg = osErrorMsg(errorCode)
|
||||||
|
else:
|
||||||
|
e.msg = osErrorMsg(errorCode) & "\nAdditional info: " & additionalInfo
|
||||||
|
if e.msg == "":
|
||||||
|
e.msg = "unknown OS error"
|
||||||
|
raise e
|
||||||
|
|
||||||
|
{.push stackTrace:off.}
|
||||||
|
proc osLastError*(): OSErrorCode =
|
||||||
|
## Retrieves the last operating system error code.
|
||||||
|
##
|
||||||
|
## This procedure is useful in the event when an OS call fails. In that case
|
||||||
|
## this procedure will return the error code describing the reason why the
|
||||||
|
## OS call failed. The ``OSErrorMsg`` procedure can then be used to convert
|
||||||
|
## this code into a string.
|
||||||
|
##
|
||||||
|
## **Warning**:
|
||||||
|
## The behaviour of this procedure varies between Windows and POSIX systems.
|
||||||
|
## On Windows some OS calls can reset the error code to ``0`` causing this
|
||||||
|
## procedure to return ``0``. It is therefore advised to call this procedure
|
||||||
|
## immediately after an OS call fails. On POSIX systems this is not a problem.
|
||||||
|
when defined(nimscript):
|
||||||
|
discard
|
||||||
|
elif defined(windows):
|
||||||
|
result = OSErrorCode(getLastError())
|
||||||
|
else:
|
||||||
|
result = OSErrorCode(errno)
|
||||||
|
{.pop.}
|
||||||
320
lib/pure/os.nim
320
lib/pure/os.nim
|
|
@ -35,136 +35,11 @@ proc c_rename(oldname, newname: cstring): cint {.
|
||||||
importc: "rename", header: "<stdio.h>".}
|
importc: "rename", header: "<stdio.h>".}
|
||||||
proc c_system(cmd: cstring): cint {.
|
proc c_system(cmd: cstring): cint {.
|
||||||
importc: "system", header: "<stdlib.h>".}
|
importc: "system", header: "<stdlib.h>".}
|
||||||
proc c_strerror(errnum: cint): cstring {.
|
|
||||||
importc: "strerror", header: "<string.h>".}
|
|
||||||
proc c_strlen(a: cstring): cint {.
|
proc c_strlen(a: cstring): cint {.
|
||||||
importc: "strlen", header: "<string.h>", noSideEffect.}
|
importc: "strlen", header: "<string.h>", noSideEffect.}
|
||||||
proc c_getenv(env: cstring): cstring {.
|
|
||||||
importc: "getenv", header: "<stdlib.h>".}
|
|
||||||
proc c_putenv(env: cstring): cint {.
|
|
||||||
importc: "putenv", header: "<stdlib.h>".}
|
|
||||||
proc c_free(p: pointer) {.
|
proc c_free(p: pointer) {.
|
||||||
importc: "free", header: "<stdlib.h>".}
|
importc: "free", header: "<stdlib.h>".}
|
||||||
|
|
||||||
var errno {.importc, header: "<errno.h>".}: cint
|
|
||||||
|
|
||||||
proc osErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} =
|
|
||||||
## Retrieves the operating system's error flag, ``errno``.
|
|
||||||
## On Windows ``GetLastError`` is checked before ``errno``.
|
|
||||||
## Returns "" if no error occurred.
|
|
||||||
##
|
|
||||||
## **Deprecated since version 0.9.4**: use the other ``osErrorMsg`` proc.
|
|
||||||
|
|
||||||
result = ""
|
|
||||||
when defined(Windows):
|
|
||||||
var err = getLastError()
|
|
||||||
if err != 0'i32:
|
|
||||||
when useWinUnicode:
|
|
||||||
var msgbuf: WideCString
|
|
||||||
if formatMessageW(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF,
|
|
||||||
nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
|
|
||||||
result = $msgbuf
|
|
||||||
if msgbuf != nil: localFree(cast[pointer](msgbuf))
|
|
||||||
else:
|
|
||||||
var msgbuf: cstring
|
|
||||||
if formatMessageA(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF,
|
|
||||||
nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
|
|
||||||
result = $msgbuf
|
|
||||||
if msgbuf != nil: localFree(msgbuf)
|
|
||||||
if errno != 0'i32:
|
|
||||||
result = $os.c_strerror(errno)
|
|
||||||
|
|
||||||
{.push warning[deprecated]: off.}
|
|
||||||
proc raiseOSError*(msg: string = "") {.noinline, rtl, extern: "nos$1",
|
|
||||||
deprecated.} =
|
|
||||||
## raises an OSError exception with the given message ``msg``.
|
|
||||||
## If ``msg == ""``, the operating system's error flag
|
|
||||||
## (``errno``) is converted to a readable error message. On Windows
|
|
||||||
## ``GetLastError`` is checked before ``errno``.
|
|
||||||
## If no error flag is set, the message ``unknown OS error`` is used.
|
|
||||||
##
|
|
||||||
## **Deprecated since version 0.9.4**: use the other ``raiseOSError`` proc.
|
|
||||||
if len(msg) == 0:
|
|
||||||
var m = osErrorMsg()
|
|
||||||
raise newException(OSError, if m.len > 0: m else: "unknown OS error")
|
|
||||||
else:
|
|
||||||
raise newException(OSError, msg)
|
|
||||||
{.pop.}
|
|
||||||
|
|
||||||
when not defined(nimfix):
|
|
||||||
{.deprecated: [osError: raiseOSError].}
|
|
||||||
|
|
||||||
proc `==`*(err1, err2: OSErrorCode): bool {.borrow.}
|
|
||||||
proc `$`*(err: OSErrorCode): string {.borrow.}
|
|
||||||
|
|
||||||
proc osErrorMsg*(errorCode: OSErrorCode): string =
|
|
||||||
## Converts an OS error code into a human readable string.
|
|
||||||
##
|
|
||||||
## The error code can be retrieved using the ``osLastError`` proc.
|
|
||||||
##
|
|
||||||
## If conversion fails, or ``errorCode`` is ``0`` then ``""`` will be
|
|
||||||
## returned.
|
|
||||||
##
|
|
||||||
## On Windows, the ``-d:useWinAnsi`` compilation flag can be used to
|
|
||||||
## make this procedure use the non-unicode Win API calls to retrieve the
|
|
||||||
## message.
|
|
||||||
result = ""
|
|
||||||
when defined(Windows):
|
|
||||||
if errorCode != OSErrorCode(0'i32):
|
|
||||||
when useWinUnicode:
|
|
||||||
var msgbuf: WideCString
|
|
||||||
if formatMessageW(0x00000100 or 0x00001000 or 0x00000200,
|
|
||||||
nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32:
|
|
||||||
result = $msgbuf
|
|
||||||
if msgbuf != nil: localFree(cast[pointer](msgbuf))
|
|
||||||
else:
|
|
||||||
var msgbuf: cstring
|
|
||||||
if formatMessageA(0x00000100 or 0x00001000 or 0x00000200,
|
|
||||||
nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32:
|
|
||||||
result = $msgbuf
|
|
||||||
if msgbuf != nil: localFree(msgbuf)
|
|
||||||
else:
|
|
||||||
if errorCode != OSErrorCode(0'i32):
|
|
||||||
result = $os.c_strerror(errorCode.int32)
|
|
||||||
|
|
||||||
proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} =
|
|
||||||
## Raises an ``OSError`` exception. The ``errorCode`` will determine the
|
|
||||||
## message, ``osErrorMsg`` will be used to get this message.
|
|
||||||
##
|
|
||||||
## The error code can be retrieved using the ``osLastError`` proc.
|
|
||||||
##
|
|
||||||
## If the error code is ``0`` or an error message could not be retrieved,
|
|
||||||
## the message ``unknown OS error`` will be used.
|
|
||||||
var e: ref OSError; new(e)
|
|
||||||
e.errorCode = errorCode.int32
|
|
||||||
if additionalInfo.len == 0:
|
|
||||||
e.msg = osErrorMsg(errorCode)
|
|
||||||
else:
|
|
||||||
e.msg = osErrorMsg(errorCode) & "\nAdditional info: " & additionalInfo
|
|
||||||
if e.msg == "":
|
|
||||||
e.msg = "unknown OS error"
|
|
||||||
raise e
|
|
||||||
|
|
||||||
{.push stackTrace:off.}
|
|
||||||
proc osLastError*(): OSErrorCode =
|
|
||||||
## Retrieves the last operating system error code.
|
|
||||||
##
|
|
||||||
## This procedure is useful in the event when an OS call fails. In that case
|
|
||||||
## this procedure will return the error code describing the reason why the
|
|
||||||
## OS call failed. The ``OSErrorMsg`` procedure can then be used to convert
|
|
||||||
## this code into a string.
|
|
||||||
##
|
|
||||||
## **Warning**:
|
|
||||||
## The behaviour of this procedure varies between Windows and POSIX systems.
|
|
||||||
## On Windows some OS calls can reset the error code to ``0`` causing this
|
|
||||||
## procedure to return ``0``. It is therefore advised to call this procedure
|
|
||||||
## immediately after an OS call fails. On POSIX systems this is not a problem.
|
|
||||||
|
|
||||||
when defined(windows):
|
|
||||||
result = OSErrorCode(getLastError())
|
|
||||||
else:
|
|
||||||
result = OSErrorCode(errno)
|
|
||||||
{.pop.}
|
|
||||||
|
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
when useWinUnicode:
|
when useWinUnicode:
|
||||||
|
|
@ -244,6 +119,60 @@ proc dirExists*(dir: string): bool {.inline.} =
|
||||||
## Synonym for existsDir
|
## Synonym for existsDir
|
||||||
existsDir(dir)
|
existsDir(dir)
|
||||||
|
|
||||||
|
when not defined(windows):
|
||||||
|
proc checkSymlink(path: string): bool =
|
||||||
|
var rawInfo: Stat
|
||||||
|
if lstat(path, rawInfo) < 0'i32: result = false
|
||||||
|
else: result = S_ISLNK(rawInfo.st_mode)
|
||||||
|
|
||||||
|
const
|
||||||
|
ExeExts* = when defined(windows): ["exe", "cmd", "bat"] else: [""] ## \
|
||||||
|
## platform specific file extension for executables. On Windows
|
||||||
|
## ``["exe", "cmd", "bat"]``, on Posix ``[""]``.
|
||||||
|
|
||||||
|
proc findExe*(exe: string, followSymlinks: bool = true;
|
||||||
|
extensions: openarray[string]=ExeExts): string {.
|
||||||
|
tags: [ReadDirEffect, ReadEnvEffect, ReadIOEffect].} =
|
||||||
|
## Searches for `exe` in the current working directory and then
|
||||||
|
## in directories listed in the ``PATH`` environment variable.
|
||||||
|
## Returns "" if the `exe` cannot be found. `exe`
|
||||||
|
## is added the `ExeExts <#ExeExts>`_ file extensions if it has none.
|
||||||
|
## If the system supports symlinks it also resolves them until it
|
||||||
|
## meets the actual file. This behavior can be disabled if desired.
|
||||||
|
for ext in extensions:
|
||||||
|
result = addFileExt(exe, ext)
|
||||||
|
if existsFile(result): return
|
||||||
|
var path = string(getEnv("PATH"))
|
||||||
|
for candidate in split(path, PathSep):
|
||||||
|
when defined(windows):
|
||||||
|
var x = (if candidate[0] == '"' and candidate[^1] == '"':
|
||||||
|
substr(candidate, 1, candidate.len-2) else: candidate) /
|
||||||
|
exe
|
||||||
|
else:
|
||||||
|
var x = expandTilde(candidate) / exe
|
||||||
|
for ext in extensions:
|
||||||
|
var x = addFileExt(x, ext)
|
||||||
|
if existsFile(x):
|
||||||
|
when not defined(windows):
|
||||||
|
while followSymlinks: # doubles as if here
|
||||||
|
if x.checkSymlink:
|
||||||
|
var r = newString(256)
|
||||||
|
var len = readlink(x, r, 256)
|
||||||
|
if len < 0:
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
if len > 256:
|
||||||
|
r = newString(len+1)
|
||||||
|
len = readlink(x, r, len)
|
||||||
|
setLen(r, len)
|
||||||
|
if isAbsolute(r):
|
||||||
|
x = r
|
||||||
|
else:
|
||||||
|
x = parentDir(x) / r
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
return x
|
||||||
|
result = ""
|
||||||
|
|
||||||
proc getLastModificationTime*(file: string): Time {.rtl, extern: "nos$1".} =
|
proc getLastModificationTime*(file: string): Time {.rtl, extern: "nos$1".} =
|
||||||
## Returns the `file`'s last modification time.
|
## Returns the `file`'s last modification time.
|
||||||
when defined(posix):
|
when defined(posix):
|
||||||
|
|
@ -706,147 +635,6 @@ proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
|
||||||
else:
|
else:
|
||||||
result = c_system(command)
|
result = c_system(command)
|
||||||
|
|
||||||
# Environment handling cannot be put into RTL, because the ``envPairs``
|
|
||||||
# iterator depends on ``environment``.
|
|
||||||
|
|
||||||
var
|
|
||||||
envComputed {.threadvar.}: bool
|
|
||||||
environment {.threadvar.}: seq[string]
|
|
||||||
|
|
||||||
when defined(windows):
|
|
||||||
# because we support Windows GUI applications, things get really
|
|
||||||
# messy here...
|
|
||||||
when useWinUnicode:
|
|
||||||
when defined(cpp):
|
|
||||||
proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.
|
|
||||||
importcpp: "(NI16*)wcschr((const wchar_t *)#, #)", header: "<string.h>".}
|
|
||||||
else:
|
|
||||||
proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.
|
|
||||||
importc: "wcschr", header: "<string.h>".}
|
|
||||||
else:
|
|
||||||
proc strEnd(cstr: cstring, c = 0'i32): cstring {.
|
|
||||||
importc: "strchr", header: "<string.h>".}
|
|
||||||
|
|
||||||
proc getEnvVarsC() =
|
|
||||||
if not envComputed:
|
|
||||||
environment = @[]
|
|
||||||
when useWinUnicode:
|
|
||||||
var
|
|
||||||
env = getEnvironmentStringsW()
|
|
||||||
e = env
|
|
||||||
if e == nil: return # an error occurred
|
|
||||||
while true:
|
|
||||||
var eend = strEnd(e)
|
|
||||||
add(environment, $e)
|
|
||||||
e = cast[WideCString](cast[ByteAddress](eend)+2)
|
|
||||||
if eend[1].int == 0: break
|
|
||||||
discard freeEnvironmentStringsW(env)
|
|
||||||
else:
|
|
||||||
var
|
|
||||||
env = getEnvironmentStringsA()
|
|
||||||
e = env
|
|
||||||
if e == nil: return # an error occurred
|
|
||||||
while true:
|
|
||||||
var eend = strEnd(e)
|
|
||||||
add(environment, $e)
|
|
||||||
e = cast[cstring](cast[ByteAddress](eend)+1)
|
|
||||||
if eend[1] == '\0': break
|
|
||||||
discard freeEnvironmentStringsA(env)
|
|
||||||
envComputed = true
|
|
||||||
|
|
||||||
else:
|
|
||||||
const
|
|
||||||
useNSGetEnviron = defined(macosx) and not defined(ios)
|
|
||||||
|
|
||||||
when useNSGetEnviron:
|
|
||||||
# From the manual:
|
|
||||||
# Shared libraries and bundles don't have direct access to environ,
|
|
||||||
# which is only available to the loader ld(1) when a complete program
|
|
||||||
# is being linked.
|
|
||||||
# The environment routines can still be used, but if direct access to
|
|
||||||
# environ is needed, the _NSGetEnviron() routine, defined in
|
|
||||||
# <crt_externs.h>, can be used to retrieve the address of environ
|
|
||||||
# at runtime.
|
|
||||||
proc NSGetEnviron(): ptr cstringArray {.
|
|
||||||
importc: "_NSGetEnviron", header: "<crt_externs.h>".}
|
|
||||||
else:
|
|
||||||
var gEnv {.importc: "environ".}: cstringArray
|
|
||||||
|
|
||||||
proc getEnvVarsC() =
|
|
||||||
# retrieves the variables of char** env of C's main proc
|
|
||||||
if not envComputed:
|
|
||||||
environment = @[]
|
|
||||||
when useNSGetEnviron:
|
|
||||||
var gEnv = NSGetEnviron()[]
|
|
||||||
var i = 0
|
|
||||||
while true:
|
|
||||||
if gEnv[i] == nil: break
|
|
||||||
add environment, $gEnv[i]
|
|
||||||
inc(i)
|
|
||||||
envComputed = true
|
|
||||||
|
|
||||||
proc findEnvVar(key: string): int =
|
|
||||||
getEnvVarsC()
|
|
||||||
var temp = key & '='
|
|
||||||
for i in 0..high(environment):
|
|
||||||
if startsWith(environment[i], temp): return i
|
|
||||||
return -1
|
|
||||||
|
|
||||||
proc getEnv*(key: string): TaintedString {.tags: [ReadEnvEffect].} =
|
|
||||||
## Returns the value of the `environment variable`:idx: named `key`.
|
|
||||||
##
|
|
||||||
## If the variable does not exist, "" is returned. To distinguish
|
|
||||||
## whether a variable exists or it's value is just "", call
|
|
||||||
## `existsEnv(key)`.
|
|
||||||
var i = findEnvVar(key)
|
|
||||||
if i >= 0:
|
|
||||||
return TaintedString(substr(environment[i], find(environment[i], '=')+1))
|
|
||||||
else:
|
|
||||||
var env = c_getenv(key)
|
|
||||||
if env == nil: return TaintedString("")
|
|
||||||
result = TaintedString($env)
|
|
||||||
|
|
||||||
proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
|
|
||||||
## Checks whether the environment variable named `key` exists.
|
|
||||||
## Returns true if it exists, false otherwise.
|
|
||||||
if c_getenv(key) != nil: return true
|
|
||||||
else: return findEnvVar(key) >= 0
|
|
||||||
|
|
||||||
proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
|
|
||||||
## Sets the value of the `environment variable`:idx: named `key` to `val`.
|
|
||||||
## If an error occurs, `EInvalidEnvVar` is raised.
|
|
||||||
|
|
||||||
# Note: by storing the string in the environment sequence,
|
|
||||||
# we guarantee that we don't free the memory before the program
|
|
||||||
# ends (this is needed for POSIX compliance). It is also needed so that
|
|
||||||
# the process itself may access its modified environment variables!
|
|
||||||
var indx = findEnvVar(key)
|
|
||||||
if indx >= 0:
|
|
||||||
environment[indx] = key & '=' & val
|
|
||||||
else:
|
|
||||||
add environment, (key & '=' & val)
|
|
||||||
indx = high(environment)
|
|
||||||
when defined(windows):
|
|
||||||
when useWinUnicode:
|
|
||||||
var k = newWideCString(key)
|
|
||||||
var v = newWideCString(val)
|
|
||||||
if setEnvironmentVariableW(k, v) == 0'i32: raiseOSError(osLastError())
|
|
||||||
else:
|
|
||||||
if setEnvironmentVariableA(key, val) == 0'i32: raiseOSError(osLastError())
|
|
||||||
else:
|
|
||||||
if c_putenv(environment[indx]) != 0'i32:
|
|
||||||
raiseOSError(osLastError())
|
|
||||||
|
|
||||||
iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} =
|
|
||||||
## Iterate over all `environments variables`:idx:. In the first component
|
|
||||||
## of the tuple is the name of the current variable stored, in the second
|
|
||||||
## its value.
|
|
||||||
getEnvVarsC()
|
|
||||||
for i in 0..high(environment):
|
|
||||||
var p = find(environment[i], '=')
|
|
||||||
yield (TaintedString(substr(environment[i], 0, p-1)),
|
|
||||||
TaintedString(substr(environment[i], p+1)))
|
|
||||||
|
|
||||||
# Templates for filtering directories and files
|
# Templates for filtering directories and files
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
template isDir(f: WIN32_FIND_DATA): bool =
|
template isDir(f: WIN32_FIND_DATA): bool =
|
||||||
|
|
|
||||||
|
|
@ -7,19 +7,14 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
# Included by the ``os`` module but a module in its own right for NimScript
|
# Forwarded by the ``os`` module but a module in its own right for NimScript
|
||||||
# support.
|
# support.
|
||||||
|
|
||||||
when not declared(os):
|
include "system/inclrtl"
|
||||||
{.pragma: rtl.}
|
|
||||||
import strutils
|
|
||||||
|
|
||||||
when defined(nimscript) or (defined(nimdoc) and not declared(os)):
|
import strutils
|
||||||
{.pragma: rtl.}
|
|
||||||
{.push hint[ConvFromXtoItselfNotNeeded]:off.}
|
|
||||||
|
|
||||||
when not declared(getEnv) or defined(nimscript):
|
type
|
||||||
type
|
|
||||||
ReadEnvEffect* = object of ReadIOEffect ## effect that denotes a read
|
ReadEnvEffect* = object of ReadIOEffect ## effect that denotes a read
|
||||||
## from an environment variable
|
## from an environment variable
|
||||||
WriteEnvEffect* = object of WriteIOEffect ## effect that denotes a write
|
WriteEnvEffect* = object of WriteIOEffect ## effect that denotes a write
|
||||||
|
|
@ -34,15 +29,15 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
|
|
||||||
OSErrorCode* = distinct int32 ## Specifies an OS Error Code.
|
OSErrorCode* = distinct int32 ## Specifies an OS Error Code.
|
||||||
|
|
||||||
{.deprecated: [FReadEnv: ReadEnvEffect, FWriteEnv: WriteEnvEffect,
|
{.deprecated: [FReadEnv: ReadEnvEffect, FWriteEnv: WriteEnvEffect,
|
||||||
FReadDir: ReadDirEffect,
|
FReadDir: ReadDirEffect,
|
||||||
FWriteDir: WriteDirEffect,
|
FWriteDir: WriteDirEffect,
|
||||||
TOSErrorCode: OSErrorCode
|
TOSErrorCode: OSErrorCode
|
||||||
].}
|
].}
|
||||||
const
|
const
|
||||||
doslikeFileSystem* = defined(windows) or defined(OS2) or defined(DOS)
|
doslikeFileSystem* = defined(windows) or defined(OS2) or defined(DOS)
|
||||||
|
|
||||||
when defined(Nimdoc): # only for proper documentation:
|
when defined(Nimdoc): # only for proper documentation:
|
||||||
const
|
const
|
||||||
CurDir* = '.'
|
CurDir* = '.'
|
||||||
## The constant string used by the operating system to refer to the
|
## The constant string used by the operating system to refer to the
|
||||||
|
|
@ -88,7 +83,7 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
## The format string to turn a filename into a `DLL`:idx: file (also
|
## The format string to turn a filename into a `DLL`:idx: file (also
|
||||||
## called `shared object`:idx: on some operating systems).
|
## called `shared object`:idx: on some operating systems).
|
||||||
|
|
||||||
elif defined(macos):
|
elif defined(macos):
|
||||||
const
|
const
|
||||||
CurDir* = ':'
|
CurDir* = ':'
|
||||||
ParDir* = "::"
|
ParDir* = "::"
|
||||||
|
|
@ -119,7 +114,7 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
# waterproof. In case of equal names the first volume found will do.
|
# waterproof. In case of equal names the first volume found will do.
|
||||||
# Two colons "::" are the relative path to the parent. Three is to the
|
# Two colons "::" are the relative path to the parent. Three is to the
|
||||||
# grandparent etc.
|
# grandparent etc.
|
||||||
elif doslikeFileSystem:
|
elif doslikeFileSystem:
|
||||||
const
|
const
|
||||||
CurDir* = '.'
|
CurDir* = '.'
|
||||||
ParDir* = ".."
|
ParDir* = ".."
|
||||||
|
|
@ -130,7 +125,7 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
ExeExt* = "exe"
|
ExeExt* = "exe"
|
||||||
ScriptExt* = "bat"
|
ScriptExt* = "bat"
|
||||||
DynlibFormat* = "$1.dll"
|
DynlibFormat* = "$1.dll"
|
||||||
elif defined(PalmOS) or defined(MorphOS):
|
elif defined(PalmOS) or defined(MorphOS):
|
||||||
const
|
const
|
||||||
DirSep* = '/'
|
DirSep* = '/'
|
||||||
AltSep* = Dirsep
|
AltSep* = Dirsep
|
||||||
|
|
@ -140,7 +135,7 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
ExeExt* = ""
|
ExeExt* = ""
|
||||||
ScriptExt* = ""
|
ScriptExt* = ""
|
||||||
DynlibFormat* = "$1.prc"
|
DynlibFormat* = "$1.prc"
|
||||||
elif defined(RISCOS):
|
elif defined(RISCOS):
|
||||||
const
|
const
|
||||||
DirSep* = '.'
|
DirSep* = '.'
|
||||||
AltSep* = '.'
|
AltSep* = '.'
|
||||||
|
|
@ -150,7 +145,7 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
ExeExt* = ""
|
ExeExt* = ""
|
||||||
ScriptExt* = ""
|
ScriptExt* = ""
|
||||||
DynlibFormat* = "lib$1.so"
|
DynlibFormat* = "lib$1.so"
|
||||||
else: # UNIX-like operating system
|
else: # UNIX-like operating system
|
||||||
const
|
const
|
||||||
CurDir* = '.'
|
CurDir* = '.'
|
||||||
ParDir* = ".."
|
ParDir* = ".."
|
||||||
|
|
@ -162,13 +157,13 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
ScriptExt* = ""
|
ScriptExt* = ""
|
||||||
DynlibFormat* = when defined(macosx): "lib$1.dylib" else: "lib$1.so"
|
DynlibFormat* = when defined(macosx): "lib$1.dylib" else: "lib$1.so"
|
||||||
|
|
||||||
const
|
const
|
||||||
ExtSep* = '.'
|
ExtSep* = '.'
|
||||||
## The character which separates the base filename from the extension;
|
## The character which separates the base filename from the extension;
|
||||||
## for example, the '.' in ``os.nim``.
|
## for example, the '.' in ``os.nim``.
|
||||||
|
|
||||||
|
|
||||||
proc joinPath*(head, tail: string): string {.
|
proc joinPath*(head, tail: string): string {.
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
noSideEffect, rtl, extern: "nos$1".} =
|
||||||
## Joins two directory names to one.
|
## Joins two directory names to one.
|
||||||
##
|
##
|
||||||
|
|
@ -206,7 +201,7 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
else:
|
else:
|
||||||
result = head & DirSep & tail
|
result = head & DirSep & tail
|
||||||
|
|
||||||
proc joinPath*(parts: varargs[string]): string {.noSideEffect,
|
proc joinPath*(parts: varargs[string]): string {.noSideEffect,
|
||||||
rtl, extern: "nos$1OpenArray".} =
|
rtl, extern: "nos$1OpenArray".} =
|
||||||
## The same as `joinPath(head, tail)`, but works with any number of
|
## The same as `joinPath(head, tail)`, but works with any number of
|
||||||
## directory parts. You need to pass at least one element or the proc
|
## directory parts. You need to pass at least one element or the proc
|
||||||
|
|
@ -215,7 +210,7 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
for i in 1..high(parts):
|
for i in 1..high(parts):
|
||||||
result = joinPath(result, parts[i])
|
result = joinPath(result, parts[i])
|
||||||
|
|
||||||
proc `/` * (head, tail: string): string {.noSideEffect.} =
|
proc `/` * (head, tail: string): string {.noSideEffect.} =
|
||||||
## The same as ``joinPath(head, tail)``
|
## The same as ``joinPath(head, tail)``
|
||||||
##
|
##
|
||||||
## Here are some examples for Unix:
|
## Here are some examples for Unix:
|
||||||
|
|
@ -227,7 +222,7 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
## assert "usr/" / "/lib" == "usr/lib"
|
## assert "usr/" / "/lib" == "usr/lib"
|
||||||
return joinPath(head, tail)
|
return joinPath(head, tail)
|
||||||
|
|
||||||
proc splitPath*(path: string): tuple[head, tail: string] {.
|
proc splitPath*(path: string): tuple[head, tail: string] {.
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
noSideEffect, rtl, extern: "nos$1".} =
|
||||||
## Splits a directory into (head, tail), so that
|
## Splits a directory into (head, tail), so that
|
||||||
## ``head / tail == path`` (except for edge cases like "/usr").
|
## ``head / tail == path`` (except for edge cases like "/usr").
|
||||||
|
|
@ -252,14 +247,14 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
result.head = ""
|
result.head = ""
|
||||||
result.tail = path
|
result.tail = path
|
||||||
|
|
||||||
proc parentDirPos(path: string): int =
|
proc parentDirPos(path: string): int =
|
||||||
var q = 1
|
var q = 1
|
||||||
if len(path) >= 1 and path[len(path)-1] in {DirSep, AltSep}: q = 2
|
if len(path) >= 1 and path[len(path)-1] in {DirSep, AltSep}: q = 2
|
||||||
for i in countdown(len(path)-q, 0):
|
for i in countdown(len(path)-q, 0):
|
||||||
if path[i] in {DirSep, AltSep}: return i
|
if path[i] in {DirSep, AltSep}: return i
|
||||||
result = -1
|
result = -1
|
||||||
|
|
||||||
proc parentDir*(path: string): string {.
|
proc parentDir*(path: string): string {.
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
noSideEffect, rtl, extern: "nos$1".} =
|
||||||
## Returns the parent directory of `path`.
|
## Returns the parent directory of `path`.
|
||||||
##
|
##
|
||||||
|
|
@ -273,7 +268,7 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
else:
|
else:
|
||||||
result = ""
|
result = ""
|
||||||
|
|
||||||
proc tailDir*(path: string): string {.
|
proc tailDir*(path: string): string {.
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
noSideEffect, rtl, extern: "nos$1".} =
|
||||||
## Returns the tail part of `path`..
|
## Returns the tail part of `path`..
|
||||||
##
|
##
|
||||||
|
|
@ -287,12 +282,12 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
return substr(path, i+1)
|
return substr(path, i+1)
|
||||||
result = ""
|
result = ""
|
||||||
|
|
||||||
proc isRootDir*(path: string): bool {.
|
proc isRootDir*(path: string): bool {.
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
noSideEffect, rtl, extern: "nos$1".} =
|
||||||
## Checks whether a given `path` is a root directory
|
## Checks whether a given `path` is a root directory
|
||||||
result = parentDirPos(path) < 0
|
result = parentDirPos(path) < 0
|
||||||
|
|
||||||
iterator parentDirs*(path: string, fromRoot=false, inclusive=true): string =
|
iterator parentDirs*(path: string, fromRoot=false, inclusive=true): string =
|
||||||
## Walks over all parent directories of a given `path`
|
## Walks over all parent directories of a given `path`
|
||||||
##
|
##
|
||||||
## If `fromRoot` is set, the traversal will start from the file system root
|
## If `fromRoot` is set, the traversal will start from the file system root
|
||||||
|
|
@ -317,7 +312,7 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
|
|
||||||
if inclusive: yield path
|
if inclusive: yield path
|
||||||
|
|
||||||
proc `/../` * (head, tail: string): string {.noSideEffect.} =
|
proc `/../`*(head, tail: string): string {.noSideEffect.} =
|
||||||
## The same as ``parentDir(head) / tail`` unless there is no parent
|
## The same as ``parentDir(head) / tail`` unless there is no parent
|
||||||
## directory. Then ``head / tail`` is performed instead.
|
## directory. Then ``head / tail`` is performed instead.
|
||||||
let sepPos = parentDirPos(head)
|
let sepPos = parentDirPos(head)
|
||||||
|
|
@ -326,11 +321,11 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
else:
|
else:
|
||||||
result = head / tail
|
result = head / tail
|
||||||
|
|
||||||
proc normExt(ext: string): string =
|
proc normExt(ext: string): string =
|
||||||
if ext == "" or ext[0] == ExtSep: result = ext # no copy needed here
|
if ext == "" or ext[0] == ExtSep: result = ext # no copy needed here
|
||||||
else: result = ExtSep & ext
|
else: result = ExtSep & ext
|
||||||
|
|
||||||
proc searchExtPos*(path: string): int =
|
proc searchExtPos*(path: string): int =
|
||||||
## Returns index of the '.' char in `path` if it signifies the beginning
|
## Returns index of the '.' char in `path` if it signifies the beginning
|
||||||
## of extension. Returns -1 otherwise.
|
## of extension. Returns -1 otherwise.
|
||||||
# BUGFIX: do not search until 0! .DS_Store is no file extension!
|
# BUGFIX: do not search until 0! .DS_Store is no file extension!
|
||||||
|
|
@ -342,7 +337,7 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
elif path[i] in {DirSep, AltSep}:
|
elif path[i] in {DirSep, AltSep}:
|
||||||
break # do not skip over path
|
break # do not skip over path
|
||||||
|
|
||||||
proc splitFile*(path: string): tuple[dir, name, ext: string] {.
|
proc splitFile*(path: string): tuple[dir, name, ext: string] {.
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
noSideEffect, rtl, extern: "nos$1".} =
|
||||||
## Splits a filename into (dir, filename, extension).
|
## Splits a filename into (dir, filename, extension).
|
||||||
## `dir` does not end in `DirSep`.
|
## `dir` does not end in `DirSep`.
|
||||||
|
|
@ -375,7 +370,7 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
result.name = substr(path, sepPos+1, dotPos-1)
|
result.name = substr(path, sepPos+1, dotPos-1)
|
||||||
result.ext = substr(path, dotPos)
|
result.ext = substr(path, dotPos)
|
||||||
|
|
||||||
proc extractFilename*(path: string): string {.
|
proc extractFilename*(path: string): string {.
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
noSideEffect, rtl, extern: "nos$1".} =
|
||||||
## Extracts the filename of a given `path`. This is the same as
|
## Extracts the filename of a given `path`. This is the same as
|
||||||
## ``name & ext`` from ``splitFile(path)``.
|
## ``name & ext`` from ``splitFile(path)``.
|
||||||
|
|
@ -385,7 +380,7 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
result = splitPath(path).tail
|
result = splitPath(path).tail
|
||||||
|
|
||||||
|
|
||||||
proc changeFileExt*(filename, ext: string): string {.
|
proc changeFileExt*(filename, ext: string): string {.
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
noSideEffect, rtl, extern: "nos$1".} =
|
||||||
## Changes the file extension to `ext`.
|
## Changes the file extension to `ext`.
|
||||||
##
|
##
|
||||||
|
|
@ -398,7 +393,7 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
if extPos < 0: result = filename & normExt(ext)
|
if extPos < 0: result = filename & normExt(ext)
|
||||||
else: result = substr(filename, 0, extPos-1) & normExt(ext)
|
else: result = substr(filename, 0, extPos-1) & normExt(ext)
|
||||||
|
|
||||||
proc addFileExt*(filename, ext: string): string {.
|
proc addFileExt*(filename, ext: string): string {.
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
noSideEffect, rtl, extern: "nos$1".} =
|
||||||
## Adds the file extension `ext` to `filename`, unless
|
## Adds the file extension `ext` to `filename`, unless
|
||||||
## `filename` already has an extension.
|
## `filename` already has an extension.
|
||||||
|
|
@ -410,7 +405,7 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
if extPos < 0: result = filename & normExt(ext)
|
if extPos < 0: result = filename & normExt(ext)
|
||||||
else: result = filename
|
else: result = filename
|
||||||
|
|
||||||
proc cmpPaths*(pathA, pathB: string): int {.
|
proc cmpPaths*(pathA, pathB: string): int {.
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
noSideEffect, rtl, extern: "nos$1".} =
|
||||||
## Compares two paths.
|
## Compares two paths.
|
||||||
##
|
##
|
||||||
|
|
@ -429,7 +424,7 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
else:
|
else:
|
||||||
result = cmpIgnoreCase(pathA, pathB)
|
result = cmpIgnoreCase(pathA, pathB)
|
||||||
|
|
||||||
proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1".} =
|
proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1".} =
|
||||||
## Checks whether a given `path` is absolute.
|
## Checks whether a given `path` is absolute.
|
||||||
##
|
##
|
||||||
## On Windows, network paths are considered absolute too.
|
## On Windows, network paths are considered absolute too.
|
||||||
|
|
@ -444,7 +439,7 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
elif defined(posix):
|
elif defined(posix):
|
||||||
result = path[0] == '/'
|
result = path[0] == '/'
|
||||||
|
|
||||||
proc unixToNativePath*(path: string, drive=""): string {.
|
proc unixToNativePath*(path: string, drive=""): string {.
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
noSideEffect, rtl, extern: "nos$1".} =
|
||||||
## Converts an UNIX-like path to a native one.
|
## Converts an UNIX-like path to a native one.
|
||||||
##
|
##
|
||||||
|
|
@ -499,12 +494,10 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
add result, path[i]
|
add result, path[i]
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
||||||
when defined(nimdoc) and not declared(os):
|
include "includes/oserr"
|
||||||
proc getEnv(x: string): string = discard
|
include "includes/osenv"
|
||||||
proc existsFile(x: string): bool = discard
|
|
||||||
|
|
||||||
when declared(getEnv) or defined(nimscript):
|
proc getHomeDir*(): string {.rtl, extern: "nos$1",
|
||||||
proc getHomeDir*(): string {.rtl, extern: "nos$1",
|
|
||||||
tags: [ReadEnvEffect, ReadIOEffect].} =
|
tags: [ReadEnvEffect, ReadIOEffect].} =
|
||||||
## Returns the home directory of the current user.
|
## Returns the home directory of the current user.
|
||||||
##
|
##
|
||||||
|
|
@ -513,7 +506,7 @@ when declared(getEnv) or defined(nimscript):
|
||||||
when defined(windows): return string(getEnv("USERPROFILE")) & "\\"
|
when defined(windows): return string(getEnv("USERPROFILE")) & "\\"
|
||||||
else: return string(getEnv("HOME")) & "/"
|
else: return string(getEnv("HOME")) & "/"
|
||||||
|
|
||||||
proc getConfigDir*(): string {.rtl, extern: "nos$1",
|
proc getConfigDir*(): string {.rtl, extern: "nos$1",
|
||||||
tags: [ReadEnvEffect, ReadIOEffect].} =
|
tags: [ReadEnvEffect, ReadIOEffect].} =
|
||||||
## Returns the config directory of the current user for applications.
|
## Returns the config directory of the current user for applications.
|
||||||
##
|
##
|
||||||
|
|
@ -528,7 +521,7 @@ when declared(getEnv) or defined(nimscript):
|
||||||
elif getEnv("XDG_CONFIG_DIR"): return string(getEnv("XDG_CONFIG_DIR")) & "/"
|
elif getEnv("XDG_CONFIG_DIR"): return string(getEnv("XDG_CONFIG_DIR")) & "/"
|
||||||
else: return string(getEnv("HOME")) & "/.config/"
|
else: return string(getEnv("HOME")) & "/.config/"
|
||||||
|
|
||||||
proc getTempDir*(): string {.rtl, extern: "nos$1",
|
proc getTempDir*(): string {.rtl, extern: "nos$1",
|
||||||
tags: [ReadEnvEffect, ReadIOEffect].} =
|
tags: [ReadEnvEffect, ReadIOEffect].} =
|
||||||
## Returns the temporary directory of the current user for applications to
|
## Returns the temporary directory of the current user for applications to
|
||||||
## save temporary files in.
|
## save temporary files in.
|
||||||
|
|
@ -544,7 +537,7 @@ when declared(getEnv) or defined(nimscript):
|
||||||
elif defined(android): return getHomeDir()
|
elif defined(android): return getHomeDir()
|
||||||
else: return "/tmp/"
|
else: return "/tmp/"
|
||||||
|
|
||||||
proc expandTilde*(path: string): string {.
|
proc expandTilde*(path: string): string {.
|
||||||
tags: [ReadEnvEffect, ReadIOEffect].} =
|
tags: [ReadEnvEffect, ReadIOEffect].} =
|
||||||
## Expands a path starting with ``~/`` to a full path.
|
## Expands a path starting with ``~/`` to a full path.
|
||||||
##
|
##
|
||||||
|
|
@ -564,70 +557,3 @@ when declared(getEnv) or defined(nimscript):
|
||||||
result = getHomeDir() / path.substr(2)
|
result = getHomeDir() / path.substr(2)
|
||||||
else:
|
else:
|
||||||
result = path
|
result = path
|
||||||
|
|
||||||
when not declared(split):
|
|
||||||
iterator split(s: string, sep: char): string =
|
|
||||||
var last = 0
|
|
||||||
if len(s) > 0:
|
|
||||||
while last <= len(s):
|
|
||||||
var first = last
|
|
||||||
while last < len(s) and s[last] != sep: inc(last)
|
|
||||||
yield substr(s, first, last-1)
|
|
||||||
inc(last)
|
|
||||||
|
|
||||||
when not defined(windows) and declared(os):
|
|
||||||
proc checkSymlink(path: string): bool =
|
|
||||||
var rawInfo: Stat
|
|
||||||
if lstat(path, rawInfo) < 0'i32: result = false
|
|
||||||
else: result = S_ISLNK(rawInfo.st_mode)
|
|
||||||
|
|
||||||
const
|
|
||||||
ExeExts* = when defined(windows): ["exe", "cmd", "bat"] else: [""] ## \
|
|
||||||
## platform specific file extension for executables. On Windows
|
|
||||||
## ``["exe", "cmd", "bat"]``, on Posix ``[""]``.
|
|
||||||
|
|
||||||
proc findExe*(exe: string, followSymlinks: bool = true;
|
|
||||||
extensions: openarray[string]=ExeExts): string {.
|
|
||||||
tags: [ReadDirEffect, ReadEnvEffect, ReadIOEffect].} =
|
|
||||||
## Searches for `exe` in the current working directory and then
|
|
||||||
## in directories listed in the ``PATH`` environment variable.
|
|
||||||
## Returns "" if the `exe` cannot be found. `exe`
|
|
||||||
## is added the `ExeExts <#ExeExts>`_ file extensions if it has none.
|
|
||||||
## If the system supports symlinks it also resolves them until it
|
|
||||||
## meets the actual file. This behavior can be disabled if desired.
|
|
||||||
for ext in extensions:
|
|
||||||
result = addFileExt(exe, ext)
|
|
||||||
if existsFile(result): return
|
|
||||||
var path = string(getEnv("PATH"))
|
|
||||||
for candidate in split(path, PathSep):
|
|
||||||
when defined(windows):
|
|
||||||
var x = (if candidate[0] == '"' and candidate[^1] == '"':
|
|
||||||
substr(candidate, 1, candidate.len-2) else: candidate) /
|
|
||||||
exe
|
|
||||||
else:
|
|
||||||
var x = expandTilde(candidate) / exe
|
|
||||||
for ext in extensions:
|
|
||||||
var x = addFileExt(x, ext)
|
|
||||||
if existsFile(x):
|
|
||||||
when not defined(windows) and declared(os):
|
|
||||||
while followSymlinks: # doubles as if here
|
|
||||||
if x.checkSymlink:
|
|
||||||
var r = newString(256)
|
|
||||||
var len = readlink(x, r, 256)
|
|
||||||
if len < 0:
|
|
||||||
raiseOSError(osLastError())
|
|
||||||
if len > 256:
|
|
||||||
r = newString(len+1)
|
|
||||||
len = readlink(x, r, len)
|
|
||||||
setLen(r, len)
|
|
||||||
if isAbsolute(r):
|
|
||||||
x = r
|
|
||||||
else:
|
|
||||||
x = parentDir(x) / r
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
return x
|
|
||||||
result = ""
|
|
||||||
|
|
||||||
when defined(nimscript) or (defined(nimdoc) and not declared(os)):
|
|
||||||
{.pop.} # hint[ConvFromXtoItselfNotNeeded]:off
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue