diff --git a/Makefile b/Makefile index 3b6b635ca..a1c47e0bf 100644 --- a/Makefile +++ b/Makefile @@ -164,6 +164,7 @@ TEST_PROGS := \ mkdir \ mv \ nl \ + paste \ seq \ sort \ test \ diff --git a/src/paste/paste.rs b/src/paste/paste.rs index c33663117..fa9d0e6c9 100644 --- a/src/paste/paste.rs +++ b/src/paste/paste.rs @@ -1,5 +1,5 @@ #![crate_name = "paste"] -#![feature(collections, core, old_io, old_path, rustc_private)] +#![feature(rustc_private)] /* * This file is part of the uutils coreutils package. @@ -13,8 +13,10 @@ extern crate getopts; extern crate libc; -use std::old_io as io; -use std::iter::repeat; +use std::io::{BufRead, BufReader, Read, stdin, Write}; +use std::iter::repeat; +use std::fs::File; +use std::path::Path; #[path = "../common/util.rs"] #[macro_use] @@ -24,7 +26,7 @@ static NAME: &'static str = "paste"; static VERSION: &'static str = "1.0.0"; pub fn uumain(args: Vec) -> i32 { - let program = args[0].clone(); + let program = &args[0]; let opts = [ getopts::optflag("s", "serial", "paste one file at a time instead of in parallel"), @@ -32,9 +34,9 @@ pub fn uumain(args: Vec) -> i32 { 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) { + let matches = match getopts::getopts(&args[1..], &opts) { Ok(m) => m, - Err(f) => crash!(1, "{}", f) + Err(e) => crash!(1, "{}", e) }; if matches.opt_present("help") { println!("{} {}", NAME, VERSION); @@ -47,75 +49,81 @@ pub fn uumain(args: Vec) -> i32 { println!("{} {}", NAME, VERSION); } else { let serial = matches.opt_present("serial"); - let delimiters = match matches.opt_str("delimiters") { - Some(m) => m, - None => "\t".to_string() - }; - paste(matches.free, serial, delimiters.as_slice()); + let delimiters = matches.opt_str("delimiters").unwrap_or("\t".to_string()); + paste(matches.free, serial, delimiters); } 0 } -fn paste(filenames: Vec, serial: bool, delimiters: &str) { - let mut files: Vec>> = filenames.into_iter().map(|name| - io::BufferedReader::new( - if name.as_slice() == "-" { - Box::new(io::stdio::stdin_raw()) as Box +fn paste(filenames: Vec, serial: bool, delimiters: String) { + let mut files: Vec>> = filenames.into_iter().map(|name| + BufReader::new( + if name == "-" { + Box::new(stdin()) as Box } else { - let r = crash_if_err!(1, io::File::open(&Path::new(name))); - Box::new(r) as Box + let r = crash_if_err!(1, File::open(Path::new(&name))); + Box::new(r) as Box } ) ).collect(); - let delimiters: Vec = delimiters.chars().map(|x| x.to_string()).collect(); + + let delimiters: Vec = unescape(delimiters).chars().map(|x| x.to_string()).collect(); let mut delim_count = 0; + if serial { for file in files.iter_mut() { let mut output = String::new(); loop { - match file.read_line() { - Ok(line) => { - output.push_str(line.as_slice().trim_right()); - output.push_str(delimiters[delim_count % delimiters.len()].as_slice()); - } - Err(f) => if f.kind == io::EndOfFile { - break - } else { - crash!(1, "{}", f.to_string()) + let mut line = String::new(); + match file.read_line(&mut line) { + Ok(0) => break, + Ok(_) => { + output.push_str(line.trim_right()); + output.push_str(&delimiters[delim_count % delimiters.len()]); } + Err(e) => crash!(1, "{}", e.to_string()) } delim_count += 1; } - println!("{}", output.as_slice().slice_to(output.len() - 1)); + println!("{}", &output[..output.len()-1]); } } else { - let mut eof : Vec = repeat(false).take(files.len()).collect(); + let mut eof: Vec = repeat(false).take(files.len()).collect(); loop { - let mut output = "".to_string(); + let mut output = String::new(); let mut eof_count = 0; for (i, file) in files.iter_mut().enumerate() { if eof[i] { eof_count += 1; } else { - match file.read_line() { - Ok(line) => output.push_str(&line.as_slice()[..line.len() - 1]), - Err(f) => if f.kind == io::EndOfFile { + let mut line = String::new(); + match file.read_line(&mut line) { + Ok(0) => { eof[i] = true; eof_count += 1; - } else { - crash!(1, "{}", f.to_string()); } + Ok(_) => output.push_str(line.trim_right()), + Err(e) => crash!(1, "{}", e.to_string()) } } - output.push_str(delimiters[delim_count % delimiters.len()].as_slice()); + output.push_str(&delimiters[delim_count % delimiters.len()]); delim_count += 1; } if files.len() == eof_count { break; } - println!("{}", output.as_slice().slice_to(output.len() - 1)); + println!("{}", &output[..output.len()-1]); delim_count = 0; } } } + +// Unescape all special characters +// TODO: this will need work to conform to GNU implementation +fn unescape(s: String) -> String { + s.replace("\\n", "\n") + .replace("\\t", "\t") + .replace("\\\\", "\\") + .replace("\\", "") +} diff --git a/test/fixtures/paste/html_colors.expected b/test/fixtures/paste/html_colors.expected new file mode 100644 index 000000000..4fb65d6aa --- /dev/null +++ b/test/fixtures/paste/html_colors.expected @@ -0,0 +1,16 @@ +white #FFFFFF +silver #C0C0C0 +gray #808080 +black #000000 +red #FF0000 +maroon #800000 +yellow #FFFF00 +olive #808000 +lime #00FF00 +green #008000 +aqua #00FFFF +teal #008080 +blue #0000FF +navy #000080 +fuchsia #FF00FF +purple #800080 diff --git a/test/fixtures/paste/html_colors.txt b/test/fixtures/paste/html_colors.txt new file mode 100644 index 000000000..290303ff0 --- /dev/null +++ b/test/fixtures/paste/html_colors.txt @@ -0,0 +1,32 @@ +white +#FFFFFF +silver +#C0C0C0 +gray +#808080 +black +#000000 +red +#FF0000 +maroon +#800000 +yellow +#FFFF00 +olive +#808000 +lime +#00FF00 +green +#008000 +aqua +#00FFFF +teal +#008080 +blue +#0000FF +navy +#000080 +fuchsia +#FF00FF +purple +#800080 diff --git a/test/paste.rs b/test/paste.rs new file mode 100644 index 000000000..b66ed01e7 --- /dev/null +++ b/test/paste.rs @@ -0,0 +1,27 @@ +use std::fs::File; +use std::io::Read; +use std::path::Path; +use std::process::Command; + +static PROGNAME: &'static str = "./paste"; + +#[test] +fn test_combine_pairs_of_lines() { + let po = Command::new(PROGNAME) + .arg("-s") + .arg("-d") + .arg("\t\n") + .arg("html_colors.txt") + .output() + .unwrap_or_else(|err| panic!("{}", err)); + + let mut f = File::open(Path::new("html_colors.expected")).unwrap_or_else(|err| { + panic!("{}", err) + }); + let mut expected = vec!(); + match f.read_to_end(&mut expected) { + Ok(_) => {}, + Err(err) => panic!("{}", err) + } + assert_eq!(String::from_utf8(po.stdout).unwrap(), String::from_utf8(expected).unwrap()); +}