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,18 +7,13 @@
|
||||||
# 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
|
import strutils
|
||||||
|
|
||||||
when defined(nimscript) or (defined(nimdoc) and not declared(os)):
|
|
||||||
{.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
|
||||||
|
|
@ -499,11 +494,9 @@ 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.
|
||||||
|
|
@ -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