summary refs log tree commit diff stats
path: root/lib/pure/includes/osenv.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pure/includes/osenv.nim')
-rw-r--r--lib/pure/includes/osenv.nim237
1 files changed, 78 insertions, 159 deletions
diff --git a/lib/pure/includes/osenv.nim b/lib/pure/includes/osenv.nim
index db291c5fd..6aaafbfda 100644
--- a/lib/pure/includes/osenv.nim
+++ b/lib/pure/includes/osenv.nim
@@ -40,112 +40,15 @@ when defined(nodejs):
 #   {.error: "requires -d:nodejs".}
 
 else:
-  when defined(windows):
-    from parseutils import skipIgnoreCase
 
   proc c_getenv(env: cstring): cstring {.
     importc: "getenv", header: "<stdlib.h>".}
-  when defined(vcc):
+  when defined(windows):
     proc c_putenv_s(envname: cstring, envval: cstring): cint {.importc: "_putenv_s", header: "<stdlib.h>".}
+    from std/private/win_setenv import setEnvImpl
   else:
     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>".}
-
-  # Environment handling cannot be put into RTL, because the `envPairs`
-  # iterator depends on `environment`.
-
-  var
-    envComputed {.threadvar.}: bool
-    environment {.threadvar.}: seq[string]
-
-  when defined(nimV2):
-    proc unpairedEnvAllocs*(): int =
-      result = environment.len
-      if result > 0: inc result
-
-  when defined(windows) and not defined(nimscript):
-    # 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) and not defined(emscripten)) or defined(nimscript)
-
-    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>".}
-    elif defined(haiku):
-      var gEnv {.importc: "environ", header: "<stdlib.h>".}: cstringArray
-    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 gEnv[i] != nil:
-          add environment, $gEnv[i]
-          inc(i)
-        envComputed = true
-
-  proc findEnvVar(key: string): int =
-    getEnvVarsC()
-    var temp = key & '='
-    for i in 0..high(environment):
-      when defined(windows):
-        if skipIgnoreCase(environment[i], temp) == len(temp): return i
-      else:
-        if startsWith(environment[i], temp): return i
-    return -1
+  proc c_unsetenv(env: cstring): cint {.importc: "unsetenv", header: "<stdlib.h>".}
 
   proc getEnv*(key: string, default = ""): string {.tags: [ReadEnvEffect].} =
     ## Returns the value of the `environment variable`:idx: named `key`.
@@ -163,16 +66,9 @@ else:
       assert getEnv("unknownEnv") == ""
       assert getEnv("unknownEnv", "doesn't exist") == "doesn't exist"
 
-    when nimvm:
-      discard "built into the compiler"
-    else:
-      var i = findEnvVar(key)
-      if i >= 0:
-        return substr(environment[i], find(environment[i], '=')+1)
-      else:
-        var env = c_getenv(key)
-        if env == nil: return default
-        result = $env
+    let env = c_getenv(key)
+    if env == nil: return default
+    result = $env
 
   proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
     ## Checks whether the environment variable named `key` exists.
@@ -186,11 +82,7 @@ else:
     runnableExamples:
       assert not existsEnv("unknownEnv")
 
-    when nimvm:
-      discard "built into the compiler"
-    else:
-      if c_getenv(key) != nil: return true
-      else: return findEnvVar(key) >= 0
+    return c_getenv(key) != nil
 
   proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
     ## Sets the value of the `environment variable`:idx: named `key` to `val`.
@@ -201,33 +93,14 @@ else:
     ## * `existsEnv proc <#existsEnv,string>`_
     ## * `delEnv proc <#delEnv,string>`_
     ## * `envPairs iterator <#envPairs.i>`_
-
-    # 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"
+    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:
-      var indx = findEnvVar(key)
-      if indx >= 0:
-        environment[indx] = key & '=' & val
-      else:
-        add environment, (key & '=' & val)
-        indx = high(environment)
-      when defined(windows) and not defined(nimscript):
-        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())
-      elif defined(vcc):
-        if c_putenv_s(key, val) != 0'i32:
-          raiseOSError(osLastError())
-      else:
-        if c_setenv(key, val, 1'i32) != 0'i32:
-          raiseOSError(osLastError())
+      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`.
@@ -238,21 +111,45 @@ else:
     ## * `existsEnv proc <#existsEnv,string>`_
     ## * `putEnv proc <#putEnv,string,string>`_
     ## * `envPairs iterator <#envPairs.i>`_
-    when nimvm:
-      discard "built into the compiler"
+    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)
+      if c_putenv_s(key, "") != 0'i32: bail
     else:
-      var indx = findEnvVar(key)
-      if indx < 0: return # Do nothing if the env var is not already set
-      when defined(windows) and not defined(nimscript):
-        when useWinUnicode:
-          var k = newWideCString(key)
-          if setEnvironmentVariableW(k, nil) == 0'i32: raiseOSError(osLastError())
-        else:
-          if setEnvironmentVariableA(key, nil) == 0'i32: raiseOSError(osLastError())
+      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:
-        if c_unsetenv(key) != 0'i32:
-          raiseOSError(osLastError())
-      environment.delete(indx)
+        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 envPairs*(): tuple[key, value: string] {.tags: [ReadEnvEffect].} =
     ## Iterate over all `environments variables`:idx:.
@@ -265,8 +162,30 @@ else:
     ## * `existsEnv proc <#existsEnv,string>`_
     ## * `putEnv proc <#putEnv,string,string>`_
     ## * `delEnv proc <#delEnv,string>`_
-    getEnvVarsC()
-    for i in 0..high(environment):
-      var p = find(environment[i], '=')
-      yield (substr(environment[i], 0, p-1),
-             substr(environment[i], p+1))
+    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))