env_linux.odin 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. #+private
  2. package os2
  3. import "base:runtime"
  4. import "base:intrinsics"
  5. import "core:sync"
  6. import "core:slice"
  7. import "core:strings"
  8. // TODO: IF NO_CRT:
  9. // Override the libc environment functions' weak linkage to
  10. // allow us to interact with 3rd party code that DOES link
  11. // to libc. Otherwise, our environment can be out of sync.
  12. // ELSE:
  13. // Just use the libc.
  14. NOT_FOUND :: -1
  15. // the environment is a 0 delimited list of <key>=<value> strings
  16. _env: [dynamic]string
  17. _env_mutex: sync.Recursive_Mutex
  18. // We need to be able to figure out if the environment variable
  19. // is contained in the original environment or not. This also
  20. // serves as a flag to determine if we have built _env.
  21. _org_env_begin: uintptr // atomic
  22. _org_env_end: uintptr // guarded by _env_mutex
  23. // Returns value + index location into _env
  24. // or -1 if not found
  25. _lookup :: proc(key: string) -> (value: string, idx: int) {
  26. sync.guard(&_env_mutex)
  27. for entry, i in _env {
  28. if k, v := _kv_from_entry(entry); k == key {
  29. return v, i
  30. }
  31. }
  32. return "", -1
  33. }
  34. _lookup_env_alloc :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
  35. if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
  36. _build_env()
  37. }
  38. if v, idx := _lookup(key); idx != -1 {
  39. found = true
  40. value, _ = clone_string(v, allocator)
  41. }
  42. return
  43. }
  44. _lookup_env_buf :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
  45. if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
  46. _build_env()
  47. }
  48. if v, idx := _lookup(key); idx != -1 {
  49. if len(buf) >= len(v) {
  50. copy(buf, v)
  51. return string(buf[:len(v)]), nil
  52. }
  53. return "", .Buffer_Full
  54. }
  55. return "", .Env_Var_Not_Found
  56. }
  57. _lookup_env :: proc{_lookup_env_alloc, _lookup_env_buf}
  58. _set_env :: proc(key, v_new: string) -> Error {
  59. if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
  60. _build_env()
  61. }
  62. sync.guard(&_env_mutex)
  63. // all key values are stored as "key=value\x00"
  64. kv_size := len(key) + len(v_new) + 2
  65. if v_curr, idx := _lookup(key); idx != NOT_FOUND {
  66. if v_curr == v_new {
  67. return nil
  68. }
  69. unordered_remove(&_env, idx)
  70. if !_is_in_org_env(v_curr) {
  71. // We allocated this key-value. Possibly resize and
  72. // overwrite the value only. Otherwise, treat as if it
  73. // wasn't in the environment in the first place.
  74. k_addr, v_addr := _kv_addr_from_val(v_curr, key)
  75. if len(v_new) > len(v_curr) {
  76. k_addr = ([^]u8)(runtime.heap_resize(k_addr, kv_size))
  77. if k_addr == nil {
  78. return .Out_Of_Memory
  79. }
  80. v_addr = &k_addr[len(key) + 1]
  81. }
  82. intrinsics.mem_copy_non_overlapping(v_addr, raw_data(v_new), len(v_new))
  83. v_addr[len(v_new)] = 0
  84. append(&_env, string(k_addr[:kv_size]))
  85. return nil
  86. }
  87. }
  88. k_addr := ([^]u8)(runtime.heap_alloc(kv_size))
  89. if k_addr == nil {
  90. return .Out_Of_Memory
  91. }
  92. intrinsics.mem_copy_non_overlapping(k_addr, raw_data(key), len(key))
  93. k_addr[len(key)] = '='
  94. val_slice := k_addr[len(key) + 1:]
  95. intrinsics.mem_copy_non_overlapping(&val_slice[0], raw_data(v_new), len(v_new))
  96. val_slice[len(v_new)] = 0
  97. append(&_env, string(k_addr[:kv_size - 1]))
  98. return nil
  99. }
  100. _unset_env :: proc(key: string) -> bool {
  101. if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
  102. _build_env()
  103. }
  104. sync.guard(&_env_mutex)
  105. v: string
  106. i: int
  107. if v, i = _lookup(key); i == -1 {
  108. return false
  109. }
  110. unordered_remove(&_env, i)
  111. if _is_in_org_env(v) {
  112. return true
  113. }
  114. // if we got this far, the environment variable
  115. // existed AND was allocated by us.
  116. k_addr, _ := _kv_addr_from_val(v, key)
  117. runtime.heap_free(k_addr)
  118. return true
  119. }
  120. _clear_env :: proc() {
  121. sync.guard(&_env_mutex)
  122. for kv in _env {
  123. if !_is_in_org_env(kv) {
  124. runtime.heap_free(raw_data(kv))
  125. }
  126. }
  127. clear(&_env)
  128. // nothing resides in the original environment either
  129. intrinsics.atomic_store_explicit(&_org_env_begin, ~uintptr(0), .Release)
  130. _org_env_end = ~uintptr(0)
  131. }
  132. _environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error) {
  133. if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
  134. _build_env()
  135. }
  136. sync.guard(&_env_mutex)
  137. env := make([dynamic]string, 0, len(_env), allocator) or_return
  138. defer if err != nil {
  139. for e in env {
  140. delete(e, allocator)
  141. }
  142. delete(env)
  143. }
  144. for entry in _env {
  145. s := clone_string(entry, allocator) or_return
  146. append(&env, s)
  147. }
  148. environ = env[:]
  149. return
  150. }
  151. // The entire environment is stored as 0 terminated strings,
  152. // so there is no need to clone/free individual variables
  153. export_cstring_environment :: proc(allocator: runtime.Allocator) -> []cstring {
  154. if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
  155. // The environment has not been modified, so we can just
  156. // send the original environment
  157. org_env := _get_original_env()
  158. n: int
  159. for ; org_env[n] != nil; n += 1 {}
  160. return slice.clone(org_env[:n + 1], allocator)
  161. }
  162. sync.guard(&_env_mutex)
  163. // NOTE: already terminated by nil pointer via + 1
  164. env := make([]cstring, len(_env) + 1, allocator)
  165. for entry, i in _env {
  166. env[i] = cstring(raw_data(entry))
  167. }
  168. return env
  169. }
  170. _build_env :: proc() {
  171. sync.guard(&_env_mutex)
  172. if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) != 0 {
  173. return
  174. }
  175. _env = make(type_of(_env), runtime.heap_allocator())
  176. cstring_env := _get_original_env()
  177. intrinsics.atomic_store_explicit(&_org_env_begin, uintptr(rawptr(cstring_env[0])), .Release)
  178. for i := 0; cstring_env[i] != nil; i += 1 {
  179. bytes := ([^]u8)(cstring_env[i])
  180. n := len(cstring_env[i])
  181. _org_env_end = uintptr(&bytes[n])
  182. append(&_env, string(bytes[:n]))
  183. }
  184. }
  185. _get_original_env :: #force_inline proc() -> [^]cstring {
  186. // essentially &argv[argc] which should be a nil pointer!
  187. #no_bounds_check env: [^]cstring = &runtime.args__[len(runtime.args__)]
  188. assert(env[0] == nil)
  189. return &env[1]
  190. }
  191. _kv_from_entry :: #force_inline proc(entry: string) -> (k, v: string) {
  192. eq_idx := strings.index_byte(entry, '=')
  193. if eq_idx == -1 {
  194. return entry, ""
  195. }
  196. return entry[:eq_idx], entry[eq_idx + 1:]
  197. }
  198. _kv_addr_from_val :: #force_inline proc(val: string, key: string) -> ([^]u8, [^]u8) {
  199. v_addr := raw_data(val)
  200. k_addr := ([^]u8)(&v_addr[-(len(key) + 1)])
  201. return k_addr, v_addr
  202. }
  203. _is_in_org_env :: #force_inline proc(env_data: string) -> bool {
  204. addr := uintptr(raw_data(env_data))
  205. return addr >= intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) && addr < _org_env_end
  206. }