diff --git a/Makefile b/Makefile index b65bafc87..a6f5f5833 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ PROGS := \ base64 \ basename \ cat \ + comm \ dirname \ echo \ env \ diff --git a/README.md b/README.md index 98ad05aff..bc0e73135 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,6 @@ To do - chown - chroot - cksum -- comm - copy - cp-hash - cp (some work done in ```dev``` branch) diff --git a/comm/comm.rs b/comm/comm.rs new file mode 100644 index 000000000..3d6223677 --- /dev/null +++ b/comm/comm.rs @@ -0,0 +1,115 @@ +#![crate_id(name="comm", vers="1.0.0", author="Michael Gehring")] + +/* + * This file is part of the uutils coreutils package. + * + * (c) Michael Gehring + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +extern crate getopts; + +use std::cmp::TotalOrd; +use std::io::{BufferedReader, IoResult, print}; +use std::io::fs::File; +use std::io::stdio::stdin; +use std::os; +use std::path::Path; + +static NAME : &'static str = "comm"; +static VERSION : &'static str = "1.0.0"; + +fn delim(col: int, opts: &getopts::Matches) -> ~str { + let mut s = StrBuf::new(); + + if col > 1 && !opts.opt_present("1") { + s.push_str("\t"); + } + if col > 2 && !opts.opt_present("2") { + s.push_str("\t"); + } + + s.to_owned() +} + +fn comm(a: &mut Box, b: &mut Box, opts: &getopts::Matches) { + + let mut ra = a.read_line(); + let mut rb = b.read_line(); + + while ra.is_ok() || rb.is_ok() { + let ord = match (ra.clone(), rb.clone()) { + (Err(_), Ok(_)) => Greater, + (Ok(_) , Err(_)) => Less, + (Ok(s0), Ok(s1)) => s0.cmp(&s1), + _ => unreachable!(), + }; + + match ord { + Less => { + if !opts.opt_present("1") { + print!("{}{}", delim(1, opts), ra.unwrap()); + } + ra = a.read_line(); + } + Greater => { + if !opts.opt_present("2") { + print!("{}{}", delim(2, opts), rb.unwrap()); + } + rb = b.read_line(); + } + Equal => { + if !opts.opt_present("3") { + print!("{}{}", delim(3, opts), ra.unwrap()); + } + ra = a.read_line(); + rb = b.read_line(); + } + } + } +} + +fn open_file(name: &str) -> IoResult> { + match name { + "-" => Ok(box stdin() as Box), + _ => { + let f = try!(File::open(&Path::new(name))); + Ok(box BufferedReader::new(f) as Box) + } + } +} + +pub fn main() { + let opts = [ + getopts::optflag("1", "", "suppress column 1 (lines uniq to FILE1)"), + getopts::optflag("2", "", "suppress column 2 (lines uniq to FILE2)"), + getopts::optflag("3", "", "suppress column 3 (lines that appear in both files)"), + getopts::optflag("h", "help", "display this help and exit"), + ]; + + let matches = match getopts::getopts(os::args().tail(), opts) { + Ok(m) => m, + Err(err) => fail!("{}", err.to_err_msg()), + }; + + if matches.opt_present("help") || matches.free.len() != 2 { + println!("{} {}", NAME, VERSION); + println!(""); + println!("Usage:"); + println!(" {} [OPTIONS] FILE1 FILE2", NAME); + println!(""); + print(getopts::usage("Compare sorted files line by line.", opts.as_slice())); + if matches.free.len() != 2 { + os::set_exit_status(1); + } + return; + } + + + let mut f1 = open_file(matches.free.get(0).clone()).unwrap(); + let mut f2 = open_file(matches.free.get(1).clone()).unwrap(); + + comm(&mut f1, &mut f2, &matches) +}