mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
ls: implement --dired
* Support ls --dired * stat-failed.sh: update of the test - we have a small difference * ls --dired: address some of the comments * fix warnings * use unwrap() * Improve test Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com> * Simplify test Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com> * Remove a word from the spell ignore Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com> * remove duplication of the spell ignore Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com> * rustfmt --------- Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com>
This commit is contained in:
parent
bc8e3818a4
commit
9b4d2c6bc4
5 changed files with 510 additions and 52 deletions
178
src/uu/ls/src/dired.rs
Normal file
178
src/uu/ls/src/dired.rs
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
// This file is part of the uutils coreutils package.
|
||||||
|
//
|
||||||
|
// For the full copyright and license information, please view the LICENSE
|
||||||
|
// file that was distributed with this source code.
|
||||||
|
// spell-checker:ignore dired subdired
|
||||||
|
|
||||||
|
use crate::Config;
|
||||||
|
use std::fmt;
|
||||||
|
use std::io::{BufWriter, Stdout, Write};
|
||||||
|
use uucore::error::UResult;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct BytePosition {
|
||||||
|
pub start: usize,
|
||||||
|
pub end: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the output structure for DIRED, containing positions for both DIRED and SUBDIRED.
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct DiredOutput {
|
||||||
|
pub dired_positions: Vec<BytePosition>,
|
||||||
|
pub subdired_positions: Vec<BytePosition>,
|
||||||
|
pub just_printed_total: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for BytePosition {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{} {}", self.start, self.end)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// When --dired is used, all lines starts with 2 spaces
|
||||||
|
static DIRED_TRAILING_OFFSET: usize = 2;
|
||||||
|
|
||||||
|
/// Calculates the byte positions for DIRED
|
||||||
|
pub fn calculate_dired_byte_positions(
|
||||||
|
output_display_len: usize,
|
||||||
|
dfn_len: usize,
|
||||||
|
dired_positions: &[BytePosition],
|
||||||
|
) -> (usize, usize) {
|
||||||
|
let offset_from_previous_line = if let Some(last_position) = dired_positions.last() {
|
||||||
|
last_position.end + 1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
let start = output_display_len + offset_from_previous_line;
|
||||||
|
let end = start + dfn_len;
|
||||||
|
(start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn indent(out: &mut BufWriter<Stdout>) -> UResult<()> {
|
||||||
|
write!(out, " ")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate_offset_and_push(dired: &mut DiredOutput, path_len: usize) {
|
||||||
|
let offset = if dired.subdired_positions.is_empty() {
|
||||||
|
DIRED_TRAILING_OFFSET
|
||||||
|
} else {
|
||||||
|
dired.subdired_positions[dired.subdired_positions.len() - 1].start + DIRED_TRAILING_OFFSET
|
||||||
|
};
|
||||||
|
dired.subdired_positions.push(BytePosition {
|
||||||
|
start: offset,
|
||||||
|
end: path_len + offset,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prints the dired output based on the given configuration and dired structure.
|
||||||
|
pub fn print_dired_output(
|
||||||
|
config: &Config,
|
||||||
|
dired: &DiredOutput,
|
||||||
|
out: &mut BufWriter<Stdout>,
|
||||||
|
) -> UResult<()> {
|
||||||
|
out.flush()?;
|
||||||
|
if config.recursive {
|
||||||
|
print_positions("//SUBDIRED//", &dired.subdired_positions);
|
||||||
|
} else if !dired.just_printed_total {
|
||||||
|
print_positions("//DIRED//", &dired.dired_positions);
|
||||||
|
}
|
||||||
|
println!("//DIRED-OPTIONS// --quoting-style={}", config.quoting_style);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to print positions with a given prefix.
|
||||||
|
fn print_positions(prefix: &str, positions: &Vec<BytePosition>) {
|
||||||
|
print!("{}", prefix);
|
||||||
|
for c in positions {
|
||||||
|
print!(" {}", c);
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_total(total_len: usize, dired: &mut DiredOutput) {
|
||||||
|
dired.just_printed_total = true;
|
||||||
|
dired.dired_positions.push(BytePosition {
|
||||||
|
start: 0,
|
||||||
|
// the 2 is from the trailing spaces
|
||||||
|
// the 1 is from the line ending (\n)
|
||||||
|
end: total_len + DIRED_TRAILING_OFFSET - 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates byte positions and updates the dired structure.
|
||||||
|
pub fn calculate_and_update_positions(
|
||||||
|
output_display_len: usize,
|
||||||
|
dfn_len: usize,
|
||||||
|
dired: &mut DiredOutput,
|
||||||
|
) {
|
||||||
|
let offset = dired
|
||||||
|
.dired_positions
|
||||||
|
.last()
|
||||||
|
.map_or(DIRED_TRAILING_OFFSET, |last_position| {
|
||||||
|
last_position.start + DIRED_TRAILING_OFFSET
|
||||||
|
});
|
||||||
|
let start = output_display_len + offset + DIRED_TRAILING_OFFSET;
|
||||||
|
let end = start + dfn_len;
|
||||||
|
update_positions(start, end, dired, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the dired positions based on the given start and end positions.
|
||||||
|
/// update when it is the first element in the list (to manage "total X"
|
||||||
|
/// insert when it isn't the about total
|
||||||
|
pub fn update_positions(start: usize, end: usize, dired: &mut DiredOutput, adjust: bool) {
|
||||||
|
if dired.just_printed_total {
|
||||||
|
if let Some(last_position) = dired.dired_positions.last_mut() {
|
||||||
|
*last_position = BytePosition {
|
||||||
|
start: if adjust {
|
||||||
|
start + last_position.end
|
||||||
|
} else {
|
||||||
|
start
|
||||||
|
},
|
||||||
|
end: if adjust { end + last_position.end } else { end },
|
||||||
|
};
|
||||||
|
dired.just_printed_total = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dired.dired_positions.push(BytePosition { start, end });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_calculate_dired_byte_positions() {
|
||||||
|
let output_display = "sample_output".to_string();
|
||||||
|
let dfn = "sample_file".to_string();
|
||||||
|
let dired_positions = vec![BytePosition { start: 5, end: 10 }];
|
||||||
|
let (start, end) =
|
||||||
|
calculate_dired_byte_positions(output_display.len(), dfn.len(), &dired_positions);
|
||||||
|
|
||||||
|
assert_eq!(start, 24);
|
||||||
|
assert_eq!(end, 35);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dired_update_positions() {
|
||||||
|
let mut dired = DiredOutput {
|
||||||
|
dired_positions: vec![BytePosition { start: 5, end: 10 }],
|
||||||
|
subdired_positions: vec![],
|
||||||
|
just_printed_total: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test with adjust = true
|
||||||
|
update_positions(15, 20, &mut dired, true);
|
||||||
|
let last_position = dired.dired_positions.last().unwrap();
|
||||||
|
assert_eq!(last_position.start, 25); // 15 + 10 (end of the previous position)
|
||||||
|
assert_eq!(last_position.end, 30); // 20 + 10 (end of the previous position)
|
||||||
|
|
||||||
|
// Test with adjust = false
|
||||||
|
update_positions(30, 35, &mut dired, false);
|
||||||
|
let last_position = dired.dired_positions.last().unwrap();
|
||||||
|
assert_eq!(last_position.start, 30);
|
||||||
|
assert_eq!(last_position.end, 35);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
// spell-checker:ignore (ToDO) cpio svgz webm somegroup nlink rmvb xspf tabsize dired
|
// spell-checker:ignore (ToDO) cpio svgz webm somegroup nlink rmvb xspf tabsize dired subdired
|
||||||
|
|
||||||
use clap::{
|
use clap::{
|
||||||
builder::{NonEmptyStringValueParser, ValueParser},
|
builder::{NonEmptyStringValueParser, ValueParser},
|
||||||
|
@ -61,7 +61,8 @@ use uucore::{
|
||||||
version_cmp::version_cmp,
|
version_cmp::version_cmp,
|
||||||
};
|
};
|
||||||
use uucore::{help_about, help_section, help_usage, parse_glob, show, show_error, show_warning};
|
use uucore::{help_about, help_section, help_usage, parse_glob, show, show_error, show_warning};
|
||||||
|
mod dired;
|
||||||
|
use dired::DiredOutput;
|
||||||
#[cfg(not(feature = "selinux"))]
|
#[cfg(not(feature = "selinux"))]
|
||||||
static CONTEXT_HELP_TEXT: &str = "print any security context of each file (not enabled)";
|
static CONTEXT_HELP_TEXT: &str = "print any security context of each file (not enabled)";
|
||||||
#[cfg(feature = "selinux")]
|
#[cfg(feature = "selinux")]
|
||||||
|
@ -167,6 +168,7 @@ enum LsError {
|
||||||
IOError(std::io::Error),
|
IOError(std::io::Error),
|
||||||
IOErrorContext(std::io::Error, PathBuf, bool),
|
IOErrorContext(std::io::Error, PathBuf, bool),
|
||||||
BlockSizeParseError(String),
|
BlockSizeParseError(String),
|
||||||
|
ConflictingArgumentDired(),
|
||||||
AlreadyListedError(PathBuf),
|
AlreadyListedError(PathBuf),
|
||||||
TimeStyleParseError(String, Vec<String>),
|
TimeStyleParseError(String, Vec<String>),
|
||||||
}
|
}
|
||||||
|
@ -179,6 +181,7 @@ impl UError for LsError {
|
||||||
Self::IOErrorContext(_, _, false) => 1,
|
Self::IOErrorContext(_, _, false) => 1,
|
||||||
Self::IOErrorContext(_, _, true) => 2,
|
Self::IOErrorContext(_, _, true) => 2,
|
||||||
Self::BlockSizeParseError(_) => 1,
|
Self::BlockSizeParseError(_) => 1,
|
||||||
|
Self::ConflictingArgumentDired() => 1,
|
||||||
Self::AlreadyListedError(_) => 2,
|
Self::AlreadyListedError(_) => 2,
|
||||||
Self::TimeStyleParseError(_, _) => 1,
|
Self::TimeStyleParseError(_, _) => 1,
|
||||||
}
|
}
|
||||||
|
@ -193,6 +196,10 @@ impl Display for LsError {
|
||||||
Self::BlockSizeParseError(s) => {
|
Self::BlockSizeParseError(s) => {
|
||||||
write!(f, "invalid --block-size argument {}", s.quote())
|
write!(f, "invalid --block-size argument {}", s.quote())
|
||||||
}
|
}
|
||||||
|
Self::ConflictingArgumentDired() => {
|
||||||
|
write!(f, "--dired requires --format=long")
|
||||||
|
}
|
||||||
|
|
||||||
Self::TimeStyleParseError(s, possible_time_styles) => {
|
Self::TimeStyleParseError(s, possible_time_styles) => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
|
@ -406,6 +413,7 @@ pub struct Config {
|
||||||
selinux_supported: bool,
|
selinux_supported: bool,
|
||||||
group_directories_first: bool,
|
group_directories_first: bool,
|
||||||
line_ending: LineEnding,
|
line_ending: LineEnding,
|
||||||
|
dired: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fields that can be removed or added to the long format
|
// Fields that can be removed or added to the long format
|
||||||
|
@ -610,6 +618,8 @@ fn extract_quoting_style(options: &clap::ArgMatches, show_control: bool) -> Quot
|
||||||
QuotingStyle::C {
|
QuotingStyle::C {
|
||||||
quotes: quoting_style::Quotes::Double,
|
quotes: quoting_style::Quotes::Double,
|
||||||
}
|
}
|
||||||
|
} else if options.get_flag(options::DIRED) {
|
||||||
|
QuotingStyle::Literal { show_control }
|
||||||
} else {
|
} else {
|
||||||
// TODO: use environment variable if available
|
// TODO: use environment variable if available
|
||||||
QuotingStyle::Shell {
|
QuotingStyle::Shell {
|
||||||
|
@ -954,6 +964,11 @@ impl Config {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let dired = options.get_flag(options::DIRED);
|
||||||
|
if dired && format != Format::Long {
|
||||||
|
return Err(Box::new(LsError::ConflictingArgumentDired()));
|
||||||
|
}
|
||||||
|
|
||||||
let dereference = if options.get_flag(options::dereference::ALL) {
|
let dereference = if options.get_flag(options::dereference::ALL) {
|
||||||
Dereference::All
|
Dereference::All
|
||||||
} else if options.get_flag(options::dereference::ARGS) {
|
} else if options.get_flag(options::dereference::ARGS) {
|
||||||
|
@ -1003,6 +1018,7 @@ impl Config {
|
||||||
},
|
},
|
||||||
group_directories_first: options.get_flag(options::GROUP_DIRECTORIES_FIRST),
|
group_directories_first: options.get_flag(options::GROUP_DIRECTORIES_FIRST),
|
||||||
line_ending: LineEnding::from_zero_flag(options.get_flag(options::ZERO)),
|
line_ending: LineEnding::from_zero_flag(options.get_flag(options::ZERO)),
|
||||||
|
dired,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1135,7 +1151,7 @@ pub fn uu_app() -> Command {
|
||||||
Arg::new(options::DIRED)
|
Arg::new(options::DIRED)
|
||||||
.long(options::DIRED)
|
.long(options::DIRED)
|
||||||
.short('D')
|
.short('D')
|
||||||
.hide(true)
|
.help("generate output designed for Emacs' dired (Directory Editor) mode")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
// The next four arguments do not override with the other format
|
// The next four arguments do not override with the other format
|
||||||
|
@ -1844,6 +1860,7 @@ pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> {
|
||||||
let mut files = Vec::<PathData>::new();
|
let mut files = Vec::<PathData>::new();
|
||||||
let mut dirs = Vec::<PathData>::new();
|
let mut dirs = Vec::<PathData>::new();
|
||||||
let mut out = BufWriter::new(stdout());
|
let mut out = BufWriter::new(stdout());
|
||||||
|
let mut dired = DiredOutput::default();
|
||||||
let initial_locs_len = locs.len();
|
let initial_locs_len = locs.len();
|
||||||
|
|
||||||
for loc in locs {
|
for loc in locs {
|
||||||
|
@ -1877,7 +1894,7 @@ pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> {
|
||||||
sort_entries(&mut files, config, &mut out);
|
sort_entries(&mut files, config, &mut out);
|
||||||
sort_entries(&mut dirs, config, &mut out);
|
sort_entries(&mut dirs, config, &mut out);
|
||||||
|
|
||||||
display_items(&files, config, &mut out)?;
|
display_items(&files, config, &mut out, &mut dired)?;
|
||||||
|
|
||||||
for (pos, path_data) in dirs.iter().enumerate() {
|
for (pos, path_data) in dirs.iter().enumerate() {
|
||||||
// Do read_dir call here to match GNU semantics by printing
|
// Do read_dir call here to match GNU semantics by printing
|
||||||
|
@ -1899,7 +1916,13 @@ pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> {
|
||||||
// Print dir heading - name... 'total' comes after error display
|
// Print dir heading - name... 'total' comes after error display
|
||||||
if initial_locs_len > 1 || config.recursive {
|
if initial_locs_len > 1 || config.recursive {
|
||||||
if pos.eq(&0usize) && files.is_empty() {
|
if pos.eq(&0usize) && files.is_empty() {
|
||||||
|
if config.dired {
|
||||||
|
dired::indent(&mut out)?;
|
||||||
|
}
|
||||||
writeln!(out, "{}:", path_data.p_buf.display())?;
|
writeln!(out, "{}:", path_data.p_buf.display())?;
|
||||||
|
if config.dired {
|
||||||
|
dired::calculate_offset_and_push(&mut dired, path_data.display_name.len());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
writeln!(out, "\n{}:", path_data.p_buf.display())?;
|
writeln!(out, "\n{}:", path_data.p_buf.display())?;
|
||||||
}
|
}
|
||||||
|
@ -1909,9 +1932,18 @@ pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> {
|
||||||
&path_data.p_buf,
|
&path_data.p_buf,
|
||||||
path_data.must_dereference,
|
path_data.must_dereference,
|
||||||
)?);
|
)?);
|
||||||
enter_directory(path_data, read_dir, config, &mut out, &mut listed_ancestors)?;
|
enter_directory(
|
||||||
|
path_data,
|
||||||
|
read_dir,
|
||||||
|
config,
|
||||||
|
&mut out,
|
||||||
|
&mut listed_ancestors,
|
||||||
|
&mut dired,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
if config.dired {
|
||||||
|
dired::print_dired_output(config, &dired, &mut out)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2022,6 +2054,7 @@ fn enter_directory(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
out: &mut BufWriter<Stdout>,
|
out: &mut BufWriter<Stdout>,
|
||||||
listed_ancestors: &mut HashSet<FileInformation>,
|
listed_ancestors: &mut HashSet<FileInformation>,
|
||||||
|
dired: &mut DiredOutput,
|
||||||
) -> UResult<()> {
|
) -> UResult<()> {
|
||||||
// Create vec of entries with initial dot files
|
// Create vec of entries with initial dot files
|
||||||
let mut entries: Vec<PathData> = if config.files == Files::All {
|
let mut entries: Vec<PathData> = if config.files == Files::All {
|
||||||
|
@ -2067,10 +2100,14 @@ fn enter_directory(
|
||||||
|
|
||||||
// Print total after any error display
|
// Print total after any error display
|
||||||
if config.format == Format::Long || config.alloc_size {
|
if config.format == Format::Long || config.alloc_size {
|
||||||
display_total(&entries, config, out)?;
|
let total = return_total(&entries, config, out)?;
|
||||||
|
write!(out, "{}", total.as_str())?;
|
||||||
|
if config.dired {
|
||||||
|
dired::add_total(total.len(), dired);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
display_items(&entries, config, out)?;
|
display_items(&entries, config, out, dired)?;
|
||||||
|
|
||||||
if config.recursive {
|
if config.recursive {
|
||||||
for e in entries
|
for e in entries
|
||||||
|
@ -2095,7 +2132,7 @@ fn enter_directory(
|
||||||
.insert(FileInformation::from_path(&e.p_buf, e.must_dereference)?)
|
.insert(FileInformation::from_path(&e.p_buf, e.must_dereference)?)
|
||||||
{
|
{
|
||||||
writeln!(out, "\n{}:", e.p_buf.display())?;
|
writeln!(out, "\n{}:", e.p_buf.display())?;
|
||||||
enter_directory(e, rd, config, out, listed_ancestors)?;
|
enter_directory(e, rd, config, out, listed_ancestors, dired)?;
|
||||||
listed_ancestors
|
listed_ancestors
|
||||||
.remove(&FileInformation::from_path(&e.p_buf, e.must_dereference)?);
|
.remove(&FileInformation::from_path(&e.p_buf, e.must_dereference)?);
|
||||||
} else {
|
} else {
|
||||||
|
@ -2154,7 +2191,11 @@ fn pad_right(string: &str, count: usize) -> String {
|
||||||
format!("{string:<count$}")
|
format!("{string:<count$}")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_total(items: &[PathData], config: &Config, out: &mut BufWriter<Stdout>) -> UResult<()> {
|
fn return_total(
|
||||||
|
items: &[PathData],
|
||||||
|
config: &Config,
|
||||||
|
out: &mut BufWriter<Stdout>,
|
||||||
|
) -> UResult<String> {
|
||||||
let mut total_size = 0;
|
let mut total_size = 0;
|
||||||
for item in items {
|
for item in items {
|
||||||
total_size += item
|
total_size += item
|
||||||
|
@ -2162,13 +2203,14 @@ fn display_total(items: &[PathData], config: &Config, out: &mut BufWriter<Stdout
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(0, |md| get_block_size(md, config));
|
.map_or(0, |md| get_block_size(md, config));
|
||||||
}
|
}
|
||||||
write!(
|
if config.dired {
|
||||||
out,
|
dired::indent(out)?;
|
||||||
|
}
|
||||||
|
Ok(format!(
|
||||||
"total {}{}",
|
"total {}{}",
|
||||||
display_size(total_size, config),
|
display_size(total_size, config),
|
||||||
config.line_ending
|
config.line_ending
|
||||||
)?;
|
))
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_additional_leading_info(
|
fn display_additional_leading_info(
|
||||||
|
@ -2207,7 +2249,12 @@ fn display_additional_leading_info(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::cognitive_complexity)]
|
#[allow(clippy::cognitive_complexity)]
|
||||||
fn display_items(items: &[PathData], config: &Config, out: &mut BufWriter<Stdout>) -> UResult<()> {
|
fn display_items(
|
||||||
|
items: &[PathData],
|
||||||
|
config: &Config,
|
||||||
|
out: &mut BufWriter<Stdout>,
|
||||||
|
dired: &mut DiredOutput,
|
||||||
|
) -> UResult<()> {
|
||||||
// `-Z`, `--context`:
|
// `-Z`, `--context`:
|
||||||
// Display the SELinux security context or '?' if none is found. When used with the `-l`
|
// Display the SELinux security context or '?' if none is found. When used with the `-l`
|
||||||
// option, print the security context to the left of the size column.
|
// option, print the security context to the left of the size column.
|
||||||
|
@ -2220,6 +2267,7 @@ fn display_items(items: &[PathData], config: &Config, out: &mut BufWriter<Stdout
|
||||||
if config.inode || config.alloc_size {
|
if config.inode || config.alloc_size {
|
||||||
let more_info =
|
let more_info =
|
||||||
display_additional_leading_info(item, &padding_collection, config, out)?;
|
display_additional_leading_info(item, &padding_collection, config, out)?;
|
||||||
|
|
||||||
write!(out, "{more_info}")?;
|
write!(out, "{more_info}")?;
|
||||||
}
|
}
|
||||||
#[cfg(not(unix))]
|
#[cfg(not(unix))]
|
||||||
|
@ -2228,7 +2276,7 @@ fn display_items(items: &[PathData], config: &Config, out: &mut BufWriter<Stdout
|
||||||
display_additional_leading_info(item, &padding_collection, config, out)?;
|
display_additional_leading_info(item, &padding_collection, config, out)?;
|
||||||
write!(out, "{more_info}")?;
|
write!(out, "{more_info}")?;
|
||||||
}
|
}
|
||||||
display_item_long(item, &padding_collection, config, out)?;
|
display_item_long(item, &padding_collection, config, out, dired)?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut longest_context_len = 1;
|
let mut longest_context_len = 1;
|
||||||
|
@ -2402,10 +2450,15 @@ fn display_item_long(
|
||||||
padding: &PaddingCollection,
|
padding: &PaddingCollection,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
out: &mut BufWriter<Stdout>,
|
out: &mut BufWriter<Stdout>,
|
||||||
|
dired: &mut DiredOutput,
|
||||||
) -> UResult<()> {
|
) -> UResult<()> {
|
||||||
|
let mut output_display: String = String::new();
|
||||||
|
if config.dired {
|
||||||
|
output_display += " ";
|
||||||
|
}
|
||||||
if let Some(md) = item.md(out) {
|
if let Some(md) = item.md(out) {
|
||||||
write!(
|
write!(
|
||||||
out,
|
output_display,
|
||||||
"{}{} {}",
|
"{}{} {}",
|
||||||
display_permissions(md, true),
|
display_permissions(md, true),
|
||||||
if item.security_context.len() > 1 {
|
if item.security_context.len() > 1 {
|
||||||
|
@ -2416,49 +2469,54 @@ fn display_item_long(
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
pad_left(&display_symlink_count(md), padding.link_count)
|
pad_left(&display_symlink_count(md), padding.link_count)
|
||||||
)?;
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
if config.long.owner {
|
if config.long.owner {
|
||||||
write!(
|
write!(
|
||||||
out,
|
output_display,
|
||||||
" {}",
|
" {}",
|
||||||
pad_right(&display_uname(md, config), padding.uname)
|
pad_right(&display_uname(md, config), padding.uname)
|
||||||
)?;
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.long.group {
|
if config.long.group {
|
||||||
write!(
|
write!(
|
||||||
out,
|
output_display,
|
||||||
" {}",
|
" {}",
|
||||||
pad_right(&display_group(md, config), padding.group)
|
pad_right(&display_group(md, config), padding.group)
|
||||||
)?;
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.context {
|
if config.context {
|
||||||
write!(
|
write!(
|
||||||
out,
|
output_display,
|
||||||
" {}",
|
" {}",
|
||||||
pad_right(&item.security_context, padding.context)
|
pad_right(&item.security_context, padding.context)
|
||||||
)?;
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Author is only different from owner on GNU/Hurd, so we reuse
|
// Author is only different from owner on GNU/Hurd, so we reuse
|
||||||
// the owner, since GNU/Hurd is not currently supported by Rust.
|
// the owner, since GNU/Hurd is not currently supported by Rust.
|
||||||
if config.long.author {
|
if config.long.author {
|
||||||
write!(
|
write!(
|
||||||
out,
|
output_display,
|
||||||
" {}",
|
" {}",
|
||||||
pad_right(&display_uname(md, config), padding.uname)
|
pad_right(&display_uname(md, config), padding.uname)
|
||||||
)?;
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
match display_len_or_rdev(md, config) {
|
match display_len_or_rdev(md, config) {
|
||||||
SizeOrDeviceId::Size(size) => {
|
SizeOrDeviceId::Size(size) => {
|
||||||
write!(out, " {}", pad_left(&size, padding.size))?;
|
write!(output_display, " {}", pad_left(&size, padding.size)).unwrap();
|
||||||
}
|
}
|
||||||
SizeOrDeviceId::Device(major, minor) => {
|
SizeOrDeviceId::Device(major, minor) => {
|
||||||
write!(
|
write!(
|
||||||
out,
|
output_display,
|
||||||
" {}, {}",
|
" {}, {}",
|
||||||
pad_left(
|
pad_left(
|
||||||
&major,
|
&major,
|
||||||
|
@ -2478,19 +2536,23 @@ fn display_item_long(
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
padding.minor,
|
padding.minor,
|
||||||
),
|
),
|
||||||
)?;
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let displayed_file = display_file_name(item, config, None, String::new(), out).contents;
|
write!(output_display, " {} ", display_date(md, config)).unwrap();
|
||||||
|
|
||||||
write!(
|
let displayed_file = display_file_name(item, config, None, String::new(), out).contents;
|
||||||
out,
|
if config.dired {
|
||||||
" {} {}{}",
|
let (start, end) = dired::calculate_dired_byte_positions(
|
||||||
display_date(md, config),
|
output_display.len(),
|
||||||
displayed_file,
|
displayed_file.len(),
|
||||||
config.line_ending
|
&dired.dired_positions,
|
||||||
)?;
|
);
|
||||||
|
dired::update_positions(start, end, dired, false);
|
||||||
|
}
|
||||||
|
write!(output_display, "{}{}", displayed_file, config.line_ending).unwrap();
|
||||||
} else {
|
} else {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let leading_char = {
|
let leading_char = {
|
||||||
|
@ -2526,7 +2588,7 @@ fn display_item_long(
|
||||||
};
|
};
|
||||||
|
|
||||||
write!(
|
write!(
|
||||||
out,
|
output_display,
|
||||||
"{}{} {}",
|
"{}{} {}",
|
||||||
format_args!("{leading_char}?????????"),
|
format_args!("{leading_char}?????????"),
|
||||||
if item.security_context.len() > 1 {
|
if item.security_context.len() > 1 {
|
||||||
|
@ -2537,41 +2599,53 @@ fn display_item_long(
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
pad_left("?", padding.link_count)
|
pad_left("?", padding.link_count)
|
||||||
)?;
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
if config.long.owner {
|
if config.long.owner {
|
||||||
write!(out, " {}", pad_right("?", padding.uname))?;
|
write!(output_display, " {}", pad_right("?", padding.uname)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.long.group {
|
if config.long.group {
|
||||||
write!(out, " {}", pad_right("?", padding.group))?;
|
write!(output_display, " {}", pad_right("?", padding.group)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.context {
|
if config.context {
|
||||||
write!(
|
write!(
|
||||||
out,
|
output_display,
|
||||||
" {}",
|
" {}",
|
||||||
pad_right(&item.security_context, padding.context)
|
pad_right(&item.security_context, padding.context)
|
||||||
)?;
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Author is only different from owner on GNU/Hurd, so we reuse
|
// Author is only different from owner on GNU/Hurd, so we reuse
|
||||||
// the owner, since GNU/Hurd is not currently supported by Rust.
|
// the owner, since GNU/Hurd is not currently supported by Rust.
|
||||||
if config.long.author {
|
if config.long.author {
|
||||||
write!(out, " {}", pad_right("?", padding.uname))?;
|
write!(output_display, " {}", pad_right("?", padding.uname)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let displayed_file = display_file_name(item, config, None, String::new(), out).contents;
|
let displayed_file = display_file_name(item, config, None, String::new(), out).contents;
|
||||||
let date_len = 12;
|
let date_len = 12;
|
||||||
|
|
||||||
writeln!(
|
write!(
|
||||||
out,
|
output_display,
|
||||||
" {} {} {}",
|
" {} {} ",
|
||||||
pad_left("?", padding.size),
|
pad_left("?", padding.size),
|
||||||
pad_left("?", date_len),
|
pad_left("?", date_len),
|
||||||
displayed_file,
|
)
|
||||||
)?;
|
.unwrap();
|
||||||
|
|
||||||
|
if config.dired {
|
||||||
|
dired::calculate_and_update_positions(
|
||||||
|
output_display.len(),
|
||||||
|
displayed_file.trim().len(),
|
||||||
|
dired,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
write!(output_display, "{}{}", displayed_file, config.line_ending).unwrap();
|
||||||
|
}
|
||||||
|
write!(out, "{}", output_display)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,14 @@
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
use std::char::from_digit;
|
use std::char::from_digit;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
// These are characters with special meaning in the shell (e.g. bash).
|
// These are characters with special meaning in the shell (e.g. bash).
|
||||||
// The first const contains characters that only have a special meaning when they appear at the beginning of a name.
|
// The first const contains characters that only have a special meaning when they appear at the beginning of a name.
|
||||||
const SPECIAL_SHELL_CHARS_START: &[char] = &['~', '#'];
|
const SPECIAL_SHELL_CHARS_START: &[char] = &['~', '#'];
|
||||||
const SPECIAL_SHELL_CHARS: &str = "`$&*()|[]{};\\'\"<>?! ";
|
const SPECIAL_SHELL_CHARS: &str = "`$&*()|[]{};\\'\"<>?! ";
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub enum QuotingStyle {
|
pub enum QuotingStyle {
|
||||||
Shell {
|
Shell {
|
||||||
escape: bool,
|
escape: bool,
|
||||||
|
@ -24,7 +26,7 @@ pub enum QuotingStyle {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub enum Quotes {
|
pub enum Quotes {
|
||||||
None,
|
None,
|
||||||
Single,
|
Single,
|
||||||
|
@ -316,6 +318,42 @@ pub fn escape_name(name: &OsStr, style: &QuotingStyle) -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for QuotingStyle {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Self::Shell {
|
||||||
|
escape,
|
||||||
|
always_quote,
|
||||||
|
show_control,
|
||||||
|
} => {
|
||||||
|
let mut style = "shell".to_string();
|
||||||
|
if escape {
|
||||||
|
style.push_str("-escape");
|
||||||
|
}
|
||||||
|
if always_quote {
|
||||||
|
style.push_str("-always-quote");
|
||||||
|
}
|
||||||
|
if show_control {
|
||||||
|
style.push_str("-show-control");
|
||||||
|
}
|
||||||
|
f.write_str(&style)
|
||||||
|
}
|
||||||
|
Self::C { .. } => f.write_str("C"),
|
||||||
|
Self::Literal { .. } => f.write_str("literal"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Quotes {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Self::None => f.write_str("None"),
|
||||||
|
Self::Single => f.write_str("Single"),
|
||||||
|
Self::Double => f.write_str("Double"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::quoting_style::{escape_name, Quotes, QuotingStyle};
|
use crate::quoting_style::{escape_name, Quotes, QuotingStyle};
|
||||||
|
@ -732,4 +770,45 @@ mod tests {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_quoting_style_display() {
|
||||||
|
let style = QuotingStyle::Shell {
|
||||||
|
escape: true,
|
||||||
|
always_quote: false,
|
||||||
|
show_control: false,
|
||||||
|
};
|
||||||
|
assert_eq!(format!("{}", style), "shell-escape");
|
||||||
|
|
||||||
|
let style = QuotingStyle::Shell {
|
||||||
|
escape: false,
|
||||||
|
always_quote: true,
|
||||||
|
show_control: false,
|
||||||
|
};
|
||||||
|
assert_eq!(format!("{}", style), "shell-always-quote");
|
||||||
|
|
||||||
|
let style = QuotingStyle::Shell {
|
||||||
|
escape: false,
|
||||||
|
always_quote: false,
|
||||||
|
show_control: true,
|
||||||
|
};
|
||||||
|
assert_eq!(format!("{}", style), "shell-show-control");
|
||||||
|
|
||||||
|
let style = QuotingStyle::C {
|
||||||
|
quotes: Quotes::Double,
|
||||||
|
};
|
||||||
|
assert_eq!(format!("{}", style), "C");
|
||||||
|
|
||||||
|
let style = QuotingStyle::Literal {
|
||||||
|
show_control: false,
|
||||||
|
};
|
||||||
|
assert_eq!(format!("{}", style), "literal");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_quotes_display() {
|
||||||
|
assert_eq!(format!("{}", Quotes::None), "None");
|
||||||
|
assert_eq!(format!("{}", Quotes::Single), "Single");
|
||||||
|
assert_eq!(format!("{}", Quotes::Double), "Double");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
//
|
//
|
||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
// spell-checker:ignore (words) READMECAREFULLY birthtime doesntexist oneline somebackup lrwx somefile somegroup somehiddenbackup somehiddenfile tabsize aaaaaaaa bbbb cccc dddddddd ncccc neee naaaaa nbcdef nfffff
|
// spell-checker:ignore (words) READMECAREFULLY birthtime doesntexist oneline somebackup lrwx somefile somegroup somehiddenbackup somehiddenfile tabsize aaaaaaaa bbbb cccc dddddddd ncccc neee naaaaa nbcdef nfffff dired subdired
|
||||||
|
|
||||||
#[cfg(any(unix, feature = "feat_selinux"))]
|
#[cfg(any(unix, feature = "feat_selinux"))]
|
||||||
use crate::common::util::expected_result;
|
use crate::common::util::expected_result;
|
||||||
|
@ -3522,3 +3522,126 @@ fn test_ls_perm_io_errors() {
|
||||||
.code_is(1)
|
.code_is(1)
|
||||||
.stderr_contains("Permission denied");
|
.stderr_contains("Permission denied");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ls_dired_incompatible() {
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--dired")
|
||||||
|
.fails()
|
||||||
|
.code_is(1)
|
||||||
|
.stderr_contains("--dired requires --format=long");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ls_dired_recursive() {
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--dired")
|
||||||
|
.arg("-l")
|
||||||
|
.arg("-R")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_does_not_contain("//DIRED//")
|
||||||
|
.stdout_contains(" total 0")
|
||||||
|
.stdout_contains("//SUBDIRED// 2 3")
|
||||||
|
.stdout_contains("//DIRED-OPTIONS// --quoting-style");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ls_dired_simple() {
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
let at = &scene.fixtures;
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--dired")
|
||||||
|
.arg("-l")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_contains(" total 0");
|
||||||
|
|
||||||
|
at.mkdir("d");
|
||||||
|
at.touch("d/a1");
|
||||||
|
let mut cmd = scene.ucmd();
|
||||||
|
cmd.arg("--dired").arg("-l").arg("d");
|
||||||
|
let result = cmd.succeeds();
|
||||||
|
result.stdout_contains(" total 0");
|
||||||
|
println!(" result.stdout = {:#?}", result.stdout_str());
|
||||||
|
|
||||||
|
let dired_line = result
|
||||||
|
.stdout_str()
|
||||||
|
.lines()
|
||||||
|
.find(|&line| line.starts_with("//DIRED//"))
|
||||||
|
.unwrap();
|
||||||
|
let positions: Vec<usize> = dired_line
|
||||||
|
.split_whitespace()
|
||||||
|
.skip(1)
|
||||||
|
.map(|s| s.parse().unwrap())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
assert_eq!(positions.len(), 2);
|
||||||
|
|
||||||
|
let start_pos = positions[0];
|
||||||
|
let end_pos = positions[1];
|
||||||
|
|
||||||
|
// Extract the filename using the positions
|
||||||
|
let filename =
|
||||||
|
String::from_utf8(result.stdout_str().as_bytes()[start_pos..end_pos].to_vec()).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(filename, "a1");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ls_dired_complex() {
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
let at = &scene.fixtures;
|
||||||
|
|
||||||
|
at.mkdir("d");
|
||||||
|
at.mkdir("d/d");
|
||||||
|
at.touch("d/a1");
|
||||||
|
at.touch("d/a22");
|
||||||
|
at.touch("d/a333");
|
||||||
|
at.touch("d/a4444");
|
||||||
|
|
||||||
|
let mut cmd = scene.ucmd();
|
||||||
|
cmd.arg("--dired").arg("-l").arg("d");
|
||||||
|
let result = cmd.succeeds();
|
||||||
|
// Number of blocks
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
result.stdout_contains(" total 4");
|
||||||
|
|
||||||
|
let output = result.stdout_str().to_string();
|
||||||
|
println!("Output:\n{}", output);
|
||||||
|
|
||||||
|
let dired_line = output
|
||||||
|
.lines()
|
||||||
|
.find(|&line| line.starts_with("//DIRED//"))
|
||||||
|
.unwrap();
|
||||||
|
let positions: Vec<usize> = dired_line
|
||||||
|
.split_whitespace()
|
||||||
|
.skip(1)
|
||||||
|
.map(|s| s.parse().unwrap())
|
||||||
|
.collect();
|
||||||
|
println!("{:?}", positions);
|
||||||
|
println!("Parsed byte positions: {:?}", positions);
|
||||||
|
assert_eq!(positions.len() % 2, 0); // Ensure there's an even number of positions
|
||||||
|
|
||||||
|
let filenames: Vec<String> = positions
|
||||||
|
.chunks(2)
|
||||||
|
.map(|chunk| {
|
||||||
|
let start_pos = chunk[0];
|
||||||
|
let end_pos = chunk[1];
|
||||||
|
let filename = String::from_utf8(output.as_bytes()[start_pos..=end_pos].to_vec())
|
||||||
|
.unwrap()
|
||||||
|
.trim()
|
||||||
|
.to_string();
|
||||||
|
println!("Extracted filename: {}", filename);
|
||||||
|
filename
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
println!("Extracted filenames: {:?}", filenames);
|
||||||
|
assert_eq!(filenames, vec!["a1", "a22", "a333", "a4444", "d"]);
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#
|
#
|
||||||
# UU_MAKE_PROFILE == 'debug' | 'release' ## build profile for *uutils* build; may be supplied by caller, defaults to 'release'
|
# UU_MAKE_PROFILE == 'debug' | 'release' ## build profile for *uutils* build; may be supplied by caller, defaults to 'release'
|
||||||
|
|
||||||
# spell-checker:ignore (paths) abmon deref discrim eacces getlimits getopt ginstall inacc infloop inotify reflink ; (misc) INT_OFLOW OFLOW baddecode submodules ; (vars/env) SRCDIR vdir rcexp xpart
|
# spell-checker:ignore (paths) abmon deref discrim eacces getlimits getopt ginstall inacc infloop inotify reflink ; (misc) INT_OFLOW OFLOW baddecode submodules ; (vars/env) SRCDIR vdir rcexp xpart dired
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
@ -261,6 +261,10 @@ sed -i -e "s/Try 'mv --help' for more information/For more information, try '--h
|
||||||
# disable these test cases
|
# disable these test cases
|
||||||
sed -i -E "s|^([^#]*2_31.*)$|#\1|g" tests/printf/printf-cov.pl
|
sed -i -E "s|^([^#]*2_31.*)$|#\1|g" tests/printf/printf-cov.pl
|
||||||
|
|
||||||
|
|
||||||
|
# with ls --dired, in case of error, we have a slightly different error position
|
||||||
|
sed -i -e "s|44 45|47 48|" tests/ls/stat-failed.sh
|
||||||
|
|
||||||
sed -i -e "s/du: invalid -t argument/du: invalid --threshold argument/" -e "s/du: option requires an argument/error: a value is required for '--threshold <SIZE>' but none was supplied/" -e "/Try 'du --help' for more information./d" tests/du/threshold.sh
|
sed -i -e "s/du: invalid -t argument/du: invalid --threshold argument/" -e "s/du: option requires an argument/error: a value is required for '--threshold <SIZE>' but none was supplied/" -e "/Try 'du --help' for more information./d" tests/du/threshold.sh
|
||||||
|
|
||||||
# disable two kind of tests:
|
# disable two kind of tests:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue