//! 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] #[linkage = "weak"] // often redefined in GNU programs 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(); // handle reinitialization request if optind == 0 { optind = 1; CURRENT_OPT = 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\0".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 { 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 }