Add CLI options for individual -r features and to counter previous options

The --enable-* options also work for imported files, this fixes #120.

Most other options are not super useful on its own, but these will be
useful when there's a config file.
This commit is contained in:
Yorhel 2021-10-05 16:24:52 +02:00
parent bfead635e4
commit b3c6f0f48a
3 changed files with 90 additions and 62 deletions

100
ncdu.pod
View file

@ -57,12 +57,12 @@ directory with many files. 10.000 files will get you an export in the order of
gzip. This scales linearly, so be prepared to handle a few tens of megabytes gzip. This scales linearly, so be prepared to handle a few tens of megabytes
when dealing with millions of files. when dealing with millions of files.
=item -e =item -e, --extended, --no-extended
Enable extended information mode. This will, in addition to the usual file Enable/disable extended information mode. This will, in addition to the usual
information, also read the ownership, permissions and last modification time file information, also read the ownership, permissions and last modification
for each file. This will result in higher memory usage (by roughly ~30%) and in time for each file. This will result in higher memory usage (by roughly ~30%)
a larger output file when exporting. and in a larger output file when exporting.
When using the file export/import function, this flag will need to be added When using the file export/import function, this flag will need to be added
both when exporting (to make sure the information is added to the export), and both when exporting (to make sure the information is added to the export), and
@ -102,40 +102,48 @@ Provide a full-screen ncurses interface while scanning a directory or importing
a file. This is the only interface that provides feedback on any non-fatal a file. This is the only interface that provides feedback on any non-fatal
errors while scanning. errors while scanning.
=item -q =item -q, --slow-ui-updates, --fast-ui-updates
Quiet mode. While scanning or importing the directory, ncdu will update the Change the UI update interval while scanning or importing. Ncdu will update the
screen 10 times a second by default, this will be decreased to once every 2 screen 10 times a second by default (C<--fast-ui-updates>), this can be
seconds in quiet mode. Use this feature to save bandwidth over remote decreased to once every 2 seconds with C<-q> or C<--slow-ui-updates>. This
connections. This option has no effect when C<-0> is used. feature can be used to save bandwidth over remote connections. This option has
no effect when C<-0> is used.
=item --enable-shell, --disable-shell
Enable or disable shell spawning from the browser. This feature is enabled by
default when scanning a live directory and disabled when importing from file.
=item --enable-delete, --disable-delete
Enable or disable the built-in file deletion feature. This feature is enabled
by default when scanning a live directory and disabled when importing from
file. Explicitly disabling the deletion feature can work as a safeguard to
prevent accidental data loss.
=item --enable-refresh, --disable-refresh
Enable or disable directory refreshing from the browser. This feature is
enabled by default when scanning a live directory and disabled when importing
from file.
=item -r =item -r
Read-only mode. This will disable the built-in file deletion feature. This Read-only mode. When given once, this is an alias for C<--disable-delete>, when
option has no effect when C<-o> is used, because there will not be a browser given twice it will also add C<--disable-shell>, thus ensuring that there is no
interface in that case. It has no effect when C<-f> is used, either, because way to modify the file system from within ncdu.
the deletion feature is disabled in that case anyway.
WARNING: This option will only prevent deletion through the file browser. It is =item --si, --no-si
still possible to spawn a shell from ncdu and delete or modify files from
there. To disable that feature as well, pass the C<-r> option twice (see
C<-rr>).
=item -rr
In addition to C<-r>, this will also disable the shell spawning feature of the
file browser.
=item --si
List sizes using base 10 prefixes, that is, powers of 1000 (KB, MB, etc), as List sizes using base 10 prefixes, that is, powers of 1000 (KB, MB, etc), as
defined in the International System of Units (SI), instead of the usual base 2 defined in the International System of Units (SI), instead of the usual base 2
prefixes, that is, powers of 1024 (KiB, MiB, etc). prefixes, that is, powers of 1024 (KiB, MiB, etc).
=item --confirm-quit =item --confirm-quit, --no-confirm-quit
Requires a confirmation before quitting ncdu. Very helpful when you Require a confirmation before quitting ncdu. Very helpful when you accidentally
accidentally press 'q' during or after a very long scan. press 'q' during or after a very long scan.
=item --color I<SCHEME> =item --color I<SCHEME>
@ -155,11 +163,16 @@ directory information from a file.
=over =over
=item -x =item -x, --one-file-system
Do not cross filesystem boundaries, i.e. only count files and directories on Do not cross filesystem boundaries, i.e. only count files and directories on
the same filesystem as the directory being scanned. the same filesystem as the directory being scanned.
=item --cross-file-system
Do cross filesystem boundaries. This is the default, but can be specified to
overrule a previously given C<-x>.
=item --exclude I<PATTERN> =item --exclude I<PATTERN>
Exclude files that match I<PATTERN>. The files will still be displayed by Exclude files that match I<PATTERN>. The files will still be displayed by
@ -171,27 +184,24 @@ can be added multiple times to add more patterns.
Exclude files that match any pattern in I<FILE>. Patterns should be separated Exclude files that match any pattern in I<FILE>. Patterns should be separated
by a newline. by a newline.
=item --exclude-caches =item --include-caches, --exclude-caches
Exclude directories containing CACHEDIR.TAG. The directories will still be Include (default) or exclude directories containing CACHEDIR.TAG. The
displayed, but not their content, and they are not counted towards the disk directories will still be displayed, but their contents will not be scanned or
usage statistics. counted towards the disk usage statistics.
See http://www.brynosaurus.com/cachedir/ L<http://www.brynosaurus.com/cachedir/>
=item -L, --follow-symlinks =item -L, --follow-symlinks, --no-follow-symlinks
Follow symlinks and count the size of the file they point to. As of ncdu 1.14, Follow (or not) symlinks and count the size of the file they point to. As of
this option will not follow symlinks to directories and will count each ncdu 1.14, this option will not follow symlinks to directories and will count
symlinked file as a unique file (i.e. unlike how hard links are handled). This each symlinked file as a unique file (i.e. unlike how hard links are handled).
is subject to change in later versions. This is subject to change in later versions.
=item --exclude-firmlinks =item --include-kernfs, --exclude-kernfs
(MacOS only) Exclude firmlinks. (Linux only) Include (default) or exclude Linux pseudo filesystems, e.g. /proc
(procfs), /sys (sysfs).
=item --exclude-kernfs
(Linux only) Exclude Linux pseudo filesystems, e.g. /proc (procfs), /sys (sysfs).
The complete list of currently known pseudo filesystems is: binfmt, bpf, cgroup, The complete list of currently known pseudo filesystems is: binfmt, bpf, cgroup,
cgroup2, debug, devpts, proc, pstore, security, selinux, sys, trace. cgroup2, debug, devpts, proc, pstore, security, selinux, sys, trace.

