use crate::*;
pub static mut OPTERR: i32 = 1;
pub static mut OPTIND: i32 = 1;
pub static mut OPTOPT: u8 = 0;
pub static mut OPTRESET: i32 = 0;
pub static mut OPTARG: *mut u8 = null_mut();
pub unsafe fn getopt(nargc: i32, nargv: *const *mut u8, ostr: *const u8) -> Option<u8> {
unsafe {
static mut PLACE: *const u8 = c"".as_ptr().cast();
let mut oli: *mut u8;
if ostr.is_null() {
return None;
}
if OPTRESET != 0 || *PLACE == 0 {
OPTRESET = 0;
if OPTIND >= nargc || {
PLACE = *nargv.offset(OPTIND as isize);
*PLACE != b'-'
} {
PLACE = c"".as_ptr().cast();
return None;
}
if *PLACE.add(1) != 0 && {
PLACE = PLACE.add(1);
*PLACE == b'-'
} {
if *PLACE.add(1) != 0 {
return Some(b'?');
}
OPTIND += 1;
PLACE = c"".as_ptr().cast();
return None;
}
}
let fresh0 = PLACE;
PLACE = PLACE.add(1);
OPTOPT = *fresh0;
if OPTOPT == b':' || {
oli = crate::libc::strchr(ostr, OPTOPT as i32);
oli.is_null()
} {
if OPTOPT == b'-' {
return None;
}
if *PLACE == 0 {
OPTIND += 1;
}
if OPTERR != 0 && *ostr != b':' {
let tmp = OPTOPT;
eprintln!("ztmux: unknown option -- {}", tmp as char);
}
return Some(b'?');
}
oli = oli.add(1);
if *oli != b':' {
OPTARG = null_mut();
if *PLACE == 0 {
OPTIND += 1;
}
} else {
if *PLACE != 0 {
OPTARG = PLACE.cast_mut();
} else {
OPTIND += 1;
if nargc <= OPTIND {
PLACE = c"".as_ptr().cast();
if *ostr == b':' {
return Some(b':');
}
if OPTERR != 0 {
let tmp = OPTOPT;
eprintln!("ztmux: option requires an argument -- {}", tmp as char);
}
return Some(b'?');
} else {
OPTARG = *nargv.offset(OPTIND as isize);
}
}
PLACE = c"".as_ptr().cast();
OPTIND += 1;
}
Some(OPTOPT)
}
}
#[cfg(test)]
mod tests {
use super::*;
static LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
unsafe fn reset() {
unsafe {
OPTRESET = 1;
OPTIND = 1;
OPTERR = 0;
OPTARG = null_mut();
OPTOPT = 0;
}
}
unsafe fn go(argv: &[*mut u8], ostr: *const u8) -> Option<u8> {
unsafe { getopt(argv.len() as i32, argv.as_ptr(), ostr) }
}
unsafe fn optind() -> i32 {
unsafe { OPTIND }
}
unsafe fn optopt() -> u8 {
unsafe { OPTOPT }
}
unsafe fn optarg() -> *mut u8 {
unsafe { OPTARG }
}
#[test]
fn simple_option_string() {
let _g = LOCK.lock().unwrap();
unsafe {
reset();
let argv: [*mut u8; 4] = [
crate::c!("prog").cast_mut(),
crate::c!("-a").cast_mut(),
crate::c!("-b").cast_mut(),
crate::c!("arg").cast_mut(),
];
let ostr = crate::c!("ab");
assert_eq!(go(&argv, ostr), Some(b'a'));
assert_eq!(optind(), 2);
assert_eq!(go(&argv, ostr), Some(b'b'));
assert_eq!(optind(), 3);
assert_eq!(go(&argv, ostr), None);
assert_eq!(optind(), 3);
}
}
#[test]
fn clustered_flags() {
let _g = LOCK.lock().unwrap();
unsafe {
reset();
let argv: [*mut u8; 2] =
[crate::c!("prog").cast_mut(), crate::c!("-ab").cast_mut()];
let ostr = crate::c!("ab");
assert_eq!(go(&argv, ostr), Some(b'a'));
assert_eq!(optind(), 1);
assert_eq!(go(&argv, ostr), Some(b'b'));
assert_eq!(optind(), 2);
assert_eq!(go(&argv, ostr), None);
}
}
#[test]
fn required_arg_separate_word() {
let _g = LOCK.lock().unwrap();
unsafe {
reset();
let argv: [*mut u8; 4] = [
crate::c!("prog").cast_mut(),
crate::c!("-a").cast_mut(),
crate::c!("val").cast_mut(),
crate::c!("-b").cast_mut(),
];
let ostr = crate::c!("a:b");
assert_eq!(go(&argv, ostr), Some(b'a'));
assert!(!optarg().is_null());
assert_eq!(crate::libc::strcmp(optarg(), crate::c!("val")), 0);
assert_eq!(optind(), 3);
assert_eq!(go(&argv, ostr), Some(b'b'));
assert_eq!(optind(), 4);
assert_eq!(go(&argv, ostr), None);
}
}
#[test]
fn required_arg_attached() {
let _g = LOCK.lock().unwrap();
unsafe {
reset();
let argv: [*mut u8; 2] =
[crate::c!("prog").cast_mut(), crate::c!("-aval").cast_mut()];
let ostr = crate::c!("a:");
assert_eq!(go(&argv, ostr), Some(b'a'));
assert!(!optarg().is_null());
assert_eq!(crate::libc::strcmp(optarg(), crate::c!("val")), 0);
assert_eq!(optind(), 2);
assert_eq!(go(&argv, ostr), None);
}
}
#[test]
fn unknown_option_returns_question_mark() {
let _g = LOCK.lock().unwrap();
unsafe {
reset();
let argv: [*mut u8; 2] =
[crate::c!("prog").cast_mut(), crate::c!("-x").cast_mut()];
let ostr = crate::c!("ab");
assert_eq!(go(&argv, ostr), Some(b'?'));
assert_eq!(optopt(), b'x');
assert_eq!(optind(), 2);
assert_eq!(go(&argv, ostr), None);
}
}
#[test]
fn missing_required_arg_default() {
let _g = LOCK.lock().unwrap();
unsafe {
reset();
let argv: [*mut u8; 2] =
[crate::c!("prog").cast_mut(), crate::c!("-a").cast_mut()];
let ostr = crate::c!("a:");
assert_eq!(go(&argv, ostr), Some(b'?'));
assert_eq!(optopt(), b'a');
}
}
#[test]
fn missing_required_arg_colon_ostr() {
let _g = LOCK.lock().unwrap();
unsafe {
reset();
let argv: [*mut u8; 2] =
[crate::c!("prog").cast_mut(), crate::c!("-a").cast_mut()];
let ostr = crate::c!(":a:");
assert_eq!(go(&argv, ostr), Some(b':'));
assert_eq!(optopt(), b'a');
}
}
#[test]
fn double_dash_terminates() {
let _g = LOCK.lock().unwrap();
unsafe {
reset();
let argv: [*mut u8; 3] = [
crate::c!("prog").cast_mut(),
crate::c!("--").cast_mut(),
crate::c!("-a").cast_mut(),
];
let ostr = crate::c!("a");
assert_eq!(go(&argv, ostr), None);
assert_eq!(optind(), 2);
}
}
#[test]
fn lone_dash_is_operand() {
let _g = LOCK.lock().unwrap();
unsafe {
reset();
let argv: [*mut u8; 2] =
[crate::c!("prog").cast_mut(), crate::c!("-").cast_mut()];
let ostr = crate::c!("a");
assert_eq!(go(&argv, ostr), None);
assert_eq!(optind(), 1);
}
}
#[test]
fn no_permutation_stops_at_first_operand() {
let _g = LOCK.lock().unwrap();
unsafe {
reset();
let argv: [*mut u8; 4] = [
crate::c!("prog").cast_mut(),
crate::c!("-a").cast_mut(),
crate::c!("file").cast_mut(),
crate::c!("-b").cast_mut(),
];
let ostr = crate::c!("ab");
assert_eq!(go(&argv, ostr), Some(b'a'));
assert_eq!(optind(), 2);
assert_eq!(go(&argv, ostr), None);
assert_eq!(optind(), 2);
}
}
#[test]
fn null_option_string_returns_none() {
let _g = LOCK.lock().unwrap();
unsafe {
reset();
let argv: [*mut u8; 2] =
[crate::c!("prog").cast_mut(), crate::c!("-a").cast_mut()];
assert_eq!(getopt(argv.len() as i32, argv.as_ptr(), null()), None);
}
}
#[test]
fn flag_clears_optarg() {
let _g = LOCK.lock().unwrap();
unsafe {
reset();
OPTARG = crate::c!("stale").cast_mut();
let argv: [*mut u8; 2] =
[crate::c!("prog").cast_mut(), crate::c!("-a").cast_mut()];
let ostr = crate::c!("a");
assert_eq!(go(&argv, ostr), Some(b'a'));
assert!(optarg().is_null());
}
}
#[test]
fn cluster_then_arg_option() {
let _g = LOCK.lock().unwrap();
unsafe {
reset();
let argv: [*mut u8; 2] =
[crate::c!("prog").cast_mut(), crate::c!("-baval").cast_mut()];
let ostr = crate::c!("ba:");
assert_eq!(go(&argv, ostr), Some(b'b'));
assert_eq!(optind(), 1);
assert_eq!(go(&argv, ostr), Some(b'a'));
assert_eq!(crate::libc::strcmp(optarg(), crate::c!("val")), 0);
assert_eq!(optind(), 2);
assert_eq!(go(&argv, ostr), None);
}
}
#[test]
fn unknown_with_colon_ostr_still_badch() {
let _g = LOCK.lock().unwrap();
unsafe {
reset();
let argv: [*mut u8; 2] =
[crate::c!("prog").cast_mut(), crate::c!("-x").cast_mut()];
let ostr = crate::c!(":ab");
assert_eq!(go(&argv, ostr), Some(b'?'));
assert_eq!(optopt(), b'x');
}
}
#[test]
fn no_options_present_returns_none() {
let _g = LOCK.lock().unwrap();
unsafe {
reset();
let argv: [*mut u8; 2] =
[crate::c!("prog").cast_mut(), crate::c!("file").cast_mut()];
let ostr = crate::c!("ab");
assert_eq!(go(&argv, ostr), None);
assert_eq!(optind(), 1);
}
}
#[test]
fn reset_reparses_second_vector() {
let _g = LOCK.lock().unwrap();
unsafe {
reset();
let ostr = crate::c!("ab");
let argv1: [*mut u8; 2] =
[crate::c!("prog").cast_mut(), crate::c!("-a").cast_mut()];
assert_eq!(go(&argv1, ostr), Some(b'a'));
assert_eq!(go(&argv1, ostr), None);
reset();
let argv2: [*mut u8; 2] =
[crate::c!("prog").cast_mut(), crate::c!("-b").cast_mut()];
assert_eq!(go(&argv2, ostr), Some(b'b'));
assert_eq!(optind(), 2);
assert_eq!(go(&argv2, ostr), None);
}
}
}