envvars.nim 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  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 variable 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 std/winlean
  52. when defined(nimPreviewSlimSystem):
  53. import std/widestrs
  54. type wchar_t {.importc: "wchar_t", header: "<stdlib.h>".} = int16
  55. proc c_wgetenv(varname: ptr wchar_t): ptr wchar_t {.importc: "_wgetenv",
  56. header: "<stdlib.h>".}
  57. proc getEnvImpl(env: cstring): WideCString =
  58. let r: WideCString = env.newWideCString
  59. cast[WideCString](c_wgetenv(cast[ptr wchar_t](r)))
  60. else:
  61. proc c_getenv(env: cstring): cstring {.
  62. importc: "getenv", header: "<stdlib.h>".}
  63. proc c_setenv(envname: cstring, envval: cstring, overwrite: cint): cint {.importc: "setenv", header: "<stdlib.h>".}
  64. proc c_unsetenv(env: cstring): cint {.importc: "unsetenv", header: "<stdlib.h>".}
  65. proc getEnvImpl(env: cstring): cstring = c_getenv(env)
  66. proc getEnv*(key: string, default = ""): string {.tags: [ReadEnvEffect].} =
  67. ## Returns the value of the `environment variable`:idx: named `key`.
  68. ##
  69. ## If the variable does not exist, `""` is returned. To distinguish
  70. ## whether a variable exists or it's value is just `""`, call
  71. ## `existsEnv(key) proc`_.
  72. ##
  73. ## See also:
  74. ## * `existsEnv proc`_
  75. ## * `putEnv proc`_
  76. ## * `delEnv proc`_
  77. ## * `envPairs iterator`_
  78. runnableExamples:
  79. assert getEnv("unknownEnv") == ""
  80. assert getEnv("unknownEnv", "doesn't exist") == "doesn't exist"
  81. let env = getEnvImpl(key)
  82. if env == nil:
  83. result = default
  84. else:
  85. result = $env
  86. proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
  87. ## Checks whether the environment variable named `key` exists.
  88. ## Returns true if it exists, false otherwise.
  89. ##
  90. ## See also:
  91. ## * `getEnv proc`_
  92. ## * `putEnv proc`_
  93. ## * `delEnv proc`_
  94. ## * `envPairs iterator`_
  95. runnableExamples:
  96. assert not existsEnv("unknownEnv")
  97. result = getEnvImpl(key) != nil
  98. proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
  99. ## Sets the value of the `environment variable`:idx: named `key` to `val`.
  100. ## If an error occurs, `OSError` is raised.
  101. ##
  102. ## See also:
  103. ## * `getEnv proc`_
  104. ## * `existsEnv proc`_
  105. ## * `delEnv proc`_
  106. ## * `envPairs iterator`_
  107. when defined(windows):
  108. if key.len == 0 or '=' in key:
  109. raise newException(OSError, "invalid key, got: " & $(key, val))
  110. if setEnvImpl(key, val, 1'i32) != 0'i32:
  111. raiseOSError(osLastError(), $(key, val))
  112. else:
  113. if c_setenv(key, val, 1'i32) != 0'i32:
  114. raiseOSError(osLastError(), $(key, val))
  115. proc delEnv*(key: string) {.tags: [WriteEnvEffect].} =
  116. ## Deletes the `environment variable`:idx: named `key`.
  117. ## If an error occurs, `OSError` is raised.
  118. ##
  119. ## See also:ven
  120. ## * `getEnv proc`_
  121. ## * `existsEnv proc`_
  122. ## * `putEnv proc`_
  123. ## * `envPairs iterator`_
  124. template bail = raiseOSError(osLastError(), key)
  125. when defined(windows):
  126. #[
  127. # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/putenv-s-wputenv-s?view=msvc-160
  128. > You can remove a variable from the environment by specifying an empty string (that is, "") for value_string
  129. note that nil is not legal
  130. ]#
  131. if key.len == 0 or '=' in key:
  132. raise newException(OSError, "invalid key, got: " & key)
  133. let envToDel = key & "="
  134. if c_putenv(cstring envToDel) != 0'i32: bail
  135. else:
  136. if c_unsetenv(key) != 0'i32: bail
  137. when defined(windows):
  138. when defined(cpp):
  139. proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.importcpp: "(NI16*)wcschr((const wchar_t *)#, #)",
  140. header: "<string.h>".}
  141. else:
  142. proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.importc: "wcschr",
  143. header: "<string.h>".}
  144. elif defined(macosx) and not defined(ios) and not defined(emscripten):
  145. # From the manual:
  146. # Shared libraries and bundles don't have direct access to environ,
  147. # which is only available to the loader ld(1) when a complete program
  148. # is being linked.
  149. # The environment routines can still be used, but if direct access to
  150. # environ is needed, the _NSGetEnviron() routine, defined in
  151. # <crt_externs.h>, can be used to retrieve the address of environ
  152. # at runtime.
  153. proc NSGetEnviron(): ptr cstringArray {.importc: "_NSGetEnviron",
  154. header: "<crt_externs.h>".}
  155. elif defined(haiku):
  156. var gEnv {.importc: "environ", header: "<stdlib.h>".}: cstringArray
  157. else:
  158. var gEnv {.importc: "environ".}: cstringArray
  159. iterator envPairsImpl(): tuple[key, value: string] {.tags: [ReadEnvEffect].} =
  160. when defined(windows):
  161. let env = getEnvironmentStringsW()
  162. var e = env
  163. if e != nil:
  164. while true:
  165. let eend = strEnd(e)
  166. let kv = $e
  167. let p = find(kv, '=')
  168. yield (substr(kv, 0, p-1), substr(kv, p+1))
  169. e = cast[WideCString](cast[ByteAddress](eend)+2)
  170. if int(eend[1]) == 0: break
  171. discard freeEnvironmentStringsW(env)
  172. else:
  173. var i = 0
  174. when defined(macosx) and not defined(ios) and not defined(emscripten):
  175. var gEnv = NSGetEnviron()[]
  176. while gEnv[i] != nil:
  177. let kv = $gEnv[i]
  178. inc(i)
  179. let p = find(kv, '=')
  180. yield (substr(kv, 0, p-1), substr(kv, p+1))
  181. proc envPairsImplSeq(): seq[tuple[key, value: string]] = discard # vmops
  182. iterator envPairs*(): tuple[key, value: string] {.tags: [ReadEnvEffect].} =
  183. ## Iterate over all `environments variables`:idx:.
  184. ##
  185. ## In the first component of the tuple is the name of the current variable stored,
  186. ## in the second its value.
  187. ##
  188. ## Works in native backends, nodejs and vm, like the following APIs:
  189. ## * `getEnv proc`_
  190. ## * `existsEnv proc`_
  191. ## * `putEnv proc`_
  192. ## * `delEnv proc`_
  193. when nimvm:
  194. for ai in envPairsImplSeq(): yield ai
  195. else:
  196. when defined(nimscript): discard
  197. else:
  198. for ai in envPairsImpl(): yield ai