hictor 0.1.6

declarative macro for __attribute__((constructor))/__attribute__((destructor))
Documentation
#![no_std]

#[macro_export]
macro_rules! ctor_decl {
    ($($tt: tt)*) => {
        #[cfg(not(any(target_os = "linux", target_os = "android",
                target_os = "freebsd", target_os = "netbsd",
                target_os = "openbsd", target_os = "dragonfly",
                target_os = "illumos", target_os = "haiku",
                target_os = "windows",
                target_os = "macos", target_os = "ios")))]
        compile_error!("ctor don't support the current target_os");
        #[cfg_attr(
            any(target_os = "linux", target_os = "android",
                target_os = "freebsd", target_os = "netbsd",
                target_os = "openbsd", target_os = "dragonfly",
                target_os = "illumos", target_os = "haiku"),
            link_section = ".init_array")]
        #[cfg_attr(
            any(target_os = "macos", target_os = "ios"),
            link_section = "__DATA,__mod_init_func")]
        #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
        $($tt)*
    }
}

#[macro_export]
macro_rules! dtor_decl {
    ($($tt: tt)*) => {
        #[cfg(not(any(target_os = "linux", target_os = "android",
                target_os = "freebsd", target_os = "netbsd",
                target_os = "openbsd", target_os = "dragonfly",
                target_os = "illumos", target_os = "haiku",
                target_os = "macos", target_os = "ios")))]
        compile_error!("dtor don't support the current target_os");
        #[cfg_attr(
            any(target_os = "linux", target_os = "android",
                target_os = "freebsd", target_os = "netbsd",
                target_os = "openbsd", target_os = "dragonfly",
                target_os = "illumos", target_os = "haiku"),
            link_section = ".fini_array")]
        #[cfg_attr(
            any(target_os = "macos", target_os = "ios"),
            link_section = "__DATA,__mod_term_func")]
        $($tt)*
    }
}

/// 要求函数类型必须是 `unsafe fn()`, 对应 `int main()`.
/// 如果存在和函数同名的模块,需要指定一个不冲突的模块名称.
/// ## Example
/// ```rust
/// unsafe fn init() { 
///     println!("init before main"); 
/// }
/// hictor::ctor!(init);
/// fn main() {
///     println!("hello world");
/// }
/// ```
#[macro_export]
macro_rules! ctor {
    ($init: ident) => {
        $crate::ctor!($init, $init);
    };
    ($mod_name: ident, $init: ident) => {
        mod $mod_name {
            $crate::ctor_decl!{ 
                #[used]
                static INIT: unsafe fn() = super::$init;
            }
        }
    };
}

/// 要求函数类型必须是 `unsafe extern "C" fn(i32, *const *const i8)`, 对应 `int main(int argc, char* argv[])`.
/// 如果存在和函数同名的模块,需要指定一个不冲突的模块名称.
/// ## Example
/// ```rust
/// unsafe extern "C" fn init(argc: i32, argv: *const *const i8) {
///     println!("before main - argc = {argc}, program = {:?}", core::ffi::CStr::from_ptr(*argv));
/// }
/// hictor::ctor_args!(init);
/// fn main() {
///     println!("hello world");
/// }
/// ```
#[macro_export]
macro_rules! ctor_args {
    ($init: ident) => {
        $crate::ctor_args!($init, $init);
    };
    ($mod_name: ident, $init: ident) => {
        mod $mod_name {
            $crate::ctor_decl!{
                #[used]
                static INIT: unsafe extern "C" fn(i32, *const *const i8) = super::$init;
            }
        }
    };
}

/// 不同平台支持更多的参数,比如linux平台下,对应`int main(int argc, char* argv[], char* env[])`.
/// 是否支持更多参数,以及具体参数类型由具体平台定义,但rust不支持变长参数类型,无法实现如下的rust函数`unsafe
/// extern "C" fn(i32, *const *const i8, ...)`, 这里无法限定类型,完全由用户自由指定并保证和target_os要求的ABI一致.
/// 如果存在和函数同名的模块,需要指定一个不冲突的模块名称.
/// ## Example
/// ```rust
/// #[cfg(target_os = "linux")]
/// unsafe extern "C" fn init(argc: i32, argv: *const *const i8, envs: *const *const i8) {
///     println!("before main - argc = {argc}, program = {:?}, env.0 = {:?}", core::ffi::CStr::from_ptr(*argv), core::ffi::CStr::from_ptr(*envs));
/// }
/// #[cfg(target_os = "linux")]
/// hictor::ctor_custom!(init);
/// fn main() {
///     println!("hello world");
/// }
/// ```
#[macro_export]
macro_rules! ctor_custom {
    ($init: ident) => {
        $crate::ctor_custom!($init, $init);
    };
    ($mod_name: ident, $init: ident) => {
        mod $mod_name {
            #[repr(transparent)]
            struct InitFunc {
                init: *const (),
            }
            unsafe impl Sync for InitFunc {}
            $crate::ctor_decl!{
                #[used]
                static INIT: InitFunc = InitFunc { init: super::$init as *const (), };
            }
        }
    };
}

/// 要求函数类型必须是`unsafe fn()`.
/// 如果存在和函数同名的模块,需要指定一个不冲突的模块名称.
/// ## Example
/// ```rust
/// unsafe fn fini() {
///     println!("after main"); 
/// }
/// hictor::dtor!(fini);
/// fn main() {
///     println!("hello world");
/// }
/// ```
#[macro_export]
macro_rules! dtor {
    ($fini: ident) => {
        $crate::dtor!($fini, $fini);
    };
    ($mod_name: ident, $fini: ident) => {
        mod $mod_name {
            use super::$fini;
            $crate::dtor_decl!{
                #[used]
                static FINI: unsafe fn() = $fini;
            }
        }
    };
}

static mut ARGS: Option<&'static [*const i8]> = None;

unsafe extern "C" fn init_args(argc: i32, argv: *const *const i8) {
    let args = core::slice::from_raw_parts(argv, argc as usize);
    ARGS = Some(args);
}

ctor_args!(init_args);

/// 获取应用程序的命令行输入
/// # Panics
/// 应该在进入main函数后才调用,否则可能因为未初始化而panic.
#[inline(always)]
pub fn args() -> &'static [*const i8] {
    unsafe { ARGS.unwrap() }
}

/// 应用程序的名字, 命令行的第一个参数.
/// # Panics
/// 应该在进入main函数后才调用,否则可能因为未初始化而panic.
pub fn program_invocation_name() -> &'static str {
    let args = args();
    let name = args[0];
    let mut n = 0;
    loop {
        let c = unsafe { name.add(n).read() };
        if c == 0 {
            let bytes =  unsafe { core::slice::from_raw_parts(args[0].cast::<u8>(), n) };
            return unsafe { core::str::from_utf8_unchecked(bytes) };
        }
        n += 1;
    }
}

/// 应用程序的名字, 命令行的第一个参数,不含路径.
/// # Panics
/// 应该在进入main函数后才调用,否则可能因为未初始化而panic.
pub fn program_invocation_short_name() -> &'static str {
    let name = program_invocation_name().as_bytes();
    let name = name.rsplit(|b| *b == b'/' || *b == b'\\').next().unwrap();
    unsafe { core::str::from_utf8_unchecked(name) }

}