Skip to content

Commit

Permalink
tail: improve GNU compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
bbara committed Feb 11, 2023
1 parent d2c047c commit 743140a
Show file tree
Hide file tree
Showing 3 changed files with 369 additions and 3 deletions.
108 changes: 106 additions & 2 deletions src/uu/tail/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ pub mod options {
pub static MAX_UNCHANGED_STATS: &str = "max-unchanged-stats";
pub static ARG_FILES: &str = "files";
pub static PRESUME_INPUT_PIPE: &str = "-presume-input-pipe"; // NOTE: three hyphens is correct
pub static B_GNU_COMPAT: &str = "b-gnu-compat";
pub static L_GNU_COMPAT: &str = "l-gnu-compat";
pub static K_GNU_COMPAT: &str = "k-gnu-compat";
pub static M_GNU_COMPAT: &str = "m-gnu-compat";
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -79,6 +83,14 @@ impl FilterMode {
}
Err(e) => return Err(UUsageError::new(1, format!("invalid number of lines: {e}"))),
}
} else if matches.get_flag(options::B_GNU_COMPAT) {
Self::Bytes(Signum::Negative(10 * 512))
} else if matches.get_flag(options::L_GNU_COMPAT) {
Self::Lines(Signum::Negative(10), b'\n')
} else if matches.get_flag(options::K_GNU_COMPAT) {
Self::Bytes(Signum::Negative(10 * 1024))
} else if matches.get_flag(options::M_GNU_COMPAT) {
Self::Bytes(Signum::Negative(10 * 1024 * 1024))
} else if zero_term {
Self::default_zero()
} else {
Expand Down Expand Up @@ -208,6 +220,62 @@ impl Settings {
.map(|v| v.map(|string| Input::from(string.clone())).collect())
.unwrap_or_default();

if !inputs.is_empty() {
const DEFAULT_MODE: char = 'l';
const DEFAULT_INTVAL: u64 = 10;
let mut name = inputs[0].display_name.to_string();
let mut default_mode = false;
if let Some(mut mode) = name.chars().last() {
if mode == 'l' || mode == 'c' || mode == 'b' || mode == 'k' || mode == 'm' {
name.pop();
} else {
mode = DEFAULT_MODE;
default_mode = true;
}
if let Some(sign) = name.chars().next() {
let mut intval_opt: Option<u64> = None;
let mut signum_opt: Option<Signum> = None;
if sign == '+' || sign == '-' {
name.remove(0);
intval_opt = if let Ok(intval) = name.parse::<u64>() {
Some(intval)
} else if !default_mode && name.is_empty() {
Some(DEFAULT_INTVAL)
} else {
None
};
}
if let Some(intval) = intval_opt {
let multiplier: u64 = if mode == 'b' {
mode = 'c';
512
} else if mode == 'k' {
mode = 'c';
1024
} else if mode == 'm' {
mode = 'c';
1024 * 1024
} else {
1
};
signum_opt = if sign == '+' {
Some(Signum::Positive(multiplier * intval))
} else {
Some(Signum::Negative(multiplier * intval))
};
}
if let Some(signum) = signum_opt {
if mode == 'c' {
settings.mode = FilterMode::Bytes(signum);
} else {
settings.mode = FilterMode::Lines(signum, b'\n');
}
inputs.remove(0);
}
}
}
}

// apply default and add '-' to inputs if none is present
if inputs.is_empty() {
inputs.push_front(Input::default());
Expand Down Expand Up @@ -366,6 +434,14 @@ pub fn uu_app() -> Command {
pub static POLLING_HELP: &str =
"Disable 'ReadDirectoryChanges' support and use polling instead";

let overrides = [
options::BYTES,
options::LINES,
options::B_GNU_COMPAT,
options::K_GNU_COMPAT,
options::L_GNU_COMPAT,
options::M_GNU_COMPAT,
];
Command::new(uucore::util_name())
.version(crate_version!())
.about(ABOUT)
Expand All @@ -376,7 +452,7 @@ pub fn uu_app() -> Command {
.short('c')
.long(options::BYTES)
.allow_hyphen_values(true)
.overrides_with_all([options::BYTES, options::LINES])
.overrides_with_all(overrides)
.help("Number of bytes to print"),
)
.arg(
Expand All @@ -394,7 +470,7 @@ pub fn uu_app() -> Command {
.short('n')
.long(options::LINES)
.allow_hyphen_values(true)
.overrides_with_all([options::BYTES, options::LINES])
.overrides_with_all(overrides)
.help("Number of lines to print"),
)
.arg(
Expand Down Expand Up @@ -479,6 +555,34 @@ pub fn uu_app() -> Command {
.num_args(1..)
.value_hint(clap::ValueHint::FilePath),
)
.arg(
Arg::new(options::B_GNU_COMPAT)
.short('b')
.action(ArgAction::SetTrue)
.overrides_with_all(overrides)
.hide(true),
)
.arg(
Arg::new(options::K_GNU_COMPAT)
.short('k')
.action(ArgAction::SetTrue)
.overrides_with_all(overrides)
.hide(true),
)
.arg(
Arg::new(options::L_GNU_COMPAT)
.short('l')
.action(ArgAction::SetTrue)
.overrides_with_all(overrides)
.hide(true),
)
.arg(
Arg::new(options::M_GNU_COMPAT)
.short('m')
.action(ArgAction::SetTrue)
.overrides_with_all(overrides)
.hide(true),
)
}

#[cfg(test)]
Expand Down
3 changes: 2 additions & 1 deletion src/uu/tail/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub fn parse_obsolete(src: &str) -> Option<Result<impl Iterator<Item = OsString>
let mut multiplier = None;
let mut c = last_char;
loop {
// not that here, we only match lower case 'k', 'c', and 'm'
// not that here, we only match lower case 'k', 'c', 'm', and 'l'
match c {
// we want to preserve order
// this also saves us 1 heap allocation
Expand All @@ -53,6 +53,7 @@ pub fn parse_obsolete(src: &str) -> Option<Result<impl Iterator<Item = OsString>
'b' => multiplier = Some(512),
'k' => multiplier = Some(1024),
'm' => multiplier = Some(1024 * 1024),
'l' => {}
'\0' => {}
_ => return Some(Err(ParseError::Syntax)),
}
Expand Down
Loading

0 comments on commit 743140a

Please sign in to comment.