202 lines
6.5 KiB
Rust
202 lines
6.5 KiB
Rust
//! getopt implementation for relibc
|
|
|
|
use core::ptr;
|
|
use header::{stdio, string};
|
|
use header::unistd::{optarg, optind, opterr, optopt};
|
|
use platform::types::*;
|
|
|
|
static mut CURRENT_OPT: *mut c_char = ptr::null_mut();
|
|
|
|
pub const no_argument: c_int = 0;
|
|
pub const required_argument: c_int = 1;
|
|
pub const optional_argument: c_int = 2;
|
|
|
|
#[repr(C)]
|
|
pub struct option {
|
|
name: *const c_char,
|
|
has_arg: c_int,
|
|
flag: *mut c_int,
|
|
val: c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn getopt_long(
|
|
argc: c_int,
|
|
argv: *const *mut c_char,
|
|
optstring: *const c_char,
|
|
longopts: *const option,
|
|
longindex: *mut c_int
|
|
) -> c_int {
|
|
// if optarg is not set, we still don't want the previous value leaking
|
|
optarg = ptr::null_mut();
|
|
|
|
if CURRENT_OPT.is_null() || *CURRENT_OPT == 0 {
|
|
if optind >= argc {
|
|
-1
|
|
} else {
|
|
let current_arg = *argv.offset(optind as isize);
|
|
if current_arg.is_null()
|
|
|| *current_arg != b'-' as c_char
|
|
|| *current_arg.offset(1) == 0
|
|
{
|
|
-1
|
|
} else if string::strcmp(current_arg, b"--\0".as_ptr() as _) == 0 {
|
|
optind += 1;
|
|
-1
|
|
} else {
|
|
// remove the '-'
|
|
let current_arg = current_arg.offset(1);
|
|
|
|
if *current_arg == b'-' as c_char && !longopts.is_null() {
|
|
let current_arg = current_arg.offset(1);
|
|
// is a long option
|
|
for i in 0.. {
|
|
let opt = &*longopts.offset(i);
|
|
if opt.name.is_null() {
|
|
break;
|
|
}
|
|
|
|
let mut end = 0;
|
|
while { let c = *current_arg.offset(end); c != 0 && c != b'=' as c_char } {
|
|
end += 1;
|
|
}
|
|
|
|
if string::strncmp(current_arg, opt.name, end as size_t) == 0 {
|
|
optind += 1;
|
|
*longindex = i as c_int;
|
|
|
|
if opt.has_arg == optional_argument {
|
|
if *current_arg.offset(end) == b'=' as c_char {
|
|
optarg = current_arg.offset(end + 1);
|
|
}
|
|
} else if opt.has_arg == required_argument {
|
|
if *current_arg.offset(end) == b'=' as c_char {
|
|
optarg = current_arg.offset(end + 1);
|
|
} else if optind < argc {
|
|
optarg = *argv.offset(optind as isize);
|
|
optind += 1;
|
|
} else {
|
|
if *optstring == b':' as c_char {
|
|
return b':' as c_int;
|
|
} else {
|
|
stdio::fputs(*argv as _, &mut *stdio::stderr);
|
|
stdio::fputs(": option '--\0".as_ptr() as _, &mut *stdio::stderr);
|
|
stdio::fputs(current_arg, &mut *stdio::stderr);
|
|
stdio::fputs("' requires an argument\n".as_ptr() as _, &mut *stdio::stderr);
|
|
return b'?' as c_int;
|
|
}
|
|
}
|
|
}
|
|
|
|
if opt.flag.is_null() {
|
|
return opt.val;
|
|
} else {
|
|
*opt.flag = opt.val;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
parse_arg(argc, argv, current_arg, optstring)
|
|
}
|
|
}
|
|
} else {
|
|
parse_arg(argc, argv, CURRENT_OPT, optstring)
|
|
}
|
|
}
|
|
|
|
unsafe fn parse_arg(
|
|
argc: c_int,
|
|
argv: *const *mut c_char,
|
|
current_arg: *mut c_char,
|
|
optstring: *const c_char
|
|
) -> c_int {
|
|
let update_current_opt = || {
|
|
CURRENT_OPT = current_arg.offset(1);
|
|
if *CURRENT_OPT == 0 {
|
|
optind += 1;
|
|
}
|
|
};
|
|
|
|
let print_error = |desc: &[u8]| {
|
|
// NOTE: we don't use fprintf to get around the usage of va_list
|
|
stdio::fputs(*argv as _, &mut *stdio::stderr);
|
|
stdio::fputs(desc.as_ptr() as _, &mut *stdio::stderr);
|
|
stdio::fputc(*current_arg as _, &mut *stdio::stderr);
|
|
stdio::fputc(b'\n' as _, &mut *stdio::stderr);
|
|
};
|
|
|
|
match find_option(*current_arg, optstring) {
|
|
Some(GetoptOption::Flag) => {
|
|
update_current_opt();
|
|
|
|
*current_arg as c_int
|
|
}
|
|
Some(GetoptOption::OptArg) => {
|
|
CURRENT_OPT = b"\0".as_ptr() as _;
|
|
if *current_arg.offset(1) == 0 {
|
|
optind += 2;
|
|
if optind > argc {
|
|
CURRENT_OPT = ptr::null_mut();
|
|
|
|
optopt = *current_arg as c_int;
|
|
let errch = if *optstring == b':' as c_char {
|
|
b':'
|
|
} else {
|
|
if opterr != 0 {
|
|
print_error(b": option requries an argument -- \0");
|
|
}
|
|
|
|
b'?'
|
|
};
|
|
errch as c_int
|
|
} else {
|
|
optarg = *argv.offset(optind as isize - 1);
|
|
|
|
*current_arg as c_int
|
|
}
|
|
} else {
|
|
optarg = current_arg.offset(1);
|
|
optind += 1;
|
|
|
|
*current_arg as c_int
|
|
}
|
|
}
|
|
None => {
|
|
// couldn't find the given option in optstring
|
|
if opterr != 0 {
|
|
print_error(b": illegal option -- \0");
|
|
}
|
|
|
|
update_current_opt();
|
|
|
|
optopt = *current_arg as c_int;
|
|
b'?' as c_int
|
|
}
|
|
}
|
|
}
|
|
|
|
enum GetoptOption {
|
|
Flag,
|
|
OptArg,
|
|
}
|
|
|
|
unsafe fn find_option(ch: c_char, optstring: *const c_char) -> Option<GetoptOption> {
|
|
let mut i = 0;
|
|
|
|
while *optstring.offset(i) != 0 {
|
|
if *optstring.offset(i) == ch {
|
|
let result = if *optstring.offset(i + 1) == b':' as c_char {
|
|
GetoptOption::OptArg
|
|
} else {
|
|
GetoptOption::Flag
|
|
};
|
|
return Some(result);
|
|
}
|
|
i += 1;
|
|
}
|
|
|
|
None
|
|
}
|