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
//!Collection of utilities to work with C stuff
#![warn(missing_docs)]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::style))]
#![no_std]

mod sys;
pub use sys::*;

#[cfg(feature = "libc")]
pub mod locale;
#[cfg(all(feature = "libc", feature = "memory"))]
pub mod memory;
#[cfg(all(feature = "libc", feature = "memory"))]
pub mod env;
#[cfg(feature = "libc")]
pub mod process;
pub mod args;
pub use args::Args;

#[cfg(feature = "libc")]
#[allow(non_camel_case_types)]
///Alias to C's `int` type
///
///With `libc` enabled, alias to `libc::c_int`
pub type int = libc::c_int;
#[cfg(not(feature = "libc"))]
#[allow(non_camel_case_types)]
///Alias to C's `int` type
pub type int = i32;

///Converts C string to Rust's, verifying it is UTF-8
///
///It is UB to pass non-C string as it requires \0
pub unsafe fn c_str_to_rust(ptr: *const u8) -> Result<&'static str, core::str::Utf8Error> {
    let len = strlen(ptr as *const i8);
    let parts = core::slice::from_raw_parts(ptr, len);
    core::str::from_utf8(parts)
}

///Converts C string to Rust's one assuming it is UTF-8
///
///It is UB to pass non-C string as it requires \0
pub unsafe fn c_str_to_rust_unchecked(ptr: *const u8) -> &'static str {
    let len = strlen(ptr as *const i8);
    let parts = core::slice::from_raw_parts(ptr, len);
    core::str::from_utf8_unchecked(parts)
}

///Creates C compatible string that ends with null character
#[macro_export]
macro_rules! c_lit {
    ($e:expr) => {
        core::concat!($e, "\0")
    };
    ($($e:tt)+) => {
        core::concat!($($e)+, "\0")
    };
}

///Declares main function with C like signature, passing CLI arugments via `Args`
///
///## Arguments
///
///- `runner` - Function name to accepts `Args` instance
///- `int` - Integer type used for `argc` and return value. In C it is `int` and therefore by default macro uses `$crate::int`.
///
///## Return value
///
///C++ `main` expects integer return value, therefore it automatically converts to `int` type
///
///## Panic
///
///If supplied command line arguments contain non-UTF8 string, then function panics
///
///## Usage
///
/// Default `int` type`
///
///```rust
///#![no_main]
///
///fn my_main(args: c_ffi::Args) -> bool {
///    match args.into_iter().count() {
///        0 => true,
///        _ => false,
///    }
///}
///
///c_ffi::c_main!(my_main);
/////In case you'd like to specify return type yourself
/////c_ffi::c_main!(my_main -> i32);
#[macro_export]
macro_rules! c_main {
    ($runner:ident) => {
        use $crate::int;
        $crate::c_main!($runner -> int);
    };
    ($runner:ident -> $int:ident) => {
        #[no_mangle]
        pub unsafe extern fn main(argc: $int, argv: *const *const u8) -> $int {
            match $crate::Args::new(argc as isize, argv) {
                Ok(args) => $runner(args).into(),
                Err(_) => panic!("Unable to use non-utf8 arguments"),
            }
        }
    }
}