1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-31 13:07:46 +00:00

Modify tail to pass some Busybox tests

This commit is contained in:
Arcterus 2014-09-25 23:16:58 -07:00
parent 062e970212
commit d17d388e2b
2 changed files with 117 additions and 35 deletions

View file

@ -3,7 +3,8 @@ Rudimentary tail implementation.
##Missing features: ##Missing features:
### Flags with features ### Flags with features
* `--bytes` : output the last K bytes; alternatively, use `-c` +K to output bytes starting with the Kth of each file * `--bytes` : does not handle size suffixes
* `--lines` : does not handle size suffixes
* `--max-unchanged-stats` : with `--follow=name`, reopen a FILE which has not changed size after N (default 5) iterations to see if it has been unlinked or renamed (this is the usual case of rotated log files). With inotify, this option is rarely useful. * `--max-unchanged-stats` : with `--follow=name`, reopen a FILE which has not changed size after N (default 5) iterations to see if it has been unlinked or renamed (this is the usual case of rotated log files). With inotify, this option is rarely useful.
* `--pid` : with `-f`, terminate after process ID, PID dies * `--pid` : with `-f`, terminate after process ID, PID dies
* `--quiet` : never output headers giving file names * `--quiet` : never output headers giving file names

View file

@ -9,6 +9,8 @@
* *
*/ */
#![feature(macro_rules)]
extern crate getopts; extern crate getopts;
use std::char; use std::char;
@ -23,9 +25,16 @@ use std::collections::ringbuf::RingBuf;
use std::io::timer::sleep; use std::io::timer::sleep;
use std::time::duration::Duration; use std::time::duration::Duration;
static PROGRAM: &'static str = "tail"; #[path = "../common/util.rs"]
mod util;
static NAME: &'static str = "tail";
static VERSION: &'static str = "0.0.1";
pub fn uumain(args: Vec<String>) -> int { pub fn uumain(args: Vec<String>) -> int {
let mut beginning = false;
let mut lines = true;
let mut byte_count = 0u;
let mut line_count = 10u; let mut line_count = 10u;
let mut sleep_msec = 1000u64; let mut sleep_msec = 1000u64;
@ -38,7 +47,8 @@ pub fn uumain(args: Vec<String>) -> int {
let args = options; let args = options;
let possible_options = [ let possible_options = [
optopt("n", "number", "Number of lines to print", "n"), optopt("c", "bytes", "Number of bytes to print", "k"),
optopt("n", "lines", "Number of lines to print", "k"),
optflag("f", "follow", "Print the file as it grows"), optflag("f", "follow", "Print the file as it grows"),
optopt("s", "sleep-interval", "Number or seconds to sleep between polling the file when running with -f", "n"), optopt("s", "sleep-interval", "Number or seconds to sleep between polling the file when running with -f", "n"),
optflag("h", "help", "help"), optflag("h", "help", "help"),
@ -48,13 +58,13 @@ pub fn uumain(args: Vec<String>) -> int {
let given_options = match getopts(args.as_slice(), possible_options) { let given_options = match getopts(args.as_slice(), possible_options) {
Ok (m) => { m } Ok (m) => { m }
Err(_) => { Err(_) => {
println!("{:s}", usage(PROGRAM, possible_options)); println!("{:s}", usage(NAME, possible_options));
return 1; return 1;
} }
}; };
if given_options.opt_present("h") { if given_options.opt_present("h") {
println!("{:s}", usage(PROGRAM, possible_options)); println!("{:s}", usage(NAME, possible_options));
return 0; return 0;
} }
if given_options.opt_present("V") { version(); return 0 } if given_options.opt_present("V") { version(); return 0 }
@ -63,9 +73,9 @@ pub fn uumain(args: Vec<String>) -> int {
if follow { if follow {
match given_options.opt_str("s") { match given_options.opt_str("s") {
Some(n) => { Some(n) => {
let parsed : Option<u64> = from_str(n.as_slice()); let parsed: Option<u64> = from_str(n.as_slice());
match parsed { match parsed {
Some(m) => { sleep_msec = m*1000 } Some(m) => { sleep_msec = m * 1000 }
None => {} None => {}
} }
} }
@ -75,19 +85,44 @@ pub fn uumain(args: Vec<String>) -> int {
match given_options.opt_str("n") { match given_options.opt_str("n") {
Some(n) => { Some(n) => {
match from_str(n.as_slice()) { let mut slice = n.as_slice();
Some(m) => { line_count = m } if slice.len() > 0 && slice.char_at(0) == '+' {
None => {} beginning = true;
slice = slice.slice_from(1);
} }
line_count = match from_str(slice) {
Some(m) => m,
None => {
show_error!("invalid number of lines ({})", slice);
return 1;
}
};
}
None => match given_options.opt_str("c") {
Some(n) => {
let mut slice = n.as_slice();
if slice.len() > 0 && slice.char_at(0) == '+' {
beginning = true;
slice = slice.slice_from(1);
}
byte_count = match from_str(slice) {
Some(m) => m,
None => {
show_error!("invalid number of bytes ({})", slice);
return 1;
}
};
lines = false;
}
None => { }
} }
None => {}
}; };
let files = given_options.free; let files = given_options.free;
if files.is_empty() { if files.is_empty() {
let mut buffer = BufferedReader::new(stdin()); let mut buffer = BufferedReader::new(stdin());
tail(&mut buffer, line_count, follow, sleep_msec); tail(&mut buffer, line_count, byte_count, beginning, lines, follow, sleep_msec);
} else { } else {
let mut multiple = false; let mut multiple = false;
let mut firstime = true; let mut firstime = true;
@ -107,7 +142,7 @@ pub fn uumain(args: Vec<String>) -> int {
let path = Path::new(file.as_slice()); let path = Path::new(file.as_slice());
let reader = File::open(&path).unwrap(); let reader = File::open(&path).unwrap();
let mut buffer = BufferedReader::new(reader); let mut buffer = BufferedReader::new(reader);
tail(&mut buffer, line_count, follow, sleep_msec); tail(&mut buffer, line_count, byte_count, beginning, lines, follow, sleep_msec);
} }
} }
@ -118,7 +153,7 @@ pub fn uumain(args: Vec<String>) -> int {
// //
// In case is found, the options vector will get rid of that object so that // In case is found, the options vector will get rid of that object so that
// getopts works correctly. // getopts works correctly.
fn obsolete (options: &[String]) -> (Vec<String>, Option<uint>) { fn obsolete(options: &[String]) -> (Vec<String>, Option<uint>) {
let mut options: Vec<String> = options.to_vec(); let mut options: Vec<String> = options.to_vec();
let mut a = 0; let mut a = 0;
let b = options.len(); let b = options.len();
@ -136,7 +171,7 @@ fn obsolete (options: &[String]) -> (Vec<String>, Option<uint>) {
// If this is the last number // If this is the last number
if pos == len - 1 { if pos == len - 1 {
options.remove(a); options.remove(a);
let number : Option<uint> = from_str(from_utf8(current.slice(1,len)).unwrap()); let number: Option<uint> = from_str(from_utf8(current.slice(1,len)).unwrap());
return (options, Some(number.unwrap())); return (options, Some(number.unwrap()));
} }
} }
@ -148,24 +183,11 @@ fn obsolete (options: &[String]) -> (Vec<String>, Option<uint>) {
(options, None) (options, None)
} }
fn tail<T: Reader> (reader: &mut BufferedReader<T>, line_count:uint, follow:bool, sleep_msec:u64) { fn tail<T: Reader>(reader: &mut BufferedReader<T>, line_count: uint, byte_count: uint, beginning: bool, lines: bool, follow: bool, sleep_msec: u64) {
// read through each line and store them in a ringbuffer that always contains if lines {
// line_count lines. When reaching the end of file, output the lines in the tail_lines(reader, line_count, beginning);
// ringbuf. } else {
let mut ringbuf : RingBuf<String> = RingBuf::new(); tail_bytes(reader, byte_count, beginning);
for io_line in reader.lines(){
match io_line {
Ok(line) => {
if line_count<=ringbuf.len(){
ringbuf.pop_front();
}
ringbuf.push(line);
}
Err(err) => fail!(err)
}
}
for line in ringbuf.iter() {
print!("{}", line);
} }
// if we follow the file, sleep a bit and print the rest if the file has grown. // if we follow the file, sleep a bit and print the rest if the file has grown.
@ -180,6 +202,65 @@ fn tail<T: Reader> (reader: &mut BufferedReader<T>, line_count:uint, follow:bool
} }
} }
fn version () { #[inline]
println!("tail version 0.0.1"); fn tail_lines<T: Reader>(reader: &mut BufferedReader<T>, mut line_count: uint, beginning: bool) {
// read through each line and store them in a ringbuffer that always contains
// line_count lines. When reaching the end of file, output the lines in the
// ringbuf.
let mut ringbuf: RingBuf<String> = RingBuf::new();
let mut lines = reader.lines().skip(
if beginning {
let temp = line_count;
line_count = ::std::uint::MAX;
temp - 1
} else {
0
}
);
for io_line in lines {
match io_line {
Ok(line) => {
if line_count <= ringbuf.len() {
ringbuf.pop_front();
}
ringbuf.push(line);
}
Err(err) => fail!(err)
}
}
for line in ringbuf.iter() {
print!("{}", line);
}
}
#[inline]
fn tail_bytes<T: Reader>(reader: &mut BufferedReader<T>, mut byte_count: uint, beginning: bool) {
let mut ringbuf: RingBuf<u8> = RingBuf::new();
let mut bytes = reader.bytes().skip(
if beginning {
let temp = byte_count;
byte_count = ::std::uint::MAX;
temp - 1
} else {
0
}
);
for io_byte in bytes {
match io_byte {
Ok(byte) => {
if byte_count <= ringbuf.len() {
ringbuf.pop_front();
}
ringbuf.push(byte);
}
Err(err) => fail!(err)
}
}
for byte in ringbuf.iter() {
print!("{}", byte);
}
}
fn version () {
println!("{} v{}", NAME, VERSION);
} }