osenv.nim 7.7 KB

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