osenv.nim 7.3 KB

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