From ab017b1e36170e4bd9e5d8e0bd6454d966259e1c Mon Sep 17 00:00:00 2001 From: Reiner Herrmann Date: Tue, 26 Mar 2019 01:15:58 +0100 Subject: Simplify command line argument parsing --- src/lib.rs | 18 +++++------------ src/tftpc.rs | 56 ++++++++++++++++++++++----------------------------- src/tftpd.rs | 66 ++++++++++++++++++------------------------------------------ 3 files changed, 49 insertions(+), 91 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f049d05..64291cb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -151,7 +151,7 @@ impl Tftp { /* build string from buffer */ String::from_utf8(buf.iter() .take_while(|&x| *x != 0) - .map(|&x| x) + .cloned() .collect()).ok() } @@ -372,26 +372,18 @@ impl Tftp { } pub fn parse_file_mode_options(&self, buf: &[u8]) -> Result<(PathBuf, String, HashMap), io::Error> { - let dataerr = io::Error::new(io::ErrorKind::InvalidData, "invalid data received"); + let dataerr = || io::Error::new(io::ErrorKind::InvalidData, "invalid data received"); let mut pos = 0; - let filename = match self.get_tftp_str(&buf[pos..]) { - Some(f) => f, - None => return Err(dataerr), - }; + let filename = self.get_tftp_str(&buf[pos..]).ok_or_else(dataerr)?; pos += filename.len() + 1; - let filename = Path::new(&filename); - - let mode = match self.get_tftp_str(&buf[pos..]) { - Some(m) => m.to_lowercase(), - None => return Err(dataerr), - }; + let mode = self.get_tftp_str(&buf[pos..]).ok_or_else(dataerr)?.to_lowercase(); pos += mode.len() + 1; let options = self.parse_options(&buf[pos..]); - Ok((filename.to_path_buf(), mode, options)) + Ok((Path::new(&filename).to_path_buf(), mode, options)) } pub fn send_error(&self, socket: &UdpSocket, code: u16, msg: &str) -> Result<(), io::Error> { diff --git a/src/tftpc.rs b/src/tftpc.rs index 240ef2c..a134293 100644 --- a/src/tftpc.rs +++ b/src/tftpc.rs @@ -131,8 +131,8 @@ impl Tftpc { let mut file = File::open(self.conf.filename.as_path())?; let err_invalidpath = || io::Error::new(io::ErrorKind::InvalidInput, "Invalid path/filename"); - let filename = self.conf.filename.file_name().ok_or(err_invalidpath())? - .to_str().ok_or(err_invalidpath())?; + let filename = self.conf.filename.file_name().ok_or_else(err_invalidpath)? + .to_str().ok_or_else(err_invalidpath)?; let metadata = file.metadata().map_err(|_| err_invalidpath())?; if !metadata.is_file() { return Err(err_invalidpath()); @@ -171,10 +171,10 @@ impl Tftpc { fn handle_rrq(&mut self, sock: &UdpSocket) -> Result { let err_invalidpath = || io::Error::new(io::ErrorKind::InvalidInput, "Invalid path/filename"); - let filename = self.conf.filename.file_name().ok_or(err_invalidpath())?; + let filename = self.conf.filename.file_name().ok_or_else(err_invalidpath)?; let outpath = env::current_dir().expect("Can't get current directory").join(filename); let mut file = File::create(outpath)?; - let filename = self.conf.filename.to_str().ok_or(err_invalidpath())?; + let filename = self.conf.filename.to_str().ok_or_else(err_invalidpath)?; let buf = self.init_req(rtftp::Opcode::RRQ, filename, 0); @@ -226,7 +226,7 @@ impl Tftpc { } } -fn usage(opts: Options, program: String, error: Option) { +fn usage(opts: &Options, program: &str, error: Option) { if let Some(err) = error { println!("{}\n", err); } @@ -234,7 +234,7 @@ fn usage(opts: Options, program: String, error: Option) { println!("{}", opts.usage(format!("RusTFTP {}\n\n{} [options] [:port]", version, program).as_str())); } -fn parse_commandline(args: &[String]) -> Result { +fn parse_commandline(args: &[String]) -> Option { let program = args[0].clone(); let mut operation = None; let mut mode = rtftp::Mode::OCTET; @@ -247,16 +247,14 @@ fn parse_commandline(args: &[String]) -> Result { opts.optopt("p", "put", "upload file to remote server", "FILE"); opts.optopt("b", "blksize", format!("negotiate a different block size (default: {})", blksize).as_ref(), "SIZE"); opts.optflag("n", "netascii","use netascii mode (instead of octet)"); - let matches = match opts.parse(&args[1..]) { - Ok(m) => m, - Err(err) => { - usage(opts, program, Some(err.to_string())); - return Err("Parsing error"); - } - }; + + let getopts_fail = |err: getopts::Fail| { usage(&opts, &program, Some(err.to_string())) }; + let conv_error = |err: std::num::ParseIntError| { usage(&opts, &program, Some(err.to_string())) }; + + let matches = opts.parse(&args[1..]).map_err(getopts_fail).ok()?; if matches.opt_present("h") || matches.free.len() != 1 { - usage(opts, program, None); - return Err("usage"); + usage(&opts, &program, None); + return None; } if let Some(f) = matches.opt_str("g") { @@ -269,8 +267,8 @@ fn parse_commandline(args: &[String]) -> Result { } if operation.is_none() || (matches.opt_present("g") && matches.opt_present("p")) { - usage(opts, program, Some("Exactly one of g (get) and p (put) required".to_string())); - return Err("get put"); + usage(&opts, &program, Some("Exactly one of g (get) and p (put) required".to_string())); + return None; } if matches.opt_present("n") { @@ -279,25 +277,19 @@ fn parse_commandline(args: &[String]) -> Result { let remote_in = matches.free[0].as_str(); let remote = match remote_in.to_socket_addrs() { - Ok(mut i) => i.next(), + Ok(i) => i, Err(_) => match (remote_in, 69).to_socket_addrs() { - Ok(mut j) => j.next(), + Ok(j) => j, Err(_) => { - usage(opts, program, Some("Failed to parse and lookup specified remote".to_string())); - return Err("lookup"); + usage(&opts, &program, Some("Failed to parse and lookup specified remote".to_string())); + return None; } }, - }; + }.next(); - blksize = match matches.opt_get_default::("b", blksize) { - Ok(b) => b, - Err(err) => { - usage(opts, program, Some(err.to_string())); - return Err("blksize"); - } - }; + blksize = matches.opt_get_default::("b", blksize).map_err(conv_error).ok()?; - Ok(Configuration { + Some(Configuration { operation: operation.unwrap(), mode, filename: filename.unwrap(), @@ -309,8 +301,8 @@ fn parse_commandline(args: &[String]) -> Result { fn main() { let args: Vec = env::args().collect(); let conf = match parse_commandline(&args) { - Ok(c) => c, - Err(_) => return, + Some(c) => c, + None => return, }; Tftpc::new(conf).start(); diff --git a/src/tftpd.rs b/src/tftpd.rs index d10287b..638c5d5 100644 --- a/src/tftpd.rs +++ b/src/tftpd.rs @@ -301,7 +301,7 @@ impl Tftpd { } } -fn usage(opts: Options, program: String, error: Option) { +fn usage(opts: &Options, program: &str, error: Option) { if let Some(err) = error { println!("{}\n", err); } @@ -309,7 +309,7 @@ fn usage(opts: Options, program: String, error: Option) { println!("{}", opts.usage(format!("RusTFTP {}\n\n{} [options]", version, program).as_str())); } -fn parse_commandline(args: &[String]) -> Result { +fn parse_commandline(args: &[String]) -> Option { let program = args[0].clone(); let mut conf: Configuration = Default::default(); let mut opts = Options::new(); @@ -321,70 +321,44 @@ fn parse_commandline(args: &[String]) -> Result { opts.optflag("r", "read-only", "allow only reading/downloading of files (RRQ)"); opts.optflag("w", "write-only", "allow only writing/uploading of files (WRQ)"); opts.optopt("t", "threads", format!("number of worker threads (default: {})", conf.threads).as_ref(), "N"); - let matches = match opts.parse(&args[1..]) { - Ok(m) => m, - Err(err) => { - usage(opts, program, Some(err.to_string())); - return Err("Parsing error"); - } - }; + + let getopts_fail = |err: getopts::Fail| { usage(&opts, &program, Some(err.to_string())) }; + let conv_error = |err: std::num::ParseIntError| { usage(&opts, &program, Some(err.to_string())) }; + + let matches = opts.parse(&args[1..]).map_err(getopts_fail).ok()?; if matches.opt_present("h") { - usage(opts, program, None); - return Err("usage"); + usage(&opts, &program, None); + return None; } - conf.port = match matches.opt_get_default("p", conf.port) { - Ok(p) => p, - Err(err) => { - usage(opts, program, Some(err.to_string())); - return Err("port"); - } - }; - conf.uid = match matches.opt_get_default("u", conf.uid) { - Ok(u) => u, - Err(err) => { - usage(opts, program, Some(err.to_string())); - return Err("uid"); - } - }; - conf.gid = match matches.opt_get_default("g", conf.gid) { - Ok(g) => g, - Err(err) => { - usage(opts, program, Some(err.to_string())); - return Err("gid"); - } - }; - conf.threads = match matches.opt_get_default("t", conf.threads) { - Ok(t) => t, - Err(err) => { - usage(opts, program, Some(err.to_string())); - return Err("threads"); - } - }; + conf.port = matches.opt_get_default("p", conf.port).map_err(conv_error).ok()?; + conf.uid = matches.opt_get_default("u", conf.uid).map_err(conv_error).ok()?; + conf.gid = matches.opt_get_default("g", conf.gid).map_err(conv_error).ok()?; + conf.threads = matches.opt_get_default("t", conf.threads).map_err(conv_error).ok()?; conf.ro = matches.opt_present("r"); conf.wo = matches.opt_present("w"); if conf.ro && conf.wo { - usage(opts, program, Some(String::from("Only one of r (read-only) and w (write-only) allowed"))); - return Err("ro and wo"); + usage(&opts, &program, Some(String::from("Only one of r (read-only) and w (write-only) allowed"))); + return None; } if matches.opt_present("d") { conf.dir = match matches.opt_str("d") { Some(d) => Path::new(&d).to_path_buf(), None => { - usage(opts, program, None); - return Err("directory"); + usage(&opts, &program, None); + return None; } }; } - Ok(conf) + Some(conf) } fn main() { let args: Vec = env::args().collect(); let conf = match parse_commandline(&args) { - Ok(c) => c, - Err(_) => return, + Some(c) => c, + None => return, }; Tftpd::new(conf).start(); -- cgit v1.2.3