diff --git a/ChangeLog b/ChangeLog index c6a8804..a9e047f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,12 @@ -# SPDX-FileCopyrightText: 2021-2023 Yoran Heling +# SPDX-FileCopyrightText: Yorhel # SPDX-License-Identifier: MIT +2.4 - 2024-04-21 + - Now requires Zig 0.12 + - Revert default color scheme back to 'off' + - Rewrite man page in mdoc, drop pod2man dependency + - Fix updating parent dir error status on refresh + 2.3 - 2023-08-04 - Now requires Zig 0.11 - Add --(enable|disable)-natsort options diff --git a/Makefile b/Makefile index 7a77bfd..bddcc22 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2023 Yoran Heling +# SPDX-FileCopyrightText: Yorhel # SPDX-License-Identifier: MIT # Optional semi-standard Makefile with some handy tools. @@ -9,12 +9,12 @@ ZIG ?= zig PREFIX ?= /usr/local BINDIR ?= ${PREFIX}/bin MANDIR ?= ${PREFIX}/share/man/man1 -ZIG_FLAGS ?= -Doptimize=ReleaseFast +ZIG_FLAGS ?= --release NCDU_VERSION=$(shell grep 'program_version = "' src/main.zig | sed -e 's/^.*"\(.\+\)".*$$/\1/') -.PHONY: build -build: release doc +.PHONY: build test +build: release release: $(ZIG) build ${ZIG_FLAGS} @@ -25,21 +25,13 @@ debug: clean: rm -rf zig-cache zig-out -distclean: clean - rm -f ncdu.1 - -doc: ncdu.1 - -ncdu.1: ncdu.pod src/main.zig - pod2man --center "ncdu manual" --release "ncdu-${NCDU_VERSION}" ncdu.pod >ncdu.1 - install: install-bin install-doc install-bin: release mkdir -p ${BINDIR} install -m0755 zig-out/bin/ncdu ${BINDIR}/ -install-doc: doc +install-doc: mkdir -p ${MANDIR} install -m0644 ncdu.1 ${MANDIR}/ @@ -52,10 +44,10 @@ uninstall-bin: uninstall-doc: rm -f ${MANDIR}/ncdu.1 -dist: doc +dist: rm -f ncdu-${NCDU_VERSION}.tar.gz mkdir ncdu-${NCDU_VERSION} - for f in ncdu.1 `git ls-files | grep -v ^\.gitignore`; do mkdir -p ncdu-${NCDU_VERSION}/`dirname $$f`; ln -s "`pwd`/$$f" ncdu-${NCDU_VERSION}/$$f; done + for f in `git ls-files | grep -v ^\.gitignore`; do mkdir -p ncdu-${NCDU_VERSION}/`dirname $$f`; ln -s "`pwd`/$$f" ncdu-${NCDU_VERSION}/$$f; done tar -cophzf ncdu-${NCDU_VERSION}.tar.gz --sort=name ncdu-${NCDU_VERSION} rm -rf ncdu-${NCDU_VERSION} @@ -73,14 +65,14 @@ static-%.tar.gz: CC="${ZIG} cc --target=$*"\ LD="${ZIG} cc --target=$*"\ AR="${ZIG} ar" RANLIB="${ZIG} ranlib"\ - CPPFLAGS=-D_GNU_SOURCE && make && make install.libs + CPPFLAGS=-D_GNU_SOURCE && make -j8 && make install.libs @# zig-build - cleaner approach but doesn't work, results in a dynamically linked binary. @#cd static-$* && PKG_CONFIG_LIBDIR="`pwd`/inst/pkg" zig build -Dtarget=$* @# --build-file ../build.zig --search-prefix inst/ --cache-dir zig -Drelease-fast=true @# Alternative approach, bypassing zig-build cd static-$* && ${ZIG} build-exe -target $*\ -Iinst/include -Iinst/include/ncursesw -lc inst/lib/libncursesw.a\ - --cache-dir zig-cache -static -fstrip -O ReleaseFast ../src/main.zig ../src/ncurses_refs.c + --cache-dir zig-cache -static -fstrip -O ReleaseFast ../src/main.zig cd static-$* && mv main ncdu && tar -czf ../static-$*.tar.gz ncdu rm -rf static-$* @@ -101,3 +93,8 @@ static:\ static-linux-x86 \ static-linux-aarch64 \ static-linux-arm + +test: + zig build test + mandoc -T lint ncdu.1 + reuse lint diff --git a/README.md b/README.md index 1d8d02d..766bd6b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ @@ -20,7 +20,7 @@ C version (1.x). ## Requirements -- Zig 0.11.0 +- Zig 0.12.0 - Some sort of POSIX-like OS - ncurses libraries and header files diff --git a/build.zig b/build.zig index c1df229..231c2e6 100644 --- a/build.zig +++ b/build.zig @@ -1,28 +1,31 @@ -// SPDX-FileCopyrightText: 2021-2023 Yoran Heling +// SPDX-FileCopyrightText: Yorhel // SPDX-License-Identifier: MIT const std = @import("std"); pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); - const optimize = b.standardOptimizeOption(.{}); + const optimize = b.standardOptimizeOption(.{ + .preferred_optimize_mode = .ReleaseFast, + }); const pie = b.option(bool, "pie", "Build with PIE support (by default false)") orelse false; const exe = b.addExecutable(.{ .name = "ncdu", - .root_source_file = .{ .path = "src/main.zig" }, + .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, + .link_libc = true, }); + exe.pie = pie; + exe.root_module.linkSystemLibrary("ncursesw", .{}); // https://github.com/ziglang/zig/blob/b52be973dfb7d1408218b8e75800a2da3dc69108/build.zig#L551-L554 - if (exe.target.isDarwin()) { + if (target.result.isDarwin()) { // useful for package maintainers exe.headerpad_max_install_names = true; } - linkNcurses(exe); - exe.pie = pie; b.installArtifact(exe); const run_cmd = b.addRunArtifact(exe); @@ -35,21 +38,16 @@ pub fn build(b: *std.Build) void { run_step.dependOn(&run_cmd.step); const unit_tests = b.addTest(.{ - .root_source_file = .{ .path = "src/main.zig" }, + .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, + .link_libc = true, }); - linkNcurses(unit_tests); unit_tests.pie = pie; + unit_tests.root_module.linkSystemLibrary("ncursesw", .{}); const run_unit_tests = b.addRunArtifact(unit_tests); const test_step = b.step("test", "Run unit tests"); test_step.dependOn(&run_unit_tests.step); } - -pub fn linkNcurses(compile_step: *std.Build.CompileStep) void { - compile_step.linkSystemLibrary("ncursesw"); - compile_step.linkLibC(); - compile_step.addCSourceFile(.{ .file = .{ .path = "src/ncurses_refs.c" }, .flags = &.{} }); -} diff --git a/ncdu.1 b/ncdu.1 index fa9119c..e6b3769 100644 --- a/ncdu.1 +++ b/ncdu.1 @@ -1,614 +1,512 @@ -.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.43) -.\" -.\" Standard preamble: -.\" ======================================================================== -.de Sp \" Vertical space (when we can't use .PP) -.if t .sp .5v -.if n .sp -.. -.de Vb \" Begin verbatim text -.ft CW -.nf -.ne \\$1 -.. -.de Ve \" End verbatim text -.ft R -.fi -.. -.\" Set up some character translations and predefined strings. \*(-- will -.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left -.\" double quote, and \*(R" will give a right double quote. \*(C+ will -.\" give a nicer C++. Capital omega is used to do unbreakable dashes and -.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, -.\" nothing in troff, for use with C<>. -.tr \(*W- -.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' -.ie n \{\ -. ds -- \(*W- -. ds PI pi -. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch -. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch -. ds L" "" -. ds R" "" -. ds C` "" -. ds C' "" -'br\} -.el\{\ -. ds -- \|\(em\| -. ds PI \(*p -. ds L" `` -. ds R" '' -. ds C` -. ds C' -'br\} -.\" -.\" Escape single quotes in literal strings from groff's Unicode transform. -.ie \n(.g .ds Aq \(aq -.el .ds Aq ' -.\" -.\" If the F register is >0, we'll generate index entries on stderr for -.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index -.\" entries marked with X<> in POD. Of course, you'll have to process the -.\" output yourself in some meaningful fashion. -.\" -.\" Avoid warning from groff about undefined register 'F'. -.de IX -.. -.nr rF 0 -.if \n(.g .if rF .nr rF 1 -.if (\n(rF:(\n(.g==0)) \{\ -. if \nF \{\ -. de IX -. tm Index:\\$1\t\\n%\t"\\$2" -.. -. if !\nF==2 \{\ -. nr % 0 -. nr F 2 -. \} -. \} -.\} -.rr rF -.\" -.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). -.\" Fear. Run. Save yourself. No user-serviceable parts. -. \" fudge factors for nroff and troff -.if n \{\ -. ds #H 0 -. ds #V .8m -. ds #F .3m -. ds #[ \f1 -. ds #] \fP -.\} -.if t \{\ -. ds #H ((1u-(\\\\n(.fu%2u))*.13m) -. ds #V .6m -. ds #F 0 -. ds #[ \& -. ds #] \& -.\} -. \" simple accents for nroff and troff -.if n \{\ -. ds ' \& -. ds ` \& -. ds ^ \& -. ds , \& -. ds ~ ~ -. ds / -.\} -.if t \{\ -. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" -. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' -. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' -. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' -. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' -. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' -.\} -. \" troff and (daisy-wheel) nroff accents -.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' -.ds 8 \h'\*(#H'\(*b\h'-\*(#H' -.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] -.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' -.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' -.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] -.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] -.ds ae a\h'-(\w'a'u*4/10)'e -.ds Ae A\h'-(\w'A'u*4/10)'E -. \" corrections for vroff -.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' -.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' -. \" for low resolution devices (crt and lpr) -.if \n(.H>23 .if \n(.V>19 \ -\{\ -. ds : e -. ds 8 ss -. ds o a -. ds d- d\h'-1'\(ga -. ds D- D\h'-1'\(hy -. ds th \o'bp' -. ds Th \o'LP' -. ds ae ae -. ds Ae AE -.\} -.rm #[ #] #H #V #F C -.\" ======================================================================== -.\" -.IX Title "NCDU 1" -.TH NCDU 1 "2023-08-01" "ncdu-2.3" "ncdu manual" -.\" For nroff, turn off justification. Always turn off hyphenation; it makes -.\" way too many mistakes in technical documents. -.if n .ad l -.nh -.SH "NAME" -ncdu \- NCurses Disk Usage -.SH "SYNOPSIS" -.IX Header "SYNOPSIS" -\&\fBncdu\fR [\fIoptions\fR] \fIdir\fR -.SH "DESCRIPTION" -.IX Header "DESCRIPTION" -ncdu (NCurses Disk Usage) is a curses-based version of the well-known 'du', and -provides a fast way to see what directories are using your disk space. -.SH "OPTIONS" -.IX Header "OPTIONS" -.SS "Mode Selection" -.IX Subsection "Mode Selection" -.IP "\fB\-h\fR, \fB\-\-help\fR" 4 -.IX Item "-h, --help" +.\" SPDX-FileCopyrightText: Yorhel +.\" SPDX-License-Identifier: MIT +.Dd April 20, 2024 +.Dt NCDU 1 +.Os +.Sh NAME +.Nm ncdu +.Nd NCurses Disk Usage +.Sh SYNOPSIS +.Nm +.Op Fl f Ar file +.Op Fl o Ar file +.Op Fl e , \-extended , \-no\-extended +.Op Fl \-ignore\-config +.Op Fl x , \-one\-file\-system , \-cross\-file\-system +.Op Fl \-exclude Ar pattern +.Op Fl X , \-exclude\-from Ar file +.Op Fl \-include\-caches , \-exclude\-caches +.Op Fl L , \-follow\-symlinks , \-no\-follow\-symlinks +.Op Fl \-include\-kernfs , \-exclude\-kernfs +.Op Fl 0 , 1 , 2 +.Op Fl q , \-slow\-ui\-updates , \-fast\-ui\-updates +.Op Fl \-enable\-shell , \-disable\-shell +.Op Fl \-enable\-delete , \-disable\-delete +.Op Fl \-enable\-refresh , \-disable\-refresh +.Op Fl r +.Op Fl \-si , \-no\-si +.Op Fl \-disk\-usage , \-apparent\-size +.Op Fl \-show\-hidden , \-hide\-hidden +.Op Fl \-show\-itemcount , \-hide\-itemcount +.Op Fl \-show\-mtime , \-hide\-mtime +.Op Fl \-show\-graph , \-hide\-graph +.Op Fl \-show\-percent , \-hide\-percent +.Op Fl \-graph\-style Ar hash | half\-block | eighth\-block +.Op Fl \-shared\-column Ar off | shared | unique +.Op Fl \-sort Ar column +.Op Fl \-enable\-natsort , \-disable\-natsort +.Op Fl \-group\-directories\-first , \-no\-group\-directories\-first +.Op Fl \-confirm\-quit , \-no\-confirm\-quit +.Op Fl \-confirm\-delete , \-no\-confirm\-delete +.Op Fl \-color Ar off | dark | dark-bg +.Op Ar path +.Nm +.Op Fl h , \-help +.Nm +.Op Fl v , V , \-version +.Sh DESCRIPTION +.Nm +(NCurses Disk Usage) is an interactive curses-based version of the well-known +.Xr du 1 , +and provides a fast way to see what directories are using your disk space. +.Sh OPTIONS +.Ss Mode Selection +.Bl -tag -width Ds +.It Fl h , \-help Print a short help message and quit. -.IP "\fB\-v\fR, \fB\-V\fR, \fB\-\-version\fR" 4 -.IX Item "-v, -V, --version" -Print ncdu version and quit. -.IP "\fB\-f\fR \fI\s-1FILE\s0\fR" 4 -.IX Item "-f FILE" -Load the given file, which has earlier been created with the \f(CW\*(C`\-o\*(C'\fR option. If -\&\fI\s-1FILE\s0\fR is equivalent to \f(CW\*(C`\-\*(C'\fR, the file is read from standard input. -.Sp -For the sake of preventing a screw-up, the current version of ncdu will assume -that the directory information in the imported file does not represent the -filesystem on which the file is being imported. That is, the refresh, file -deletion and shell spawning options in the browser will be disabled. -.IP "\fIdir\fR" 4 -.IX Item "dir" +.It Fl v , V , \-version +Print version and quit. +.It Fl f Ar file +Load the given file, which has earlier been created with the +.Fl o +flag. +If +.Ar file +is equivalent to '\-', the file is read from standard input. +.Pp +For the sake of preventing a screw-up, the current version of +.Nm +will assume that the directory information in the imported file does not +represent the filesystem on which the file is being imported. +That is, the refresh, file deletion and shell spawning options in the browser +will be disabled. +.It Ar dir Scan the given directory. -.IP "\fB\-o\fR \fI\s-1FILE\s0\fR" 4 -.IX Item "-o FILE" -Export all necessary information to \fI\s-1FILE\s0\fR instead of opening the browser -interface. If \fI\s-1FILE\s0\fR is \f(CW\*(C`\-\*(C'\fR, the data is written to standard output. See the -examples section below for some handy use cases. -.Sp +.It Fl o Ar file +Export all necessary information to +.Ar file +instead of opening the browser interface. +If +.Ar file +is '\-', the data is written to standard output. +See the examples section below for some handy use cases. +.Pp Be warned that the exported data may grow quite large when exporting a -directory with many files. 10.000 files will get you an export in the order of -600 to 700 KiB uncompressed, or a little over 100 KiB when compressed with -gzip. This scales linearly, so be prepared to handle a few tens of megabytes -when dealing with millions of files. -.IP "\fB\-e\fR, \fB\-\-extended\fR, \fB\-\-no\-extended\fR" 4 -.IX Item "-e, --extended, --no-extended" -Enable/disable extended information mode. This will, in addition to the usual -file information, also read the ownership, permissions and last modification -time for each file. This will result in higher memory usage (by roughly ~30%) -and in a larger output file when exporting. -.Sp -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 -when importing (to read this extra information in memory). This flag has no -effect when importing a file that has been exported without the extended -information. -.Sp +directory with many files. +10.000 files will get you an export in the order of 600 to 700 KiB +uncompressed, or a little over 100 KiB when compressed with gzip. +This scales linearly, so be prepared to handle a few tens of megabytes when +dealing with millions of files. +.It Fl e , \-extended , \-no\-extended +Enable/disable extended information mode. +This will, in addition to the usual file information, also read the ownership, +permissions and last modification time for each file. +This will result in higher memory usage (by roughly ~30%) and in a larger +output file when exporting. +.Pp +When using the file export/import function, this flag should be added both when +exporting (to make sure the information is added to the export) and when +importing (to read this extra information in memory). +This flag has no effect when importing a file that has been exported without +the extended information. +.Pp This enables viewing and sorting by the latest child mtime, or modified time, using 'm' and 'M', respectively. -.IP "\fB\-\-ignore\-config\fR" 4 -.IX Item "--ignore-config" +.It Fl \-ignore\-config Do not attempt to load any configuration files. -.SS "Scan Options" -.IX Subsection "Scan Options" -These options affect the scanning progress, and have no effect when importing +.El +.Ss Scan Options +These options affect the scanning progress, they have no effect when importing directory information from a file. -.IP "\fB\-x\fR, \fB\-\-one\-file\-system\fR" 4 -.IX Item "-x, --one-file-system" +.Bl -tag -width Ds +.It Fl x , \-one\-file\-system Do not cross filesystem boundaries, i.e. only count files and directories on the same filesystem as the directory being scanned. -.IP "\fB\-\-cross\-file\-system\fR" 4 -.IX Item "--cross-file-system" -Do cross filesystem boundaries. This is the default, but can be specified to -overrule a previously given \f(CW\*(C`\-x\*(C'\fR. -.IP "\fB\-\-exclude\fR \fI\s-1PATTERN\s0\fR" 4 -.IX Item "--exclude PATTERN" -Exclude files that match \fI\s-1PATTERN\s0\fR. The files will still be displayed by -default, but are not counted towards the disk usage statistics. This argument -can be added multiple times to add more patterns. -.IP "\fB\-X\fR \fI\s-1FILE\s0\fR, \fB\-\-exclude\-from\fR \fI\s-1FILE\s0\fR" 4 -.IX Item "-X FILE, --exclude-from FILE" -Exclude files that match any pattern in \fI\s-1FILE\s0\fR. Patterns should be separated -by a newline. -.IP "\fB\-\-include\-caches\fR, \fB\-\-exclude\-caches\fR" 4 -.IX Item "--include-caches, --exclude-caches" -Include (default) or exclude directories containing \s-1CACHEDIR.TAG.\s0 The -directories will still be displayed, but their contents will not be scanned or -counted towards the disk usage statistics. - -.IP "\fB\-L\fR, \fB\-\-follow\-symlinks\fR, \fB\-\-no\-follow\-symlinks\fR" 4 -.IX Item "-L, --follow-symlinks, --no-follow-symlinks" -Follow (or not) symlinks and count the size of the file they point to. As of -ncdu 1.14, this option will not follow symlinks to directories and will count -each symlinked file as a unique file (i.e. unlike how hard links are handled). -This is subject to change in later versions. -.IP "\fB\-\-include\-kernfs\fR, \fB\-\-exclude\-kernfs\fR" 4 -.IX Item "--include-kernfs, --exclude-kernfs" -(Linux only) Include (default) or exclude Linux pseudo filesystems, e.g. /proc -(procfs), /sys (sysfs). -.Sp +.It Fl \-cross\-file\-system +Do cross filesystem boundaries. +This is the default, but can be specified to overrule a previously configured +.Fl x . +.It Fl \-exclude Ar pattern +Exclude files that match +.Ar pattern . +The files are still displayed by default, but are not counted towards the disk +usage statistics. +This argument can be added multiple times to add more patterns. +.It Fl X , \-exclude\-from Ar file +Exclude files that match any pattern in +.Ar file . +Patterns should be separated by a newline. +.It Fl \-include\-caches , \-exclude\-caches +Include (default) or exclude directories containing +.Pa CACHEDIR.TAG . +Excluded cache directories are still displayed, but their contents will not be +scanned or counted towards the disk usage statistics. +.Lk https://bford.info/cachedir/ +.It Fl L , \-follow\-symlinks , \-no\-follow\-symlinks +Follow (or not) symlinks and count the size of the file they point to. +This option does not follow symlinks to directories and will cause each +symlinked file to count as a unique file. +This is different from how hard links are handled. +The exact counting behavior of this flag is subject to change in the future. +.It Fl \-include\-kernfs , \-exclude\-kernfs +(Linux only) Include (default) or exclude Linux pseudo filesystems such as +.Pa /proc +(procfs) and +.Pa /sys +(sysfs). +.Pp The complete list of currently known pseudo filesystems is: binfmt, bpf, cgroup, cgroup2, debug, devpts, proc, pstore, security, selinux, sys, trace. -.SS "Interface options" -.IX Subsection "Interface options" -.IP "\fB\-0\fR" 4 -.IX Item "-0" -Don't give any feedback while scanning a directory or importing a file, other -than when a fatal error occurs. Ncurses will not be initialized until the scan -is complete. When exporting the data with \f(CW\*(C`\-o\*(C'\fR, ncurses will not be -initialized at all. This option is the default when exporting to standard -output. -.IP "\fB\-1\fR" 4 -.IX Item "-1" -Similar to \f(CW\*(C`\-0\*(C'\fR, but does give feedback on the scanning progress with a single -line of output. This option is the default when exporting to a file. -.Sp +.El +.Ss Interface Options +.Bl -tag -width Ds +.It Fl 0 +Don't give any feedback while scanning a directory or importing a file, except +when a fatal error occurs. +Ncurses will not be initialized until the scan is complete. +When exporting the data with +.Fl o , +ncurses will not be initialized at all. +This option is the default when exporting to standard output. +.It Fl 1 +Similar to +.Fl 0 , +but does give feedback on the scanning progress with a single line of output. +This option is the default when exporting to a file. +.Pp In some cases, the ncurses browser interface which you'll see after the -scan/import is complete may look garbled when using this option. If you're not -exporting to a file, \f(CW\*(C`\-2\*(C'\fR is probably a better choice. -.IP "\fB\-2\fR" 4 -.IX Item "-2" -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 -errors while scanning. -.IP "\fB\-q\fR, \fB\-\-slow\-ui\-updates\fR, \fB\-\-fast\-ui\-updates\fR" 4 -.IX Item "-q, --slow-ui-updates, --fast-ui-updates" -Change the \s-1UI\s0 update interval while scanning or importing. Ncdu will update the -screen 10 times a second by default (\f(CW\*(C`\-\-fast\-ui\-updates\*(C'\fR), this can be -decreased to once every 2 seconds with \f(CW\*(C`\-q\*(C'\fR or \f(CW\*(C`\-\-slow\-ui\-updates\*(C'\fR. This -feature can be used to save bandwidth over remote connections. This option has -no effect when \f(CW\*(C`\-0\*(C'\fR is used. -.IP "\fB\-\-enable\-shell\fR, \fB\-\-disable\-shell\fR" 4 -.IX 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. -.IP "\fB\-\-enable\-delete\fR, \fB\-\-disable\-delete\fR" 4 -.IX 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. -.IP "\fB\-\-enable\-refresh\fR, \fB\-\-disable\-refresh\fR" 4 -.IX 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. -.IP "\fB\-r\fR" 4 -.IX Item "-r" -Read-only mode. When given once, this is an alias for \f(CW\*(C`\-\-disable\-delete\*(C'\fR, when -given twice it will also add \f(CW\*(C`\-\-disable\-shell\*(C'\fR, thus ensuring that there is no -way to modify the file system from within ncdu. -.IP "\fB\-\-si\fR, \fB\-\-no\-si\fR" 4 -.IX Item "--si, --no-si" -List sizes using base 10 prefixes, that is, powers of 1000 (\s-1KB, MB,\s0 etc), as -defined in the International System of Units (\s-1SI\s0), instead of the usual base 2 -prefixes, that is, powers of 1024 (KiB, MiB, etc). -.IP "\fB\-\-disk\-usage\fR, \fB\-\-apparent\-size\fR" 4 -.IX Item "--disk-usage, --apparent-size" -Select whether to display disk usage (default) or apparent sizes. Can also be -toggled in the browser with the 'a' key. -.IP "\fB\-\-show\-hidden\fR, \fB\-\-hide\-hidden\fR" 4 -.IX Item "--show-hidden, --hide-hidden" -Show (default) or hide \*(L"hidden\*(R" and excluded files. Can also be toggled in the -browser with the 'e' key. -.IP "\fB\-\-show\-itemcount\fR, \fB\-\-hide\-itemcount\fR" 4 -.IX Item "--show-itemcount, --hide-itemcount" -Show or hide (default) the item counts column. Can also be toggled in the -browser with the 'c' key. -.IP "\fB\-\-show\-mtime\fR, \fB\-\-hide\-mtime\fR" 4 -.IX Item "--show-mtime, --hide-mtime" -Show or hide (default) the last modification time column. Can also be toggled -in the browser with the 'm' key. This option is ignored when not in extended -mode (see \f(CW\*(C`\-e\*(C'\fR). -.IP "\fB\-\-show\-graph\fR, \fB\-\-hide\-graph\fR" 4 -.IX Item "--show-graph, --hide-graph" -Show (default) or hide the relative size bar column. Can also be toggled in the -browser with the 'g' key. -.IP "\fB\-\-show\-percent\fR, \fB\-\-hide\-percent\fR" 4 -.IX Item "--show-percent, --hide-percent" -Show (default) or hide the relative size percent column. Can also be toggled in -the browser with the 'g' key. -.IP "\fB\-\-graph\-style\fR \fI\s-1OPTION\s0\fR" 4 -.IX Item "--graph-style OPTION" -Change the way that the relative size bar column is drawn. Recognized values -are \fIhash\fR to draw \s-1ASCII\s0 \f(CW\*(C`#\*(C'\fR characters (default and most portable), -\&\fIhalf-block\fR to use half-block drawing characters or \fIeighth-block\fR to use -eighth-block drawing characters. Eighth-block characters are the most precise -but may not render correctly in all terminals. -.IP "\fB\-\-shared\-column\fR \fI\s-1OPTION\s0\fR" 4 -.IX Item "--shared-column OPTION" -Set to \fIoff\fR to disable the shared size column for directories, \fIshared\fR -(default) to display shared directory sizes as a separate column or \fIunique\fR -to display unique directory sizes as a separate column. These options can also -be cycled through in the browser with the 'u' key. -.IP "\fB\-\-sort\fR \fI\s-1COLUMN\s0\fR" 4 -.IX Item "--sort COLUMN" -Change the default column to sort on. Accepted values are \fIdisk-usage\fR (the -default), \fIname\fR, \fIapparent-size\fR, \fIitemcount\fR or \fImtime\fR. The latter only -makes sense in extended mode, see \f(CW\*(C`\-e\*(C'\fR. -.Sp -The column can be suffixed with \fI\-asc\fR or \fI\-desc\fR to set the order to -ascending or descending, respectively. e.g. \f(CW\*(C`\-\-sort=name\-desc\*(C'\fR will sort by -name in descending order. -.IP "\fB\-\-enable\-natsort\fR, \fB\-\-disable\-natsort\fR" 4 -.IX Item "--enable-natsort, --disable-natsort" +scan/import is complete may look garbled when using this option. +If you're not exporting to a file, +.Fl 2 +is usually a better choice. +.It Fl 2 +Show 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 errors while +scanning. +.It Fl q , \-slow\-ui\-updates , \-fast\-ui\-updates +Change the UI update interval while scanning or importing. +.Nm +updates the screen 10 times a second by default (with +.Fl \-fast\-ui\-updates +), this can be decreased to once every 2 seconds with +.Fl q +or +.Fl \-slow\-ui\-updates . +This option can be used to save bandwidth over remote connections. +This option has no effect in combination with +.Fl 0 . +.It Fl \-enable\-shell , \-disable\-shell +Enable or disable shell spawning from the file browser. +This feature is enabled by default when scanning a live directory and disabled +when importing from file. +.It Fl \-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. +.It Fl \-enable\-refresh , \-disable\-refresh +Enable or disable directory refreshing from the file browser. +This feature is enabled by default when scanning a live directory and disabled +when importing from file. +.It Fl r +Read-only mode. +When given once, this is an alias for +.Fl \-disable\-delete , +when given twice it will also add +.Fl \-disable\-shell , +thus ensuring that there is no way to modify the file system from within +.Nm . +.It Fl \-si , \-no\-si +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 +prefixes (KiB, MiB, etc). +.It Fl \-disk\-usage , \-apparent\-size +Select whether to display disk usage (default) or apparent sizes. +Can also be toggled in the file browser with the 'a' key. +.It Fl \-show\-hidden , \-hide\-hidden +Show (default) or hide "hidden" and excluded files. +Can also be toggled in the file browser with the 'e' key. +.It Fl \-show\-itemcount , \-hide\-itemcount +Show or hide (default) the item counts column. +Can also be toggled in the file browser with the 'c' key. +.It Fl \-show\-mtime , \-hide\-mtime +Show or hide (default) the last modification time column. +Can also be toggled in the file browser with the 'm' key. +This option is ignored when not in extended mode, see +.Fl e . +.It Fl \-show\-graph , \-hide\-graph +Show (default) or hide the relative size bar column. +Can also be toggled in the file browser with the 'g' key. +.It Fl \-show\-percent , \-hide\-percent +Show (default) or hide the relative size percent column. +Can also be toggled in the file browser with the 'g' key. +.It Fl \-graph\-style Ar hash | half\-block | eighth\-block +Change the way that the relative size bar column is drawn. +Recognized values are +.Ar hash +to draw ASCII '#' characters (default and most portable), +.Ar half\-block +to use half-block drawing characters or +.Ar eighth\-block +to use eighth-block drawing characters. +Eighth-block characters are the most precise but may not render correctly in +all terminals. +.It Fl \-shared\-column Ar off | shared | unique +Set to +.Ar off +to disable the shared size column for directories, +.Ar shared +(default) to display shared directory sizes as a separate column or +.Ar unique +to display unique directory sizes as a separate column. +These options can also be cycled through in the file browser with the 'u' key. +.It Fl \-sort Ar column +Change the default column to sort on. +Accepted values are +.Ar disk\-usage +(the default), +.Ar name , apparent\-size , itemcount +or +.Ar mtime . +The latter only makes sense in extended mode, see +.Fl e . +.Pp +The column name can be suffixed with +.Li \-asc +or +.Li \-desc +to change the order to ascending or descending, respectively. +For example, +.Li \-\-sort=name\-desc +to sort by name in descending order. +.It Fl \-enable\-natsort , \-disable\-natsort Enable (default) or disable natural sort when sorting by file name. -.IP "\fB\-\-group\-directories\-first\fR, \fB\-\-no\-group\-directories\-first\fR" 4 -.IX Item "--group-directories-first, --no-group-directories-first" +.It Fl \-group\-directories\-first , \-no\-group\-directories\-first Sort (or not) directories before files. -.IP "\fB\-\-confirm\-quit\fR, \fB\-\-no\-confirm\-quit\fR" 4 -.IX Item "--confirm-quit, --no-confirm-quit" -Require a confirmation before quitting ncdu. Very helpful when you accidentally -press 'q' during or after a very long scan. -.IP "\fB\-\-confirm\-delete\fR, \fB\-\-no\-confirm\-delete\fR" 4 -.IX Item "--confirm-delete, --no-confirm-delete" -Require a confirmation before deleting a file or directory. Enabled by default, -but can be disabled if you're absolutely sure you won't accidentally press 'd'. -.IP "\fB\-\-color\fR \fI\s-1SCHEME\s0\fR" 4 -.IX Item "--color SCHEME" -Select a color scheme. The following schemes are recognized: \fIoff\fR to disable -colors, \fIdark\fR for a color scheme intended for dark backgrounds and \fIdark-bg\fR -for a variation of the \fIdark\fR color scheme that also works in terminals with a -light background. -.Sp -The default is \fIdark-bg\fR unless the \f(CW\*(C`NO_COLOR\*(C'\fR environment variable is set. -.SH "CONFIGURATION" -.IX Header "CONFIGURATION" -Ncdu can be configured by placing command-line options in \f(CW\*(C`/etc/ncdu.conf\*(C'\fR or -\&\f(CW\*(C`$HOME/.config/ncdu/config\*(C'\fR. If both files exist, the system configuration -will be loaded before the user configuration, allowing users to override -options set in the system configuration. Options given on the command line will -override options set in the configuration files. The files will not be read at -all when \f(CW\*(C`\-\-ignore\-config\*(C'\fR is given on the command line. -.PP -The configuration file format is simply one command line option per line. Lines -starting with \f(CW\*(C`#\*(C'\fR are ignored. Example configuration file: -.PP -.Vb 2 -\& # Always enable extended mode -\& \-e -\& -\& # Disable file deletion -\& \-\-disable\-delete -\& -\& # Exclude .git directories -\& \-\-exclude .git -.Ve -.SH "KEYS" -.IX Header "KEYS" -.IP "\fB?\fR" 4 -.IX Item "?" -Show help + keys + about screen -.IP "\fBup\fR, \fBdown\fR, \fBj\fR, \fBk\fR" 4 -.IX Item "up, down, j, k" +.It Fl \-confirm\-quit , \-no\-confirm\-quit +Require a confirmation before quitting ncdu. +Can be helpful when you accidentally press 'q' during or after a very long scan. +.It Fl \-confirm\-delete , \-no\-confirm\-delete +Require a confirmation before deleting a file or directory. +Enabled by default, but can be disabled if you're absolutely sure you won't +accidentally press 'd'. +.It Fl \-color Ar off | dark | dark-bg +Set the color scheme. +The following schemes are recognized: +.Ar off +to disable colors, +.Ar dark +for a color scheme intended for dark backgrounds and +.Ar dark\-bg +for a variation of the +.Ar dark +color scheme that also works in terminals with a light background. +.Pp +The default is +.Ar off . +.El +.Sh CONFIGURATION +.Nm +can be configured by placing command-line options in +.Pa /etc/ncdu.conf +or +.Pa $HOME/.config/ncdu/config . +If both files exist, the system configuration will be loaded before the user +configuration, allowing users to override options set in the system +configuration. +Options given on the command line will override options set in the +configuration files. +The files will not be read at all when +.Fl \-ignore\-config +is given on the command line. +.Pp +The configuration file format is simply one command line option per line. +Lines starting with '#' are ignored. +Example configuration file: +.Bd -literal -offset indent +# Always enable extended mode +\-e + +# Disable file deletion +\-\-disable\-delete + +# Exclude .git directories +\-\-exclude .git +.Ed +.Sh KEYS +.Bl -tag -width Ds +.It ? +Open help + keys + about screen +.It up , down , j , k Cycle through the items -.IP "\fBright\fR, \fBenter\fR, \fBl\fR" 4 -.IX Item "right, enter, l" +.It right, enter, l Open selected directory -.IP "\fBleft\fR, \fB<\fR, \fBh\fR" 4 -.IX Item "left, <, h" +.It left, <, h Go to parent directory -.IP "\fBn\fR" 4 -.IX Item "n" +.It n Order by filename (press again for descending order) -.IP "\fBs\fR" 4 -.IX Item "s" +.It s Order by filesize (press again for descending order) -.IP "\fBC\fR" 4 -.IX Item "C" +.It C Order by number of items (press again for descending order) -.IP "\fBa\fR" 4 -.IX Item "a" +.It a Toggle between showing disk usage and showing apparent size. -.IP "\fBM\fR" 4 -.IX Item "M" -Order by latest child mtime, or modified time. (press again for descending order) -Requires the \-e flag. -.IP "\fBd\fR" 4 -.IX Item "d" -Delete the selected file or directory. An error message will be shown when the -contents of the directory do not match or do not exist anymore on the -filesystem. -.IP "\fBt\fR" 4 -.IX Item "t" +.It M +Order by latest child mtime, or modified time (press again for descending +order). +Requires the +.Fl e +flag. +.It d +Delete the selected file or directory. +An error message will be shown when the contents of the directory do not match +or do not exist anymore on the filesystem. +.It t Toggle dirs before files when sorting. -.IP "\fBg\fR" 4 -.IX Item "g" -Toggle between showing percentage, graph, both, or none. Percentage is relative -to the size of the current directory, graph is relative to the largest item in -the current directory. -.IP "\fBu\fR" 4 -.IX Item "u" +.It g +Toggle between showing percentage, graph, both, or none. +Percentage is relative to the size of the current directory, graph is relative +to the largest item in the current directory. +.It u Toggle display of the shared / unique size column for directories that share -hard links. This column is only visible if the current listing contains -directories with shared hard links. -.IP "\fBc\fR" 4 -.IX Item "c" +hard links. +This column is only visible if the current listing contains directories with +shared hard links. +.It c Toggle display of child item counts. -.IP "\fBm\fR" 4 -.IX Item "m" -Toggle display of latest child mtime, or modified time. Requires the \-e flag. -.IP "\fBe\fR" 4 -.IX Item "e" -Show/hide 'hidden' or 'excluded' files and directories. Please note that even -though you can't see the hidden files and directories, they are still there and -they are still included in the directory sizes. If you suspect that the totals -shown at the bottom of the screen are not correct, make sure you haven't -enabled this option. -.IP "\fBi\fR" 4 -.IX Item "i" +.It m +Toggle display of latest child mtime, or modified time. +Requires the +.Fl e +flag. +.It e +Show/hide 'hidden' or 'excluded' files and directories. +Be aware that even if you can't see the hidden files and directories, they are +still there and they are still included in the directory sizes. +If you suspect that the totals shown at the bottom of the screen are not +correct, make sure you haven't enabled this option. +.It i Show information about the current selected item. -.IP "\fBr\fR" 4 -.IX Item "r" +.It r Refresh/recalculate the current directory. -.IP "\fBb\fR" 4 -.IX Item "b" +.It b Spawn shell in current directory. -.Sp -Ncdu will determine your preferred shell from the \f(CW\*(C`NCDU_SHELL\*(C'\fR or \f(CW\*(C`SHELL\*(C'\fR -variable (in that order), or will call \f(CW\*(C`/bin/sh\*(C'\fR if neither are set. This -allows you to also configure another command to be run when he 'b' key is -pressed. For example, to spawn the \fBvifm\fR\|(1) file manager instead of a shell, -run ncdu as follows: -.Sp -.Vb 2 -\& export NCDU_SHELL=vifm -\& ncdu -.Ve -.Sp -Ncdu will set the \f(CW\*(C`NCDU_LEVEL\*(C'\fR environment variable or increment it before -spawning the shell. This variable allows you to detect when your shell is -running from within ncdu, which can be useful to avoid nesting multiple -instances of ncdu. Ncdu itself does not (currently) warn when attempting to run -nested instances. -.IP "\fBq\fR" 4 -.IX Item "q" +.Pp +.Nm +determines your preferred shell from the +.Ev NCDU_SHELL +or +.Ev SHELL +environment variable (in that order), or calls +.Pa /bin/sh +if neither are set. +This allows you to also configure another command to be run when he 'b' key is +pressed. +For example, to spawn the +.Xr vifm 1 +file manager instead of a shell, run +.Nm +as follows: +.Dl NCDU_SHELL=vifm ncdu +The +.Ev NCDU_LEVEL +environment variable is set or incremented before spawning the shell, allowing +you to detect if your shell is running from within +.Nm . +This can be useful to avoid nesting multiple instances, although +.Nm +itself does not (currently) warn about or prevent this situation. +.It q Quit -.SH "FILE FLAGS" -.IX Header "FILE FLAGS" -Entries in the browser interface may be prefixed by a one-character flag. These -flags have the following meaning: -.IP "\fB!\fR" 4 -.IX Item "!" +.El +.Sh FILE FLAGS +Entries in the browser interface may be prefixed by a one\-character flag. +These flags have the following meaning: +.Bl -tag -width Ds +.It ! An error occurred while reading this directory. -.IP "\fB.\fR" 4 -.IX Item "." -An error occurred while reading a subdirectory, so the indicated size may not be -correct. -.IP "\fB<\fR" 4 -.IX Item "<" +.It \. +An error occurred while reading a subdirectory, so the indicated size may not +be correct. +.It < File or directory is excluded from the statistics by using exclude patterns. -.IP "\fB>\fR" 4 -.IX Item ">" +.It > Directory is on another filesystem. -.IP "\fB^\fR" 4 -.IX Item "^" -Directory is excluded from the statistics due to being a Linux pseudo filesystem. -.IP "\fB@\fR" 4 -.IX Item "@" +.It ^ +Directory is excluded from the statistics due to being a Linux pseudo +filesystem. +.It @ This is neither a file nor a folder (symlink, socket, ...). -.IP "\fBH\fR" 4 -.IX Item "H" +.It H Same file was already counted (hard link). -.IP "\fBe\fR" 4 -.IX Item "e" +.It e Empty directory. -.SH "EXAMPLES" -.IX Header "EXAMPLES" +.El +.Sh EXAMPLES To scan and browse the directory you're currently in, all you need is a simple: -.PP -.Vb 1 -\& ncdu -.Ve -.PP -If you want to scan a full filesystem, your root filesystem, for example, then -you'll want to use \f(CW\*(C`\-x\*(C'\fR: -.PP -.Vb 1 -\& ncdu \-x / -.Ve -.PP +.Dl ncdu +If you want to scan a full filesystem, for example your root filesystem, then +you'll want to use +.Fl x : +.Dl ncdu \-x / +.Pp Since scanning a large directory may take a while, you can scan a directory and export the results for later viewing: -.PP -.Vb 3 -\& ncdu \-1xo\- / | gzip >export.gz -\& # ...some time later: -\& zcat export.gz | ncdu \-f\- -.Ve -.PP -To export from a cron job, make sure to replace \f(CW\*(C`\-1\*(C'\fR with \f(CW\*(C`\-0\*(C'\fR to suppress -any unnecessary output. -.PP +.Bd -literal -offset indent +ncdu \-1xo\- / | gzip >export.gz +# ...some time later: +zcat export.gz | ncdu \-f\- +.Ed +To export from a cron job, make sure to replace +.Fl 1 +with +.Fl 0 +to suppress any unnecessary output. +.Pp You can also export a directory and browse it once scanning is done: -.PP -.Vb 1 -\& ncdu \-o\- | tee export.file | ./ncdu \-f\- -.Ve -.PP +.Dl ncdu \-o\- | tee export.file | ./ncdu \-f\- The same is possible with gzip compression, but is a bit kludgey: -.PP -.Vb 1 -\& ncdu \-o\- | gzip | tee export.gz | gunzip | ./ncdu \-f\- -.Ve -.PP +.Dl ncdu \-o\- | gzip | tee export.gz | gunzip | ./ncdu \-f\- +.Pp To scan a system remotely, but browse through the files locally: -.PP -.Vb 1 -\& ssh \-C user@system ncdu \-o\- / | ./ncdu \-f\- -.Ve -.PP -The \f(CW\*(C`\-C\*(C'\fR option to ssh enables compression, which will be very useful over -slow links. Remote scanning and local viewing has two major advantages when -compared to running ncdu directly on the remote system: You can browse through -the scanned directory on the local system without any network latency, and ncdu -does not keep the entire directory structure in memory when exporting, so you +.Dl ssh \-C user@system ncdu \-o\- / | ./ncdu \-f\- +The +.Fl C +option to ssh enables compression, which will be very useful over slow links. +Remote scanning and local viewing has two major advantages when +compared to running +.Nm +directly on the remote system: You can browse through the scanned directory on +the local system without any network latency, and +.Nm +does not keep the entire directory structure in memory when exporting, so this won't consume much memory on the remote system. -.SH "HARD LINKS" -.IX Header "HARD LINKS" -Every disk usage analysis utility has its own way of (not) counting hard links. -There does not seem to be any universally agreed method of handling hard links, -and it is even inconsistent among different versions of ncdu. This section -explains what each version of ncdu does. -.PP -ncdu 1.5 and below does not support any hard link detection at all: each link -is considered a separate inode and its size is counted for every link. This -means that the displayed directory sizes are incorrect when analyzing -directories which contain hard links. -.PP -ncdu 1.6 has basic hard link detection: When a link to a previously encountered -inode is detected, the link is considered to have a file size of zero bytes. -Its size is not counted again, and the link is indicated in the browser -interface with a 'H' mark. The displayed directory sizes are only correct when -all links to an inode reside within that directory. When this is not the case, -the sizes may or may not be correct, depending on which links were considered -as \*(L"duplicate\*(R" and which as \*(L"original\*(R". The indicated size of the topmost -directory (that is, the one specified on the command line upon starting ncdu) -is always correct. -.PP -ncdu 1.7 and later has improved hard link detection. Each file that has more -than two links has the \*(L"H\*(R" mark visible in the browser interface. Each hard -link is counted exactly once for every directory it appears in. The indicated -size of each directory is therefore, correctly, the sum of the sizes of all -unique inodes that can be found in that directory. Note, however, that this may -not always be same as the space that will be reclaimed after deleting the -directory, as some inodes may still be accessible from hard links outside it. -.SH "BUGS" -.IX Header "BUGS" -Directory hard links and firmlinks (MacOS) are not supported. They will not be -detected as being hard links, and may thus be scanned and counted multiple -times. -.PP +.Sh SEE ALSO +.Xr du 1 , +.Xr tree 1 . +.Pp +.Nm +has a website: +.Lk https://dev.yorhel.nl/ncdu +.Sh AUTHORS +Written by +.An Yorhel Aq Mt projects@yorhel.nl +.Sh BUGS +Directory hard links and firmlinks (MacOS) are not supported. +They are not detected as being hard links, and will thus get scanned and +counted multiple times. +.Pp Some minor glitches may appear when displaying filenames that contain multibyte or multicolumn characters. -.PP +.Pp The unique and shared directory sizes are calculated based on the assumption that the link count of hard links does not change during a filesystem scan or -in between refreshes. If it does, for example after deleting a hard link, then -these numbers will be very much incorrect and a full refresh by restarting ncdu -is needed to get correct numbers again. -.PP -All sizes are internally represented as a signed 64bit integer. If you have a -directory larger than 8 EiB minus one byte, ncdu will clip its size to 8 EiB -minus one byte. When deleting or refreshing items in a directory with a clipped -size, the resulting sizes will be incorrect. Likewise, item counts are stored -in a 32\-bit integer, so will be incorrect in the unlikely event that you happen -to have more than 4 billion items in a directory. -.PP +in between refreshes. +If this does happen, for example when a hard link is deleted, then these +numbers will be very much incorrect and a full refresh by restarting ncdu is +needed to get correct numbers again. +.Pp +All sizes are internally represented as a signed 64bit integer. +If you have a directory larger than 8 EiB minus one byte, ncdu will clip its +size to 8 EiB minus one byte. +When deleting or refreshing items in a directory with a clipped size, the +resulting sizes will be incorrect. +Likewise, item counts are stored in a 32-bit integer, so will be incorrect in +the unlikely event that you happen to have more than 4 billion items in a +directory. +.Pp Please report any other bugs you may find at the bug tracker, which can be -found on the web site at https://dev.yorhel.nl/ncdu -.SH "AUTHOR" -.IX Header "AUTHOR" -Written by Yoran Heling . -.SH "SEE ALSO" -.IX Header "SEE ALSO" -\&\fBdu\fR\|(1) +found on the web site at +.Lk https://dev.yorhel.nl/ncdu diff --git a/src/browser.zig b/src/browser.zig index 2b8c169..be4190b 100644 --- a/src/browser.zig +++ b/src/browser.zig @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021-2023 Yoran Heling +// SPDX-FileCopyrightText: Yorhel // SPDX-License-Identifier: MIT const std = @import("std"); @@ -349,8 +349,8 @@ const info = struct { var links_idx: usize = 0; fn lt(_: void, a: *model.Link, b: *model.Link) bool { - var pa = a.path(false); - var pb = b.path(false); + const pa = a.path(false); + const pb = b.path(false); defer main.allocator.free(pa); defer main.allocator.free(pb); return std.mem.lessThan(u8, pa, pb); @@ -670,7 +670,7 @@ const help = struct { ui.style(.num); box.move(7, 43); ui.addstr(main.program_version); ui.style(.default); - box.move(9, 9); ui.addstr("Written by Yoran Heling "); + box.move(9, 11); ui.addstr("Written by Yorhel "); box.move(10,16); ui.addstr("https://dev.yorhel.nl/ncdu"); } diff --git a/src/delete.zig b/src/delete.zig index c59aca8..ae262a8 100644 --- a/src/delete.zig +++ b/src/delete.zig @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021-2023 Yoran Heling +// SPDX-FileCopyrightText: Yorhel // SPDX-License-Identifier: MIT const std = @import("std"); @@ -45,7 +45,7 @@ fn deleteItem(dir: std.fs.Dir, path: [:0]const u8, ptr: *align(1) ?*model.Entry) return true; if (entry.dir()) |d| { - var fd = dir.openDirZ(path, .{.no_follow = true}, false) catch |e| return err(e); + var fd = dir.openDirZ(path, .{ .no_follow = true, .iterate = false }) catch |e| return err(e); var it = &d.sub; parent = d; defer parent = parent.parent.?; diff --git a/src/exclude.zig b/src/exclude.zig index 8470d2f..391426b 100644 --- a/src/exclude.zig +++ b/src/exclude.zig @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021-2023 Yoran Heling +// SPDX-FileCopyrightText: Yorhel // SPDX-License-Identifier: MIT const std = @import("std"); @@ -56,7 +56,7 @@ const Pattern = struct { fn parse(pat_: []const u8) *const Pattern { var pat = std.mem.trimLeft(u8, pat_, "/"); - var top = main.allocator.create(Pattern) catch unreachable; + const top = main.allocator.create(Pattern) catch unreachable; var tail = top; tail.sub = null; while (std.mem.indexOfScalar(u8, pat, '/')) |idx| { @@ -147,7 +147,7 @@ fn PatternList(comptime withsub: bool) type { fn append(self: *Self, pat: *const Pattern) void { std.debug.assert((pat.sub != null) == withsub); if (pat.isliteral) { - var e = self.literals.getOrPut(main.allocator, pat) catch unreachable; + const e = self.literals.getOrPut(main.allocator, pat) catch unreachable; if (!e.found_existing) { e.key_ptr.* = pat; e.value_ptr.* = if (withsub) .{} else {}; @@ -250,16 +250,16 @@ pub fn getPatterns(path_: []const u8) Patterns { var pat = root; defer pat.deinit(); while (std.mem.indexOfScalar(u8, path, '/')) |idx| { - var name = main.allocator.dupeZ(u8, path[0..idx]) catch unreachable; + const name = main.allocator.dupeZ(u8, path[0..idx]) catch unreachable; defer main.allocator.free(name); path = path[idx+1..]; - var sub = pat.enter(name); + const sub = pat.enter(name); pat.deinit(); pat = sub; } - var name = main.allocator.dupeZ(u8, path) catch unreachable; + const name = main.allocator.dupeZ(u8, path) catch unreachable; defer main.allocator.free(name); return pat.enter(name); } diff --git a/src/main.zig b/src/main.zig index d9f9238..c55626d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,7 +1,7 @@ -// SPDX-FileCopyrightText: 2021-2023 Yoran Heling +// SPDX-FileCopyrightText: Yorhel // SPDX-License-Identifier: MIT -pub const program_version = "2.3"; +pub const program_version = "2.4"; const std = @import("std"); const model = @import("model.zig"); @@ -287,7 +287,7 @@ fn tryReadArgsFile(path: [:0]const u8) void { error.EndOfStream => if (line_fbs.getPos() catch unreachable == 0) break, else => |e| ui.die("Error reading from {s}: {s}\nRun with --ignore-config to skip reading config files.\n", .{ path, ui.errorString(e) }), }; - var line_ = line_fbs.getWritten(); + const line_ = line_fbs.getWritten(); var line = std.mem.trim(u8, line_, &std.ascii.whitespace); if (line.len == 0 or line[0] == '#') continue; @@ -365,7 +365,7 @@ fn spawnShell() void { else env.put("NCDU_LEVEL", "1") catch unreachable; - const shell = std.os.getenvZ("NCDU_SHELL") orelse std.os.getenvZ("SHELL") orelse "/bin/sh"; + const shell = std.posix.getenvZ("NCDU_SHELL") orelse std.posix.getenvZ("SHELL") orelse "/bin/sh"; var child = std.process.Child.init(&.{shell}, allocator); child.cwd = path.items; child.env_map = &env; @@ -433,7 +433,6 @@ pub fn main() void { config.thousands_sep = span; } } - if (std.os.getenvZ("NO_COLOR") == null) config.ui_color = .darkbg; const loadConf = blk: { var args = std.process.ArgIteratorPosix.init(); @@ -446,12 +445,12 @@ pub fn main() void { if (loadConf) { tryReadArgsFile("/etc/ncdu.conf"); - if (std.os.getenvZ("XDG_CONFIG_HOME")) |p| { - var path = std.fs.path.joinZ(allocator, &.{p, "ncdu", "config"}) catch unreachable; + if (std.posix.getenvZ("XDG_CONFIG_HOME")) |p| { + const path = std.fs.path.joinZ(allocator, &.{p, "ncdu", "config"}) catch unreachable; defer allocator.free(path); tryReadArgsFile(path); - } else if (std.os.getenvZ("HOME")) |p| { - var path = std.fs.path.joinZ(allocator, &.{p, ".config", "ncdu", "config"}) catch unreachable; + } else if (std.posix.getenvZ("HOME")) |p| { + const path = std.fs.path.joinZ(allocator, &.{p, ".config", "ncdu", "config"}) catch unreachable; defer allocator.free(path); tryReadArgsFile(path); } @@ -460,8 +459,9 @@ pub fn main() void { var scan_dir: ?[]const u8 = null; var import_file: ?[:0]const u8 = null; var export_file: ?[:0]const u8 = null; + var quit_after_scan = false; { - var arglist = std.process.argsAlloc(allocator) catch unreachable; + const arglist = std.process.argsAlloc(allocator) catch unreachable; defer std.process.argsFree(allocator, arglist); var args = Args.init(arglist); _ = args.next(); // program name @@ -479,6 +479,7 @@ 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")) import_file = allocator.dupeZ(u8, args.arg()) catch unreachable else if (opt.is("--ignore-config")) {} + else if (opt.is("--quit-after-scan")) quit_after_scan = true // undocumented feature to help with benchmarking scan/import else if (argConfig(&args, opt)) {} else ui.die("Unrecognized option '{s}'.\n", .{opt.val}); } @@ -504,7 +505,7 @@ pub fn main() void { event_delay_timer = std.time.Timer.start() catch unreachable; defer ui.deinit(); - var out_file = if (export_file) |f| ( + const out_file = if (export_file) |f| ( if (std.mem.eql(u8, f, "-")) stdout else std.fs.cwd().createFileZ(f, .{}) catch |e| ui.die("Error opening export file: {s}.\n", .{ui.errorString(e)}) @@ -515,7 +516,7 @@ pub fn main() void { config.imported = true; } else scan.scanRoot(scan_dir orelse ".", out_file) catch |e| ui.die("Error opening directory: {s}.\n", .{ui.errorString(e)}); - if (out_file != null) return; + if (quit_after_scan or out_file != null) return; config.can_shell = config.can_shell orelse !config.imported; config.can_delete = config.can_delete orelse !config.imported; @@ -571,7 +572,7 @@ pub fn handleEvent(block: bool, force_draw: bool) void { var firstblock = block; while (true) { - var ch = ui.getch(firstblock); + const ch = ui.getch(firstblock); if (ch == 0) return; if (ch == -1) return handleEvent(firstblock, true); switch (state) { diff --git a/src/model.zig b/src/model.zig index bf44638..66e8400 100644 --- a/src/model.zig +++ b/src/model.zig @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021-2023 Yoran Heling +// SPDX-FileCopyrightText: Yorhel // SPDX-License-Identifier: MIT const std = @import("std"); @@ -109,16 +109,11 @@ pub const Entry = extern struct { }; } - // Set the 'err' flag on Dirs and Files, propagating 'suberr' to parents. - pub fn setErr(self: *Self, parent: *Dir) void { - if (self.dir()) |d| d.pack.err = true - else if (self.file()) |f| f.pack.err = true - else unreachable; - var it: ?*Dir = if (&parent.entry == self) parent.parent else parent; - while (it) |p| : (it = p.parent) { - if (p.pack.suberr) break; - p.pack.suberr = true; - } + fn hasErr(self: *Self) bool { + return + if (self.file()) |f| f.pack.err + else if (self.dir()) |d| d.pack.err or d.pack.suberr + else false; } pub fn addStats(self: *Entry, parent: *Dir, nlink: u31) void { @@ -265,6 +260,19 @@ pub const Dir = extern struct { i -= 1; } } + + // Only updates the suberr of this Dir, assumes child dirs have already + // been updated and does not propagate to parents. + pub fn updateSubErr(self: *@This()) void { + self.pack.suberr = false; + var sub = self.sub; + while (sub) |e| : (sub = e.next) { + if (e.hasErr()) { + self.pack.suberr = true; + break; + } + } + } }; // File that's been hardlinked (i.e. nlink > 1) @@ -319,7 +327,7 @@ pub const devices = struct { var lookup = std.AutoHashMap(u64, DevId).init(main.allocator); pub fn getId(dev: u64) DevId { - var d = lookup.getOrPut(dev) catch unreachable; + const d = lookup.getOrPut(dev) catch unreachable; if (!d.found_existing) { d.value_ptr.* = @as(DevId, @intCast(list.items.len)); list.append(dev) catch unreachable; @@ -400,7 +408,7 @@ pub const inodes = struct { nlink += 1; var parent: ?*Dir = it.parent; while (parent) |p| : (parent = p.parent) { - var de = dirs.getOrPut(p) catch unreachable; + const de = dirs.getOrPut(p) catch unreachable; if (de.found_existing) de.value_ptr.* += 1 else de.value_ptr.* = 1; } diff --git a/src/scan.zig b/src/scan.zig index 95ec78b..891902f 100644 --- a/src/scan.zig +++ b/src/scan.zig @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021-2023 Yoran Heling +// SPDX-FileCopyrightText: Yorhel // SPDX-License-Identifier: MIT const std = @import("std"); @@ -32,17 +32,17 @@ const Stat = struct { } fn read(parent: std.fs.Dir, name: [:0]const u8, follow: bool) !Stat { - const stat = try std.os.fstatatZ(parent.fd, name, if (follow) 0 else std.os.AT.SYMLINK_NOFOLLOW); + const stat = try std.posix.fstatatZ(parent.fd, name, if (follow) 0 else std.posix.AT.SYMLINK_NOFOLLOW); return Stat{ .blocks = clamp(Stat, .blocks, stat.blocks), .size = clamp(Stat, .size, stat.size), .dev = truncate(Stat, .dev, stat.dev), .ino = truncate(Stat, .ino, stat.ino), .nlink = clamp(Stat, .nlink, stat.nlink), - .hlinkc = stat.nlink > 1 and !std.os.system.S.ISDIR(stat.mode), - .dir = std.os.system.S.ISDIR(stat.mode), - .reg = std.os.system.S.ISREG(stat.mode), - .symlink = std.os.system.S.ISLNK(stat.mode), + .hlinkc = stat.nlink > 1 and !std.posix.S.ISDIR(stat.mode), + .dir = std.posix.S.ISDIR(stat.mode), + .reg = std.posix.S.ISREG(stat.mode), + .symlink = std.posix.S.ISLNK(stat.mode), .ext = .{ .mtime = clamp(model.Ext, .mtime, stat.mtime().tv_sec), .uid = truncate(model.Ext, .uid, stat.uid), @@ -176,7 +176,7 @@ const ScanDir = struct { }; var f = e.file().?; switch (t) { - .err => e.setErr(self.dir), + .err => f.pack.err = true, .other_fs => f.pack.other_fs = true, .kernfs => f.pack.kernfs = true, .excluded => f.pack.excluded = true, @@ -231,16 +231,17 @@ const ScanDir = struct { } fn final(self: *Self) void { - if (self.entries.count() == 0) // optimization for the common case - return; - var it = &self.dir.sub; - while (it.*) |e| { - if (self.entries.contains(e)) { - e.delStatsRec(self.dir); - it.* = e.next; - } else - it = &e.next; + if (self.entries.count() > 0) { + var it = &self.dir.sub; + while (it.*) |e| { + if (self.entries.contains(e)) { + e.delStatsRec(self.dir); + it.* = e.next; + } else + it = &e.next; + } } + self.dir.updateSubErr(); } fn deinit(self: *Self) void { @@ -261,6 +262,7 @@ const ScanDir = struct { const Context = struct { // When scanning to RAM parents: ?std.ArrayList(ScanDir) = null, + refreshing: ?*model.Dir = null, // When scanning to a file wr: ?*Writer = null, @@ -293,14 +295,17 @@ const Context = struct { wr.print("{d}", .{std.time.timestamp()}) catch |e| writeErr(e); wr.writeByte('}') catch |e| writeErr(e); - var self = main.allocator.create(Self) catch unreachable; + const self = main.allocator.create(Self) catch unreachable; self.* = .{ .wr = buf }; return self; } fn initMem(dir: ?*model.Dir) *Self { var self = main.allocator.create(Self) catch unreachable; - self.* = .{ .parents = std.ArrayList(ScanDir).init(main.allocator) }; + self.* = .{ + .parents = std.ArrayList(ScanDir).init(main.allocator), + .refreshing = dir, + }; if (dir) |d| self.parents.?.append(ScanDir.init(d)) catch unreachable; return self; } @@ -311,6 +316,8 @@ const Context = struct { defer counting_hardlinks = false; main.handleEvent(false, true); model.inodes.addAllStats(); + var p = self.refreshing; + while (p) |d| : (p = d.parent) d.updateSubErr(); } if (self.wr) |wr| { wr.writer().writeByte(']') catch |e| writeErr(e); @@ -353,7 +360,7 @@ const Context = struct { // Set a flag to indicate that there was an error listing file entries in the current directory. // (Such errors are silently ignored when exporting to a file, as the directory metadata has already been written) fn setDirlistError(self: *Self) void { - if (self.parents) |*p| p.items[p.items.len-1].dir.entry.setErr(p.items[p.items.len-1].dir); + if (self.parents) |*p| p.items[p.items.len-1].dir.pack.err = true; } const Special = enum { err, other_fs, kernfs, excluded }; @@ -447,8 +454,8 @@ const Context = struct { var active_context: *Context = undefined; // Read and index entries of the given dir. -fn scanDir(ctx: *Context, pat: *const exclude.Patterns, dir: std.fs.IterableDir, dir_dev: u64) void { - var it = main.allocator.create(std.fs.IterableDir.Iterator) catch unreachable; +fn scanDir(ctx: *Context, pat: *const exclude.Patterns, dir: std.fs.Dir, dir_dev: u64) void { + var it = main.allocator.create(std.fs.Dir.Iterator) catch unreachable; defer main.allocator.destroy(it); it.* = dir.iterate(); while(true) { @@ -468,7 +475,7 @@ fn scanDir(ctx: *Context, pat: *const exclude.Patterns, dir: std.fs.IterableDir, continue; } - ctx.stat = Stat.read(dir.dir, ctx.name, false) catch { + ctx.stat = Stat.read(dir, ctx.name, false) catch { ctx.addSpecial(.err); continue; }; @@ -478,7 +485,7 @@ fn scanDir(ctx: *Context, pat: *const exclude.Patterns, dir: std.fs.IterableDir, } if (main.config.follow_symlinks and ctx.stat.symlink) { - if (Stat.read(dir.dir, ctx.name, true)) |nstat| { + if (Stat.read(dir, ctx.name, true)) |nstat| { if (!nstat.dir) { ctx.stat = nstat; // Symlink targets may reside on different filesystems, @@ -495,20 +502,19 @@ fn scanDir(ctx: *Context, pat: *const exclude.Patterns, dir: std.fs.IterableDir, var edir = if (!ctx.stat.dir) null - else if (dir.dir.openDirZ(ctx.name, .{ .no_follow = true }, true)) |d| std.fs.IterableDir{.dir = d} - else |_| { + else dir.openDirZ(ctx.name, .{ .no_follow = true, .iterate = true }) catch { ctx.addSpecial(.err); continue; }; defer if (edir != null) edir.?.close(); - if (@import("builtin").os.tag == .linux and main.config.exclude_kernfs and ctx.stat.dir and isKernfs(edir.?.dir, ctx.stat.dev)) { + if (@import("builtin").os.tag == .linux and main.config.exclude_kernfs and ctx.stat.dir and isKernfs(edir.?, ctx.stat.dev)) { ctx.addSpecial(.kernfs); continue; } if (main.config.exclude_caches and ctx.stat.dir) { - if (edir.?.dir.openFileZ("CACHEDIR.TAG", .{})) |f| { + if (edir.?.openFileZ("CACHEDIR.TAG", .{})) |f| { const sig = "Signature: 8a477f597d28d172789f06886806bc55"; var buf: [sig.len]u8 = undefined; if (f.reader().readAll(&buf)) |len| { @@ -555,14 +561,13 @@ pub fn setupRefresh(parent: *model.Dir) void { // To be called after setupRefresh() (or from scanRoot()) pub fn scan() void { defer active_context.deinit(); - var dir_ = std.fs.cwd().openDirZ(active_context.pathZ(), .{}, true) catch |e| { + var dir = std.fs.cwd().openDirZ(active_context.pathZ(), .{ .iterate = true }) catch |e| { active_context.last_error = main.allocator.dupeZ(u8, active_context.path.items) catch unreachable; active_context.fatal_error = e; while (main.state == .refresh or main.state == .scan) main.handleEvent(true, true); return; }; - var dir = std.fs.IterableDir{.dir = dir_}; defer dir.close(); var pat = exclude.getPatterns(active_context.pathZ()); defer pat.deinit(); diff --git a/src/ui.zig b/src/ui.zig index 2678994..993f252 100644 --- a/src/ui.zig +++ b/src/ui.zig @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021-2023 Yoran Heling +// SPDX-FileCopyrightText: Yorhel // SPDX-License-Identifier: MIT // Ncurses wrappers and TUI helper functions. @@ -192,17 +192,9 @@ test "shorten" { try t("ą́ą́ą́ą́ą́ą́", 5, "ą́...̨́ą́"); // Combining marks, similarly bad. } -// ncurses_refs.c -extern fn ncdu_acs_ulcorner() c.chtype; -extern fn ncdu_acs_llcorner() c.chtype; -extern fn ncdu_acs_urcorner() c.chtype; -extern fn ncdu_acs_lrcorner() c.chtype; -extern fn ncdu_acs_hline() c.chtype; -extern fn ncdu_acs_vline() c.chtype; - const StyleAttr = struct { fg: i16, bg: i16, attr: u32 }; const StyleDef = struct { - name: []const u8, + name: [:0]const u8, off: StyleAttr, dark: StyleAttr, darkbg: StyleAttr, @@ -287,8 +279,8 @@ const styles = [_]StyleDef{ }; pub const Style = lbl: { - comptime var fields: [styles.len]std.builtin.Type.EnumField = undefined; - inline for (&fields, styles, 0..) |*field, s, i| { + var fields: [styles.len]std.builtin.Type.EnumField = undefined; + for (&fields, styles, 0..) |*field, s, i| { field.* = .{ .name = s.name, .value = i, @@ -350,9 +342,9 @@ pub fn init() void { if (inited) return; clearScr(); if (main.config.nc_tty) { - var tty = c.fopen("/dev/tty", "r+"); - if (tty == null) die("Error opening /dev/tty: {s}.\n", .{ c.strerror(@intFromEnum(std.c.getErrno(-1))) }); - var term = c.newterm(null, tty, tty); + const tty = c.fopen("/dev/tty", "r+"); + if (tty == null) die("Error opening /dev/tty: {s}.\n", .{ c.strerror(@intFromEnum(std.posix.errno(-1))) }); + const term = c.newterm(null, tty, tty); if (term == null) die("Error initializing ncurses.\n", .{}); _ = c.set_term(term); } else { @@ -481,14 +473,14 @@ pub fn addnum(bg: Bg, v: u64) void { // Print a file mode, takes 10 columns pub fn addmode(mode: u32) void { - addch(switch (mode & std.os.S.IFMT) { - std.os.S.IFDIR => 'd', - std.os.S.IFREG => '-', - std.os.S.IFLNK => 'l', - std.os.S.IFIFO => 'p', - std.os.S.IFSOCK => 's', - std.os.S.IFCHR => 'c', - std.os.S.IFBLK => 'b', + addch(switch (mode & std.posix.S.IFMT) { + std.posix.S.IFDIR => 'd', + std.posix.S.IFREG => '-', + std.posix.S.IFLNK => 'l', + std.posix.S.IFIFO => 'p', + std.posix.S.IFSOCK => 's', + std.posix.S.IFCHR => 'c', + std.posix.S.IFBLK => 'b', else => '?' }); addch(if (mode & 0o400 > 0) 'r' else '-'); @@ -499,7 +491,7 @@ pub fn addmode(mode: u32) void { addch(if (mode & 0o2000 > 0) 's' else if (mode & 0o010 > 0) @as(u7, 'x') else '-'); addch(if (mode & 0o004 > 0) 'r' else '-'); addch(if (mode & 0o002 > 0) 'w' else '-'); - addch(if (mode & 0o1000 > 0) (if (std.os.S.ISDIR(mode)) @as(u7, 't') else 'T') else if (mode & 0o001 > 0) @as(u7, 'x') else '-'); + addch(if (mode & 0o1000 > 0) (if (std.posix.S.ISDIR(mode)) @as(u7, 't') else 'T') else if (mode & 0o001 > 0) @as(u7, 'x') else '-'); } // Print a timestamp, takes 25 columns @@ -535,20 +527,21 @@ pub const Box = struct { style(.default); if (width < 6 or height < 3) return s; - const ulcorner = ncdu_acs_ulcorner(); - const llcorner = ncdu_acs_llcorner(); - const urcorner = ncdu_acs_urcorner(); - const lrcorner = ncdu_acs_lrcorner(); - const acs_hline = ncdu_acs_hline(); - const acs_vline = ncdu_acs_vline(); + const acs_map = @extern(*[128]c.chtype, .{ .name = "acs_map" }); + const ulcorner = acs_map['l']; + const llcorner = acs_map['m']; + const urcorner = acs_map['k']; + const lrcorner = acs_map['j']; + const acs_hline = acs_map['q']; + const acs_vline = acs_map['x']; var i: u32 = 0; while (i < height) : (i += 1) { s.move(i, 0); - addch(if (i == 0) ulcorner else if (i == height-1) llcorner else acs_hline); - hline(if (i == 0 or i == height-1) acs_vline else ' ', width-2); + addch(if (i == 0) ulcorner else if (i == height-1) llcorner else acs_vline); + hline(if (i == 0 or i == height-1) acs_hline else ' ', width-2); s.move(i, width-1); - addch(if (i == 0) urcorner else if (i == height-1) lrcorner else acs_hline); + addch(if (i == 0) urcorner else if (i == height-1) lrcorner else acs_vline); } s.move(0, 3); @@ -599,5 +592,5 @@ pub fn getch(block: bool) i32 { return ch; } die("Error reading keyboard input, assuming TTY has been lost.\n(Potentially nonsensical error message: {s})\n", - .{ c.strerror(@intFromEnum(std.c.getErrno(-1))) }); + .{ c.strerror(@intFromEnum(std.posix.errno(-1))) }); } diff --git a/src/util.zig b/src/util.zig index f0fc1d4..bc486d9 100644 --- a/src/util.zig +++ b/src/util.zig @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021-2023 Yoran Heling +// SPDX-FileCopyrightText: Yorhel // SPDX-License-Identifier: MIT const std = @import("std");