summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/uu/ls/src/ls.rs122
-rw-r--r--tests/by-util/test_ls.rs120
2 files changed, 228 insertions, 14 deletions
diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs
index 5da64abe5..6e8c5ed63 100644
--- a/src/uu/ls/src/ls.rs
+++ b/src/uu/ls/src/ls.rs
@@ -5,7 +5,7 @@
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.
-// spell-checker:ignore (ToDO) cpio svgz webm somegroup nlink rmvb xspf tabsize
+// spell-checker:ignore (ToDO) cpio svgz webm somegroup nlink rmvb xspf tabsize dired
#[macro_use]
extern crate uucore;
@@ -137,6 +137,8 @@ pub mod options {
pub static IGNORE: &str = "ignore";
pub static CONTEXT: &str = "context";
pub static GROUP_DIRECTORIES_FIRST: &str = "group-directories-first";
+ pub static ZERO: &str = "zero";
+ pub static DIRED: &str = "dired";
}
const DEFAULT_TERM_WIDTH: u16 = 80;
@@ -337,6 +339,7 @@ pub struct Config {
context: bool,
selinux_supported: bool,
group_directories_first: bool,
+ eol: char,
}
// Fields that can be removed or added to the long format
@@ -481,7 +484,7 @@ impl Config {
Time::Modification
};
- let needs_color = match options.value_of(options::COLOR) {
+ let mut needs_color = match options.value_of(options::COLOR) {
None => options.is_present(options::COLOR),
Some(val) => match val {
"" | "always" | "yes" | "force" => true,
@@ -490,12 +493,6 @@ impl Config {
},
};
- let color = if needs_color {
- Some(LsColors::from_env().unwrap_or_default())
- } else {
- None
- };
-
let cmd_line_bs = options.value_of(options::size::BLOCK_SIZE);
let opt_si = cmd_line_bs.is_some()
&& options
@@ -607,7 +604,7 @@ impl Config {
};
#[allow(clippy::needless_bool)]
- let show_control = if options.is_present(options::HIDE_CONTROL_CHARS) {
+ let mut show_control = if options.is_present(options::HIDE_CONTROL_CHARS) {
false
} else if options.is_present(options::SHOW_CONTROL_CHARS) {
true
@@ -619,7 +616,7 @@ impl Config {
.value_of(options::QUOTING_STYLE)
.map(|cmd_line_qs| cmd_line_qs.to_owned());
- let quoting_style = if let Some(style) = opt_quoting_style {
+ let mut quoting_style = if let Some(style) = opt_quoting_style {
match style.as_str() {
"literal" => QuotingStyle::Literal { show_control },
"shell" => QuotingStyle::Shell {
@@ -750,6 +747,80 @@ impl Config {
}
}
+ // According to ls info page, `--zero` implies the following flags:
+ // - `--show-control-chars`
+ // - `--format=single-column`
+ // - `--color=none`
+ // - `--quoting-style=literal`
+ // Current GNU ls implementation allows `--zero` Behavior to be
+ // overridden by later flags.
+ let zero_formats_opts = [
+ options::format::ACROSS,
+ options::format::COLUMNS,
+ options::format::COMMAS,
+ options::format::LONG,
+ options::format::LONG_NO_GROUP,
+ options::format::LONG_NO_OWNER,
+ options::format::LONG_NUMERIC_UID_GID,
+ options::format::ONE_LINE,
+ options::FORMAT,
+ ];
+ let zero_colors_opts = [options::COLOR];
+ let zero_show_control_opts = [options::HIDE_CONTROL_CHARS, options::SHOW_CONTROL_CHARS];
+ let zero_quoting_style_opts = [
+ options::QUOTING_STYLE,
+ options::quoting::C,
+ options::quoting::ESCAPE,
+ options::quoting::LITERAL,
+ ];
+ let get_last = |flag: &str| -> usize { options.index_of(flag).unwrap_or(0) };
+ if get_last(options::ZERO)
+ > zero_formats_opts
+ .into_iter()
+ .map(get_last)
+ .max()
+ .unwrap_or(0)
+ {
+ format = if format == Format::Long {
+ format
+ } else {
+ Format::OneLine
+ };
+ }
+ if get_last(options::ZERO)
+ > zero_colors_opts
+ .into_iter()
+ .map(get_last)
+ .max()
+ .unwrap_or(0)
+ {
+ needs_color = false;
+ }
+ if get_last(options::ZERO)
+ > zero_show_control_opts
+ .into_iter()
+ .map(get_last)
+ .max()
+ .unwrap_or(0)
+ {
+ show_control = true;
+ }
+ if get_last(options::ZERO)
+ > zero_quoting_style_opts
+ .into_iter()
+ .map(get_last)
+ .max()
+ .unwrap_or(0)
+ {
+ quoting_style = QuotingStyle::Literal { show_control };
+ }
+
+ let color = if needs_color {
+ Some(LsColors::from_env().unwrap_or_default())
+ } else {
+ None
+ };
+
let dereference = if options.is_present(options::dereference::ALL) {
Dereference::All
} else if options.is_present(options::dereference::ARGS) {
@@ -798,6 +869,11 @@ impl Config {
}
},
group_directories_first: options.is_present(options::GROUP_DIRECTORIES_FIRST),
+ eol: if options.is_present(options::ZERO) {
+ '\0'
+ } else {
+ '\n'
+ },
})
}
}
@@ -913,6 +989,19 @@ pub fn uu_app<'a>() -> Command<'a> {
options::format::COLUMNS,
]),
)
+ .arg(
+ Arg::new(options::ZERO)
+ .long(options::ZERO)
+ .conflicts_with(options::DIRED)
+ .overrides_with(options::ZERO)
+ .help("List entries separated by ASCII NUL characters."),
+ )
+ .arg(
+ Arg::new(options::DIRED)
+ .long(options::DIRED)
+ .short('D')
+ .hide(true),
+ )
// The next four arguments do not override with the other format
// options, see the comment in Config::from for the reason.
// Ideally, they would use Arg::override_with, with their own name
@@ -1884,7 +1973,12 @@ fn display_total(items: &[PathData], config: &Config, out: &mut BufWriter<Stdout
.as_ref()
.map_or(0, |md| get_block_size(md, config));
}
- writeln!(out, "total {}", display_size(total_size, config))?;
+ write!(
+ out,
+ "total {}{}",
+ display_size(total_size, config),
+ config.eol
+ )?;
Ok(())
}
@@ -1994,12 +2088,12 @@ fn display_items(items: &[PathData], config: &Config, out: &mut BufWriter<Stdout
// Current col is never zero again if names have been printed.
// So we print a newline.
if current_col > 0 {
- writeln!(out,)?;
+ write!(out, "{}", config.eol)?;
}
}
_ => {
for name in names {
- writeln!(out, "{}", name.contents)?;
+ write!(out, "{}{}", name.contents, config.eol)?;
}
}
};
@@ -2199,7 +2293,7 @@ fn display_item_long(
let dfn = display_file_name(item, config, None, "".to_owned(), out).contents;
- writeln!(out, " {} {}", display_date(md, config), dfn)?;
+ write!(out, " {} {}{}", display_date(md, config), dfn, config.eol)?;
} else {
#[cfg(unix)]
let leading_char = {
diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs
index dc6ce3bd1..f1f139387 100644
--- a/tests/by-util/test_ls.rs
+++ b/tests/by-util/test_ls.rs
@@ -797,6 +797,126 @@ fn test_ls_commas() {
}
#[test]
+fn test_ls_zero() {
+ let scene = TestScenario::new(util_name!());
+ let at = &scene.fixtures;
+ at.mkdir("0-test-zero");
+ at.touch(&at.plus_as_string("2-test-zero"));
+ at.touch(&at.plus_as_string("3-test-zero"));
+
+ let ignored_opts = [
+ "--quoting-style=c",
+ "--color=always",
+ "-m",
+ "--hide-control-chars",
+ ];
+
+ scene
+ .ucmd()
+ .arg("--zero")
+ .succeeds()
+ .stdout_only("0-test-zero\x002-test-zero\x003-test-zero\x00");
+
+ for opt in ignored_opts {
+ scene
+ .ucmd()
+ .args(&[opt, "--zero"])
+ .succeeds()
+ .stdout_only("0-test-zero\x002-test-zero\x003-test-zero\x00");
+ }
+
+ scene
+ .ucmd()
+ .args(&["--zero", "--quoting-style=c"])
+ .succeeds()
+ .stdout_only("\"0-test-zero\"\x00\"2-test-zero\"\x00\"3-test-zero\"\x00");
+
+ scene
+ .ucmd()
+ .args(&["--zero", "--color=always"])
+ .succeeds()
+ .stdout_only("\x1b[1;34m0-test-zero\x1b[0m\x002-test-zero\x003-test-zero\x00");
+
+ scene
+ .ucmd()
+ .args(&["--zero", "-m"])
+ .succeeds()
+ .stdout_only("0-test-zero, 2-test-zero, 3-test-zero\x00");
+
+ scene
+ .ucmd()
+ .args(&["--zero", "--hide-control-chars"])
+ .succeeds()
+ .stdout_only("0-test-zero\x002-test-zero\x003-test-zero\x00");
+
+ scene
+ .ucmd()
+ .args(&["--zero", "--quoting-style=c", "--zero"])
+ .succeeds()
+ .stdout_only("0-test-zero\x002-test-zero\x003-test-zero\x00");
+
+ #[cfg(unix)]
+ {
+ at.touch(&at.plus_as_string("1\ntest-zero"));
+
+ let ignored_opts = [
+ "--quoting-style=c",
+ "--color=always",
+ "-m",
+ "--hide-control-chars",
+ ];
+
+ scene
+ .ucmd()
+ .arg("--zero")
+ .succeeds()
+ .stdout_only("0-test-zero\x001\ntest-zero\x002-test-zero\x003-test-zero\x00");
+
+ for opt in ignored_opts {
+ scene
+ .ucmd()
+ .args(&[opt, "--zero"])
+ .succeeds()
+ .stdout_only("0-test-zero\x001\ntest-zero\x002-test-zero\x003-test-zero\x00");
+ }
+
+ scene
+ .ucmd()
+ .args(&["--zero", "--quoting-style=c"])
+ .succeeds()
+ .stdout_only(
+ "\"0-test-zero\"\x00\"1\\ntest-zero\"\x00\"2-test-zero\"\x00\"3-test-zero\"\x00",
+ );
+
+ scene
+ .ucmd()
+ .args(&["--zero", "--color=always"])
+ .succeeds()
+ .stdout_only(
+ "\x1b[1;34m0-test-zero\x1b[0m\x001\ntest-zero\x002-test-zero\x003-test-zero\x00",
+ );
+
+ scene
+ .ucmd()
+ .args(&["--zero", "-m"])
+ .succeeds()
+ .stdout_only("0-test-zero, 1\ntest-zero, 2-test-zero, 3-test-zero\x00");
+
+ scene
+ .ucmd()
+ .args(&["--zero", "--hide-control-chars"])
+ .succeeds()
+ .stdout_only("0-test-zero\x001?test-zero\x002-test-zero\x003-test-zero\x00");
+ }
+
+ scene
+ .ucmd()
+ .args(&["-l", "--zero"])
+ .succeeds()
+ .stdout_contains("total ");
+}
+
+#[test]
fn test_ls_commas_trailing() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;