envvars.nim 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2022 Nim contributors
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## The `std/envvars` module implements environment variables handling.
  10. import std/oserrors
  11. type
  12. ReadEnvEffect* = object of ReadIOEffect ## Effect that denotes a read
  13. ## from an environment variable.
  14. WriteEnvEffect* = object of WriteIOEffect ## Effect that denotes a write
  15. ## to an environment variable.
  16. when not defined(nimscript):
  17. when defined(nodejs):
  18. proc getEnv*(key: string, default = ""): string {.tags: [ReadEnvEffect].} =
  19. var ret = default.cstring
  20. let key2 = key.cstring
  21. {.emit: "const value = process.env[`key2`];".}
  22. {.emit: "if (value !== undefined) { `ret` = value };".}
  23. result = $ret
  24. proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
  25. var key2 = key.cstring
  26. var ret: bool
  27. {.emit: "`ret` = `key2` in process.env;".}
  28. result = ret
  29. proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
  30. var key2 = key.cstring
  31. var val2 = val.cstring
  32. {.emit: "process.env[`key2`] = `val2`;".}
  33. proc delEnv*(key: string) {.tags: [WriteEnvEffect].} =
  34. var key2 = key.cstring
  35. {.emit: "delete process.env[`key2`];".}
  36. iterator envPairsImpl(): tuple[key, value: string] {.tags: [ReadEnvEffect].} =
  37. var num: int
  38. var keys: RootObj
  39. {.emit: "`keys` = Object.keys(process.env); `num` = `keys`.length;".}
  40. for i in 0..<num:
  41. var key, value: cstring
  42. {.emit: "`key` = `keys`[`i`]; `value` = process.env[`key`];".}
  43. yield ($key, $value)
  44. # commented because it must keep working with js+VM
  45. # elif defined(js):
  46. # {.error: "requires -d:nodejs".}
  47. else:
  48. when defined(windows):
  49. proc c_putenv(envstring: cstring): cint {.importc: "_putenv", header: "<stdlib.h>".}
  50. from std/private/win_setenv import setEnvImpl
  51. import winlean
  52. when defined(nimPreviewSlimSystem):
  53. import std/widestrs
  54. proc c_wgetenv(varname: WideCString): WideCString {.importc: "_wgetenv",
  55. header: "<stdlib.h>".}
  56. proc getEnvImpl(env: cstring): WideCString = c_wgetenv(env.newWideCString)
  57. else:
  58. proc c_getenv(env: cstring): cstring {.
  59. importc: "getenv", header: "<stdlib.h>".}
  60. proc c_setenv(envname: cstring, envval: cstring, overwrite: cint): cint {.importc: "setenv", header: "<stdlib.h>".}
  61. proc c_unsetenv(env: cstring): cint {.importc: "unsetenv", header: "<stdlib.h>".}
  62. proc getEnvImpl(env: cstring): cstring = c_getenv(env)
  63. proc getEnv*(key: string, default = ""): string {.tags: [ReadEnvEffect].} =
  64. ## Returns the value of the `environment variable`:idx: named `key`.
  65. ##
  66. ## If the variable does not exist, `""` is returned. To distinguish
  67. ## whether a variable exists or it's value is just `""`, call
  68. ## `existsEnv(key) proc`_.
  69. ##
  70. ## See also:
  71. ## * `existsEnv proc`_
  72. ## * `putEnv proc`_
  73. ## * `delEnv proc`_
  74. ## * `envPairs iterator`_
  75. runnableExamples:
  76. assert getEnv("unknownEnv") == ""
  77. assert getEnv("unknownEnv", "doesn't exist") == "doesn't exist"
  78. let env = getEnvImpl(key)
  79. if env == nil: return default
  80. result = $env
  81. proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
  82. ## Checks whether the environment variable named `key` exists.
  83. ## Returns true if it exists, false otherwise.
  84. ##
  85. ## See also:
  86. ## * `getEnv proc`_
  87. ## * `putEnv proc`_
  88. ## * `delEnv proc`_
  89. ## * `envPairs iterator`_
  90. runnableExamples:
  91. assert not existsEnv("unknownEnv")
  92. return getEnvImpl(key) != nil
  93. proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
  94. ## Sets the value of the `environment variable`:idx: named `key` to `val`.
  95. ## If an error occurs, `OSError` is raised.
  96. ##
  97. ## See also:
  98. ## * `getEnv proc`_
  99. ## * `existsEnv proc`_
  100. ## * `delEnv proc`_
  101. ## * `envPairs iterator`_
  102. when defined(windows):
  103. if key.len == 0 or '=' in key:
  104. raise newException(OSError, "invalid key, got: " & $(key, val))
  105. if setEnvImpl(key, val, 1'i32) != 0'i32:
  106. raiseOSError(osLastError(), $(key, val))
  107. else:
  108. if c_setenv(key, val, 1'i32) != 0'i32:
  109. raiseOSError(osLastError(), $(key, val))
  110. proc delEnv*(key: string) {.tags: [WriteEnvEffect].} =
  111. ## Deletes the `environment variable`:idx: named `key`.
  112. ## If an error occurs, `OSError` is raised.
  113. ##
  114. ## See also:ven
  115. ## * `getEnv proc`_
  116. ## * `existsEnv proc`_
  117. ## * `putEnv proc`_
  118. ## * `envPairs iterator`_
  119. template bail = raiseOSError(osLastError(), key)
  120. when defined(windows):
  121. #[
  122. # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/putenv-s-wputenv-s?view=msvc-160
  123. > You can remove a variable from the environment by specifying an empty string (that is, "") for value_string
  124. note that nil is not legal
  125. ]#
  126. if key.len == 0 or '=' in key:
  127. raise newException(OSError, "invalid key, got: " & key)
  128. let envToDel = key & "="
  129. if c_putenv(cstring envToDel) != 0'i32: bail
  130. else:
  131. if c_unsetenv(key) != 0'i32: bail
  132. when defined(windows):
  133. when defined(cpp):
  134. proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.importcpp: "(NI16*)wcschr((const wchar_t *)#, #)",
  135. header: "<string.h>".}
  136. else:
  137. proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.importc: "wcschr",
  138. header: "<string.h>".}
  139. elif defined(macosx) and not defined(ios) and not defined(emscripten):
  140. # From the manual:
  141. # Shared libraries and bundles don't have direct access to environ,
  142. # which is only available to the loader ld(1) when a complete program
  143. # is being linked.
  144. # The environment routines can still be used, but if direct access to
  145. # environ is needed, the _NSGetEnviron() routine, defined in
  146. # <crt_externs.h>, can be used to retrieve the address of environ
  147. # at runtime.
  148. proc NSGetEnviron(): ptr cstringArray {.importc: "_NSGetEnviron",
  149. header: "<crt_externs.h>".}
  150. elif defined(haiku):
  151. var gEnv {.importc: "environ", header: "<stdlib.h>".}: cstringArray
  152. else:
  153. var gEnv {.importc: "environ".}: cstringArray
  154. iterator envPairsImpl(): tuple[key, value: string] {.tags: [ReadEnvEffect].} =
  155. when defined(windows):
  156. block:
  157. template impl(get_fun, typ, size, zero, free_fun) =
  158. let env = get_fun()
  159. var e = env
  160. if e == nil: break
  161. while true:
  162. let eend = strEnd(e)
  163. let kv = $e
  164. let p = find(kv, '=')
  165. yield (substr(kv, 0, p-1), substr(kv, p+1))
  166. e = cast[typ](cast[ByteAddress](eend)+size)
  167. if typeof(zero)(eend[1]) == zero: break
  168. discard free_fun(env)
  169. impl(getEnvironmentStringsW, WideCString, 2, 0, freeEnvironmentStringsW)
  170. else:
  171. var i = 0
  172. when defined(macosx) and not defined(ios) and not defined(emscripten):
  173. var gEnv = NSGetEnviron()[]
  174. while gEnv[i] != nil:
  175. let kv = $gEnv[i]
  176. inc(i)
  177. let p = find(kv, '=')
  178. yield (substr(kv, 0, p-1), substr(kv, p+1))
  179. proc envPairsImplSeq(): seq[tuple[key, value: string]] = discard # vmops
  180. iterator envPairs*(): tuple[key, value: string] {.tags: [ReadEnvEffect].} =
  181. ## Iterate over all `environments variables`:idx:.
  182. ##
  183. ## In the first component of the tuple is the name of the current variable stored,
  184. ## in the second its value.
  185. ##
  186. ## Works in native backends, nodejs and vm, like the following APIs:
  187. ## * `getEnv proc`_
  188. ## * `existsEnv proc`_
  189. ## * `putEnv proc`_
  190. ## * `delEnv proc`_
  191. when nimvm:
  192. for ai in envPairsImplSeq(): yield ai
  193. else:
  194. when defined(nimscript): discard
  195. else:
  196. for ai in envPairsImpl(): yield ai