123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- //! 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<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
- }
|