getopt.rs(3): adds testing
This commit is contained in:
parent
40da8135f1
commit
8f990ba515
6
Makefile
6
Makefile
@ -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
|
||||||
|
151
src/getopt.rs
151
src/getopt.rs
@ -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
|
||||||
@ -89,10 +111,24 @@ impl GetOpt for Vec<String> {
|
|||||||
* Otherwise, getopt() shall return -1 when all command line
|
* Otherwise, getopt() shall return -1 when all command line
|
||||||
* options are parsed. */
|
* options are parsed. */
|
||||||
58 => { /* numerical ASCII value for ':' */
|
58 => { /* numerical ASCII value for ':' */
|
||||||
Some(Err(OptError::MissingArg(optopt.to_string())))
|
Some(Opt {
|
||||||
|
/* opt argument */
|
||||||
|
arg: None,
|
||||||
|
/* opt index */
|
||||||
|
ind: std::ptr::addr_of_mut!(optind),
|
||||||
|
/* error containing option */
|
||||||
|
opt: Err(OptError::MissingArg(optopt.to_string())),
|
||||||
|
})
|
||||||
},
|
},
|
||||||
63 => { /* numerical ASCII value for '?' */
|
63 => { /* numerical ASCII value for '?' */
|
||||||
Some(Err(OptError::UnknownOpt(optopt.to_string())))
|
Some(Opt {
|
||||||
|
/* opt argument */
|
||||||
|
arg: None,
|
||||||
|
/* opt index */
|
||||||
|
ind: std::ptr::addr_of_mut!(optind),
|
||||||
|
/* error containing option */
|
||||||
|
opt: Err(OptError::UnknownOpt(optopt.to_string())),
|
||||||
|
})
|
||||||
},
|
},
|
||||||
/* From getopt(3p):
|
/* From getopt(3p):
|
||||||
*
|
*
|
||||||
@ -109,31 +145,56 @@ impl GetOpt for Vec<String> {
|
|||||||
* getopt() shall return -1 after incrementing optind. */
|
* getopt() shall return -1 after incrementing optind. */
|
||||||
-1 => return None,
|
-1 => return None,
|
||||||
opt => {
|
opt => {
|
||||||
let arg = CStr::from_ptr(optarg)
|
let arg: Option<String>;
|
||||||
.to_string_lossy()
|
|
||||||
.into_owned();
|
|
||||||
|
|
||||||
Some(Ok(Opt {
|
if optarg.is_null() { arg = None; }
|
||||||
arg: Some(arg), /* opt argument */
|
else {
|
||||||
/* From getopt(3p):
|
arg = Some(CStr::from_ptr(optarg)
|
||||||
*
|
.to_string_lossy()
|
||||||
* The variable optind is the index of the next element
|
.into_owned());
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* This API is can be utilized with the index() and
|
|
||||||
* set_index() methods implemented for Opt. */
|
|
||||||
ind: std::ptr::addr_of_mut!(optind),
|
|
||||||
opt: opt.to_string(), /* option itself */
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some(Opt {
|
||||||
|
arg, /* opt argument */
|
||||||
|
ind: std::ptr::addr_of_mut!(optind), /* opt index */
|
||||||
|
/* option itself */
|
||||||
|
opt: Ok((opt as u8 as char).to_string()),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* delloc argv */
|
||||||
|
let _ = Box::from_raw(boxed);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use GetOpt;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn testing() {
|
||||||
|
let argv: Vec<String> = ["test", "-b", "-f", "arg", "-o", "arg"]
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
while let Some(opt) = argv.getopt(":abf:o:") {
|
||||||
|
match opt.opt() {
|
||||||
|
Ok("a") => assert_eq!(opt.ind(), 1),
|
||||||
|
Ok("b") => assert_eq!(opt.ind(), 2),
|
||||||
|
Ok("f") | Ok("o") => {
|
||||||
|
assert_eq!(opt.arg(), Some("arg".into()));
|
||||||
|
},
|
||||||
|
_ => assert!(false),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(opt) = argv.getopt("abc:") {
|
||||||
|
opt.clone().set_ind(1);
|
||||||
|
assert_eq!(opt.ind(), 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user