1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

ls: correct fallbacks for terminal width

If options::WIDTH is not given, we should try to use the terminal width.
If that is unavailable, we should fall back to the 'COLUMNS' environment variable.
If that is unavailable (or invalid), we should fall back to a default of 80.
This commit is contained in:
Michael Debertol 2021-08-11 14:32:46 +02:00
parent 13b6d003bb
commit 13a62489c5
2 changed files with 49 additions and 76 deletions

View file

@ -127,6 +127,8 @@ pub mod options {
pub static IGNORE: &str = "ignore";
}
const DEFAULT_TERM_WIDTH: u16 = 80;
#[derive(Debug)]
enum LsError {
InvalidLineWidth(String),
@ -229,7 +231,7 @@ struct Config {
inode: bool,
color: Option<LsColors>,
long: LongFormat,
width: Option<u16>,
width: u16,
quoting_style: QuotingStyle,
indicator_style: IndicatorStyle,
time_style: TimeStyle,
@ -399,10 +401,25 @@ impl Config {
let width = match options.value_of(options::WIDTH) {
Some(x) => match x.parse::<u16>() {
Ok(u) => Some(u),
Ok(u) => u,
Err(_) => return Err(LsError::InvalidLineWidth(x.into()).into()),
},
None => termsize::get().map(|s| s.cols),
None => match termsize::get() {
Some(size) => size.cols,
None => match std::env::var("COLUMNS") {
Ok(columns) => match columns.parse() {
Ok(columns) => columns,
Err(_) => {
show_error!(
"ignoring invalid width in environment variable COLUMNS: '{}'",
columns
);
DEFAULT_TERM_WIDTH
}
},
Err(_) => DEFAULT_TERM_WIDTH,
},
},
};
#[allow(clippy::needless_bool)]
@ -1411,15 +1428,10 @@ fn display_items(items: &[PathData], config: &Config, out: &mut BufWriter<Stdout
} else {
let names = items.iter().filter_map(|i| display_file_name(i, config));
match (&config.format, config.width) {
(Format::Columns, Some(width)) => {
display_grid(names, width, Direction::TopToBottom, out)
}
(Format::Across, Some(width)) => {
display_grid(names, width, Direction::LeftToRight, out)
}
(Format::Commas, width_opt) => {
let term_width = width_opt.unwrap_or(1);
match config.format {
Format::Columns => display_grid(names, config.width, Direction::TopToBottom, out),
Format::Across => display_grid(names, config.width, Direction::LeftToRight, out),
Format::Commas => {
let mut current_col = 0;
let mut names = names;
if let Some(name) = names.next() {
@ -1428,7 +1440,7 @@ fn display_items(items: &[PathData], config: &Config, out: &mut BufWriter<Stdout
}
for name in names {
let name_width = name.width as u16;
if current_col + name_width + 1 > term_width {
if current_col + name_width + 1 > config.width {
current_col = name_width + 2;
let _ = write!(out, ",\n{}", name.contents);
} else {

View file

@ -184,16 +184,10 @@ fn test_ls_columns() {
// Columns is the default
let result = scene.ucmd().succeeds();
#[cfg(not(windows))]
result.stdout_only("test-columns-1\ntest-columns-2\ntest-columns-3\ntest-columns-4\n");
#[cfg(windows)]
result.stdout_only("test-columns-1 test-columns-2 test-columns-3 test-columns-4\n");
for option in &["-C", "--format=columns"] {
let result = scene.ucmd().arg(option).succeeds();
#[cfg(not(windows))]
result.stdout_only("test-columns-1\ntest-columns-2\ntest-columns-3\ntest-columns-4\n");
#[cfg(windows)]
result.stdout_only("test-columns-1 test-columns-2 test-columns-3 test-columns-4\n");
}
@ -205,6 +199,22 @@ fn test_ls_columns() {
.succeeds()
.stdout_only("test-columns-1 test-columns-3\ntest-columns-2 test-columns-4\n");
}
for option in &["-C", "--format=columns"] {
scene
.ucmd()
.env("COLUMNS", "40")
.arg(option)
.succeeds()
.stdout_only("test-columns-1 test-columns-3\ntest-columns-2 test-columns-4\n");
}
scene
.ucmd()
.env("COLUMNS", "garbage")
.succeeds()
.stdout_is("test-columns-1 test-columns-2 test-columns-3 test-columns-4\n")
.stderr_is("ls: ignoring invalid width in environment variable COLUMNS: 'garbage'");
}
#[test]
@ -220,12 +230,8 @@ fn test_ls_across() {
let result = scene.ucmd().arg(option).succeeds();
// Because the test terminal has width 0, this is the same output as
// the columns option.
if cfg!(unix) {
result.stdout_only("test-across-1\ntest-across-2\ntest-across-3\ntest-across-4\n");
} else {
result.stdout_only("test-across-1 test-across-2 test-across-3 test-across-4\n");
}
}
for option in &["-x", "--format=across"] {
// Because the test terminal has width 0, this is the same output as
@ -250,12 +256,8 @@ fn test_ls_commas() {
for option in &["-m", "--format=commas"] {
let result = scene.ucmd().arg(option).succeeds();
if cfg!(unix) {
result.stdout_only("test-commas-1,\ntest-commas-2,\ntest-commas-3,\ntest-commas-4\n");
} else {
result.stdout_only("test-commas-1, test-commas-2, test-commas-3, test-commas-4\n");
}
}
for option in &["-m", "--format=commas"] {
scene
@ -571,13 +573,11 @@ fn test_ls_sort_name() {
at.touch("test-1");
at.touch("test-2");
let sep = if cfg!(unix) { "\n" } else { " " };
scene
.ucmd()
.arg("--sort=name")
.succeeds()
.stdout_is(["test-1", "test-2", "test-3\n"].join(sep));
.stdout_is("test-1 test-2 test-3\n");
let scene_dot = TestScenario::new(util_name!());
let at = &scene_dot.fixtures;
@ -591,7 +591,7 @@ fn test_ls_sort_name() {
.arg("--sort=name")
.arg("-A")
.succeeds()
.stdout_is([".a", ".b", "a", "b\n"].join(sep));
.stdout_is(".a .b a b\n");
}
#[test]
@ -612,27 +612,15 @@ fn test_ls_order_size() {
scene.ucmd().arg("-al").succeeds();
let result = scene.ucmd().arg("-S").succeeds();
#[cfg(not(windows))]
result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n");
#[cfg(windows)]
result.stdout_only("test-4 test-3 test-2 test-1\n");
let result = scene.ucmd().arg("-S").arg("-r").succeeds();
#[cfg(not(windows))]
result.stdout_only("test-1\ntest-2\ntest-3\ntest-4\n");
#[cfg(windows)]
result.stdout_only("test-1 test-2 test-3 test-4\n");
let result = scene.ucmd().arg("--sort=size").succeeds();
#[cfg(not(windows))]
result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n");
#[cfg(windows)]
result.stdout_only("test-4 test-3 test-2 test-1\n");
let result = scene.ucmd().arg("--sort=size").arg("-r").succeeds();
#[cfg(not(windows))]
result.stdout_only("test-1\ntest-2\ntest-3\ntest-4\n");
#[cfg(windows)]
result.stdout_only("test-1 test-2 test-3 test-4\n");
}
@ -755,9 +743,6 @@ fn test_ls_styles() {
at.touch("test2");
let result = scene.ucmd().arg("--full-time").arg("-x").succeeds();
#[cfg(not(windows))]
assert_eq!(result.stdout_str(), "test\ntest2\n");
#[cfg(windows)]
assert_eq!(result.stdout_str(), "test test2\n");
}
@ -794,27 +779,15 @@ fn test_ls_order_time() {
// ctime was changed at write, so the order is 4 3 2 1
let result = scene.ucmd().arg("-t").succeeds();
#[cfg(not(windows))]
result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n");
#[cfg(windows)]
result.stdout_only("test-4 test-3 test-2 test-1\n");
let result = scene.ucmd().arg("--sort=time").succeeds();
#[cfg(not(windows))]
result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n");
#[cfg(windows)]
result.stdout_only("test-4 test-3 test-2 test-1\n");
let result = scene.ucmd().arg("-tr").succeeds();
#[cfg(not(windows))]
result.stdout_only("test-1\ntest-2\ntest-3\ntest-4\n");
#[cfg(windows)]
result.stdout_only("test-1 test-2 test-3 test-4\n");
let result = scene.ucmd().arg("--sort=time").arg("-r").succeeds();
#[cfg(not(windows))]
result.stdout_only("test-1\ntest-2\ntest-3\ntest-4\n");
#[cfg(windows)]
result.stdout_only("test-1 test-2 test-3 test-4\n");
// 3 was accessed last in the read
@ -826,28 +799,20 @@ fn test_ls_order_time() {
// It seems to be dependent on the platform whether the access time is actually set
if file3_access > file4_access {
if cfg!(not(windows)) {
result.stdout_only("test-3\ntest-4\ntest-2\ntest-1\n");
} else {
result.stdout_only("test-3 test-4 test-2 test-1\n");
}
} else {
// Access time does not seem to be set on Windows and some other
// systems so the order is 4 3 2 1
if cfg!(not(windows)) {
result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n");
} else {
result.stdout_only("test-4 test-3 test-2 test-1\n");
}
}
}
// test-2 had the last ctime change when the permissions were set
// So the order should be 2 4 3 1
#[cfg(unix)]
{
let result = scene.ucmd().arg("-tc").succeeds();
result.stdout_only("test-2\ntest-4\ntest-3\ntest-1\n");
result.stdout_only("test-2 test-4 test-3 test-1\n");
}
}
@ -2009,11 +1974,7 @@ fn test_ls_path() {
};
scene.ucmd().arg(&abs_path).run().stdout_is(expected_stdout);
let expected_stdout = if cfg!(windows) {
format!("{} {}\n", path, file1)
} else {
format!("{}\n{}\n", path, file1)
};
let expected_stdout = format!("{} {}\n", path, file1);
scene
.ucmd()
.arg(file1)