c_ffi/
env.rs

1//!Environment variables
2//!
3//!This module is only available with `libc` feature
4//!
5//!Note that it limits environment variable names to 1024, and asserts in debug mod against using
6//!bigger names.
7//!
8//!## Required features:
9//!
10//! - `memory`
11
12use 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)]
20///Possible errors when getting environment variable.
21pub enum EnvError {
22    ///Environment variable with the name is not present.
23    NotPresent,
24    ///Environment variable cannot be converted to unicode.
25    NotUnicode,
26}
27
28///Gets environment variable using `getenv`
29///
30///Note that the result is is valid until next call of `get_var`
31pub 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))]
63///Sets environment variable using `setenv`.
64///
65///Returns `true` on success.
66pub 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)]
93///Sets environment variable using `SetEnvironmentVariableW` on windows.
94///
95///Returns `true` on success.
96pub 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))]
143///Unsets environment variable using `unsetenv`.
144///
145///Returns `true` on success.
146pub 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)]
166///Sets environment variable using `SetEnvironmentVariableW` on windows.
167///
168///Returns `true` on success.
169pub 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}