diff --git a/README.md b/README.md index 7190fd7..fc31103 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,7 @@ Usage: dust -S (Custom Stack size - Use if you see: 'fatal runtime error: stack Usage: dust --skip-total (No total row will be displayed) Usage: dust -z 40000/30MB/20kib (Exclude output files/directories below size 40000 bytes / 30MB / 20KiB) Usage: dust -j (Prints JSON representation of directories, try: dust -j | jq) +Usage: dust --files0-from=FILE (Reads null-terminated file paths from FILE); If FILE is - then read from stdin ``` ## Config file diff --git a/completions/_dust b/completions/_dust index b1fe76d..c6ff4d0 100644 --- a/completions/_dust +++ b/completions/_dust @@ -43,6 +43,7 @@ _dust() { '--atime=[just like -mtime, but based on file access time]: : ' \ '-y+[just like -mtime, but based on file change time]: : ' \ '--ctime=[just like -mtime, but based on file change time]: : ' \ +'--files0-from=[run dust on NUL-terminated file names specified in file; if argument is -, then read names from standard input]: :_files' \ '-p[Subdirectories will not have their path shortened]' \ '--full-paths[Subdirectories will not have their path shortened]' \ '-L[dereference sym links - Treat sym links as directories and go into them]' \ diff --git a/completions/_dust.ps1 b/completions/_dust.ps1 index 29637ca..145a036 100644 --- a/completions/_dust.ps1 +++ b/completions/_dust.ps1 @@ -49,6 +49,7 @@ Register-ArgumentCompleter -Native -CommandName 'dust' -ScriptBlock { [CompletionResult]::new('--atime', 'atime', [CompletionResultType]::ParameterName, 'just like -mtime, but based on file access time') [CompletionResult]::new('-y', 'y', [CompletionResultType]::ParameterName, 'just like -mtime, but based on file change time') [CompletionResult]::new('--ctime', 'ctime', [CompletionResultType]::ParameterName, 'just like -mtime, but based on file change time') + [CompletionResult]::new('--files0-from', 'files0-from', [CompletionResultType]::ParameterName, 'run dust on NUL-terminated file names specified in file; if argument is -, then read names from standard input') [CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'Subdirectories will not have their path shortened') [CompletionResult]::new('--full-paths', 'full-paths', [CompletionResultType]::ParameterName, 'Subdirectories will not have their path shortened') [CompletionResult]::new('-L', 'L ', [CompletionResultType]::ParameterName, 'dereference sym links - Treat sym links as directories and go into them') diff --git a/completions/dust.bash b/completions/dust.bash index 2f65629..c7ed805 100644 --- a/completions/dust.bash +++ b/completions/dust.bash @@ -19,7 +19,7 @@ _dust() { case "${cmd}" in dust) - opts="-d -T -n -p -X -I -L -x -s -r -c -C -b -B -z -R -f -i -v -e -t -w -P -D -F -o -S -j -M -A -y -h -V --depth --threads --number-of-lines --full-paths --ignore-directory --ignore-all-in-file --dereference-links --limit-filesystem --apparent-size --reverse --no-colors --force-colors --no-percent-bars --bars-on-right --min-size --screen-reader --skip-total --filecount --ignore_hidden --invert-filter --filter --file_types --terminal_width --no-progress --print-errors --only-dir --only-file --output-format --stack-size --output-json --mtime --atime --ctime --help --version [PATH]..." + opts="-d -T -n -p -X -I -L -x -s -r -c -C -b -B -z -R -f -i -v -e -t -w -P -D -F -o -S -j -M -A -y -h -V --depth --threads --number-of-lines --full-paths --ignore-directory --ignore-all-in-file --dereference-links --limit-filesystem --apparent-size --reverse --no-colors --force-colors --no-percent-bars --bars-on-right --min-size --screen-reader --skip-total --filecount --ignore_hidden --invert-filter --filter --file_types --terminal_width --no-progress --print-errors --only-dir --only-file --output-format --stack-size --output-json --mtime --atime --ctime --files0-from --help --version [PATH]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -159,6 +159,10 @@ _dust() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --files0-from) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; *) COMPREPLY=() ;; diff --git a/completions/dust.elv b/completions/dust.elv index a2571a9..f8cfc8a 100644 --- a/completions/dust.elv +++ b/completions/dust.elv @@ -46,6 +46,7 @@ set edit:completion:arg-completer[dust] = {|@words| cand --atime 'just like -mtime, but based on file access time' cand -y 'just like -mtime, but based on file change time' cand --ctime 'just like -mtime, but based on file change time' + cand --files0-from 'run dust on NUL-terminated file names specified in file; if argument is -, then read names from standard input' cand -p 'Subdirectories will not have their path shortened' cand --full-paths 'Subdirectories will not have their path shortened' cand -L 'dereference sym links - Treat sym links as directories and go into them' diff --git a/completions/dust.fish b/completions/dust.fish index 6c333d4..f0056a5 100644 --- a/completions/dust.fish +++ b/completions/dust.fish @@ -12,6 +12,7 @@ complete -c dust -s S -l stack-size -d 'Specify memory to use as stack size - us complete -c dust -s M -l mtime -d '+/-n matches files modified more/less than n days ago , and n matches files modified exactly n days ago, days are rounded down.That is +n => (−∞, curr−(n+1)), n => [curr−(n+1), curr−n), and -n => (𝑐𝑢𝑟𝑟−𝑛, +∞)' -r complete -c dust -s A -l atime -d 'just like -mtime, but based on file access time' -r complete -c dust -s y -l ctime -d 'just like -mtime, but based on file change time' -r +complete -c dust -l files0-from -d 'run dust on NUL-terminated file names specified in file; if argument is -, then read names from standard input' -r -F complete -c dust -s p -l full-paths -d 'Subdirectories will not have their path shortened' complete -c dust -s L -l dereference-links -d 'dereference sym links - Treat sym links as directories and go into them' complete -c dust -s x -l limit-filesystem -d 'Only count the files and directories on the same filesystem as the supplied directory' diff --git a/man-page/dust.1 b/man-page/dust.1 index 5932669..bea0029 100644 --- a/man-page/dust.1 +++ b/man-page/dust.1 @@ -4,7 +4,7 @@ .SH NAME Dust \- Like du but more intuitive .SH SYNOPSIS -\fBdust\fR [\fB\-d\fR|\fB\-\-depth\fR] [\fB\-T\fR|\fB\-\-threads\fR] [\fB\-n\fR|\fB\-\-number\-of\-lines\fR] [\fB\-p\fR|\fB\-\-full\-paths\fR] [\fB\-X\fR|\fB\-\-ignore\-directory\fR] [\fB\-I\fR|\fB\-\-ignore\-all\-in\-file\fR] [\fB\-L\fR|\fB\-\-dereference\-links\fR] [\fB\-x\fR|\fB\-\-limit\-filesystem\fR] [\fB\-s\fR|\fB\-\-apparent\-size\fR] [\fB\-r\fR|\fB\-\-reverse\fR] [\fB\-c\fR|\fB\-\-no\-colors\fR] [\fB\-C\fR|\fB\-\-force\-colors\fR] [\fB\-b\fR|\fB\-\-no\-percent\-bars\fR] [\fB\-B\fR|\fB\-\-bars\-on\-right\fR] [\fB\-z\fR|\fB\-\-min\-size\fR] [\fB\-R\fR|\fB\-\-screen\-reader\fR] [\fB\-\-skip\-total\fR] [\fB\-f\fR|\fB\-\-filecount\fR] [\fB\-i\fR|\fB\-\-ignore_hidden\fR] [\fB\-v\fR|\fB\-\-invert\-filter\fR] [\fB\-e\fR|\fB\-\-filter\fR] [\fB\-t\fR|\fB\-\-file_types\fR] [\fB\-w\fR|\fB\-\-terminal_width\fR] [\fB\-P\fR|\fB\-\-no\-progress\fR] [\fB\-\-print\-errors\fR] [\fB\-D\fR|\fB\-\-only\-dir\fR] [\fB\-F\fR|\fB\-\-only\-file\fR] [\fB\-o\fR|\fB\-\-output\-format\fR] [\fB\-S\fR|\fB\-\-stack\-size\fR] [\fB\-j\fR|\fB\-\-output\-json\fR] [\fB\-M\fR|\fB\-\-mtime\fR] [\fB\-A\fR|\fB\-\-atime\fR] [\fB\-y\fR|\fB\-\-ctime\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fIPATH\fR] +\fBdust\fR [\fB\-d\fR|\fB\-\-depth\fR] [\fB\-T\fR|\fB\-\-threads\fR] [\fB\-n\fR|\fB\-\-number\-of\-lines\fR] [\fB\-p\fR|\fB\-\-full\-paths\fR] [\fB\-X\fR|\fB\-\-ignore\-directory\fR] [\fB\-I\fR|\fB\-\-ignore\-all\-in\-file\fR] [\fB\-L\fR|\fB\-\-dereference\-links\fR] [\fB\-x\fR|\fB\-\-limit\-filesystem\fR] [\fB\-s\fR|\fB\-\-apparent\-size\fR] [\fB\-r\fR|\fB\-\-reverse\fR] [\fB\-c\fR|\fB\-\-no\-colors\fR] [\fB\-C\fR|\fB\-\-force\-colors\fR] [\fB\-b\fR|\fB\-\-no\-percent\-bars\fR] [\fB\-B\fR|\fB\-\-bars\-on\-right\fR] [\fB\-z\fR|\fB\-\-min\-size\fR] [\fB\-R\fR|\fB\-\-screen\-reader\fR] [\fB\-\-skip\-total\fR] [\fB\-f\fR|\fB\-\-filecount\fR] [\fB\-i\fR|\fB\-\-ignore_hidden\fR] [\fB\-v\fR|\fB\-\-invert\-filter\fR] [\fB\-e\fR|\fB\-\-filter\fR] [\fB\-t\fR|\fB\-\-file_types\fR] [\fB\-w\fR|\fB\-\-terminal_width\fR] [\fB\-P\fR|\fB\-\-no\-progress\fR] [\fB\-\-print\-errors\fR] [\fB\-D\fR|\fB\-\-only\-dir\fR] [\fB\-F\fR|\fB\-\-only\-file\fR] [\fB\-o\fR|\fB\-\-output\-format\fR] [\fB\-S\fR|\fB\-\-stack\-size\fR] [\fB\-j\fR|\fB\-\-output\-json\fR] [\fB\-M\fR|\fB\-\-mtime\fR] [\fB\-A\fR|\fB\-\-atime\fR] [\fB\-y\fR|\fB\-\-ctime\fR] [\fB\-\-files0\-from\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fIPATH\fR] .SH DESCRIPTION Like du but more intuitive .SH OPTIONS @@ -112,6 +112,9 @@ just like \-mtime, but based on file access time \fB\-y\fR, \fB\-\-ctime\fR just like \-mtime, but based on file change time .TP +\fB\-\-files0\-from\fR +run dust on NUL\-terminated file names specified in file; if argument is \-, then read names from standard input +.TP \fB\-h\fR, \fB\-\-help\fR Print help .TP diff --git a/src/cli.rs b/src/cli.rs index 7587cb0..a429297 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -286,4 +286,12 @@ pub fn build_cli() -> Command { .value_parser(value_parser!(String)) .help("just like -mtime, but based on file change time") ) + .arg( + Arg::new("files0_from") + .long("files0-from") + .value_hint(clap::ValueHint::AnyPath) + .value_parser(value_parser!(String)) + .num_args(1) + .help("run dust on NUL-terminated file names specified in file; if argument is -, then read names from standard input"), + ) } diff --git a/src/config.rs b/src/config.rs index b9810b9..71f3890 100644 --- a/src/config.rs +++ b/src/config.rs @@ -36,9 +36,17 @@ pub struct Config { pub threads: Option, pub output_json: Option, pub print_errors: Option, + pub files0_from: Option, } impl Config { + pub fn get_files_from(&self, options: &ArgMatches) -> Option { + let from_file = options.get_one::("files0_from"); + match from_file { + None => self.files0_from.as_ref().map(|x| x.to_string()), + Some(x) => Some(x.to_string()), + } + } pub fn get_no_colors(&self, options: &ArgMatches) -> bool { Some(true) == self.no_colors || options.get_flag("no_colors") } diff --git a/src/main.rs b/src/main.rs index e2712fb..62e70cc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,8 +21,11 @@ use regex::Error; use std::collections::HashSet; use std::env; use std::fs::read_to_string; +use std::io; use std::panic; use std::process; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering; use std::sync::Arc; use std::sync::Mutex; use sysinfo::{System, SystemExt}; @@ -119,17 +122,49 @@ fn main() { let error_listen_for_ctrlc = Arc::new(Mutex::new(errors)); let errors_for_rayon = error_listen_for_ctrlc.clone(); let errors_final = error_listen_for_ctrlc.clone(); + let is_in_listing = Arc::new(AtomicBool::new(false)); + let cloned_is_in_listing = Arc::clone(&is_in_listing); ctrlc::set_handler(move || { error_listen_for_ctrlc.lock().unwrap().abort = true; println!("\nAborting"); + if cloned_is_in_listing.load(Ordering::Relaxed) { + process::exit(1); + } }) .expect("Error setting Ctrl-C handler"); - let target_dirs = match options.get_many::("params") { - Some(values) => values.map(|v| v.as_str()).collect::>(), - None => vec!["."], + is_in_listing.store(true, Ordering::Relaxed); + let target_dirs = match config.get_files_from(&options) { + Some(path) => { + if path == "-" { + let mut targets_to_add = io::stdin() + .lines() + .map_while(Result::ok) + .collect::>(); + + if targets_to_add.is_empty() { + eprintln!("No input provided, defaulting to current directory"); + targets_to_add.push(".".to_owned()); + } + targets_to_add + } else { + // read file + match read_to_string(path) { + Ok(file_content) => file_content.lines().map(|x| x.to_string()).collect(), + Err(e) => { + eprintln!("Error reading file: {e}"); + vec![".".to_owned()] + } + } + } + } + None => match options.get_many::("params") { + Some(values) => values.cloned().collect(), + None => vec![".".to_owned()], + }, }; + is_in_listing.store(false, Ordering::Relaxed); let summarize_file_types = options.get_flag("types");