osenv.nim 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  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 = ""): TaintedString {.tags: [ReadEnvEffect].} =
  6. var ret: cstring
  7. let key2 = key.cstring
  8. {.emit: "`ret` = process.env[`key2`];".}
  9. result = $ret
  10. proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
  11. var key2 = key.cstring
  12. var ret: bool
  13. {.emit: "`ret` = `key2` in process.env;".}
  14. result = ret
  15. proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
  16. var key2 = key.cstring
  17. var val2 = val.cstring
  18. {.emit: "process.env[`key2`] = `val2`;".}
  19. proc delEnv*(key: string) {.tags: [WriteEnvEffect].} =
  20. var key2 = key.cstring
  21. {.emit: "delete process.env[`key2`];".}
  22. iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} =
  23. var num: int
  24. var keys: RootObj
  25. {.emit: "`keys` = Object.keys(process.env); `num` = `keys`.length;".}
  26. for i in 0..<num:
  27. var key, value: cstring
  28. {.emit: "`key` = `keys`[`i`]; `value` = process.env[`key`];".}
  29. yield ($key, $value)
  30. # commented because it must keep working with js+VM
  31. # elif defined(js):
  32. # {.error: "requires -d:nodejs".}
  33. else:
  34. when defined(windows):
  35. from parseutils import skipIgnoreCase
  36. proc c_getenv(env: cstring): cstring {.
  37. importc: "getenv", header: "<stdlib.h>".}
  38. proc c_putenv(env: cstring): cint {.
  39. importc: "putenv", header: "<stdlib.h>".}
  40. proc c_unsetenv(env: cstring): cint {.
  41. importc: "unsetenv", header: "<stdlib.h>".}
  42. # Environment handling cannot be put into RTL, because the ``envPairs``
  43. # iterator depends on ``environment``.
  44. var
  45. envComputed {.threadvar.}: bool
  46. environment {.threadvar.}: seq[string]
  47. when defined(nimV2):
  48. proc unpairedEnvAllocs*(): int =
  49. result = environment.len
  50. if result > 0: inc result
  51. when defined(windows) and not defined(nimscript):
  52. # because we support Windows GUI applications, things get really
  53. # messy here...
  54. when useWinUnicode:
  55. when defined(cpp):
  56. proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.
  57. importcpp: "(NI16*)wcschr((const wchar_t *)#, #)", header: "<string.h>".}
  58. else:
  59. proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.
  60. importc: "wcschr", header: "<string.h>".}
  61. else:
  62. proc strEnd(cstr: cstring, c = 0'i32): cstring {.
  63. importc: "strchr", header: "<string.h>".}
  64. proc getEnvVarsC() =
  65. if not envComputed:
  66. environment = @[]
  67. when useWinUnicode:
  68. var
  69. env = getEnvironmentStringsW()
  70. e = env
  71. if e == nil: return # an error occurred
  72. while true:
  73. var eend = strEnd(e)
  74. add(environment, $e)
  75. e = cast[WideCString](cast[ByteAddress](eend)+2)
  76. if eend[1].int == 0: break
  77. discard freeEnvironmentStringsW(env)
  78. else:
  79. var
  80. env = getEnvironmentStringsA()
  81. e = env
  82. if e == nil: return # an error occurred
  83. while true:
  84. var eend = strEnd(e)
  85. add(environment, $e)
  86. e = cast[cstring](cast[ByteAddress](eend)+1)
  87. if eend[1] == '\0': break
  88. discard freeEnvironmentStringsA(env)
  89. envComputed = true
  90. else:
  91. const
  92. useNSGetEnviron = (defined(macosx) and not defined(ios) and not defined(emscripten)) or defined(nimscript)
  93. when useNSGetEnviron:
  94. # From the manual:
  95. # Shared libraries and bundles don't have direct access to environ,
  96. # which is only available to the loader ld(1) when a complete program
  97. # is being linked.
  98. # The environment routines can still be used, but if direct access to
  99. # environ is needed, the _NSGetEnviron() routine, defined in
  100. # <crt_externs.h>, can be used to retrieve the address of environ
  101. # at runtime.
  102. proc NSGetEnviron(): ptr cstringArray {.
  103. importc: "_NSGetEnviron", header: "<crt_externs.h>".}
  104. elif defined(haiku):
  105. var gEnv {.importc: "environ", header: "<stdlib.h>".}: cstringArray
  106. else:
  107. var gEnv {.importc: "environ".}: cstringArray
  108. proc getEnvVarsC() =
  109. # retrieves the variables of char** env of C's main proc
  110. if not envComputed:
  111. environment = @[]
  112. when useNSGetEnviron:
  113. var gEnv = NSGetEnviron()[]
  114. var i = 0
  115. while gEnv[i] != nil:
  116. add environment, $gEnv[i]
  117. inc(i)
  118. envComputed = true
  119. proc findEnvVar(key: string): int =
  120. getEnvVarsC()
  121. var temp = key & '='
  122. for i in 0..high(environment):
  123. when defined(windows):
  124. if skipIgnoreCase(environment[i], temp) == len(temp): return i
  125. else:
  126. if startsWith(environment[i], temp): return i
  127. return -1
  128. proc getEnv*(key: string, default = ""): TaintedString {.tags: [ReadEnvEffect].} =
  129. ## Returns the value of the `environment variable`:idx: named `key`.
  130. ##
  131. ## If the variable does not exist, `""` is returned. To distinguish
  132. ## whether a variable exists or it's value is just `""`, call
  133. ## `existsEnv(key) proc <#existsEnv,string>`_.
  134. ##
  135. ## See also:
  136. ## * `existsEnv proc <#existsEnv,string>`_
  137. ## * `putEnv proc <#putEnv,string,string>`_
  138. ## * `delEnv proc <#delEnv,string>`_
  139. ## * `envPairs iterator <#envPairs.i>`_
  140. runnableExamples:
  141. assert getEnv("unknownEnv") == ""
  142. assert getEnv("unknownEnv", "doesn't exist") == "doesn't exist"
  143. when nimvm:
  144. discard "built into the compiler"
  145. else:
  146. var i = findEnvVar(key)
  147. if i >= 0:
  148. return TaintedString(substr(environment[i], find(environment[i], '=')+1))
  149. else:
  150. var env = c_getenv(key)
  151. if env == nil: return TaintedString(default)
  152. result = TaintedString($env)
  153. proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
  154. ## Checks whether the environment variable named `key` exists.
  155. ## Returns true if it exists, false otherwise.
  156. ##
  157. ## See also:
  158. ## * `getEnv proc <#getEnv,string,string>`_
  159. ## * `putEnv proc <#putEnv,string,string>`_
  160. ## * `delEnv proc <#delEnv,string>`_
  161. ## * `envPairs iterator <#envPairs.i>`_
  162. runnableExamples:
  163. assert not existsEnv("unknownEnv")
  164. when nimvm:
  165. discard "built into the compiler"
  166. else:
  167. if c_getenv(key) != nil: return true
  168. else: return findEnvVar(key) >= 0
  169. proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
  170. ## Sets the value of the `environment variable`:idx: named `key` to `val`.
  171. ## If an error occurs, `OSError` is raised.
  172. ##
  173. ## See also:
  174. ## * `getEnv proc <#getEnv,string,string>`_
  175. ## * `existsEnv proc <#existsEnv,string>`_
  176. ## * `delEnv proc <#delEnv,string>`_
  177. ## * `envPairs iterator <#envPairs.i>`_
  178. # Note: by storing the string in the environment sequence,
  179. # we guarantee that we don't free the memory before the program
  180. # ends (this is needed for POSIX compliance). It is also needed so that
  181. # the process itself may access its modified environment variables!
  182. when nimvm:
  183. discard "built into the compiler"
  184. else:
  185. var indx = findEnvVar(key)
  186. if indx >= 0:
  187. environment[indx] = key & '=' & val
  188. else:
  189. add environment, (key & '=' & val)
  190. indx = high(environment)
  191. when defined(windows) and not defined(nimscript):
  192. when useWinUnicode:
  193. var k = newWideCString(key)
  194. var v = newWideCString(val)
  195. if setEnvironmentVariableW(k, v) == 0'i32: raiseOSError(osLastError())
  196. else:
  197. if setEnvironmentVariableA(key, val) == 0'i32: raiseOSError(osLastError())
  198. else:
  199. if c_putenv(environment[indx]) != 0'i32:
  200. raiseOSError(osLastError())
  201. proc delEnv*(key: string) {.tags: [WriteEnvEffect].} =
  202. ## Deletes the `environment variable`:idx: named `key`.
  203. ## If an error occurs, `OSError` is raised.
  204. ##
  205. ## See also:ven
  206. ## * `getEnv proc <#getEnv,string,string>`_
  207. ## * `existsEnv proc <#existsEnv,string>`_
  208. ## * `putEnv proc <#putEnv,string,string>`_
  209. ## * `envPairs iterator <#envPairs.i>`_
  210. when nimvm:
  211. discard "built into the compiler"
  212. else:
  213. var indx = findEnvVar(key)
  214. if indx < 0: return # Do nothing if the env var is not already set
  215. when defined(windows) and not defined(nimscript):
  216. when useWinUnicode:
  217. var k = newWideCString(key)
  218. if setEnvironmentVariableW(k, nil) == 0'i32: raiseOSError(osLastError())
  219. else:
  220. if setEnvironmentVariableA(key, nil) == 0'i32: raiseOSError(osLastError())
  221. else:
  222. if c_unsetenv(key) != 0'i32:
  223. raiseOSError(osLastError())
  224. environment.delete(indx)
  225. iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} =
  226. ## Iterate over all `environments variables`:idx:.
  227. ##
  228. ## In the first component of the tuple is the name of the current variable stored,
  229. ## in the second its value.
  230. ##
  231. ## See also:
  232. ## * `getEnv proc <#getEnv,string,string>`_
  233. ## * `existsEnv proc <#existsEnv,string>`_
  234. ## * `putEnv proc <#putEnv,string,string>`_
  235. ## * `delEnv proc <#delEnv,string>`_
  236. getEnvVarsC()
  237. for i in 0..high(environment):
  238. var p = find(environment[i], '=')
  239. yield (TaintedString(substr(environment[i], 0, p-1)),
  240. TaintedString(substr(environment[i], p+1)))