From 966bfde70f322f2f8c4f728d5b8a2ad91ead5099 Mon Sep 17 00:00:00 2001 From: Mariano Casco Date: Mon, 30 May 2016 15:40:17 -0300 Subject: [PATCH] tail: follow multiple files If multiple files are passed as arguments with the -f option, a vector of BufReaders is built as the files are first tailed, so that follow() can take control for the rest of the time the program is running. follow() loops over each reader and prints all new available content on each file before moving on to the next. --- src/tail/tail.rs | 54 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/src/tail/tail.rs b/src/tail/tail.rs index 41c4cac72..a9f66f3a5 100755 --- a/src/tail/tail.rs +++ b/src/tail/tail.rs @@ -147,22 +147,29 @@ pub fn uumain(args: Vec) -> i32 { unbounded_tail(buffer, &settings); } else { let mut multiple = false; - let mut firstime = 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); + println!("==> {} <==", filename); } - firstime = 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, &settings); } } @@ -294,16 +301,25 @@ 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>, settings: &Settings) { assert!(settings.follow); + let mut last = readers.len(); + 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 reader in &mut readers { + // Print all new content since the last pass + loop { + let mut datum = String::new(); + match reader.read_line(&mut datum) { + Ok(0) => break, + Ok(_) => { + // TODO: Print headers if i != last + print!("{}", datum); + }, + Err(err) => panic!(err) + } } } } @@ -312,7 +328,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,7 +371,7 @@ 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(); let mut buf = vec![0; BLOCK_SIZE as usize]; @@ -460,8 +476,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); } }