diff options
author | ringabout <43030857+ringabout@users.noreply.github.com> | 2022-10-19 01:44:26 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-18 19:44:26 +0200 |
commit | b07526b2c77c75484d6008ea37d13cf022830c1a (patch) | |
tree | a6f01fd1b2270d41b46b173a736cc8fcd35aae3c /lib | |
parent | b13ef07f5827fa63515cbb49d16723b3ae439f95 (diff) | |
download | Nim-b07526b2c77c75484d6008ea37d13cf022830c1a.tar.gz |
refactor envvars, oserrors; register vmops (#20592)
* refactor envvars, oserrors; register vmops * remove type definitions
Diffstat (limited to 'lib')
-rw-r--r-- | lib/pure/includes/osenv.nim | 209 | ||||
-rw-r--r-- | lib/pure/includes/oserr.nim | 121 | ||||
-rw-r--r-- | lib/pure/os.nim | 14 |
3 files changed, 5 insertions, 339 deletions
diff --git a/lib/pure/includes/osenv.nim b/lib/pure/includes/osenv.nim deleted file mode 100644 index a1d906519..000000000 --- a/lib/pure/includes/osenv.nim +++ /dev/null @@ -1,209 +0,0 @@ -# Include file that implements 'getEnv' and friends. Do not import it! - -when not declared(os): - {.error: "This is an include file for os.nim!".} - -when not defined(nimscript): - when defined(nodejs): - proc getEnv*(key: string, default = ""): string {.tags: [ReadEnvEffect].} = - var ret = default.cstring - let key2 = key.cstring - {.emit: "const value = process.env[`key2`];".} - {.emit: "if (value !== undefined) { `ret` = value };".} - result = $ret - - proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = - var key2 = key.cstring - var ret: bool - {.emit: "`ret` = `key2` in process.env;".} - result = ret - - proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = - var key2 = key.cstring - var val2 = val.cstring - {.emit: "process.env[`key2`] = `val2`;".} - - proc delEnv*(key: string) {.tags: [WriteEnvEffect].} = - var key2 = key.cstring - {.emit: "delete process.env[`key2`];".} - - iterator envPairsImpl(): tuple[key, value: string] {.tags: [ReadEnvEffect].} = - var num: int - var keys: RootObj - {.emit: "`keys` = Object.keys(process.env); `num` = `keys`.length;".} - for i in 0..<num: - var key, value: cstring - {.emit: "`key` = `keys`[`i`]; `value` = process.env[`key`];".} - yield ($key, $value) - - # commented because it must keep working with js+VM - # elif defined(js): - # {.error: "requires -d:nodejs".} - - else: - - when defined(windows): - when defined(nimPreviewSlimSystem): - import std/widestrs - proc c_putenv(envstring: cstring): cint {.importc: "_putenv", header: "<stdlib.h>".} - from std/private/win_setenv import setEnvImpl - proc c_wgetenv(varname: WideCString): WideCString {.importc: "_wgetenv", - header: "<stdlib.h>".} - proc getEnvImpl(env: cstring): WideCString = c_wgetenv(env.newWideCString) - else: - proc c_getenv(env: cstring): cstring {. - importc: "getenv", header: "<stdlib.h>".} - proc c_setenv(envname: cstring, envval: cstring, overwrite: cint): cint {.importc: "setenv", header: "<stdlib.h>".} - proc c_unsetenv(env: cstring): cint {.importc: "unsetenv", header: "<stdlib.h>".} - proc getEnvImpl(env: cstring): cstring = c_getenv(env) - - proc getEnv*(key: string, default = ""): string {.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) proc`_. - ## - ## See also: - ## * `existsEnv proc`_ - ## * `putEnv proc`_ - ## * `delEnv proc`_ - ## * `envPairs iterator`_ - runnableExamples: - assert getEnv("unknownEnv") == "" - assert getEnv("unknownEnv", "doesn't exist") == "doesn't exist" - - let env = getEnvImpl(key) - if env == nil: return default - result = $env - - proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = - ## Checks whether the environment variable named `key` exists. - ## Returns true if it exists, false otherwise. - ## - ## See also: - ## * `getEnv proc`_ - ## * `putEnv proc`_ - ## * `delEnv proc`_ - ## * `envPairs iterator`_ - runnableExamples: - assert not existsEnv("unknownEnv") - - return getEnvImpl(key) != nil - - proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = - ## Sets the value of the `environment variable`:idx: named `key` to `val`. - ## If an error occurs, `OSError` is raised. - ## - ## See also: - ## * `getEnv proc`_ - ## * `existsEnv proc`_ - ## * `delEnv proc`_ - ## * `envPairs iterator`_ - when defined(windows): - if key.len == 0 or '=' in key: - raise newException(OSError, "invalid key, got: " & $(key, val)) - if setEnvImpl(key, val, 1'i32) != 0'i32: - raiseOSError(osLastError(), $(key, val)) - else: - if c_setenv(key, val, 1'i32) != 0'i32: - raiseOSError(osLastError(), $(key, val)) - - proc delEnv*(key: string) {.tags: [WriteEnvEffect].} = - ## Deletes the `environment variable`:idx: named `key`. - ## If an error occurs, `OSError` is raised. - ## - ## See also:ven - ## * `getEnv proc`_ - ## * `existsEnv proc`_ - ## * `putEnv proc`_ - ## * `envPairs iterator`_ - template bail = raiseOSError(osLastError(), key) - when defined(windows): - #[ - # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/putenv-s-wputenv-s?view=msvc-160 - > You can remove a variable from the environment by specifying an empty string (that is, "") for value_string - note that nil is not legal - ]# - if key.len == 0 or '=' in key: - raise newException(OSError, "invalid key, got: " & key) - let envToDel = key & "=" - if c_putenv(cstring envToDel) != 0'i32: bail - else: - if c_unsetenv(key) != 0'i32: bail - - when defined(windows): - 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>".} - elif defined(macosx) and not defined(ios) and not defined(emscripten): - # 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>".} - elif defined(haiku): - var gEnv {.importc: "environ", header: "<stdlib.h>".}: cstringArray - else: - var gEnv {.importc: "environ".}: cstringArray - - iterator envPairsImpl(): tuple[key, value: string] {.tags: [ReadEnvEffect].} = - when defined(windows): - block: - template impl(get_fun, typ, size, zero, free_fun) = - let env = get_fun() - var e = env - if e == nil: break - while true: - let eend = strEnd(e) - let kv = $e - let p = find(kv, '=') - yield (substr(kv, 0, p-1), substr(kv, p+1)) - e = cast[typ](cast[ByteAddress](eend)+size) - if typeof(zero)(eend[1]) == zero: break - discard free_fun(env) - when useWinUnicode: - impl(getEnvironmentStringsW, WideCString, 2, 0, freeEnvironmentStringsW) - else: - impl(getEnvironmentStringsA, cstring, 1, '\0', freeEnvironmentStringsA) - else: - var i = 0 - when defined(macosx) and not defined(ios) and not defined(emscripten): - var gEnv = NSGetEnviron()[] - while gEnv[i] != nil: - let kv = $gEnv[i] - inc(i) - let p = find(kv, '=') - yield (substr(kv, 0, p-1), substr(kv, p+1)) - -proc envPairsImplSeq(): seq[tuple[key, value: string]] = discard # vmops - -iterator envPairs*(): tuple[key, value: string] {.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. - ## - ## Works in native backends, nodejs and vm, like the following APIs: - ## * `getEnv proc`_ - ## * `existsEnv proc`_ - ## * `putEnv proc`_ - ## * `delEnv proc`_ - when nimvm: - for ai in envPairsImplSeq(): yield ai - else: - when defined(nimscript): discard - else: - for ai in envPairsImpl(): yield ai diff --git a/lib/pure/includes/oserr.nim b/lib/pure/includes/oserr.nim deleted file mode 100644 index c58fdb22c..000000000 --- a/lib/pure/includes/oserr.nim +++ /dev/null @@ -1,121 +0,0 @@ -# Include file that implements 'osErrorMsg' and friends. Do not import it! - -when not declared(os): - {.error: "This is an include file for os.nim!".} - -when not defined(nimscript): - var errno {.importc, header: "<errno.h>".}: cint - - proc c_strerror(errnum: cint): cstring {. - importc: "strerror", header: "<string.h>".} - - when defined(windows): - import winlean - when useWinUnicode and defined(nimPreviewSlimSystem): - import std/widestrs - -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. - ## - ## See also: - ## * `raiseOSError proc`_ - ## * `osLastError proc`_ - runnableExamples: - when defined(linux): - assert osErrorMsg(OSErrorCode(0)) == "" - assert osErrorMsg(OSErrorCode(1)) == "Operation not permitted" - assert osErrorMsg(OSErrorCode(2)) == "No such file or directory" - - 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 newOSError*( - errorCode: OSErrorCode, additionalInfo = "" -): owned(ref OSError) {.noinline.} = - ## Creates a new `OSError exception <system.html#OSError>`_. - ## - ## The `errorCode` will determine the - ## message, `osErrorMsg proc`_ 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. - ## - ## See also: - ## * `osErrorMsg proc`_ - ## * `osLastError proc`_ - var e: owned(ref OSError); new(e) - e.errorCode = errorCode.int32 - e.msg = osErrorMsg(errorCode) - if additionalInfo.len > 0: - if e.msg.len > 0 and e.msg[^1] != '\n': e.msg.add '\n' - e.msg.add "Additional info: " - e.msg.add additionalInfo - # don't add trailing `.` etc, which negatively impacts "jump to file" in IDEs. - if e.msg == "": - e.msg = "unknown OS error" - return e - -proc raiseOSError*(errorCode: OSErrorCode, additionalInfo = "") {.noinline.} = - ## Raises an `OSError exception <system.html#OSError>`_. - ## - ## Read the description of the `newOSError proc`_ to learn - ## how the exception object is created. - raise newOSError(errorCode, additionalInfo) - -{.push stackTrace:off.} -proc osLastError*(): OSErrorCode {.sideEffect.} = - ## 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. - ## - ## See also: - ## * `osErrorMsg proc`_ - ## * `raiseOSError proc`_ - when defined(nimscript): - discard - elif defined(windows): - result = cast[OSErrorCode](getLastError()) - else: - result = OSErrorCode(errno) -{.pop.} diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 3e7c7cfc9..2779e8e68 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -81,11 +81,6 @@ else: proc normalizePathAux(path: var string){.inline, raises: [], noSideEffect.} type - ReadEnvEffect* = object of ReadIOEffect ## Effect that denotes a read - ## from an environment variable. - WriteEnvEffect* = object of WriteIOEffect ## Effect that denotes a write - ## to an environment variable. - ReadDirEffect* = object of ReadIOEffect ## Effect that denotes a read ## operation from the directory ## structure. @@ -93,8 +88,6 @@ type ## operation to ## the directory structure. - OSErrorCode* = distinct int32 ## Specifies an OS Error Code. - import std/private/osseps export osseps @@ -884,8 +877,11 @@ proc unixToNativePath*(path: string, drive=""): string {. add result, path[i] inc(i) -include "includes/oserr" -include "includes/osenv" +import std/oserrors +export oserrors + +import std/envvars +export envvars proc getHomeDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect, ReadIOEffect].} = |