getopt.rs(3): adds testing

This commit is contained in:
Emma Tebibyte 2024-06-22 22:18:13 -06:00
parent 40da8135f1
commit 8f990ba515
Signed by untrusted user: emma
GPG Key ID: 06FA419A1698C270
2 changed files with 111 additions and 46 deletions

View File

@ -49,9 +49,13 @@ install: dist
cp -r $(DESTDIR)/* / cp -r $(DESTDIR)/* /
.PHONY: test .PHONY: test
test: build test: build /tmp/getopt
tests/posix-compat.sh tests/posix-compat.sh
/tmp/getopt: src/getopt.rs
$(RUSTC) --test -o /tmp/getopt src/getopt.rs
/tmp/getopt
.PHONY: rustlibs .PHONY: rustlibs
rustlibs: build/o/libsysexits.rlib build/o/libgetopt.rlib \ rustlibs: build/o/libsysexits.rlib build/o/libgetopt.rlib \
build/o/libstrerror.rlib build/o/libstrerror.rlib

View File

@ -32,49 +32,71 @@ extern "C" {
) -> c_int; ) -> c_int;
} }
pub struct Opt { #[derive(Clone, Debug)]
pub arg: Option<String>,
ind: *mut i32,
pub opt: String,
}
impl Opt {
pub fn index(&self) -> usize { unsafe { *self.ind as usize } }
pub fn set_index(&self, ind: i32) { unsafe { *self.ind = ind; } }
}
pub enum OptError { pub enum OptError {
MissingArg(String), MissingArg(String),
UnknownOpt(String), UnknownOpt(String),
} }
#[derive(Clone, Debug)]
pub struct Opt {
arg: Option<String>,
ind: *mut i32,
opt: Result<String, OptError>,
}
impl Opt {
pub fn arg(&self) -> Option<String> { self.arg.clone() }
/* sets optarg if default is desired */
pub fn arg_or(&self, default: impl std::fmt::Display) -> String {
default.to_string()
}
pub fn opt(&self) -> Result<&str, OptError> {
self.opt.as_ref().map(|o| o.as_str()).map_err(OptError::clone)
}
/* From getopt(3p):
*
* The variable optind is the index of the next element of the argv[]
* vector to be processed. It shall be initialized to 1 by the system, and
* getopt() shall update it when it finishes with each element of argv[].
* If the application sets optind to zero before calling getopt(), the
* behavior is unspecified. When an element of argv[] contains multiple
* option characters, it is unspecified how getopt() determines which
* options have already been processed. */
pub fn ind(&self) -> usize { unsafe { *self.ind as usize } }
pub fn set_ind(&self, ind: i32) { unsafe { *self.ind = ind; } }
}
/* function signature */ /* function signature */
pub trait GetOpt { pub trait GetOpt {
fn getopt(&self, optstring: &str) -> Option<Result<Opt, OptError>>; fn getopt(&self, optstring: &str) -> Option<Opt>;
} }
impl GetOpt for Vec<String> { impl GetOpt for Vec<String> {
fn getopt(&self, optstring: &str) -> Option<Result<Opt, OptError>> { fn getopt(&self, optstring: &str) -> Option<Opt> {
let c_strings: Vec<_> = self let c_strings: Vec<_> = self
.iter() .iter()
.cloned() .cloned()
.map(CString::new) .map(|x| CString::new(x).unwrap().into_raw())
.map(Result::unwrap)
.collect(); .collect();
let boxed = Box::into_raw(c_strings.into_boxed_slice());
let argv = boxed as *const *mut i8;
/* these operations must be separated out into separate operations so /* these operations must be separated out into separate operations so
* the CStrings can live long enough */ * the CStrings can live long enough */
let argv: Vec<_> = c_strings.iter().map(|x| x.as_ptr()).collect();
let argv_ptr = argv.as_ptr() as *const *mut c_char;
let opts = CString::new(optstring).unwrap().into_raw(); let opts = CString::new(optstring).unwrap().into_raw();
let len = self.len() as c_int; let len = self.len() as c_int;
unsafe { unsafe {
match getopt(len, argv_ptr, opts) { let ret = match getopt(len, argv, opts) {
/* From getopt(3p): /* From getopt(3p):
* *
* The getopt() f unction shall return the next option character * The getopt() function shall return the next option character
* specified on the command line. * specified on the command line.
* *
* A <colon> (':') shall be returned if getopt() detects a * A <colon> (':') shall be returned if getopt() detects a