#![doc(html_root_url = "https://docs.rs/get_env/0.1.0")]
#![cfg_attr(
any(
all(target_os = "linux", target_env = "gnu"),
target_os = "macos"
),
feature(used)
)]
#![warn(
missing_copy_implementations,
missing_debug_implementations,
missing_docs,
trivial_numeric_casts,
unused_extern_crates,
unused_import_braces,
unused_qualifications,
unused_results,
)] #![cfg_attr(feature = "cargo-clippy", warn(clippy_pedantic))]
#![cfg_attr(
feature = "cargo-clippy",
allow(type_complexity, option_option, indexing_slicing)
)]
#[macro_use]
extern crate lazy_static;
#[cfg(target_family = "unix")]
extern crate libc;
#[cfg(target_family = "unix")]
use libc::c_char;
#[cfg(
any(
target_family = "windows",
target_os = "macos",
target_os = "ios"
)
)]
use std::env;
#[cfg(target_family = "unix")]
use std::{ffi::CStr, os::unix::ffi::OsStringExt};
use std::{ffi::OsString, io, sync};
#[cfg(any(target_os = "android", target_os = "linux"))]
use std::{fs, io::Read};
lazy_static! {
static ref ARGV: sync::RwLock<Option<Option<Vec<OsString>>>> = sync::RwLock::new(None);
static ref ENVP: sync::RwLock<Option<Option<Vec<(OsString, OsString)>>>> =
sync::RwLock::new(None);
}
pub fn args() -> Option<Vec<String>> {
args_os().map(|x| x.into_iter().map(|a| a.into_string().unwrap()).collect())
}
pub fn vars() -> Option<Vec<(String, String)>> {
vars_os().map(|x| {
x.into_iter()
.map(|(a, b)| (a.into_string().unwrap(), b.into_string().unwrap()))
.collect()
})
}
pub fn args_os() -> Option<Vec<OsString>> {
if ARGV.read().unwrap().is_none() {
let mut write = ARGV.write().unwrap();
if write.is_none() {
*write = Some(argv_from_global().ok().or_else(|| argv_from_proc().ok()));
}
}
ARGV.read().unwrap().as_ref().unwrap().clone()
}
pub fn vars_os() -> Option<Vec<(OsString, OsString)>> {
if ENVP.read().unwrap().is_none() {
let mut write = ENVP.write().unwrap();
if write.is_none() {
*write = Some(envp_from_global().ok().or_else(|| envp_from_proc().ok()));
}
}
ENVP.read().unwrap().as_ref().unwrap().clone()
}
fn argv_from_global() -> Result<Vec<OsString>, ()> {
#[cfg(
any(
target_family = "windows",
target_os = "macos",
target_os = "ios"
)
)]
{
Ok(env::args_os().collect())
}
#[cfg(
not(
any(
target_family = "windows",
target_os = "macos",
target_os = "ios"
)
)
)]
{
Err(())
}
}
fn argv_from_proc() -> Result<Vec<OsString>, io::Error> {
#[cfg(any(target_os = "android", target_os = "linux"))]
{
let mut cmdline = Vec::new();
let _ = fs::File::open("/proc/self/cmdline")?
.read_to_end(&mut cmdline)
.unwrap(); if let Some(b'\0') = cmdline.last() {
let null = cmdline.pop().unwrap();
assert_eq!(null, b'\0');
}
Ok(cmdline
.split(|&x| x == b'\0')
.map(|x| OsStringExt::from_vec(x.to_vec()))
.collect::<Vec<_>>())
}
#[cfg(not(any(target_os = "android", target_os = "linux")))]
{
Err(io::Error::new(
io::ErrorKind::NotFound,
"no /proc/self/cmdline equivalent",
))
}
}
fn envp_from_global() -> Result<Vec<(OsString, OsString)>, ()> {
#[cfg(target_family = "unix")]
{
unsafe fn environ() -> *mut *const *const c_char {
#[cfg(target_os = "macos")]
{
extern "C" {
fn _NSGetEnviron() -> *mut *const *const c_char;
}
_NSGetEnviron()
}
#[cfg(not(target_os = "macos"))]
{
extern "C" {
static mut environ: *const *const c_char;
}
&mut environ
}
}
unsafe {
let mut environ = *environ();
if environ.is_null() {
return Err(());
}
let mut result = Vec::new();
while !(*environ).is_null() {
if let Some(key_value) = parse_env(CStr::from_ptr(*environ).to_bytes()) {
result.push(key_value);
}
environ = environ.offset(1);
}
Ok(result)
}
}
#[cfg(target_family = "windows")]
{
Ok(env::vars_os().collect())
}
}
fn envp_from_proc() -> Result<Vec<(OsString, OsString)>, io::Error> {
#[cfg(any(target_os = "android", target_os = "linux"))]
{
let mut envp = Vec::new();
let _ = fs::File::open("/proc/self/environ")?
.read_to_end(&mut envp)
.unwrap(); if let Some(b'\0') = envp.last() {
let null = envp.pop().unwrap();
assert_eq!(null, b'\0');
}
Ok(envp
.split(|&x| x == b'\0')
.flat_map(|x| parse_env(x))
.collect::<Vec<_>>())
}
#[cfg(not(any(target_os = "android", target_os = "linux")))]
{
Err(io::Error::new(
io::ErrorKind::NotFound,
"no /proc/self/environ equivalent",
))
}
}
#[cfg(target_family = "unix")]
fn parse_env(input: &[u8]) -> Option<(OsString, OsString)> {
if input.is_empty() {
return None;
}
let pos = input[1..].iter().position(|&x| x == b'=').map(|p| p + 1);
pos.map(|p| {
(
OsStringExt::from_vec(input[..p].to_vec()),
OsStringExt::from_vec(input[p + 1..].to_vec()),
)
})
}
#[doc(hidden)]
#[cfg(
any(
all(target_os = "linux", target_env = "gnu"),
target_os = "macos"
)
)]
#[cfg_attr(
all(target_os = "linux", target_env = "gnu"),
link_section = ".init_array"
)]
#[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")]
#[used] pub static GRAB_ARGV_ENVP: extern "C" fn(
argc: libc::c_int,
argv: *const *const c_char,
envp: *const *const c_char,
) = {
extern "C" fn grab_argv_envp(
_argc: libc::c_int, argv: *const *const c_char, envp: *const *const c_char,
) {
unsafe {
let mut collect_args: Vec<OsString> = Vec::new();
let mut argv = argv;
while !(*argv).is_null() {
collect_args.push(OsStringExt::from_vec(
CStr::from_ptr(*argv).to_bytes().to_vec(),
));
argv = argv.offset(1);
}
let mut collect_vars: Vec<(OsString, OsString)> = Vec::new();
let mut envp = envp;
while !(*envp).is_null() {
let x = CStr::from_ptr(*envp).to_bytes();
if let Some(x) = parse_env(x) {
collect_vars.push(x);
}
envp = envp.offset(1);
}
*ARGV.write().unwrap() = Some(Some(collect_args));
*ENVP.write().unwrap() = Some(Some(collect_vars));
}
}
grab_argv_envp
};
#[cfg(test)]
mod tests {
use super::*;
use std::env;
#[test]
fn same() {
let args = vec![
ARGV.read().unwrap().clone().unwrap_or(None),
argv_from_global().ok(),
argv_from_proc().ok(),
Some(env::args_os().collect::<Vec<_>>()),
];
let mut args2 = args.clone().into_iter().flat_map(|x| x).collect::<Vec<_>>();
args2.dedup();
assert!(args2.len() == 1, "{:?}", args);
let args = vec![
ENVP.read().unwrap().clone().unwrap_or(None),
envp_from_global().ok(),
envp_from_proc().ok(),
Some(env::vars_os().collect::<Vec<_>>()),
];
let mut args2 = args.clone().into_iter().flat_map(|x| x).collect::<Vec<_>>();
args2.dedup();
assert!(args2.len() == 1, "{:?}", args);
}
}