1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
//! See documentation on `args`.

#[cfg(all(
    target_os = "linux",
    any(not(target_env = "gnu"), feature = "force_walk")
))]
mod imp {
    use once_cell::sync::Lazy;
    use std::ffi::CStr;
    use std::os::raw::*;

    extern "C" {
        pub static __environ: *const *const c_char;
    }

    fn raw_args() -> (c_int, *const *const c_char) {
        let mut walk_environ = unsafe { __environ as *const usize };
        walk_environ = walk_environ.wrapping_offset(-1);
        let mut i = 0;

        loop {
            let argc_ptr = walk_environ.wrapping_offset(-1) as *const c_int;
            let argc = unsafe { *argc_ptr };
            if argc == i {
                break (argc, walk_environ as *const *const c_char);
            }
            walk_environ = walk_environ.wrapping_offset(-1);
            i += 1;
        }
    }

    /// Get the arguments as a vector, even if the function is called from a C program via FFI.
    /// Supports Windows, macOS, and Linux, and will return an empty Vec on unsupported platforms.
    pub fn args() -> Vec<String> {
        static ARGS: Lazy<Vec<String>> = Lazy::new(|| {
            let (argc, argv) = raw_args();
            (0..argc)
                .map(|i| unsafe {
                    CStr::from_ptr(*argv.wrapping_offset(i as isize))
                        .to_string_lossy()
                        .into()
                })
                .collect()
        });
        ARGS.clone()
    }
}

#[cfg(all(
    target_os = "linux",
    all(target_env = "gnu", not(feature = "force_walk"))
))]
mod imp {
    use once_cell::sync::OnceCell;
    use std::ffi::CStr;
    use std::os::raw::*;

    static ARGS: OnceCell<Vec<String>> = OnceCell::new();

    #[used]
    #[link_section = ".init_array.00099"]
    #[no_mangle]
    static SET_ARGS: [extern "C" fn(c_int, *const *const c_char, *const *const c_char); 1] = {
        extern "C" fn set_args(
            argc: c_int,
            argv: *const *const c_char,
            _env: *const *const c_char,
        ) {
            ARGS.set(
                (0..argc)
                    .map(|i| unsafe {
                        let cstr = CStr::from_ptr(*argv.offset(i as isize));
                        String::from_utf8_lossy(cstr.to_bytes()).into()
                    })
                    .collect(),
            )
            .unwrap();
        }

        [set_args]
    };

    /// Get the arguments as a vector, even if the function is called from a C program via FFI.
    /// Supports Windows, macOS, and Linux, and will return an empty Vec on unsupported platforms.
    pub fn args() -> Vec<String> {
        ARGS.get().unwrap().clone()
    }
}

#[cfg(not(target_os = "linux"))]
mod imp {
    /// Get the arguments as a vector, even if the function is called from a C program via FFI.
    /// Supports Windows, macOS, and Linux, and will return an empty Vec on unsupported platforms.
    pub fn args() -> Vec<String> {
        std::env::args().collect()
    }
}

pub use imp::args;

#[cfg(test)]
mod tests {
    #[test]
    fn args() {
        assert_eq!(super::args(), std::env::args().collect::<Vec<_>>());
    }
}