View file

@ -729,7 +729,7 @@ pub fn draw() void {
if (main.config.imported) { if (main.config.imported) {
ui.move(0, saturateSub(ui.cols, 10)); ui.move(0, saturateSub(ui.cols, 10));
ui.addstr("[imported]"); ui.addstr("[imported]");
} else if (main.config.read_only) { } else if (!main.config.can_delete) {
ui.move(0, saturateSub(ui.cols, 10)); ui.move(0, saturateSub(ui.cols, 10));
ui.addstr("[readonly]"); ui.addstr("[readonly]");
} }
@ -840,27 +840,23 @@ pub fn keyInput(ch: i32) void {
'?' => state = .help, '?' => state = .help,
'i' => if (dir_items.items.len > 0) info.set(dir_items.items[cursor_idx], .info), 'i' => if (dir_items.items.len > 0) info.set(dir_items.items[cursor_idx], .info),
'r' => { 'r' => {
if (main.config.imported) if (!main.config.can_refresh)
message = "Directory imported from file, refreshing is disabled." message = "Directory refresh feature disabled."
else { else {
main.state = .refresh; main.state = .refresh;
scan.setupRefresh(dir_parent); scan.setupRefresh(dir_parent);
} }
}, },
'b' => { 'b' => {
if (main.config.imported) if (!main.config.can_shell)
message = "Shell feature not available for imported directories." message = "Shell feature disabled."
else if (!main.config.can_shell)
message = "Shell feature disabled in read-only mode."
else else
main.state = .shell; main.state = .shell;
}, },
'd' => { 'd' => {
if (dir_items.items.len == 0) { if (dir_items.items.len == 0) {
} else if (main.config.imported) } else if (!main.config.can_delete)
message = "Deletion feature not available for imported directories." message = "Deletion feature disabled."
else if (main.config.read_only)
message = "Deletion feature disabled in read-only mode."
else if (dir_items.items[cursor_idx]) |e| { else if (dir_items.items[cursor_idx]) |e| {
main.state = .delete; main.state = .delete;
const next = const next =

View file

@ -65,9 +65,10 @@ pub const config = struct {
pub var sort_order: SortOrder = .desc; pub var sort_order: SortOrder = .desc;
pub var sort_dirsfirst: bool = false; pub var sort_dirsfirst: bool = false;
pub var read_only: bool = false;
pub var imported: bool = false; pub var imported: bool = false;
pub var can_delete: bool = true;
pub var can_shell: bool = true; pub var can_shell: bool = true;
pub var can_refresh: bool = true;
pub var confirm_quit: bool = false; pub var confirm_quit: bool = false;
pub var confirm_delete: bool = true; pub var confirm_delete: bool = true;
pub var ignore_delete_errors: bool = false; pub var ignore_delete_errors: bool = false;
@ -266,6 +267,9 @@ pub fn main() void {
var import_file: ?[:0]const u8 = null; var import_file: ?[:0]const u8 = null;
var export_file: ?[:0]const u8 = null; var export_file: ?[:0]const u8 = null;
var has_scan_ui = false; var has_scan_ui = false;
var has_can_delete = false;
var has_can_shell = false;
var has_can_refresh = false;
_ = args.next(); // program name _ = args.next(); // program name
while (args.next()) |opt| { while (args.next()) |opt| {
if (!opt.opt) { if (!opt.opt) {
@ -276,11 +280,20 @@ pub fn main() void {
} }
if (opt.is("-h") or opt.is("-?") or opt.is("--help")) help() if (opt.is("-h") or opt.is("-?") or opt.is("--help")) help()
else if(opt.is("-v") or opt.is("-V") or opt.is("--version")) version() else if(opt.is("-v") or opt.is("-V") or opt.is("--version")) version()
else if(opt.is("-q")) config.update_delay = 2*std.time.ns_per_s else if(opt.is("-q") or opt.is("--slow-ui-updates")) config.update_delay = 2*std.time.ns_per_s
else if(opt.is("-x")) config.same_fs = true else if(opt.is("--fast-ui-updates")) config.update_delay = 100*std.time.ns_per_ms
else if(opt.is("-e")) config.extended = true else if(opt.is("-x") or opt.is("--one-file-system")) config.same_fs = true
else if(opt.is("-r") and config.read_only) config.can_shell = false else if(opt.is("--cross-file-system")) config.same_fs = false
else if(opt.is("-r")) config.read_only = true else if(opt.is("-e") or opt.is("--extended")) config.extended = true
else if(opt.is("--no-extended")) config.extended = false
else if(opt.is("-r") and !config.can_delete) config.can_shell = false
else if(opt.is("-r")) config.can_delete = false
else if(opt.is("--enable-shell")) { has_can_shell = true; config.can_shell = true; }
else if(opt.is("--disable-shell")) { has_can_shell = true; config.can_shell = false; }
else if(opt.is("--enable-delete")) { has_can_delete = true; config.can_delete = true; }
else if(opt.is("--disable-delete")) { has_can_delete = true; config.can_delete = false; }
else if(opt.is("--enable-refresh")) { has_can_refresh = true; config.can_refresh = true; }
else if(opt.is("--disable-refresh")) { has_can_refresh = true; config.can_refresh = false; }
else if(opt.is("-0")) { has_scan_ui = true; config.scan_ui = .none; } else if(opt.is("-0")) { has_scan_ui = true; config.scan_ui = .none; }
else if(opt.is("-1")) { has_scan_ui = true; config.scan_ui = .line; } else if(opt.is("-1")) { has_scan_ui = true; config.scan_ui = .line; }
else if(opt.is("-2")) { has_scan_ui = true; config.scan_ui = .full; } else if(opt.is("-2")) { has_scan_ui = true; config.scan_ui = .full; }
@ -289,14 +302,19 @@ pub fn main() void {
else if(opt.is("-f") and import_file != null) ui.die("The -f flag can only be given once.\n", .{}) else if(opt.is("-f") and import_file != null) ui.die("The -f flag can only be given once.\n", .{})
else if(opt.is("-f")) import_file = args.arg() else if(opt.is("-f")) import_file = args.arg()
else if(opt.is("--si")) config.si = true else if(opt.is("--si")) config.si = true
else if(opt.is("--no-si")) config.si = false
else if(opt.is("-L") or opt.is("--follow-symlinks")) config.follow_symlinks = true else if(opt.is("-L") or opt.is("--follow-symlinks")) config.follow_symlinks = true
else if(opt.is("--no-follow-symlinks")) config.follow_symlinks = false
else if(opt.is("--exclude")) config.exclude_patterns.append(args.arg()) catch unreachable else if(opt.is("--exclude")) config.exclude_patterns.append(args.arg()) catch unreachable
else if(opt.is("-X") or opt.is("--exclude-from")) { else if(opt.is("-X") or opt.is("--exclude-from")) {
const arg = args.arg(); const arg = args.arg();
readExcludeFile(arg) catch |e| ui.die("Error reading excludes from {s}: {s}.\n", .{ arg, ui.errorString(e) }); readExcludeFile(arg) catch |e| ui.die("Error reading excludes from {s}: {s}.\n", .{ arg, ui.errorString(e) });
} else if(opt.is("--exclude-caches")) config.exclude_caches = true } else if(opt.is("--exclude-caches")) config.exclude_caches = true
else if(opt.is("--include-caches")) config.exclude_caches = false
else if(opt.is("--exclude-kernfs")) config.exclude_kernfs = true else if(opt.is("--exclude-kernfs")) config.exclude_kernfs = true
else if(opt.is("--include-kernfs")) config.exclude_kernfs = false
else if(opt.is("--confirm-quit")) config.confirm_quit = true else if(opt.is("--confirm-quit")) config.confirm_quit = true
else if(opt.is("--no-confirm-quit")) config.confirm_quit = false
else if(opt.is("--color")) { else if(opt.is("--color")) {
const val = args.arg(); const val = args.arg();
if (std.mem.eql(u8, val, "off")) config.ui_color = .off if (std.mem.eql(u8, val, "off")) config.ui_color = .off
@ -337,6 +355,10 @@ pub fn main() void {
catch |e| ui.die("Error opening directory: {s}.\n", .{ui.errorString(e)}); catch |e| ui.die("Error opening directory: {s}.\n", .{ui.errorString(e)});
if (out_file != null) return; if (out_file != null) return;
if (config.imported and !has_can_shell) config.can_shell = false;
if (config.imported and !has_can_delete) config.can_delete = false;
if (config.imported and !has_can_refresh) config.can_refresh = false;
config.scan_ui = .full; // in case we're refreshing from the UI, always in full mode. config.scan_ui = .full; // in case we're refreshing from the UI, always in full mode.
ui.init(); ui.init();
state = .browse; state = .browse;