#![doc(html_root_url = "https://docs.rs/argv/0.1.13")]
#![deny(unsafe_op_in_unsafe_fn)]
#![allow(
clippy::cast_sign_loss,
clippy::extra_unused_type_parameters,
clippy::let_underscore_untyped,
clippy::must_use_candidate,
clippy::needless_doctest_main,
clippy::similar_names
)]
use std::ffi::OsStr;
pub fn iter() -> Iter {
Iter {
platform_specific: crate::r#impl::iter(),
}
}
pub struct Iter {
platform_specific: crate::r#impl::Iter,
}
impl Iterator for Iter {
type Item = &'static OsStr;
fn next(&mut self) -> Option<Self::Item> {
self.platform_specific.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.platform_specific.size_hint()
}
}
impl ExactSizeIterator for Iter {
fn len(&self) -> usize {
self.platform_specific.len()
}
}
#[cfg(all(target_os = "linux", target_env = "gnu", not(miri)))]
mod r#impl {
use std::ffi::{CStr, OsStr};
use std::mem;
use std::os::raw::{c_char, c_int};
use std::os::unix::ffi::OsStrExt;
use std::ptr;
static mut ARGC: c_int = 0;
static mut ARGV: *const *const c_char = ptr::null();
#[cfg(target_os = "linux")]
#[link_section = ".init_array"]
#[used]
static CAPTURE: unsafe extern "C" fn(c_int, *const *const c_char) = capture;
#[allow(dead_code)]
unsafe extern "C" fn capture(argc: c_int, argv: *const *const c_char) {
unsafe {
ARGC = argc;
ARGV = argv;
}
}
pub(crate) fn iter() -> Iter {
let argc = unsafe { ARGC };
let argv = unsafe { ARGV };
let max_end = unsafe { argv.offset(argc as isize) };
let mut end = argv;
while end < max_end && !unsafe { *end }.is_null() {
end = unsafe { end.add(1) };
}
Iter { next: argv, end }
}
pub(crate) struct Iter {
next: *const *const c_char,
end: *const *const c_char,
}
impl Iterator for Iter {
type Item = &'static OsStr;
fn next(&mut self) -> Option<Self::Item> {
if ptr::eq(self.next, self.end) {
None
} else {
let ptr = unsafe { *self.next };
let c_str = unsafe { CStr::from_ptr(ptr) };
self.next = unsafe { self.next.offset(1) };
Some(OsStr::from_bytes(c_str.to_bytes()))
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
}
}
impl ExactSizeIterator for Iter {
fn len(&self) -> usize {
(self.end as usize - self.next as usize) / mem::size_of::<*const c_char>()
}
}
unsafe impl Send for Iter {}
unsafe impl Sync for Iter {}
}
#[cfg(any(not(target_os = "linux"), not(target_env = "gnu"), miri))]
mod r#impl {
use std::ffi::OsStr;
use std::sync::Once;
use std::{env, iter, ptr, slice};
static ONCE: Once = Once::new();
static mut ARGV: Vec<&'static OsStr> = Vec::new();
pub(crate) fn iter() -> Iter {
ONCE.call_once(|| {
let argv = env::args_os()
.map(|arg| -> &OsStr { Box::leak(arg.into_boxed_os_str()) })
.collect();
unsafe { ARGV = argv }
});
let argv = unsafe { &*ptr::addr_of!(ARGV) };
argv.iter().copied()
}
pub(crate) type Iter = iter::Copied<slice::Iter<'static, &'static OsStr>>;
}
const _AUTO_TRAITS: () = {
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
let _ = assert_send::<Iter>;
let _ = assert_sync::<Iter>;
};