diff --git a/src/tail/tail.rs b/src/tail/tail.rs index c22177498..a806af722 100755 --- a/src/tail/tail.rs +++ b/src/tail/tail.rs @@ -147,22 +147,32 @@ pub fn uumain(args: Vec) -> i32 { unbounded_tail(buffer, &settings); } else { let mut multiple = false; - let mut firstime = true; + let mut first_header = true; + let mut readers = Vec::new(); if files.len() > 1 { multiple = true; } - for file in &files { + for filename in &files { if multiple { - if !firstime { println!(""); } - println!("==> {} <==", file); + if !first_header { println!(""); } + println!("==> {} <==", filename); } - firstime = false; + first_header = false; - let path = Path::new(file); - let reader = File::open(&path).unwrap(); - bounded_tail(reader, &settings); + let path = Path::new(filename); + let file = File::open(&path).unwrap(); + bounded_tail(&file, &settings); + + if settings.follow { + let reader = BufReader::new(file); + readers.push(reader); + } + } + + if settings.follow { + follow(readers, &files, &settings); } } @@ -294,16 +304,29 @@ fn obsolete(options: &[String]) -> (Vec, Option) { /// block read at a time. const BLOCK_SIZE: u64 = 1 << 16; -fn follow(mut reader: BufReader, settings: &Settings) { +fn follow(mut readers: Vec>, filenames: &Vec, settings: &Settings) { assert!(settings.follow); + let mut last = readers.len() - 1; + loop { sleep(Duration::new(0, settings.sleep_msec*1000)); - loop { - let mut datum = String::new(); - match reader.read_line(&mut datum) { - Ok(0) => break, - Ok(_) => print!("{}", datum), - Err(err) => panic!(err) + + for (i, reader) in readers.iter_mut().enumerate() { + // Print all new content since the last pass + loop { + let mut datum = String::new(); + match reader.read_line(&mut datum) { + Ok(0) => break, + Ok(_) => { + if i != last { + println!(""); + println!("==> {} <==", filenames[i]); + last = i; + } + print!("{}", datum); + }, + Err(err) => panic!(err) + } } } } @@ -312,7 +335,7 @@ fn follow(mut reader: BufReader, settings: &Settings) { /// Iterate over bytes in the file, in reverse, until `should_stop` returns /// true. The `file` is left seek'd to the position just after the byte that /// `should_stop` returned true for. -fn backwards_thru_file(file: &mut File, size: u64, buf: &mut Vec, delimiter: u8, should_stop: &mut F) +fn backwards_thru_file(mut file: &File, size: u64, buf: &mut Vec, delimiter: u8, should_stop: &mut F) where F: FnMut(u8) -> bool { assert!(buf.len() >= BLOCK_SIZE as usize); @@ -355,16 +378,8 @@ fn backwards_thru_file(file: &mut File, size: u64, buf: &mut Vec, delimit /// end of the file, and then read the file "backwards" in blocks of size /// `BLOCK_SIZE` until we find the location of the first line/byte. This ends up /// being a nice performance win for very large files. -fn bounded_tail(mut file: File, settings: &Settings) { +fn bounded_tail(mut file: &File, settings: &Settings) { let size = file.seek(SeekFrom::End(0)).unwrap(); - if size == 0 { - if settings.follow { - let reader = BufReader::new(file); - follow(reader, settings); - } - return; - } - let mut buf = vec![0; BLOCK_SIZE as usize]; // Find the position in the file to start printing from. @@ -397,12 +412,6 @@ fn bounded_tail(mut file: File, settings: &Settings) { break; } } - - // Continue following changes, if requested. - if settings.follow { - let reader = BufReader::new(file); - follow(reader, settings); - } } fn unbounded_tail(mut reader: BufReader, settings: &Settings) { @@ -474,8 +483,10 @@ fn unbounded_tail(mut reader: BufReader, settings: &Settings) { } } + // TODO: make following stdin work with the new follow() signature + // maybe wrap stdin in a 1-element vec? if settings.follow { - follow(reader, settings); + //follow(reader, settings); } } diff --git a/tests/fixtures/tail/foobar2.txt b/tests/fixtures/tail/foobar2.txt new file mode 100644 index 000000000..25973d326 --- /dev/null +++ b/tests/fixtures/tail/foobar2.txt @@ -0,0 +1,2 @@ +un +deux diff --git a/tests/fixtures/tail/foobar_follow_multiple.expected b/tests/fixtures/tail/foobar_follow_multiple.expected new file mode 100644 index 000000000..140a9088d --- /dev/null +++ b/tests/fixtures/tail/foobar_follow_multiple.expected @@ -0,0 +1,15 @@ +==> foobar.txt <== +dos +tres +quattro +cinco +seis +siette +ocho +nueve +diez +once + +==> foobar2.txt <== +un +deux diff --git a/tests/fixtures/tail/foobar_follow_multiple_appended.expected b/tests/fixtures/tail/foobar_follow_multiple_appended.expected new file mode 100644 index 000000000..0896d3743 --- /dev/null +++ b/tests/fixtures/tail/foobar_follow_multiple_appended.expected @@ -0,0 +1,4 @@ + +==> foobar.txt <== +doce +trece diff --git a/tests/test_tail.rs b/tests/test_tail.rs index fbd527b39..c842354d1 100644 --- a/tests/test_tail.rs +++ b/tests/test_tail.rs @@ -8,6 +8,7 @@ use uu_tail::parse_size; static UTIL_NAME: &'static str = "tail"; static FOOBAR_TXT: &'static str = "foobar.txt"; +static FOOBAR_2_TXT: &'static str = "foobar2.txt"; static FOOBAR_WITH_NULL_TXT: &'static str = "foobar_with_null.txt"; #[test] @@ -56,6 +57,26 @@ fn test_follow() { child.kill().unwrap(); } +#[test] +fn test_follow_multiple() { + let (at, mut ucmd) = testing(UTIL_NAME); + let mut child = ucmd.arg("-f").arg(FOOBAR_TXT).arg(FOOBAR_2_TXT).run_no_wait(); + + let expected = at.read("foobar_follow_multiple.expected"); + assert_eq!(read_size(&mut child, expected.len()), expected); + + let first_append = "trois\n"; + at.append(FOOBAR_2_TXT, first_append); + assert_eq!(read_size(&mut child, first_append.len()), first_append); + + let second_append = "doce\ntrece\n"; + let expected = at.read("foobar_follow_multiple_appended.expected"); + at.append(FOOBAR_TXT, second_append); + assert_eq!(read_size(&mut child, expected.len()), expected); + + child.kill().unwrap(); +} + #[test] fn test_single_big_args() { const FILE: &'static str = "single_big_args.txt";