Skip to content

Commit 4901f37

Browse files
committed
ls: Add support for newline separated format for recent/older
Documented in GNU manual. Also improve test_ls to test for both recent and older files.
1 parent e5f2f79 commit 4901f37

File tree

2 files changed

+82
-28
lines changed

2 files changed

+82
-28
lines changed

src/uu/ls/src/ls.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,16 @@ fn parse_time_style(options: &clap::ArgMatches) -> Result<(String, Option<String
297297
match time_styles.get(field) {
298298
Some(formats) => ok(*formats),
299299
None => match field.chars().next().unwrap() {
300-
'+' => Ok((field[1..].to_string(), None)),
300+
'+' => {
301+
// recent/older formats are (optionally) separated by a newline
302+
let mut it = field[1..].split('\n');
303+
let recent = it.next().unwrap_or_default();
304+
let older = it.next();
305+
match it.next() {
306+
None => ok((recent, older)),
307+
Some(_) => Err(LsError::TimeStyleParseError(String::from(field))),
308+
}
309+
}
301310
_ => Err(LsError::TimeStyleParseError(String::from(field))),
302311
},
303312
}

tests/by-util/test_ls.rs

Lines changed: 72 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ use std::collections::HashMap;
1919
use std::ffi::OsStr;
2020
#[cfg(target_os = "linux")]
2121
use std::os::unix::ffi::OsStrExt;
22-
use std::path::Path;
2322
#[cfg(not(windows))]
2423
use std::path::PathBuf;
2524
use std::thread::sleep;
2625
use std::time::Duration;
26+
use std::{path::Path, time::SystemTime};
2727
use uutests::new_ucmd;
2828
#[cfg(unix)]
2929
use uutests::unwrap_or_return;
@@ -1923,75 +1923,96 @@ fn test_ls_order_birthtime() {
19231923
}
19241924

19251925
#[test]
1926-
fn test_ls_styles() {
1926+
fn test_ls_time_styles() {
19271927
let scene = TestScenario::new(util_name!());
19281928
let at = &scene.fixtures;
1929+
// Create a recent and old (<6 months) file, as format can be different.
19291930
at.touch("test");
1931+
let f3 = at.make_file("test-old");
1932+
f3.set_modified(SystemTime::now() - Duration::from_secs(3600 * 24 * 365))
1933+
.unwrap();
19301934

1931-
let re_full = Regex::new(
1935+
let re_full_recent = Regex::new(
19321936
r"[a-z-]* \d* [\w.]* [\w.]* \d* \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d* (\+|\-)\d{4} test\n",
19331937
)
19341938
.unwrap();
1935-
let re_long =
1939+
let re_long_recent =
19361940
Regex::new(r"[a-z-]* \d* [\w.]* [\w.]* \d* \d{4}-\d{2}-\d{2} \d{2}:\d{2} test\n").unwrap();
1937-
let re_iso =
1941+
let re_iso_recent =
19381942
Regex::new(r"[a-z-]* \d* [\w.]* [\w.]* \d* \d{2}-\d{2} \d{2}:\d{2} test\n").unwrap();
1939-
let re_locale =
1943+
let re_locale_recent =
19401944
Regex::new(r"[a-z-]* \d* [\w.]* [\w.]* \d* [A-Z][a-z]{2} ( |\d)\d \d{2}:\d{2} test\n")
19411945
.unwrap();
1942-
let re_custom_format =
1943-
Regex::new(r"[a-z-]* \d* [\w.]* [\w.]* \d* \d{4}__\d{2} test\n").unwrap();
1946+
let re_full_old = Regex::new(
1947+
r"[a-z-]* \d* [\w.]* [\w.]* \d* \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d* (\+|\-)\d{4} test-old\n",
1948+
)
1949+
.unwrap();
1950+
let re_long_old =
1951+
Regex::new(r"[a-z-]* \d* [\w.]* [\w.]* \d* \d{4}-\d{2}-\d{2} \d{2}:\d{2} test-old\n")
1952+
.unwrap();
1953+
let re_iso_old =
1954+
Regex::new(r"[a-z-]* \d* [\w.]* [\w.]* \d* \d{4}-\d{2}-\d{2} test-old\n").unwrap();
1955+
let re_locale_old =
1956+
Regex::new(r"[a-z-]* \d* [\w.]* [\w.]* \d* [A-Z][a-z]{2} ( |\d)\d \d{4} test-old\n")
1957+
.unwrap();
19441958

19451959
//full-iso
19461960
scene
19471961
.ucmd()
19481962
.arg("-l")
19491963
.arg("--time-style=full-iso")
19501964
.succeeds()
1951-
.stdout_matches(&re_full);
1965+
.stdout_matches(&re_full_recent)
1966+
.stdout_matches(&re_full_old);
19521967
//long-iso
19531968
scene
19541969
.ucmd()
19551970
.arg("-l")
19561971
.arg("--time-style=long-iso")
19571972
.succeeds()
1958-
.stdout_matches(&re_long);
1973+
.stdout_matches(&re_long_recent)
1974+
.stdout_matches(&re_long_old);
19591975
//iso
19601976
scene
19611977
.ucmd()
19621978
.arg("-l")
19631979
.arg("--time-style=iso")
19641980
.succeeds()
1965-
.stdout_matches(&re_iso);
1981+
.stdout_matches(&re_iso_recent)
1982+
.stdout_matches(&re_iso_old);
19661983
//locale
19671984
scene
19681985
.ucmd()
19691986
.arg("-l")
19701987
.arg("--time-style=locale")
19711988
.succeeds()
1972-
.stdout_matches(&re_locale);
1989+
.stdout_matches(&re_locale_recent)
1990+
.stdout_matches(&re_locale_old);
19731991

19741992
//posix-full-iso
19751993
scene
19761994
.ucmd()
19771995
.arg("-l")
19781996
.arg("--time-style=posix-full-iso")
19791997
.succeeds()
1980-
.stdout_matches(&re_full);
1998+
.stdout_matches(&re_full_recent)
1999+
.stdout_matches(&re_full_old);
19812000
//posix-long-iso
19822001
scene
19832002
.ucmd()
19842003
.arg("-l")
19852004
.arg("--time-style=posix-long-iso")
19862005
.succeeds()
1987-
.stdout_matches(&re_long);
2006+
.stdout_matches(&re_long_recent)
2007+
.stdout_matches(&re_long_old);
19882008
//posix-iso
19892009
scene
19902010
.ucmd()
19912011
.arg("-l")
19922012
.arg("--time-style=posix-iso")
19932013
.succeeds()
1994-
.stdout_matches(&re_iso);
2014+
.stdout_matches(&re_iso_recent)
2015+
.stdout_matches(&re_iso_old);
19952016

19962017
//posix-* with LC_TIME/LC_ALL=POSIX is equivalent to locale
19972018
scene
@@ -2000,22 +2021,40 @@ fn test_ls_styles() {
20002021
.arg("-l")
20012022
.arg("--time-style=posix-full-iso")
20022023
.succeeds()
2003-
.stdout_matches(&re_locale);
2024+
.stdout_matches(&re_locale_recent)
2025+
.stdout_matches(&re_locale_old);
20042026
scene
20052027
.ucmd()
20062028
.env("LC_ALL", "POSIX")
20072029
.arg("-l")
20082030
.arg("--time-style=posix-iso")
20092031
.succeeds()
2010-
.stdout_matches(&re_locale);
2032+
.stdout_matches(&re_locale_recent)
2033+
.stdout_matches(&re_locale_old);
20112034

20122035
//+FORMAT
2036+
let re_custom_format_recent =
2037+
Regex::new(r"[a-z-]* \d* [\w.]* [\w.]* \d* \d{4}__\d{2} test\n").unwrap();
2038+
let re_custom_format_old =
2039+
Regex::new(r"[a-z-]* \d* [\w.]* [\w.]* \d* \d{4}__\d{2} test-old\n").unwrap();
20132040
scene
20142041
.ucmd()
20152042
.arg("-l")
20162043
.arg("--time-style=+%Y__%M")
20172044
.succeeds()
2018-
.stdout_matches(&re_custom_format);
2045+
.stdout_matches(&re_custom_format_recent)
2046+
.stdout_matches(&re_custom_format_old);
2047+
2048+
//+FORMAT_RECENT\nFORMAT_OLD
2049+
let re_custom_format_old =
2050+
Regex::new(r"[a-z-]* \d* [\w.]* [\w.]* \d* \d{4}--\d{2} test-old\n").unwrap();
2051+
scene
2052+
.ucmd()
2053+
.arg("-l")
2054+
.arg("--time-style=+%Y__%M\n%Y--%M")
2055+
.succeeds()
2056+
.stdout_matches(&re_custom_format_recent)
2057+
.stdout_matches(&re_custom_format_old);
20192058

20202059
// Also fails due to not having full clap support for time_styles
20212060
scene
@@ -2024,58 +2063,64 @@ fn test_ls_styles() {
20242063
.arg("--time-style=invalid")
20252064
.fails_with_code(2);
20262065

2066+
// Cannot have 2 new lines in custom format
2067+
scene
2068+
.ucmd()
2069+
.arg("-l")
2070+
.arg("--time-style=+%Y__%M\n%Y--%M\n")
2071+
.fails_with_code(2);
2072+
20272073
//Overwrite options tests
20282074
scene
20292075
.ucmd()
20302076
.arg("-l")
20312077
.arg("--time-style=long-iso")
20322078
.arg("--time-style=iso")
20332079
.succeeds()
2034-
.stdout_matches(&re_iso);
2080+
.stdout_matches(&re_iso_recent);
20352081
scene
20362082
.ucmd()
20372083
.arg("--time-style=iso")
20382084
.arg("--full-time")
20392085
.succeeds()
2040-
.stdout_matches(&re_full);
2086+
.stdout_matches(&re_full_recent);
20412087
scene
20422088
.ucmd()
20432089
.arg("--full-time")
20442090
.arg("--time-style=iso")
20452091
.succeeds()
2046-
.stdout_matches(&re_iso);
2092+
.stdout_matches(&re_iso_recent);
20472093

20482094
scene
20492095
.ucmd()
20502096
.arg("--full-time")
20512097
.arg("--time-style=iso")
20522098
.arg("--full-time")
20532099
.succeeds()
2054-
.stdout_matches(&re_full);
2100+
.stdout_matches(&re_full_recent);
20552101

20562102
scene
20572103
.ucmd()
20582104
.arg("--full-time")
20592105
.arg("-x")
20602106
.arg("-l")
20612107
.succeeds()
2062-
.stdout_matches(&re_full);
2108+
.stdout_matches(&re_full_recent);
20632109

2064-
at.touch("test2");
20652110
scene
20662111
.ucmd()
20672112
.arg("--full-time")
20682113
.arg("-x")
20692114
.succeeds()
2070-
.stdout_is("test test2\n");
2115+
.stdout_is("test test-old\n");
20712116

20722117
// Time style can also be setup from environment
20732118
scene
20742119
.ucmd()
20752120
.env("TIME_STYLE", "full-iso")
20762121
.arg("-l")
20772122
.succeeds()
2078-
.stdout_matches(&re_full);
2123+
.stdout_matches(&re_full_recent);
20792124

20802125
// ... but option takes precedence
20812126
scene
@@ -2084,7 +2129,7 @@ fn test_ls_styles() {
20842129
.arg("-l")
20852130
.arg("--time-style=long-iso")
20862131
.succeeds()
2087-
.stdout_matches(&re_long);
2132+
.stdout_matches(&re_long_recent);
20882133
}
20892134

20902135
#[test]

0 commit comments

Comments
 (0)