1use crate::{c_str_to_rust, memory, sys};
13use crate::memory::Malloc;
14
15use core::{cmp, mem, ptr};
16
17const NAME_LEN: usize = 1024;
18
19#[derive(Debug)]
20pub enum EnvError {
22 NotPresent,
24 NotUnicode,
26}
27
28pub fn get_var(name: &str) -> Result<&'static str, EnvError> {
32 debug_assert!(name.len() > 0, "Empty variable name makes no sense");
33 debug_assert!(name.len() <= NAME_LEN);
34
35 let mut name_buff = mem::MaybeUninit::<[i8; NAME_LEN + 1]>::uninit();
36
37 let len = cmp::min(NAME_LEN, name.len());
38
39 unsafe {
40 let name_ptr = name_buff.as_mut_ptr() as *mut i8;
41 ptr::copy_nonoverlapping(name.as_ptr() as *const i8, name_ptr, len);
42 ptr::write(name_ptr.add(len), 0);
43 }
44
45 let result = unsafe {
46 sys::getenv(name_buff.as_ptr() as *const i8)
47 };
48
49 if result.is_null() {
50 return Err(EnvError::NotPresent);
51 }
52
53 unsafe { c_str_to_rust(result as *const u8).map_err(|_| EnvError::NotUnicode) }
54}
55
56#[cfg(windows)]
57extern "system" {
58 pub fn MultiByteToWideChar(cp: libc::c_uint, flags: libc::c_ulong, in_str: *const i8, in_size: libc::c_int, out_str: *mut u16, out_size: libc::c_int) -> libc::c_int;
59 pub fn SetEnvironmentVariableW(name: *const u16, value: *const u16) -> libc::c_int;
60}
61
62#[cfg(not(windows))]
63pub fn set_var(name: &str, value: &str) -> bool {
67 debug_assert!(name.len() > 0, "Empty variable name makes no sense");
68 debug_assert!(value.len() > 0, "Empty variable value makes no sense");
69 debug_assert!(name.len() <= NAME_LEN);
70
71 let mut name_buff = mem::MaybeUninit::<[i8; NAME_LEN + 1]>::uninit();
72
73 let len = cmp::min(NAME_LEN, name.len());
74
75 unsafe {
76 let name_ptr = name_buff.as_mut_ptr() as *mut i8;
77 ptr::copy_nonoverlapping(name.as_ptr() as *const i8, name_ptr, len);
78 ptr::write(name_ptr.add(len), 0);
79 }
80
81 let value_store = memory::Box::malloc(mem::size_of::<u8>() * value.len() + mem::size_of::<u8>());
82
83 unsafe {
84 ptr::copy_nonoverlapping(value.as_ptr() as *const i8, value_store.cast::<i8>(), value.len());
85 ptr::write(value_store.cast::<i8>().add(value.len()), 0);
86
87 libc::setenv(name_buff.as_ptr() as *const i8, value_store.const_cast::<i8>(), 1) == 0
88 }
89
90}
91
92#[cfg(windows)]
93pub fn set_var(name: &str, value: &str) -> bool {
97 const U16_SIZE: usize = mem::size_of::<u16>();
98
99 debug_assert!(name.len() > 0, "Empty variable name makes no sense");
100 debug_assert!(value.len() > 0, "Empty variable value makes no sense");
101
102 let name_size = unsafe {
103 MultiByteToWideChar(65001, 0, name.as_ptr() as *const _, name.len() as libc::c_int, ptr::null_mut(), 0)
104 };
105
106 if name_size == 0 {
107 return false;
108 }
109
110 let value_size = unsafe {
111 MultiByteToWideChar(65001, 0, value.as_ptr() as *const _, value.len() as libc::c_int, ptr::null_mut(), 0)
112 };
113
114 if value_size == 0 {
115 return false;
116 }
117
118 let name_store = memory::Box::malloc(U16_SIZE * name_size as usize + U16_SIZE);
119 let value_store = memory::Box::malloc(U16_SIZE * value_size as usize + U16_SIZE);
120
121 unsafe {
122 let mut result = MultiByteToWideChar(65001, 0, name.as_ptr() as *const _, name.len() as libc::c_int, name_store.cast::<u16>(), name_size);
123
124 if result == 0 {
125 return false;
126 }
127
128 ptr::write(name_store.cast::<u16>().offset(result as isize), 0);
129
130 result = MultiByteToWideChar(65001, 0, value.as_ptr() as *const _, value.len() as libc::c_int, value_store.cast::<u16>(), value_size);
131
132 if result == 0 {
133 return false;
134 }
135
136 ptr::write(value_store.cast::<u16>().offset(result as isize), 0);
137
138 SetEnvironmentVariableW(name_store.const_cast::<u16>(), value_store.const_cast::<u16>()) != 0
139 }
140}
141
142#[cfg(not(windows))]
143pub fn unset_var(name: &str) -> bool {
147 debug_assert!(name.len() > 0, "Empty variable name makes no sense");
148 debug_assert!(name.len() <= NAME_LEN);
149
150 let mut name_buff = mem::MaybeUninit::<[i8; NAME_LEN + 1]>::uninit();
151
152 let len = cmp::min(NAME_LEN, name.len());
153
154 unsafe {
155 let name_ptr = name_buff.as_mut_ptr() as *mut i8;
156 ptr::copy_nonoverlapping(name.as_ptr() as *const i8, name_ptr, len);
157 ptr::write(name_ptr.add(len), 0);
158 }
159
160 unsafe {
161 libc::unsetenv(name_buff.as_ptr() as *const i8) == 0
162 }
163}
164
165#[cfg(windows)]
166pub fn unset_var(name: &str) -> bool {
170 const U16_SIZE: usize = mem::size_of::<u16>();
171
172 debug_assert!(name.len() > 0, "Empty variable name makes no sense");
173
174 let name_size = unsafe {
175 MultiByteToWideChar(65001, 0, name.as_ptr() as *const _, name.len() as libc::c_int, ptr::null_mut(), 0)
176 };
177
178 if name_size == 0 {
179 return false;
180 }
181
182 let name_store = memory::Box::malloc(U16_SIZE * name_size as usize + U16_SIZE);
183
184 unsafe {
185 let result = MultiByteToWideChar(65001, 0, name.as_ptr() as *const _, name.len() as libc::c_int, name_store.cast::<u16>(), name_size);
186
187 if result == 0 {
188 return false;
189 }
190
191 ptr::write(name_store.cast::<u16>().offset(result as isize), 0);
192
193 SetEnvironmentVariableW(name_store.const_cast::<u16>(), ptr::null()) != 0
194 }
195}