envvars.nim 8.2 KB

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