From fac630d07cd5de4785c74d9a0a3b9d42670bd239 Mon Sep 17 00:00:00 2001 From: Arcterus Date: Fri, 28 Feb 2014 09:19:32 -0800 Subject: [PATCH 1/3] Add paste (closes #120) --- Makefile | 1 + paste/paste.rs | 102 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 paste/paste.rs diff --git a/Makefile b/Makefile index ca4309339..991eeb617 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,7 @@ PROGS := \ du \ false \ mkdir \ + paste \ printenv \ pwd \ rm \ diff --git a/paste/paste.rs b/paste/paste.rs new file mode 100644 index 000000000..2809c3d10 --- /dev/null +++ b/paste/paste.rs @@ -0,0 +1,102 @@ +#[crate_id(name = "paste", vers = "1.0.0", author = "Arcterus")]; + +/* + * This file is part of the uutils coreutils package. + * + * (c) Arcterus + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#[feature(macro_rules)]; + +extern crate getopts; + +use std::io; +use std::os; + +#[path = "../common/util.rs"] +mod util; + +static NAME: &'static str = "paste"; +static VERSION: &'static str = "1.0.0"; + +fn main() { + let args = os::args(); + let program = args[0].clone(); + + let opts = ~[ + getopts::optflag("s", "serial", "paste one file at a time instead of in parallel"), + getopts::optopt("d", "delimiters", "reuse characters from LIST instead of TABs", "LIST"), + getopts::optflag("h", "help", "display this help and exit"), + getopts::optflag("V", "version", "output version information and exit") + ]; + let matches = match getopts::getopts(args.tail(), opts) { + Ok(m) => m, + Err(f) => crash!(1, "{}", f.to_err_msg()) + }; + if matches.opt_present("help") { + println!("{} {}", NAME, VERSION); + println!(""); + println!("Usage:"); + println!(" {0:s} [OPTION]... [FILE]...", program); + println!(""); + print!("{}", getopts::usage("Write lines consisting of the sequentially corresponding lines from each FILE, separated by TABs, to standard output.", opts)); + } else if matches.opt_present("version") { + println!("{} {}", NAME, VERSION); + } else { + let serial = matches.opt_present("serial"); + let delimiters = match matches.opt_str("delimiters") { + Some(m) => m, + None => ~"\t" + }; + paste(matches.free, serial, delimiters); + } +} + +fn paste(filenames: ~[~str], serial: bool, delimiters: ~str) { + let mut files: ~[io::BufferedReader] = filenames.move_iter().map(|name| + io::BufferedReader::new(crash_if_err!(1, io::File::open(&Path::new(name)))) + ).collect(); + let mut delim_count = 0; + if serial { + for file in files.mut_iter() { + let mut output = ~""; + loop { + output = output + match file.read_line() { + Ok(line) => format!("{}{}", line.trim_right(), delimiters.char_at(delim_count % delimiters.len())), + Err(f) => if f.kind == io::EndOfFile { + break + } else { + crash!(1, "{}", f.to_str()) + } + }; + delim_count += 1; + } + output.pop_char(); + println!("{}", output); + } + } else { + loop { + let mut output = ~""; + let mut eof = 0; + for file in files.mut_iter() { + output = output + match file.read_line() { + Ok(line) => format!("{}{}", line.trim_right(), delimiters.char_at(delim_count % delimiters.len())), + Err(f) => if f.kind == io::EndOfFile { + eof += 1; + delimiters.char_at(delim_count % delimiters.len()).to_str() + } else { + crash!(1, "{}", f.to_str()) + } + }; + } + if files.len() == eof { + break; + } + output.pop_char(); + println!("{}", output); + } + } +} From 2a875c0f495eebf8d5922f80de07bcc463745b9a Mon Sep 17 00:00:00 2001 From: Arcterus Date: Fri, 28 Feb 2014 10:11:24 -0800 Subject: [PATCH 2/3] paste: make MUCH faster and fix a couple bugs --- paste/paste.rs | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/paste/paste.rs b/paste/paste.rs index 2809c3d10..848c46a32 100644 --- a/paste/paste.rs +++ b/paste/paste.rs @@ -59,13 +59,14 @@ fn paste(filenames: ~[~str], serial: bool, delimiters: ~str) { let mut files: ~[io::BufferedReader] = filenames.move_iter().map(|name| io::BufferedReader::new(crash_if_err!(1, io::File::open(&Path::new(name)))) ).collect(); + let delimiters: ~[~str] = delimiters.chars().map(|x| x.to_str()).collect(); let mut delim_count = 0; if serial { for file in files.mut_iter() { let mut output = ~""; loop { output = output + match file.read_line() { - Ok(line) => format!("{}{}", line.trim_right(), delimiters.char_at(delim_count % delimiters.len())), + Ok(line) => line.trim_right() + delimiters[delim_count % delimiters.len()], Err(f) => if f.kind == io::EndOfFile { break } else { @@ -78,25 +79,33 @@ fn paste(filenames: ~[~str], serial: bool, delimiters: ~str) { println!("{}", output); } } else { + let mut eof = std::vec::from_elem(files.len(), false); loop { let mut output = ~""; - let mut eof = 0; - for file in files.mut_iter() { - output = output + match file.read_line() { - Ok(line) => format!("{}{}", line.trim_right(), delimiters.char_at(delim_count % delimiters.len())), - Err(f) => if f.kind == io::EndOfFile { - eof += 1; - delimiters.char_at(delim_count % delimiters.len()).to_str() - } else { - crash!(1, "{}", f.to_str()) + let mut eof_count = 0; + for (i, file) in files.mut_iter().enumerate() { + if eof[i] { + eof_count += 1; + } else { + match file.read_line() { + Ok(line) => output = output + line.slice_to(line.len() - 1), + Err(f) => if f.kind == io::EndOfFile { + eof[i] = true; + eof_count += 1; + } else { + crash!(1, "{}", f.to_str()); + } } - }; + } + output = output + delimiters[delim_count % delimiters.len()]; + delim_count += 1; } - if files.len() == eof { + if files.len() == eof_count { break; } output.pop_char(); println!("{}", output); + delim_count = 0; } } } From e442cadbafff2a0ab74abb8a45332958fd23b3e8 Mon Sep 17 00:00:00 2001 From: Arcterus Date: Fri, 28 Feb 2014 10:13:54 -0800 Subject: [PATCH 3/3] Remove paste from the to-do list --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 593a493ee..3bd2cf028 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,6 @@ To do - numfmt - od - operand2sig -- paste - pathchk - pinky - pr