mirror of
https://github.com/bootandy/dust.git
synced 2025-12-08 13:50:41 -08:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3428c5c35c | ||
|
|
4fc1897678 | ||
|
|
08b9c756ee | ||
|
|
394231683d |
6
Cargo.lock
generated
6
Cargo.lock
generated
@@ -919,11 +919,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.6"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
|
||||
checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "du-dust"
|
||||
description = "A more intuitive version of du"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
authors = ["bootandy <bootandy@gmail.com>", "nebkor <code@ardent.nebcorp.com>"]
|
||||
edition = "2021"
|
||||
readme = "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
|
||||
|
||||
@@ -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]' \
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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=()
|
||||
;;
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -36,9 +36,17 @@ pub struct Config {
|
||||
pub threads: Option<usize>,
|
||||
pub output_json: Option<bool>,
|
||||
pub print_errors: Option<bool>,
|
||||
pub files0_from: Option<String>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn get_files_from(&self, options: &ArgMatches) -> Option<String> {
|
||||
let from_file = options.get_one::<String>("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")
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::fs;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
@@ -95,16 +96,20 @@ fn clean_inodes(
|
||||
|
||||
fn sort_by_inode(a: &Node, b: &Node) -> std::cmp::Ordering {
|
||||
// Sorting by inode is quicker than by sorting by name/size
|
||||
if let Some(x) = a.inode_device {
|
||||
if let Some(y) = b.inode_device {
|
||||
match (a.inode_device, b.inode_device) {
|
||||
(Some(x), Some(y)) => {
|
||||
if x.0 != y.0 {
|
||||
return x.0.cmp(&y.0);
|
||||
x.0.cmp(&y.0)
|
||||
} else if x.1 != y.1 {
|
||||
return x.1.cmp(&y.1);
|
||||
x.1.cmp(&y.1)
|
||||
} else {
|
||||
a.name.cmp(&b.name)
|
||||
}
|
||||
}
|
||||
(Some(_), None) => Ordering::Greater,
|
||||
(None, Some(_)) => Ordering::Less,
|
||||
(None, None) => a.name.cmp(&b.name),
|
||||
}
|
||||
a.name.cmp(&b.name)
|
||||
}
|
||||
|
||||
fn ignore_file(entry: &DirEntry, walk_data: &WalkData) -> bool {
|
||||
@@ -244,6 +249,7 @@ fn walk(dir: PathBuf, walk_data: &WalkData, depth: usize) -> Option<Node> {
|
||||
}
|
||||
|
||||
mod tests {
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use super::*;
|
||||
|
||||
@@ -281,4 +287,41 @@ mod tests {
|
||||
assert_eq!(clean_inodes(n.clone(), &mut inodes, true), Some(n.clone()));
|
||||
assert_eq!(clean_inodes(n.clone(), &mut inodes, true), Some(n.clone()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_total_ordering_of_sort_by_inode() {
|
||||
use std::str::FromStr;
|
||||
|
||||
let a = Node {
|
||||
name: PathBuf::from_str("a").unwrap(),
|
||||
size: 0,
|
||||
children: vec![],
|
||||
inode_device: Some((3, 66310)),
|
||||
depth: 0,
|
||||
};
|
||||
|
||||
let b = Node {
|
||||
name: PathBuf::from_str("b").unwrap(),
|
||||
size: 0,
|
||||
children: vec![],
|
||||
inode_device: None,
|
||||
depth: 0,
|
||||
};
|
||||
|
||||
let c = Node {
|
||||
name: PathBuf::from_str("c").unwrap(),
|
||||
size: 0,
|
||||
children: vec![],
|
||||
inode_device: Some((1, 66310)),
|
||||
depth: 0,
|
||||
};
|
||||
|
||||
assert_eq!(sort_by_inode(&a, &b), Ordering::Greater);
|
||||
assert_eq!(sort_by_inode(&a, &c), Ordering::Greater);
|
||||
assert_eq!(sort_by_inode(&c, &b), Ordering::Greater);
|
||||
|
||||
assert_eq!(sort_by_inode(&b, &a), Ordering::Less);
|
||||
assert_eq!(sort_by_inode(&c, &a), Ordering::Less);
|
||||
assert_eq!(sort_by_inode(&b, &c), Ordering::Less);
|
||||
}
|
||||
}
|
||||
|
||||
41
src/main.rs
41
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::<String>("params") {
|
||||
Some(values) => values.map(|v| v.as_str()).collect::<Vec<&str>>(),
|
||||
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::<Vec<String>>();
|
||||
|
||||
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::<String>("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");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user