mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
Merge pull request #7813 from drinkcat/ls-opt-2
ls: More performance optimizations
This commit is contained in:
commit
96b714781a
1 changed files with 207 additions and 184 deletions
|
@ -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) somegroup nlink tabsize dired subdired dtype colorterm stringly
|
// spell-checker:ignore (ToDO) somegroup nlink tabsize dired subdired dtype colorterm stringly nohash
|
||||||
|
|
||||||
use std::iter;
|
use std::iter;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
|
@ -27,6 +27,7 @@ use std::{
|
||||||
use std::{collections::HashSet, io::IsTerminal};
|
use std::{collections::HashSet, io::IsTerminal};
|
||||||
|
|
||||||
use ansi_width::ansi_width;
|
use ansi_width::ansi_width;
|
||||||
|
use chrono::format::{Item, StrftimeItems};
|
||||||
use chrono::{DateTime, Local, TimeDelta};
|
use chrono::{DateTime, Local, TimeDelta};
|
||||||
use clap::{
|
use clap::{
|
||||||
Arg, ArgAction, Command,
|
Arg, ArgAction, Command,
|
||||||
|
@ -273,32 +274,64 @@ enum TimeStyle {
|
||||||
Format(String),
|
Format(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the given date is considered recent (i.e., in the last 6 months).
|
/// A struct/impl used to format a file DateTime, precomputing the format for performance reasons.
|
||||||
fn is_recent(time: DateTime<Local>) -> bool {
|
struct TimeStyler {
|
||||||
// According to GNU a Gregorian year has 365.2425 * 24 * 60 * 60 == 31556952 seconds on the average.
|
// default format, always specified.
|
||||||
time + TimeDelta::try_seconds(31_556_952 / 2).unwrap() > Local::now()
|
default: Vec<Item<'static>>,
|
||||||
|
// format for a recent time, only specified it is is different from the default
|
||||||
|
recent: Option<Vec<Item<'static>>>,
|
||||||
|
// If `recent` is set, cache the threshold time when we switch from recent to default format.
|
||||||
|
recent_time_threshold: Option<DateTime<Local>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TimeStyle {
|
impl TimeStyler {
|
||||||
/// Format the given time according to this time format style.
|
/// Create a TimeStyler based on a TimeStyle specification.
|
||||||
fn format(&self, time: DateTime<Local>) -> String {
|
fn new(style: &TimeStyle) -> TimeStyler {
|
||||||
let recent = is_recent(time);
|
let default: Vec<Item<'static>> = match style {
|
||||||
match (self, recent) {
|
TimeStyle::FullIso => StrftimeItems::new("%Y-%m-%d %H:%M:%S.%f %z").parse(),
|
||||||
(Self::FullIso, _) => time.format("%Y-%m-%d %H:%M:%S.%f %z").to_string(),
|
TimeStyle::LongIso => StrftimeItems::new("%Y-%m-%d %H:%M").parse(),
|
||||||
(Self::LongIso, _) => time.format("%Y-%m-%d %H:%M").to_string(),
|
TimeStyle::Iso => StrftimeItems::new("%Y-%m-%d ").parse(),
|
||||||
(Self::Iso, true) => time.format("%m-%d %H:%M").to_string(),
|
// In this version of chrono translating can be done
|
||||||
(Self::Iso, false) => time.format("%Y-%m-%d ").to_string(),
|
// The function is chrono::datetime::DateTime::format_localized
|
||||||
// spell-checker:ignore (word) datetime
|
// However it's currently still hard to get the current pure-rust-locale
|
||||||
//In this version of chrono translating can be done
|
// So it's not yet implemented
|
||||||
//The function is chrono::datetime::DateTime::format_localized
|
TimeStyle::Locale => StrftimeItems::new("%b %e %Y").parse(),
|
||||||
//However it's currently still hard to get the current pure-rust-locale
|
TimeStyle::Format(fmt) => {
|
||||||
//So it's not yet implemented
|
// TODO (#7802): Replace with new_lenient
|
||||||
(Self::Locale, true) => time.format("%b %e %H:%M").to_string(),
|
StrftimeItems::new(custom_tz_fmt::custom_time_format(fmt).as_str()).parse_to_owned()
|
||||||
(Self::Locale, false) => time.format("%b %e %Y").to_string(),
|
}
|
||||||
(Self::Format(fmt), _) => time
|
|
||||||
.format(custom_tz_fmt::custom_time_format(fmt).as_str())
|
|
||||||
.to_string(),
|
|
||||||
}
|
}
|
||||||
|
.unwrap();
|
||||||
|
let recent = match style {
|
||||||
|
TimeStyle::Iso => Some(StrftimeItems::new("%m-%d %H:%M")),
|
||||||
|
// See comment above about locale
|
||||||
|
TimeStyle::Locale => Some(StrftimeItems::new("%b %e %H:%M")),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
.map(|x| x.collect());
|
||||||
|
let recent_time_threshold = if recent.is_some() {
|
||||||
|
// According to GNU a Gregorian year has 365.2425 * 24 * 60 * 60 == 31556952 seconds on the average.
|
||||||
|
Some(Local::now() - TimeDelta::try_seconds(31_556_952 / 2).unwrap())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
TimeStyler {
|
||||||
|
default,
|
||||||
|
recent,
|
||||||
|
recent_time_threshold,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Format a DateTime, using `recent` format if available, and the DateTime
|
||||||
|
/// is recent enough.
|
||||||
|
fn format(&self, time: DateTime<Local>) -> String {
|
||||||
|
if self.recent.is_none() || time <= self.recent_time_threshold.unwrap() {
|
||||||
|
time.format_with_items(self.default.iter())
|
||||||
|
} else {
|
||||||
|
time.format_with_items(self.recent.as_ref().unwrap().iter())
|
||||||
|
}
|
||||||
|
.to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2047,15 +2080,40 @@ fn show_dir_name(
|
||||||
write!(out, ":")
|
write!(out, ":")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A struct to encapsulate state that is passed around from `list` functions.
|
||||||
|
struct ListState<'a> {
|
||||||
|
out: BufWriter<Stdout>,
|
||||||
|
style_manager: Option<StyleManager<'a>>,
|
||||||
|
// TODO: More benchmarking with different use cases is required here.
|
||||||
|
// From experiments, BTreeMap may be faster than HashMap, especially as the
|
||||||
|
// number of users/groups is very limited. It seems like nohash::IntMap
|
||||||
|
// performance was equivalent to BTreeMap.
|
||||||
|
// It's possible a simple vector linear(binary?) search implementation would be even faster.
|
||||||
|
#[cfg(unix)]
|
||||||
|
uid_cache: HashMap<u32, String>,
|
||||||
|
#[cfg(unix)]
|
||||||
|
gid_cache: HashMap<u32, String>,
|
||||||
|
|
||||||
|
time_styler: TimeStyler,
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::cognitive_complexity)]
|
#[allow(clippy::cognitive_complexity)]
|
||||||
pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> {
|
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 dired = DiredOutput::default();
|
let mut dired = DiredOutput::default();
|
||||||
let mut style_manager = config.color.as_ref().map(StyleManager::new);
|
|
||||||
let initial_locs_len = locs.len();
|
let initial_locs_len = locs.len();
|
||||||
|
|
||||||
|
let mut state = ListState {
|
||||||
|
out: BufWriter::new(stdout()),
|
||||||
|
style_manager: config.color.as_ref().map(StyleManager::new),
|
||||||
|
#[cfg(unix)]
|
||||||
|
uid_cache: HashMap::new(),
|
||||||
|
#[cfg(unix)]
|
||||||
|
gid_cache: HashMap::new(),
|
||||||
|
time_styler: TimeStyler::new(&config.time_style),
|
||||||
|
};
|
||||||
|
|
||||||
for loc in locs {
|
for loc in locs {
|
||||||
let path_data = PathData::new(PathBuf::from(loc), None, None, config, true);
|
let path_data = PathData::new(PathBuf::from(loc), None, None, config, true);
|
||||||
|
|
||||||
|
@ -2065,11 +2123,11 @@ pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> {
|
||||||
// Proper GNU handling is don't show if dereferenced symlink DNE
|
// Proper GNU handling is don't show if dereferenced symlink DNE
|
||||||
// but only for the base dir, for a child dir show, and print ?s
|
// but only for the base dir, for a child dir show, and print ?s
|
||||||
// in long format
|
// in long format
|
||||||
if path_data.get_metadata(&mut out).is_none() {
|
if path_data.get_metadata(&mut state.out).is_none() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let show_dir_contents = match path_data.file_type(&mut out) {
|
let show_dir_contents = match path_data.file_type(&mut state.out) {
|
||||||
Some(ft) => !config.directory && ft.is_dir(),
|
Some(ft) => !config.directory && ft.is_dir(),
|
||||||
None => {
|
None => {
|
||||||
set_exit_code(1);
|
set_exit_code(1);
|
||||||
|
@ -2084,19 +2142,19 @@ pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sort_entries(&mut files, config, &mut out);
|
sort_entries(&mut files, config, &mut state.out);
|
||||||
sort_entries(&mut dirs, config, &mut out);
|
sort_entries(&mut dirs, config, &mut state.out);
|
||||||
|
|
||||||
if let Some(style_manager) = style_manager.as_mut() {
|
if let Some(style_manager) = state.style_manager.as_mut() {
|
||||||
// ls will try to write a reset before anything is written if normal
|
// ls will try to write a reset before anything is written if normal
|
||||||
// color is given
|
// color is given
|
||||||
if style_manager.get_normal_style().is_some() {
|
if style_manager.get_normal_style().is_some() {
|
||||||
let to_write = style_manager.reset(true);
|
let to_write = style_manager.reset(true);
|
||||||
write!(out, "{to_write}")?;
|
write!(state.out, "{to_write}")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
display_items(&files, config, &mut out, &mut dired, &mut style_manager)?;
|
display_items(&files, config, &mut state, &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
|
||||||
|
@ -2104,7 +2162,7 @@ pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> {
|
||||||
let read_dir = match fs::read_dir(&path_data.p_buf) {
|
let read_dir = match fs::read_dir(&path_data.p_buf) {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
// flush stdout buffer before the error to preserve formatting and order
|
// flush stdout buffer before the error to preserve formatting and order
|
||||||
out.flush()?;
|
state.out.flush()?;
|
||||||
show!(LsError::IOErrorContext(
|
show!(LsError::IOErrorContext(
|
||||||
path_data.p_buf.clone(),
|
path_data.p_buf.clone(),
|
||||||
err,
|
err,
|
||||||
|
@ -2119,10 +2177,10 @@ pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> {
|
||||||
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 {
|
if config.dired {
|
||||||
dired::indent(&mut out)?;
|
dired::indent(&mut state.out)?;
|
||||||
}
|
}
|
||||||
show_dir_name(path_data, &mut out, config)?;
|
show_dir_name(path_data, &mut state.out, config)?;
|
||||||
writeln!(out)?;
|
writeln!(state.out)?;
|
||||||
if config.dired {
|
if config.dired {
|
||||||
// First directory displayed
|
// First directory displayed
|
||||||
let dir_len = path_data.display_name.len();
|
let dir_len = path_data.display_name.len();
|
||||||
|
@ -2132,9 +2190,9 @@ pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> {
|
||||||
dired::add_dir_name(&mut dired, dir_len);
|
dired::add_dir_name(&mut dired, dir_len);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
writeln!(out)?;
|
writeln!(state.out)?;
|
||||||
show_dir_name(path_data, &mut out, config)?;
|
show_dir_name(path_data, &mut state.out, config)?;
|
||||||
writeln!(out)?;
|
writeln!(state.out)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut listed_ancestors = HashSet::new();
|
let mut listed_ancestors = HashSet::new();
|
||||||
|
@ -2146,14 +2204,13 @@ pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> {
|
||||||
path_data,
|
path_data,
|
||||||
read_dir,
|
read_dir,
|
||||||
config,
|
config,
|
||||||
&mut out,
|
&mut state,
|
||||||
&mut listed_ancestors,
|
&mut listed_ancestors,
|
||||||
&mut dired,
|
&mut dired,
|
||||||
&mut style_manager,
|
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
if config.dired && !config.hyperlink {
|
if config.dired && !config.hyperlink {
|
||||||
dired::print_dired_output(config, &dired, &mut out)?;
|
dired::print_dired_output(config, &dired, &mut state.out)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2266,10 +2323,9 @@ fn enter_directory(
|
||||||
path_data: &PathData,
|
path_data: &PathData,
|
||||||
read_dir: ReadDir,
|
read_dir: ReadDir,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
out: &mut BufWriter<Stdout>,
|
state: &mut ListState,
|
||||||
listed_ancestors: &mut HashSet<FileInformation>,
|
listed_ancestors: &mut HashSet<FileInformation>,
|
||||||
dired: &mut DiredOutput,
|
dired: &mut DiredOutput,
|
||||||
style_manager: &mut Option<StyleManager>,
|
|
||||||
) -> 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 {
|
||||||
|
@ -2298,7 +2354,7 @@ fn enter_directory(
|
||||||
let dir_entry = match raw_entry {
|
let dir_entry = match raw_entry {
|
||||||
Ok(path) => path,
|
Ok(path) => path,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
out.flush()?;
|
state.out.flush()?;
|
||||||
show!(LsError::IOError(err));
|
show!(LsError::IOError(err));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -2311,18 +2367,18 @@ fn enter_directory(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
sort_entries(&mut entries, config, out);
|
sort_entries(&mut entries, config, &mut state.out);
|
||||||
|
|
||||||
// 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 {
|
||||||
let total = return_total(&entries, config, out)?;
|
let total = return_total(&entries, config, &mut state.out)?;
|
||||||
write!(out, "{}", total.as_str())?;
|
write!(state.out, "{}", total.as_str())?;
|
||||||
if config.dired {
|
if config.dired {
|
||||||
dired::add_total(dired, total.len());
|
dired::add_total(dired, total.len());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
display_items(&entries, config, out, dired, style_manager)?;
|
display_items(&entries, config, state, dired)?;
|
||||||
|
|
||||||
if config.recursive {
|
if config.recursive {
|
||||||
for e in entries
|
for e in entries
|
||||||
|
@ -2335,7 +2391,7 @@ fn enter_directory(
|
||||||
{
|
{
|
||||||
match fs::read_dir(&e.p_buf) {
|
match fs::read_dir(&e.p_buf) {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
out.flush()?;
|
state.out.flush()?;
|
||||||
show!(LsError::IOErrorContext(
|
show!(LsError::IOErrorContext(
|
||||||
e.p_buf.clone(),
|
e.p_buf.clone(),
|
||||||
err,
|
err,
|
||||||
|
@ -2349,34 +2405,26 @@ fn enter_directory(
|
||||||
{
|
{
|
||||||
// when listing several directories in recursive mode, we show
|
// when listing several directories in recursive mode, we show
|
||||||
// "dirname:" at the beginning of the file list
|
// "dirname:" at the beginning of the file list
|
||||||
writeln!(out)?;
|
writeln!(state.out)?;
|
||||||
if config.dired {
|
if config.dired {
|
||||||
// We already injected the first dir
|
// We already injected the first dir
|
||||||
// Continue with the others
|
// Continue with the others
|
||||||
// 2 = \n + \n
|
// 2 = \n + \n
|
||||||
dired.padding = 2;
|
dired.padding = 2;
|
||||||
dired::indent(out)?;
|
dired::indent(&mut state.out)?;
|
||||||
let dir_name_size = e.p_buf.to_string_lossy().len();
|
let dir_name_size = e.p_buf.to_string_lossy().len();
|
||||||
dired::calculate_subdired(dired, dir_name_size);
|
dired::calculate_subdired(dired, dir_name_size);
|
||||||
// inject dir name
|
// inject dir name
|
||||||
dired::add_dir_name(dired, dir_name_size);
|
dired::add_dir_name(dired, dir_name_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
show_dir_name(e, out, config)?;
|
show_dir_name(e, &mut state.out, config)?;
|
||||||
writeln!(out)?;
|
writeln!(state.out)?;
|
||||||
enter_directory(
|
enter_directory(e, rd, config, state, listed_ancestors, dired)?;
|
||||||
e,
|
|
||||||
rd,
|
|
||||||
config,
|
|
||||||
out,
|
|
||||||
listed_ancestors,
|
|
||||||
dired,
|
|
||||||
style_manager,
|
|
||||||
)?;
|
|
||||||
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 {
|
||||||
out.flush()?;
|
state.out.flush()?;
|
||||||
show!(LsError::AlreadyListedError(e.p_buf.clone()));
|
show!(LsError::AlreadyListedError(e.p_buf.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2398,10 +2446,10 @@ fn get_metadata_with_deref_opt(p_buf: &Path, dereference: bool) -> std::io::Resu
|
||||||
fn display_dir_entry_size(
|
fn display_dir_entry_size(
|
||||||
entry: &PathData,
|
entry: &PathData,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
out: &mut BufWriter<Stdout>,
|
state: &mut ListState,
|
||||||
) -> (usize, usize, usize, usize, usize, usize) {
|
) -> (usize, usize, usize, usize, usize, usize) {
|
||||||
// TODO: Cache/memorize the display_* results so we don't have to recalculate them.
|
// TODO: Cache/memorize the display_* results so we don't have to recalculate them.
|
||||||
if let Some(md) = entry.get_metadata(out) {
|
if let Some(md) = entry.get_metadata(&mut state.out) {
|
||||||
let (size_len, major_len, minor_len) = match display_len_or_rdev(md, config) {
|
let (size_len, major_len, minor_len) = match display_len_or_rdev(md, config) {
|
||||||
SizeOrDeviceId::Device(major, minor) => {
|
SizeOrDeviceId::Device(major, minor) => {
|
||||||
(major.len() + minor.len() + 2usize, major.len(), minor.len())
|
(major.len() + minor.len() + 2usize, major.len(), minor.len())
|
||||||
|
@ -2410,8 +2458,8 @@ fn display_dir_entry_size(
|
||||||
};
|
};
|
||||||
(
|
(
|
||||||
display_symlink_count(md).len(),
|
display_symlink_count(md).len(),
|
||||||
display_uname(md, config).len(),
|
display_uname(md, config, state).len(),
|
||||||
display_group(md, config).len(),
|
display_group(md, config, state).len(),
|
||||||
size_len,
|
size_len,
|
||||||
major_len,
|
major_len,
|
||||||
minor_len,
|
minor_len,
|
||||||
|
@ -2511,9 +2559,8 @@ fn display_additional_leading_info(
|
||||||
fn display_items(
|
fn display_items(
|
||||||
items: &[PathData],
|
items: &[PathData],
|
||||||
config: &Config,
|
config: &Config,
|
||||||
out: &mut BufWriter<Stdout>,
|
state: &mut ListState,
|
||||||
dired: &mut DiredOutput,
|
dired: &mut DiredOutput,
|
||||||
style_manager: &mut Option<StyleManager>,
|
|
||||||
) -> UResult<()> {
|
) -> 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`
|
||||||
|
@ -2525,31 +2572,31 @@ fn display_items(
|
||||||
});
|
});
|
||||||
|
|
||||||
if config.format == Format::Long {
|
if config.format == Format::Long {
|
||||||
let padding_collection = calculate_padding_collection(items, config, out);
|
let padding_collection = calculate_padding_collection(items, config, state);
|
||||||
|
|
||||||
for item in items {
|
for item in items {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
if config.inode || config.alloc_size {
|
if config.inode || config.alloc_size {
|
||||||
let more_info =
|
let more_info = display_additional_leading_info(
|
||||||
display_additional_leading_info(item, &padding_collection, config, out)?;
|
item,
|
||||||
|
&padding_collection,
|
||||||
|
config,
|
||||||
|
&mut state.out,
|
||||||
|
)?;
|
||||||
|
|
||||||
write!(out, "{more_info}")?;
|
write!(state.out, "{more_info}")?;
|
||||||
}
|
}
|
||||||
#[cfg(not(unix))]
|
#[cfg(not(unix))]
|
||||||
if config.alloc_size {
|
if config.alloc_size {
|
||||||
let more_info =
|
let more_info = display_additional_leading_info(
|
||||||
display_additional_leading_info(item, &padding_collection, config, out)?;
|
item,
|
||||||
write!(out, "{more_info}")?;
|
&padding_collection,
|
||||||
|
config,
|
||||||
|
&mut state.out,
|
||||||
|
)?;
|
||||||
|
write!(state.out, "{more_info}")?;
|
||||||
}
|
}
|
||||||
display_item_long(
|
display_item_long(item, &padding_collection, config, state, dired, quoted)?;
|
||||||
item,
|
|
||||||
&padding_collection,
|
|
||||||
config,
|
|
||||||
out,
|
|
||||||
dired,
|
|
||||||
style_manager,
|
|
||||||
quoted,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut longest_context_len = 1;
|
let mut longest_context_len = 1;
|
||||||
|
@ -2563,16 +2610,16 @@ fn display_items(
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let padding = calculate_padding_collection(items, config, out);
|
let padding = calculate_padding_collection(items, config, state);
|
||||||
|
|
||||||
// we need to apply normal color to non filename output
|
// we need to apply normal color to non filename output
|
||||||
if let Some(style_manager) = style_manager {
|
if let Some(style_manager) = &mut state.style_manager {
|
||||||
write!(out, "{}", style_manager.apply_normal())?;
|
write!(state.out, "{}", style_manager.apply_normal())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut names_vec = Vec::new();
|
let mut names_vec = Vec::new();
|
||||||
for i in items {
|
for i in items {
|
||||||
let more_info = display_additional_leading_info(i, &padding, config, out)?;
|
let more_info = display_additional_leading_info(i, &padding, config, &mut state.out)?;
|
||||||
// it's okay to set current column to zero which is used to decide
|
// it's okay to set current column to zero which is used to decide
|
||||||
// whether text will wrap or not, because when format is grid or
|
// whether text will wrap or not, because when format is grid or
|
||||||
// column ls will try to place the item name in a new line if it
|
// column ls will try to place the item name in a new line if it
|
||||||
|
@ -2582,8 +2629,7 @@ fn display_items(
|
||||||
config,
|
config,
|
||||||
prefix_context,
|
prefix_context,
|
||||||
more_info,
|
more_info,
|
||||||
out,
|
state,
|
||||||
style_manager,
|
|
||||||
LazyCell::new(Box::new(|| 0)),
|
LazyCell::new(Box::new(|| 0)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2598,7 +2644,7 @@ fn display_items(
|
||||||
names,
|
names,
|
||||||
config.width,
|
config.width,
|
||||||
Direction::TopToBottom,
|
Direction::TopToBottom,
|
||||||
out,
|
&mut state.out,
|
||||||
quoted,
|
quoted,
|
||||||
config.tab_size,
|
config.tab_size,
|
||||||
)?;
|
)?;
|
||||||
|
@ -2608,7 +2654,7 @@ fn display_items(
|
||||||
names,
|
names,
|
||||||
config.width,
|
config.width,
|
||||||
Direction::LeftToRight,
|
Direction::LeftToRight,
|
||||||
out,
|
&mut state.out,
|
||||||
quoted,
|
quoted,
|
||||||
config.tab_size,
|
config.tab_size,
|
||||||
)?;
|
)?;
|
||||||
|
@ -2616,7 +2662,7 @@ fn display_items(
|
||||||
Format::Commas => {
|
Format::Commas => {
|
||||||
let mut current_col = 0;
|
let mut current_col = 0;
|
||||||
if let Some(name) = names.next() {
|
if let Some(name) = names.next() {
|
||||||
write_os_str(out, &name)?;
|
write_os_str(&mut state.out, &name)?;
|
||||||
current_col = ansi_width(&name.to_string_lossy()) as u16 + 2;
|
current_col = ansi_width(&name.to_string_lossy()) as u16 + 2;
|
||||||
}
|
}
|
||||||
for name in names {
|
for name in names {
|
||||||
|
@ -2624,23 +2670,23 @@ fn display_items(
|
||||||
// If the width is 0 we print one single line
|
// If the width is 0 we print one single line
|
||||||
if config.width != 0 && current_col + name_width + 1 > config.width {
|
if config.width != 0 && current_col + name_width + 1 > config.width {
|
||||||
current_col = name_width + 2;
|
current_col = name_width + 2;
|
||||||
writeln!(out, ",")?;
|
writeln!(state.out, ",")?;
|
||||||
} else {
|
} else {
|
||||||
current_col += name_width + 2;
|
current_col += name_width + 2;
|
||||||
write!(out, ", ")?;
|
write!(state.out, ", ")?;
|
||||||
}
|
}
|
||||||
write_os_str(out, &name)?;
|
write_os_str(&mut state.out, &name)?;
|
||||||
}
|
}
|
||||||
// Current col is never zero again if names have been printed.
|
// Current col is never zero again if names have been printed.
|
||||||
// So we print a newline.
|
// So we print a newline.
|
||||||
if current_col > 0 {
|
if current_col > 0 {
|
||||||
write!(out, "{}", config.line_ending)?;
|
write!(state.out, "{}", config.line_ending)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
for name in names {
|
for name in names {
|
||||||
write_os_str(out, &name)?;
|
write_os_str(&mut state.out, &name)?;
|
||||||
write!(out, "{}", config.line_ending)?;
|
write!(state.out, "{}", config.line_ending)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -2751,7 +2797,7 @@ fn display_grid(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This writes to the BufWriter out a single string of the output of `ls -l`.
|
/// This writes to the BufWriter state.out a single string of the output of `ls -l`.
|
||||||
///
|
///
|
||||||
/// It writes the following keys, in order:
|
/// It writes the following keys, in order:
|
||||||
/// * `inode` ([`get_inode`], config-optional)
|
/// * `inode` ([`get_inode`], config-optional)
|
||||||
|
@ -2784,21 +2830,20 @@ fn display_item_long(
|
||||||
item: &PathData,
|
item: &PathData,
|
||||||
padding: &PaddingCollection,
|
padding: &PaddingCollection,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
out: &mut BufWriter<Stdout>,
|
state: &mut ListState,
|
||||||
dired: &mut DiredOutput,
|
dired: &mut DiredOutput,
|
||||||
style_manager: &mut Option<StyleManager>,
|
|
||||||
quoted: bool,
|
quoted: bool,
|
||||||
) -> UResult<()> {
|
) -> UResult<()> {
|
||||||
let mut output_display: Vec<u8> = Vec::with_capacity(128);
|
let mut output_display: Vec<u8> = Vec::with_capacity(128);
|
||||||
|
|
||||||
// apply normal color to non filename outputs
|
// apply normal color to non filename outputs
|
||||||
if let Some(style_manager) = style_manager {
|
if let Some(style_manager) = &mut state.style_manager {
|
||||||
output_display.extend(style_manager.apply_normal().as_bytes());
|
output_display.extend(style_manager.apply_normal().as_bytes());
|
||||||
}
|
}
|
||||||
if config.dired {
|
if config.dired {
|
||||||
output_display.extend(b" ");
|
output_display.extend(b" ");
|
||||||
}
|
}
|
||||||
if let Some(md) = item.get_metadata(out) {
|
if let Some(md) = item.get_metadata(&mut state.out) {
|
||||||
#[cfg(any(not(unix), target_os = "android", target_os = "macos"))]
|
#[cfg(any(not(unix), target_os = "android", target_os = "macos"))]
|
||||||
// TODO: See how Mac should work here
|
// TODO: See how Mac should work here
|
||||||
let is_acl_set = false;
|
let is_acl_set = false;
|
||||||
|
@ -2817,12 +2862,12 @@ fn display_item_long(
|
||||||
|
|
||||||
if config.long.owner {
|
if config.long.owner {
|
||||||
output_display.extend(b" ");
|
output_display.extend(b" ");
|
||||||
output_display.extend_pad_right(&display_uname(md, config), padding.uname);
|
output_display.extend_pad_right(display_uname(md, config, state), padding.uname);
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.long.group {
|
if config.long.group {
|
||||||
output_display.extend(b" ");
|
output_display.extend(b" ");
|
||||||
output_display.extend_pad_right(&display_group(md, config), padding.group);
|
output_display.extend_pad_right(display_group(md, config, state), padding.group);
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.context {
|
if config.context {
|
||||||
|
@ -2834,7 +2879,7 @@ fn display_item_long(
|
||||||
// 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 {
|
||||||
output_display.extend(b" ");
|
output_display.extend(b" ");
|
||||||
output_display.extend_pad_right(&display_uname(md, config), padding.uname);
|
output_display.extend_pad_right(display_uname(md, config, state), padding.uname);
|
||||||
}
|
}
|
||||||
|
|
||||||
match display_len_or_rdev(md, config) {
|
match display_len_or_rdev(md, config) {
|
||||||
|
@ -2867,7 +2912,7 @@ fn display_item_long(
|
||||||
};
|
};
|
||||||
|
|
||||||
output_display.extend(b" ");
|
output_display.extend(b" ");
|
||||||
output_display.extend(display_date(md, config).as_bytes());
|
output_display.extend(display_date(md, config, state).as_bytes());
|
||||||
output_display.extend(b" ");
|
output_display.extend(b" ");
|
||||||
|
|
||||||
let item_name = display_item_name(
|
let item_name = display_item_name(
|
||||||
|
@ -2875,8 +2920,7 @@ fn display_item_long(
|
||||||
config,
|
config,
|
||||||
None,
|
None,
|
||||||
String::new(),
|
String::new(),
|
||||||
out,
|
state,
|
||||||
style_manager,
|
|
||||||
LazyCell::new(Box::new(|| {
|
LazyCell::new(Box::new(|| {
|
||||||
ansi_width(&String::from_utf8_lossy(&output_display))
|
ansi_width(&String::from_utf8_lossy(&output_display))
|
||||||
})),
|
})),
|
||||||
|
@ -2971,8 +3015,7 @@ fn display_item_long(
|
||||||
config,
|
config,
|
||||||
None,
|
None,
|
||||||
String::new(),
|
String::new(),
|
||||||
out,
|
state,
|
||||||
style_manager,
|
|
||||||
LazyCell::new(Box::new(|| {
|
LazyCell::new(Box::new(|| {
|
||||||
ansi_width(&String::from_utf8_lossy(&output_display))
|
ansi_width(&String::from_utf8_lossy(&output_display))
|
||||||
})),
|
})),
|
||||||
|
@ -2995,7 +3038,7 @@ fn display_item_long(
|
||||||
write_os_str(&mut output_display, &displayed_item)?;
|
write_os_str(&mut output_display, &displayed_item)?;
|
||||||
output_display.extend(config.line_ending.to_string().as_bytes());
|
output_display.extend(config.line_ending.to_string().as_bytes());
|
||||||
}
|
}
|
||||||
out.write_all(&output_display)?;
|
state.out.write_all(&output_display)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -3005,71 +3048,45 @@ fn get_inode(metadata: &Metadata) -> String {
|
||||||
format!("{}", metadata.ino())
|
format!("{}", metadata.ino())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Currently getpwuid is `linux` target only. If it's broken out into
|
// Currently getpwuid is `linux` target only. If it's broken state.out into
|
||||||
// a posix-compliant attribute this can be updated...
|
// a posix-compliant attribute this can be updated...
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::sync::LazyLock;
|
|
||||||
#[cfg(unix)]
|
|
||||||
use std::sync::Mutex;
|
|
||||||
#[cfg(unix)]
|
|
||||||
use uucore::entries;
|
use uucore::entries;
|
||||||
use uucore::fs::FileInformation;
|
use uucore::fs::FileInformation;
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn cached_uid2usr(uid: u32) -> String {
|
fn display_uname<'a>(metadata: &Metadata, config: &Config, state: &'a mut ListState) -> &'a String {
|
||||||
static UID_CACHE: LazyLock<Mutex<HashMap<u32, String>>> =
|
let uid = metadata.uid();
|
||||||
LazyLock::new(|| Mutex::new(HashMap::new()));
|
|
||||||
|
|
||||||
let mut uid_cache = UID_CACHE.lock().unwrap();
|
state.uid_cache.entry(uid).or_insert_with(|| {
|
||||||
uid_cache
|
if config.long.numeric_uid_gid {
|
||||||
.entry(uid)
|
uid.to_string()
|
||||||
.or_insert_with(|| entries::uid2usr(uid).unwrap_or_else(|_| uid.to_string()))
|
} else {
|
||||||
.clone()
|
entries::uid2usr(uid).unwrap_or_else(|_| uid.to_string())
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn display_uname(metadata: &Metadata, config: &Config) -> String {
|
fn display_group<'a>(metadata: &Metadata, config: &Config, state: &'a mut ListState) -> &'a String {
|
||||||
if config.long.numeric_uid_gid {
|
let gid = metadata.gid();
|
||||||
metadata.uid().to_string()
|
state.gid_cache.entry(gid).or_insert_with(|| {
|
||||||
} else {
|
if cfg!(target_os = "redox") || config.long.numeric_uid_gid {
|
||||||
cached_uid2usr(metadata.uid())
|
gid.to_string()
|
||||||
}
|
} else {
|
||||||
}
|
entries::gid2grp(gid).unwrap_or_else(|_| gid.to_string())
|
||||||
|
}
|
||||||
#[cfg(all(unix, not(target_os = "redox")))]
|
})
|
||||||
fn cached_gid2grp(gid: u32) -> String {
|
|
||||||
static GID_CACHE: LazyLock<Mutex<HashMap<u32, String>>> =
|
|
||||||
LazyLock::new(|| Mutex::new(HashMap::new()));
|
|
||||||
|
|
||||||
let mut gid_cache = GID_CACHE.lock().unwrap();
|
|
||||||
gid_cache
|
|
||||||
.entry(gid)
|
|
||||||
.or_insert_with(|| entries::gid2grp(gid).unwrap_or_else(|_| gid.to_string()))
|
|
||||||
.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(unix, not(target_os = "redox")))]
|
|
||||||
fn display_group(metadata: &Metadata, config: &Config) -> String {
|
|
||||||
if config.long.numeric_uid_gid {
|
|
||||||
metadata.gid().to_string()
|
|
||||||
} else {
|
|
||||||
cached_gid2grp(metadata.gid())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "redox")]
|
|
||||||
fn display_group(metadata: &Metadata, _config: &Config) -> String {
|
|
||||||
metadata.gid().to_string()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(unix))]
|
#[cfg(not(unix))]
|
||||||
fn display_uname(_metadata: &Metadata, _config: &Config) -> String {
|
fn display_uname(_metadata: &Metadata, _config: &Config, _state: &mut ListState) -> &'static str {
|
||||||
"somebody".to_string()
|
"somebody"
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(unix))]
|
#[cfg(not(unix))]
|
||||||
fn display_group(_metadata: &Metadata, _config: &Config) -> String {
|
fn display_group(_metadata: &Metadata, _config: &Config, _state: &mut ListState) -> &'static str {
|
||||||
"somegroup".to_string()
|
"somegroup"
|
||||||
}
|
}
|
||||||
|
|
||||||
// The implementations for get_time are separated because some options, such
|
// The implementations for get_time are separated because some options, such
|
||||||
|
@ -3099,9 +3116,9 @@ fn get_time(md: &Metadata, config: &Config) -> Option<DateTime<Local>> {
|
||||||
Some(time.into())
|
Some(time.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_date(metadata: &Metadata, config: &Config) -> String {
|
fn display_date(metadata: &Metadata, config: &Config, state: &mut ListState) -> String {
|
||||||
match get_time(metadata, config) {
|
match get_time(metadata, config) {
|
||||||
Some(time) => config.time_style.format(time),
|
Some(time) => state.time_styler.format(time),
|
||||||
None => "???".into(),
|
None => "???".into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3207,8 +3224,7 @@ fn display_item_name(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
prefix_context: Option<usize>,
|
prefix_context: Option<usize>,
|
||||||
more_info: String,
|
more_info: String,
|
||||||
out: &mut BufWriter<Stdout>,
|
state: &mut ListState,
|
||||||
style_manager: &mut Option<StyleManager>,
|
|
||||||
current_column: LazyCell<usize, Box<dyn FnOnce() -> usize + '_>>,
|
current_column: LazyCell<usize, Box<dyn FnOnce() -> usize + '_>>,
|
||||||
) -> OsString {
|
) -> OsString {
|
||||||
// This is our return value. We start by `&path.display_name` and modify it along the way.
|
// This is our return value. We start by `&path.display_name` and modify it along the way.
|
||||||
|
@ -3221,9 +3237,16 @@ fn display_item_name(
|
||||||
name = create_hyperlink(&name, path);
|
name = create_hyperlink(&name, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(style_manager) = style_manager {
|
if let Some(style_manager) = &mut state.style_manager {
|
||||||
let len = name.len();
|
let len = name.len();
|
||||||
name = color_name(name, path, style_manager, out, None, is_wrap(len));
|
name = color_name(
|
||||||
|
name,
|
||||||
|
path,
|
||||||
|
style_manager,
|
||||||
|
&mut state.out,
|
||||||
|
None,
|
||||||
|
is_wrap(len),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.format != Format::Long && !more_info.is_empty() {
|
if config.format != Format::Long && !more_info.is_empty() {
|
||||||
|
@ -3233,7 +3256,7 @@ fn display_item_name(
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.indicator_style != IndicatorStyle::None {
|
if config.indicator_style != IndicatorStyle::None {
|
||||||
let sym = classify_file(path, out);
|
let sym = classify_file(path, &mut state.out);
|
||||||
|
|
||||||
let char_opt = match config.indicator_style {
|
let char_opt = match config.indicator_style {
|
||||||
IndicatorStyle::Classify => sym,
|
IndicatorStyle::Classify => sym,
|
||||||
|
@ -3260,8 +3283,8 @@ fn display_item_name(
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.format == Format::Long
|
if config.format == Format::Long
|
||||||
&& path.file_type(out).is_some()
|
&& path.file_type(&mut state.out).is_some()
|
||||||
&& path.file_type(out).unwrap().is_symlink()
|
&& path.file_type(&mut state.out).unwrap().is_symlink()
|
||||||
&& !path.must_dereference
|
&& !path.must_dereference
|
||||||
{
|
{
|
||||||
match path.p_buf.read_link() {
|
match path.p_buf.read_link() {
|
||||||
|
@ -3271,7 +3294,7 @@ fn display_item_name(
|
||||||
// We might as well color the symlink output after the arrow.
|
// We might as well color the symlink output after the arrow.
|
||||||
// This makes extra system calls, but provides important information that
|
// This makes extra system calls, but provides important information that
|
||||||
// people run `ls -l --color` are very interested in.
|
// people run `ls -l --color` are very interested in.
|
||||||
if let Some(style_manager) = style_manager {
|
if let Some(style_manager) = &mut state.style_manager {
|
||||||
// We get the absolute path to be able to construct PathData with valid Metadata.
|
// We get the absolute path to be able to construct PathData with valid Metadata.
|
||||||
// This is because relative symlinks will fail to get_metadata.
|
// This is because relative symlinks will fail to get_metadata.
|
||||||
let mut absolute_target = target.clone();
|
let mut absolute_target = target.clone();
|
||||||
|
@ -3287,7 +3310,7 @@ fn display_item_name(
|
||||||
// Because we use an absolute path, we can assume this is guaranteed to exist.
|
// Because we use an absolute path, we can assume this is guaranteed to exist.
|
||||||
// Otherwise, we use path.md(), which will guarantee we color to the same
|
// Otherwise, we use path.md(), which will guarantee we color to the same
|
||||||
// color of non-existent symlinks according to style_for_path_with_metadata.
|
// color of non-existent symlinks according to style_for_path_with_metadata.
|
||||||
if path.get_metadata(out).is_none()
|
if path.get_metadata(&mut state.out).is_none()
|
||||||
&& get_metadata_with_deref_opt(
|
&& get_metadata_with_deref_opt(
|
||||||
target_data.p_buf.as_path(),
|
target_data.p_buf.as_path(),
|
||||||
target_data.must_dereference,
|
target_data.must_dereference,
|
||||||
|
@ -3300,7 +3323,7 @@ fn display_item_name(
|
||||||
escape_name(target.as_os_str(), &config.quoting_style),
|
escape_name(target.as_os_str(), &config.quoting_style),
|
||||||
path,
|
path,
|
||||||
style_manager,
|
style_manager,
|
||||||
out,
|
&mut state.out,
|
||||||
Some(&target_data),
|
Some(&target_data),
|
||||||
is_wrap(name.len()),
|
is_wrap(name.len()),
|
||||||
));
|
));
|
||||||
|
@ -3439,7 +3462,7 @@ fn get_security_context(config: &Config, p_buf: &Path, must_dereference: bool) -
|
||||||
fn calculate_padding_collection(
|
fn calculate_padding_collection(
|
||||||
items: &[PathData],
|
items: &[PathData],
|
||||||
config: &Config,
|
config: &Config,
|
||||||
out: &mut BufWriter<Stdout>,
|
state: &mut ListState,
|
||||||
) -> PaddingCollection {
|
) -> PaddingCollection {
|
||||||
let mut padding_collections = PaddingCollection {
|
let mut padding_collections = PaddingCollection {
|
||||||
inode: 1,
|
inode: 1,
|
||||||
|
@ -3456,7 +3479,7 @@ fn calculate_padding_collection(
|
||||||
for item in items {
|
for item in items {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
if config.inode {
|
if config.inode {
|
||||||
let inode_len = if let Some(md) = item.get_metadata(out) {
|
let inode_len = if let Some(md) = item.get_metadata(&mut state.out) {
|
||||||
display_inode(md).len()
|
display_inode(md).len()
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
|
@ -3465,7 +3488,7 @@ fn calculate_padding_collection(
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.alloc_size {
|
if config.alloc_size {
|
||||||
if let Some(md) = item.get_metadata(out) {
|
if let Some(md) = item.get_metadata(&mut state.out) {
|
||||||
let block_size_len = display_size(get_block_size(md, config), config).len();
|
let block_size_len = display_size(get_block_size(md, config), config).len();
|
||||||
padding_collections.block_size = block_size_len.max(padding_collections.block_size);
|
padding_collections.block_size = block_size_len.max(padding_collections.block_size);
|
||||||
}
|
}
|
||||||
|
@ -3474,7 +3497,7 @@ fn calculate_padding_collection(
|
||||||
if config.format == Format::Long {
|
if config.format == Format::Long {
|
||||||
let context_len = item.security_context.len();
|
let context_len = item.security_context.len();
|
||||||
let (link_count_len, uname_len, group_len, size_len, major_len, minor_len) =
|
let (link_count_len, uname_len, group_len, size_len, major_len, minor_len) =
|
||||||
display_dir_entry_size(item, config, out);
|
display_dir_entry_size(item, config, state);
|
||||||
padding_collections.link_count = link_count_len.max(padding_collections.link_count);
|
padding_collections.link_count = link_count_len.max(padding_collections.link_count);
|
||||||
padding_collections.uname = uname_len.max(padding_collections.uname);
|
padding_collections.uname = uname_len.max(padding_collections.uname);
|
||||||
padding_collections.group = group_len.max(padding_collections.group);
|
padding_collections.group = group_len.max(padding_collections.group);
|
||||||
|
@ -3502,7 +3525,7 @@ fn calculate_padding_collection(
|
||||||
fn calculate_padding_collection(
|
fn calculate_padding_collection(
|
||||||
items: &[PathData],
|
items: &[PathData],
|
||||||
config: &Config,
|
config: &Config,
|
||||||
out: &mut BufWriter<Stdout>,
|
state: &mut ListState,
|
||||||
) -> PaddingCollection {
|
) -> PaddingCollection {
|
||||||
let mut padding_collections = PaddingCollection {
|
let mut padding_collections = PaddingCollection {
|
||||||
link_count: 1,
|
link_count: 1,
|
||||||
|
@ -3515,7 +3538,7 @@ fn calculate_padding_collection(
|
||||||
|
|
||||||
for item in items {
|
for item in items {
|
||||||
if config.alloc_size {
|
if config.alloc_size {
|
||||||
if let Some(md) = item.get_metadata(out) {
|
if let Some(md) = item.get_metadata(&mut state.out) {
|
||||||
let block_size_len = display_size(get_block_size(md, config), config).len();
|
let block_size_len = display_size(get_block_size(md, config), config).len();
|
||||||
padding_collections.block_size = block_size_len.max(padding_collections.block_size);
|
padding_collections.block_size = block_size_len.max(padding_collections.block_size);
|
||||||
}
|
}
|
||||||
|
@ -3523,7 +3546,7 @@ fn calculate_padding_collection(
|
||||||
|
|
||||||
let context_len = item.security_context.len();
|
let context_len = item.security_context.len();
|
||||||
let (link_count_len, uname_len, group_len, size_len, _major_len, _minor_len) =
|
let (link_count_len, uname_len, group_len, size_len, _major_len, _minor_len) =
|
||||||
display_dir_entry_size(item, config, out);
|
display_dir_entry_size(item, config, state);
|
||||||
padding_collections.link_count = link_count_len.max(padding_collections.link_count);
|
padding_collections.link_count = link_count_len.max(padding_collections.link_count);
|
||||||
padding_collections.uname = uname_len.max(padding_collections.uname);
|
padding_collections.uname = uname_len.max(padding_collections.uname);
|
||||||
padding_collections.group = group_len.max(padding_collections.group);
|
padding_collections.group = group_len.max(padding_collections.group);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue