unistd: add a preliminary implementation of getopt()

This commit is contained in:
Alex Lyon
2018-03-18 00:28:31 -07:00
parent edead8e085
commit af78348d4a
9 changed files with 257 additions and 5 deletions
+2
View File
@@ -9,3 +9,5 @@ cbindgen = { path = "../../cbindgen" }
[dependencies]
platform = { path = "../platform" }
stdio = { path = "../stdio" }
string = { path = "../string" }
+142
View File
@@ -0,0 +1,142 @@
//! getopt implementation for Redox, following http://pubs.opengroup.org/onlinepubs/009695399/functions/getopt.html
use super::platform::types::*;
use super::platform;
use super::stdio;
use super::string;
use core::ptr;
#[no_mangle]
pub static mut optarg: *mut c_char = ptr::null_mut();
#[no_mangle]
pub static mut optind: c_int = 1;
#[no_mangle]
pub static mut opterr: c_int = 1;
#[no_mangle]
pub static mut optopt: c_int = -1;
static mut current_opt: *mut c_char = ptr::null_mut();
#[no_mangle]
pub unsafe extern "C" fn getopt(
argc: c_int,
argv: *const *mut c_char,
optstring: *const c_char,
) -> c_int {
if current_opt == ptr::null_mut() || *current_opt == 0 {
let current_arg = *argv.offset(optind as isize);
// XXX: is argc check needed?
if optind > argc || current_arg == ptr::null_mut() || *current_arg != b'-' as c_char
|| string::strcmp(current_arg, b"-\0".as_ptr() as _) == 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);
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 _, stdio::stderr);
stdio::fputs(desc.as_ptr() as _, stdio::stderr);
stdio::fputc(*current_arg as _, stdio::stderr);
stdio::fputc(b'\n' as _, 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 _;
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
}
+5 -5
View File
@@ -3,10 +3,15 @@
#![no_std]
extern crate platform;
extern crate stdio;
extern crate string;
pub use platform::types::*;
pub use getopt::*;
use core::ptr;
mod getopt;
pub const R_OK: c_int = 1;
pub const W_OK: c_int = 2;
pub const X_OK: c_int = 4;
@@ -204,11 +209,6 @@ pub extern "C" fn getlogin_r(name: *mut c_char, namesize: size_t) -> c_int {
unimplemented!();
}
#[no_mangle]
pub extern "C" fn getopt(argc: c_int, argv: *const *mut c_char, opstring: *const c_char) -> c_int {
unimplemented!();
}
#[no_mangle]
pub extern "C" fn getpagesize() -> c_int {
unimplemented!();