on master help sign in
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

commit5114c0189d9c9a99312aa82f1d7217109c1ae28d authorNathan Fisher <nfisher1226@users.noreply.github.com> dateWed 4 May 2022, 01:56 (5 years ago) parentf2727d7
3 files changed+67−13
CHANGELOG.md+1 src/less.rs+59−8 src/output.rs+7−5
CHANGELOG.md+1
@@ -3,6 +3,7 @@
33## Features
44
55- Correctly render tab stops in --show-all, see #2038 (@Synthetica9)
6+- Enable BusyBox less as pager, see #2162 (@nfisher1226)
67
78## Bugfixes
89
src/less.rs+59−8
@@ -3,21 +3,38 @@
33use std::ffi::OsStr;
44use std::process::Command;
55
6−pub fn retrieve_less_version(less_path: &dyn AsRef<OsStr>) -> Option<usize> {
6+#[derive(Debug, PartialEq)]
7+pub enum LessVersion {
8+ Less(usize),
9+ BusyBox,
10+}
11+
12+pub fn retrieve_less_version(less_path: &dyn AsRef<OsStr>) -> Option<LessVersion> {
713 let resolved_path = grep_cli::resolve_binary(less_path.as_ref()).ok()?;
814
915 let cmd = Command::new(resolved_path).arg("--version").output().ok()?;
10− parse_less_version(&cmd.stdout)
16+ if cmd.status.success() {
17+ parse_less_version(&cmd.stdout)
18+ } else {
19+ parse_less_version_busybox(&cmd.stderr)
20+ }
1121}
1222
13−fn parse_less_version(output: &[u8]) -> Option<usize> {
23+fn parse_less_version(output: &[u8]) -> Option<LessVersion> {
1424 if !output.starts_with(b"less ") {
1525 return None;
1626 }
1727
1828 let version = std::str::from_utf8(&output[5..]).ok()?;
1929 let end = version.find(|c: char| !c.is_ascii_digit())?;
20− version[..end].parse::<usize>().ok()
30+ Some(LessVersion::Less(version[..end].parse::<usize>().ok()?))
31+}
32+
33+fn parse_less_version_busybox(output: &[u8]) -> Option<LessVersion> {
34+ match std::str::from_utf8(output) {
35+ Ok(version) if version.contains("BusyBox ") => Some(LessVersion::BusyBox),
36+ _ => None,
37+ }
2138}
2239
2340#[test]
@@ -30,7 +47,7 @@ For information about the terms of redistribution,
3047see the file named README in the less distribution.
3148Homepage: http://www.greenwoodsoftware.com/less";
3249
33− assert_eq!(Some(487), parse_less_version(output));
50+ assert_eq!(Some(LessVersion::Less(487)), parse_less_version(output));
3451}
3552
3653#[test]
@@ -43,7 +60,7 @@ For information about the terms of redistribution,
4360see the file named README in the less distribution.
4461Homepage: http://www.greenwoodsoftware.com/less";
4562
46− assert_eq!(Some(529), parse_less_version(output));
63+ assert_eq!(Some(LessVersion::Less(529)), parse_less_version(output));
4764}
4865
4966#[test]
@@ -56,7 +73,7 @@ For information about the terms of redistribution,
5673see the file named README in the less distribution.
5774Home page: http://www.greenwoodsoftware.com/less";
5875
59− assert_eq!(Some(551), parse_less_version(output));
76+ assert_eq!(Some(LessVersion::Less(551)), parse_less_version(output));
6077}
6178
6279#[test]
@@ -69,7 +86,7 @@ For information about the terms of redistribution,
6986see the file named README in the less distribution.
7087Home page: https://greenwoodsoftware.com/less";
7188
72− assert_eq!(Some(581), parse_less_version(output));
89+ assert_eq!(Some(LessVersion::Less(581)), parse_less_version(output));
7390}
7491
7592#[test]
@@ -77,4 +94,38 @@ fn test_parse_less_version_wrong_program() {
7794 let output = b"more from util-linux 2.34";
7895
7996 assert_eq!(None, parse_less_version(output));
97+ assert_eq!(None, parse_less_version_busybox(output));
98+}
99+
100+#[test]
101+fn test_parse_less_version_busybox() {
102+ let output = b"pkg/less: unrecognized option '--version'
103+BusyBox v1.35.0 (2022-04-21 10:38:11 EDT) multi-call binary.
104+
105+Usage: less [-EFIMmNSRh~] [FILE]...
106+
107+View FILE (or stdin) one screenful at a time
108+
109+ -E Quit once the end of a file is reached
110+ -F Quit if entire file fits on first screen
111+ -I Ignore case in all searches
112+ -M,-m Display status line with line numbers
113+ and percentage through the file
114+ -N Prefix line number to each line
115+ -S Truncate long lines
116+ -R Remove color escape codes in input
117+ -~ Suppress ~s displayed past EOF";
118+
119+ assert_eq!(
120+ Some(LessVersion::BusyBox),
121+ parse_less_version_busybox(output)
122+ );
123+}
124+
125+#[test]
126+fn test_parse_less_version_invalid_utf_8() {
127+ let output = b"\xff";
128+
129+ assert_eq!(None, parse_less_version(output));
130+ assert_eq!(None, parse_less_version_busybox(output));
80131}
src/output.rs+7−5
@@ -4,7 +4,7 @@ use std::process::Child;
44
55use crate::error::*;
66#[cfg(feature = "paging")]
7−use crate::less::retrieve_less_version;
7+use crate::less::{retrieve_less_version, LessVersion};
88#[cfg(feature = "paging")]
99use crate::paging::PagingMode;
1010#[cfg(feature = "paging")]
@@ -83,13 +83,13 @@ impl OutputType {
8383 let replace_arguments_to_less = pager.source == PagerSource::EnvVarPager;
8484
8585 if args.is_empty() || replace_arguments_to_less {
86− p.arg("--RAW-CONTROL-CHARS");
86+ p.arg("-R"); // Short version of --RAW-CONTROL-CHARS for maximum compatibility
8787 if single_screen_action == SingleScreenAction::Quit {
88− p.arg("--quit-if-one-screen");
88+ p.arg("-F"); // Short version of --quit-if-one-screen for compatibility
8989 }
9090
9191 if wrapping_mode == WrappingMode::NoWrapping(true) {
92− p.arg("--chop-long-lines");
92+ p.arg("-S"); // Short version of --chop-long-lines for compatibility
9393 }
9494
9595 // Passing '--no-init' fixes a bug with '--quit-if-one-screen' in older
@@ -103,7 +103,9 @@ impl OutputType {
103103 None => {
104104 p.arg("--no-init");
105105 }
106− Some(version) if (version < 530 || (cfg!(windows) && version < 558)) => {
106+ Some(LessVersion::Less(version))
107+ if (version < 530 || (cfg!(windows) && version < 558)) =>
108+ {
107109 p.arg("--no-init");
108110 }
109111 _ => {}