Add support for BusyBox less as pager (#2162)

* Add support for BusyBox less as pager

* Run tests/syntax-tests/update.sh to update tests

* Address reviewer's concerns with pull request
* Revert all changes in `test` directory
* Minimize overall diff size
* Detect busybox from separate helper function
* Pass equivalent options to BusyBox from same code by changing from long to
  short options

* Remove redundant `if` statement from previous commit
Add test for invalid utf-8
Add `parse_less_version_busybox` to test for invalid program
Add commenting around short options
This commit is contained in:
Nathan Fisher 2022-05-04 01:56:38 -04:00 committed by GitHub
parent f2727d74d5
commit 5114c0189d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 67 additions and 13 deletions

View file

@ -3,6 +3,7 @@
## Features
- Correctly render tab stops in --show-all, see #2038 (@Synthetica9)
- Enable BusyBox less as pager, see #2162 (@nfisher1226)
## Bugfixes

View file

@ -3,21 +3,38 @@
use std::ffi::OsStr;
use std::process::Command;
pub fn retrieve_less_version(less_path: &dyn AsRef<OsStr>) -> Option<usize> {
#[derive(Debug, PartialEq)]
pub enum LessVersion {
Less(usize),
BusyBox,
}
pub fn retrieve_less_version(less_path: &dyn AsRef<OsStr>) -> Option<LessVersion> {
let resolved_path = grep_cli::resolve_binary(less_path.as_ref()).ok()?;
let cmd = Command::new(resolved_path).arg("--version").output().ok()?;
parse_less_version(&cmd.stdout)
if cmd.status.success() {
parse_less_version(&cmd.stdout)
} else {
parse_less_version_busybox(&cmd.stderr)
}
}
fn parse_less_version(output: &[u8]) -> Option<usize> {
fn parse_less_version(output: &[u8]) -> Option<LessVersion> {
if !output.starts_with(b"less ") {
return None;
}
let version = std::str::from_utf8(&output[5..]).ok()?;
let end = version.find(|c: char| !c.is_ascii_digit())?;
version[..end].parse::<usize>().ok()
Some(LessVersion::Less(version[..end].parse::<usize>().ok()?))
}
fn parse_less_version_busybox(output: &[u8]) -> Option<LessVersion> {
match std::str::from_utf8(output) {
Ok(version) if version.contains("BusyBox ") => Some(LessVersion::BusyBox),
_ => None,
}
}
#[test]
@ -30,7 +47,7 @@ For information about the terms of redistribution,
see the file named README in the less distribution.
Homepage: http://www.greenwoodsoftware.com/less";
assert_eq!(Some(487), parse_less_version(output));
assert_eq!(Some(LessVersion::Less(487)), parse_less_version(output));
}
#[test]
@ -43,7 +60,7 @@ For information about the terms of redistribution,
see the file named README in the less distribution.
Homepage: http://www.greenwoodsoftware.com/less";
assert_eq!(Some(529), parse_less_version(output));
assert_eq!(Some(LessVersion::Less(529)), parse_less_version(output));
}
#[test]
@ -56,7 +73,7 @@ For information about the terms of redistribution,
see the file named README in the less distribution.
Home page: http://www.greenwoodsoftware.com/less";
assert_eq!(Some(551), parse_less_version(output));
assert_eq!(Some(LessVersion::Less(551)), parse_less_version(output));
}
#[test]
@ -69,7 +86,7 @@ For information about the terms of redistribution,
see the file named README in the less distribution.
Home page: https://greenwoodsoftware.com/less";
assert_eq!(Some(581), parse_less_version(output));
assert_eq!(Some(LessVersion::Less(581)), parse_less_version(output));
}
#[test]
@ -77,4 +94,38 @@ fn test_parse_less_version_wrong_program() {
let output = b"more from util-linux 2.34";
assert_eq!(None, parse_less_version(output));
assert_eq!(None, parse_less_version_busybox(output));
}
#[test]
fn test_parse_less_version_busybox() {
let output = b"pkg/less: unrecognized option '--version'
BusyBox v1.35.0 (2022-04-21 10:38:11 EDT) multi-call binary.
Usage: less [-EFIMmNSRh~] [FILE]...
View FILE (or stdin) one screenful at a time
-E Quit once the end of a file is reached
-F Quit if entire file fits on first screen
-I Ignore case in all searches
-M,-m Display status line with line numbers
and percentage through the file
-N Prefix line number to each line
-S Truncate long lines
-R Remove color escape codes in input
-~ Suppress ~s displayed past EOF";
assert_eq!(
Some(LessVersion::BusyBox),
parse_less_version_busybox(output)
);
}
#[test]
fn test_parse_less_version_invalid_utf_8() {
let output = b"\xff";
assert_eq!(None, parse_less_version(output));
assert_eq!(None, parse_less_version_busybox(output));
}

View file

@ -4,7 +4,7 @@ use std::process::Child;
use crate::error::*;
#[cfg(feature = "paging")]
use crate::less::retrieve_less_version;
use crate::less::{retrieve_less_version, LessVersion};
#[cfg(feature = "paging")]
use crate::paging::PagingMode;
#[cfg(feature = "paging")]
@ -83,13 +83,13 @@ impl OutputType {
let replace_arguments_to_less = pager.source == PagerSource::EnvVarPager;
if args.is_empty() || replace_arguments_to_less {
p.arg("--RAW-CONTROL-CHARS");
p.arg("-R"); // Short version of --RAW-CONTROL-CHARS for maximum compatibility
if single_screen_action == SingleScreenAction::Quit {
p.arg("--quit-if-one-screen");
p.arg("-F"); // Short version of --quit-if-one-screen for compatibility
}
if wrapping_mode == WrappingMode::NoWrapping(true) {
p.arg("--chop-long-lines");
p.arg("-S"); // Short version of --chop-long-lines for compatibility
}
// Passing '--no-init' fixes a bug with '--quit-if-one-screen' in older
@ -103,7 +103,9 @@ impl OutputType {
None => {
p.arg("--no-init");
}
Some(version) if (version < 530 || (cfg!(windows) && version < 558)) => {
Some(LessVersion::Less(version))
if (version < 530 || (cfg!(windows) && version < 558)) =>
{
p.arg("--no-init");
}
_ => {}