diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2017-09-01 10:35:50 +0200 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2017-09-01 10:35:50 +0200 |
commit | 50666a1f8bc5961ada8e9ecdcd9c3bc9bc8561d0 (patch) | |
tree | c7ff1ac85a5d29ba3056f3d178c9bb19c76687f1 /lib/pure/includes | |
parent | 8ce42738648e70e562e7f1bbe925257cddf0b392 (diff) | |
download | Nim-50666a1f8bc5961ada8e9ecdcd9c3bc9bc8561d0.tar.gz |
refactor os.nim and ospaths.nim
Diffstat (limited to 'lib/pure/includes')
-rw-r--r-- | lib/pure/includes/osenv.nim | 159 | ||||
-rw-r--r-- | lib/pure/includes/oserr.nim | 132 |
2 files changed, 291 insertions, 0 deletions
diff --git a/lib/pure/includes/osenv.nim b/lib/pure/includes/osenv.nim new file mode 100644 index 000000000..63500c5fa --- /dev/null +++ b/lib/pure/includes/osenv.nim @@ -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))) diff --git a/lib/pure/includes/oserr.nim b/lib/pure/includes/oserr.nim new file mode 100644 index 000000000..ef3f82628 --- /dev/null +++ b/lib/pure/includes/oserr.nim @@ -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.} |