summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2021-04-29 12:48:45 +0200
committerYorhel <git@yorhel.nl>2021-04-29 12:48:52 +0200
commit0783d357937e5e705321c0f92f6c4043f317b909 (patch)
tree93d2da37d87d4011928488a601bbcaa55a265973
parent9337cdc99e8c205e6e75de25de29a5be53ea858c (diff)
WIP: Experimenting with a rewrite to Zig & a new data model
The new data model is supposed to solve a few problems with ncdu 1.x's 'struct dir': - Reduce memory overhead, - Fix extremely slow counting of hard links in some scenarios (issue #121) - Add support for counting 'shared' data with other directories (issue #36) Quick memory usage comparison of my root directory with ~3.5 million files (normal / extended mode): ncdu 1.15.1: 379M / 451M new (unaligned): 145M / 178M new (aligned): 155M / 200M There's still a /lot/ of to-do's left before this is usable, however, and there's a bunch of issues I haven't really decided on yet, such as which TUI library to use. Backporting this data model to the C version of ncdu is also possible, but somewhat painful. Let's first see how far I get with Zig.
-rw-r--r--.gitignore19
-rw-r--r--Makefile.am47
-rw-r--r--build.zig20
-rw-r--r--configure.ac75
-rw-r--r--deps/khashl.h349
-rw-r--r--deps/yopt.h198
-rw-r--r--src/browser.c567
-rw-r--r--src/browser.h37
-rw-r--r--src/delete.c253
-rw-r--r--src/delete.h37
-rw-r--r--src/dir.h141
-rw-r--r--src/dir_common.c232
-rw-r--r--src/dir_export.c194
-rw-r--r--src/dir_import.c615
-rw-r--r--src/dir_mem.c215
-rw-r--r--src/dir_scan.c405
-rw-r--r--src/dirlist.c398
-rw-r--r--src/dirlist.h86
-rw-r--r--src/exclude.c139
-rw-r--r--src/exclude.h35
-rw-r--r--src/global.h132
-rw-r--r--src/help.c212
-rw-r--r--src/help.h37
-rw-r--r--src/main.c359
-rw-r--r--src/main.zig72
-rw-r--r--src/model.zig350
-rw-r--r--src/path.c246
-rw-r--r--src/path.h47
-rw-r--r--src/quit.c50
-rw-r--r--src/quit.h37
-rw-r--r--src/scan.zig138
-rw-r--r--src/shell.c82
-rw-r--r--src/shell.h35
-rw-r--r--src/util.c434
-rw-r--r--src/util.h195
-rwxr-xr-xstatic/build.sh130
36 files changed, 581 insertions, 6037 deletions
diff --git a/.gitignore b/.gitignore
index 4533894..53dc7b4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,21 +1,4 @@
-Makefile
-Makefile.in
-aclocal.m4
-autom4te.cache/
-compile
-config.h
-config.h.in
-config.log
-config.status
-configure
-depcomp
-install-sh
-missing
-.deps/
-.dirstamp
-*.o
-stamp-h1
-ncdu
+zig-cache/
ncdu.1
*~
*.swp
diff --git a/Makefile.am b/Makefile.am
deleted file mode 100644
index 01a79c4..0000000
--- a/Makefile.am
+++ /dev/null
@@ -1,47 +0,0 @@
-AM_CPPFLAGS=-I$(srcdir)/deps
-bin_PROGRAMS=ncdu
-
-ncdu_SOURCES=\
- src/browser.c\
- src/delete.c\
- src/dirlist.c\
- src/dir_common.c\
- src/dir_export.c\
- src/dir_import.c\
- src/dir_mem.c\
- src/dir_scan.c\
- src/exclude.c\
- src/help.c\
- src/shell.c\
- src/quit.c\
- src/main.c\
- src/path.c\
- src/util.c
-
-noinst_HEADERS=\
- deps/yopt.h\
- deps/khashl.h\
- src/browser.h\
- src/delete.h\
- src/dir.h\
- src/dirlist.h\
- src/exclude.h\
- src/global.h\
- src/help.h\
- src/shell.h\
- src/quit.h\
- src/path.h\
- src/util.h
-
-
-man_MANS=ncdu.1
-EXTRA_DIST=ncdu.1 doc/ncdu.pod
-
-# Don't "clean" ncdu.1, it should be in the tarball so that pod2man isn't a
-# build dependency for those who use the tarball.
-ncdu.1: $(srcdir)/doc/ncdu.pod
- pod2man --center "ncdu manual" --release "@PACKAGE@-@VERSION@" "$(srcdir)/doc/ncdu.pod" >ncdu.1
-
-update-deps:
- wget -q https://raw.github.com/attractivechaos/klib/master/khashl.h -O "$(srcdir)/deps/khashl.h"
- wget -q http://g.blicky.net/ylib.git/plain/yopt.h -O "$(srcdir)/deps/yopt.h"
diff --git a/build.zig b/build.zig
new file mode 100644
index 0000000..1f693d3
--- /dev/null
+++ b/build.zig
@@ -0,0 +1,20 @@
+const std = @import("std");
+
+pub fn build(b: *std.build.Builder) void {
+ const target = b.standardTargetOptions(.{});
+ const mode = b.standardReleaseOptions();
+
+ const exe = b.addExecutable("ncdu", "src/main.zig");
+ exe.setTarget(target);
+ exe.setBuildMode(mode);
+ exe.install();
+
+ const run_cmd = exe.run();
+ run_cmd.step.dependOn(b.getInstallStep());
+ if (b.args) |args| {
+ run_cmd.addArgs(args);
+ }
+
+ const run_step = b.step("run", "Run the app");
+ run_step.dependOn(&run_cmd.step);
+}
diff --git a/configure.ac b/configure.ac
deleted file mode 100644
index a5d1cd4..0000000
--- a/configure.ac
+++ /dev/null
@@ -1,75 +0,0 @@
-
-AC_INIT([ncdu],[1.15.1],[projects@yorhel.nl])
-AC_CONFIG_SRCDIR([src/global.h])
-AC_CONFIG_HEADER([config.h])
-AM_INIT_AUTOMAKE([foreign std-options subdir-objects])
-
-# Check for programs.
-AC_PROG_CC
-AC_PROG_INSTALL
-AC_PROG_RANLIB
-PKG_PROG_PKG_CONFIG
-
-# Check for header files.
-AC_CHECK_HEADERS(
- [limits.h sys/time.h sys/types.h sys/stat.h dirent.h unistd.h fnmatch.h ncurses.h],[],
- AC_MSG_ERROR([required header file not found]))
-
-AC_CHECK_HEADERS([locale.h sys/statfs.h linux/magic.h])
-
-# Check for typedefs, structures, and compiler characteristics.
-AC_TYPE_INT64_T
-AC_TYPE_UINT64_T
-AC_SYS_LARGEFILE
-AC_STRUCT_ST_BLOCKS
-
-# Check for library functions.
-AC_CHECK_FUNCS(
- [getcwd gettimeofday fnmatch chdir rmdir unlink lstat system getenv],[],
- AC_MSG_ERROR([required function missing]))
-
-AC_CHECK_FUNCS(statfs)
-
-AC_CHECK_HEADERS([sys/attr.h])
-
-AC_CHECK_FUNCS([getattrlist])
-
-AC_CHECK_DECLS([ATTR_CMNEXT_NOFIRMLINKPATH], [], [], [[#include <sys/attr.h>]])
-
-# Look for ncurses library to link to
-ncurses=auto
-AC_ARG_WITH([ncurses],
- [AS_HELP_STRING([--with-ncurses], [compile/link with ncurses library] )],
- [ncurses=ncurses])
-AC_ARG_WITH([ncursesw],
- [AS_HELP_STRING([--with-ncursesw], [compile/link with wide-char ncurses library @<:@default@:>@])],
- [ncurses=ncursesw])
-if test "$ncurses" = "auto" -o "$ncurses" = "ncursesw"; then
- PKG_CHECK_MODULES([NCURSES], [ncursesw], [LIBS="$LIBS $NCURSES_LIBS"; ncurses=ncursesw],
- [AC_CHECK_LIB([ncursesw],
- [initscr],
- [LIBS="$LIBS -lncursesw"; ncurses=ncursesw],
- [ncurses=ncurses])
- ])
-fi
-if test "$ncurses" = "ncurses"; then
- PKG_CHECK_MODULES([NCURSES], [ncurses], [LIBS="$LIBS $NCURSES_LIBS"],
- [AC_CHECK_LIB([ncurses],
- [initscr],
- [LIBS="$LIBS -lncurses"],
- [AC_MSG_ERROR(ncurses library is required)])
- ])
-fi
-
-# Configure default shell for spawning shell when $SHELL is not set
-AC_ARG_WITH([shell],
- [AS_HELP_STRING([--with-shell],
- [used interpreter as default shell (default is /bin/sh)])],
- [DEFAULT_SHELL=$withval],
- [DEFAULT_SHELL=/bin/sh])
-AC_MSG_NOTICE([Using $DEFAULT_SHELL as the default shell if \$SHELL is not set])
-AC_DEFINE_UNQUOTED(DEFAULT_SHELL, "$DEFAULT_SHELL", [Used default shell interpreter])
-
-
-AC_CONFIG_FILES([Makefile])
-AC_OUTPUT
diff --git a/deps/khashl.h b/deps/khashl.h
deleted file mode 100644
index 951feda..0000000
--- a/deps/khashl.h
+++ /dev/null
@@ -1,349 +0,0 @@
-/* The MIT License
-
- Copyright (c) 2019 by Attractive Chaos <attractor@live.co.uk>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be
- included in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-#ifndef __AC_KHASHL_H
-#define __AC_KHASHL_H
-
-#define AC_VERSION_KHASHL_H "0.1"
-
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-
-/************************************
- * Compiler specific configurations *
- ************************************/
-
-#if UINT_MAX == 0xffffffffu
-typedef unsigned int khint32_t;
-#elif ULONG_MAX == 0xffffffffu
-typedef unsigned long khint32_t;
-#endif
-
-#if ULONG_MAX == ULLONG_MAX
-typedef unsigned long khint64_t;
-#else
-typedef unsigned long long khint64_t;
-#endif
-
-#ifndef kh_inline
-#ifdef _MSC_VER
-#define kh_inline __inline
-#else
-#define kh_inline inline
-#endif
-#endif /* kh_inline */
-
-#ifndef klib_unused
-#if (defined __clang__ && __clang_major__ >= 3) || (defined __GNUC__ && __GNUC__ >= 3)
-#define klib_unused __attribute__ ((__unused__))
-#else
-#define klib_unused
-#endif
-#endif /* klib_unused */
-
-#define KH_LOCAL static kh_inline klib_unused
-
-typedef khint32_t khint_t;
-
-/******************
- * malloc aliases *
- ******************/
-
-#ifndef kcalloc
-#define kcalloc(N,Z) calloc(N,Z)
-#endif
-#ifndef kmalloc
-#define kmalloc(Z) malloc(Z)
-#endif
-#ifndef krealloc
-#define krealloc(P,Z) realloc(P,Z)
-#endif
-#ifndef kfree
-#define kfree(P) free(P)
-#endif
-
-/****************************
- * Simple private functions *
- ****************************/
-
-#define __kh_used(flag, i) (flag[i>>5] >> (i&0x1fU) & 1U)
-#define __kh_set_used(flag, i) (flag[i>>5] |= 1U<<(i&0x1fU))
-#define __kh_set_unused(flag, i) (flag[i>>5] &= ~(1U<<(i&0x1fU)))
-
-#define __kh_fsize(m) ((m) < 32? 1 : (m)>>5)
-
-static kh_inline khint_t __kh_h2b(khint_t hash, khint_t bits) { return hash * 2654435769U >> (32 - bits); }
-
-/*******************
- * Hash table base *
- *******************/
-
-#define __KHASHL_TYPE(HType, khkey_t) \
- typedef struct { \
- khint_t bits, count; \
- khint32_t *used; \
- khkey_t *keys; \
- } HType;
-
-#define __KHASHL_PROTOTYPES(HType, prefix, khkey_t) \
- extern HType *prefix##_init(void); \
- extern void prefix##_destroy(HType *h); \
- extern void prefix##_clear(HType *h); \
- extern khint_t prefix##_getp(const HType *h, const khkey_t *key); \
- extern int prefix##_resize(HType *h, khint_t new_n_buckets); \
- extern khint_t prefix##_putp(HType *h, const khkey_t *key, int *absent); \
- extern void prefix##_del(HType *h, khint_t k);
-
-#define __KHASHL_IMPL_BASIC(SCOPE, HType, prefix) \
- SCOPE HType *prefix##_init(void) { \
- return (HType*)kcalloc(1, sizeof(HType)); \
- } \
- SCOPE void prefix##_destroy(HType *h) { \
- if (!h) return; \
- kfree((void *)h->keys); kfree(h->used); \
- kfree(h); \
- } \
- SCOPE void prefix##_clear(HType *h) { \
- if (h && h->used) { \
- uint32_t n_buckets = 1U << h->bits; \
- memset(h->used, 0, __kh_fsize(n_buckets) * sizeof(khint32_t)); \
- h->count = 0; \
- } \
- }
-
-#define __KHASHL_IMPL_GET(SCOPE, HType, prefix, khkey_t, __hash_fn, __hash_eq) \
- SCOPE khint_t prefix##_getp(const HType *h, const khkey_t *key) { \
- khint_t i, last, n_buckets, mask; \
- if (h->keys == 0) return 0; \
- n_buckets = 1U << h->bits; \
- mask = n_buckets - 1U; \
- i = last = __kh_h2b(__hash_fn(*key), h->bits); \
- while (__kh_used(h->used, i) && !__hash_eq(h->keys[i], *key)) { \
- i = (i + 1U) & mask; \
- if (i == last) return n_buckets; \
- } \
- return !__kh_used(h->used, i)? n_buckets : i; \
- } \
- SCOPE khint_t prefix##_get(const HType *h, khkey_t key) { return prefix##_getp(h, &key); }
-
-#define __KHASHL_IMPL_RESIZE(SCOPE, HType, prefix, khkey_t, __hash_fn, __hash_eq) \
- SCOPE int prefix##_resize(HType *h, khint_t new_n_buckets) { \
- khint32_t *new_used = 0; \
- khint_t j = 0, x = new_n_buckets, n_buckets, new_bits, new_mask; \
- while ((x >>= 1) != 0) ++j; \
- if (new_n_buckets & (new_n_buckets - 1)) ++j; \
- new_bits = j > 2? j : 2; \
- new_n_buckets = 1U << new_bits; \
- if (h->count > (new_n_buckets>>1) + (new_n_buckets>>2)) return 0; /* requested size is too small */ \
- new_used = (khint32_t*)kmalloc(__kh_fsize(new_n_buckets) * sizeof(khint32_t)); \
- memset(new_used, 0, __kh_fsize(new_n_buckets) * sizeof(khint32_t)); \
- if (!new_used) return -1; /* not enough memory */ \
- n_buckets = h->keys? 1U<<h->bits : 0U; \
- if (n_buckets < new_n_buckets) { /* expand */ \
- khkey_t *new_keys = (khkey_t*)krealloc((void*)h->keys, new_n_buckets * sizeof(khkey_t)); \
- if (!new_keys) { kfree(new_used); return -1; } \
- h->keys = new_keys; \
- } /* otherwise shrink */ \
- new_mask = new_n_buckets - 1; \
- for (j = 0; j != n_buckets; ++j) { \
- khkey_t key; \
- if (!__kh_used(h->used, j)) continue; \
- key = h->keys[j]; \
- __kh_set_unused(h->used, j); \
- while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
- khint_t i; \
- i = __kh_h2b(__hash_fn(key), new_bits); \
- while (__kh_used(new_used, i)) i = (i + 1) & new_mask; \
- __kh_set_used(new_used, i); \
- if (i < n_buckets && __kh_used(h->used, i)) { /* kick out the existing element */ \
- { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
- __kh_set_unused(h->used, i); /* mark it as deleted in the old hash table */ \
- } else { /* write the element and jump out of the loop */ \
- h->keys[i] = key; \
- break; \
- } \
- } \
- } \
- if (n_buckets > new_n_buckets) /* shrink the hash table */ \
- h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
- kfree(h->used); /* free the working space */ \
- h->used = new_used, h->bits = new_bits; \
- return 0; \
- }
-
-#define __KHASHL_IMPL_PUT(SCOPE, HType, prefix, khkey_t, __hash_fn, __hash_eq) \
- SCOPE khint_t prefix##_putp(HType *h, const khkey_t *key, int *absent) { \
- khint_t n_buckets, i, last, mask; \
- n_buckets = h->keys? 1U<<h->bits : 0U; \
- *absent = -1; \
- if (h->count >= (n_buckets>>1) + (n_buckets>>2)) { /* rehashing */ \
- if (prefix##_resize(h, n_buckets + 1U) < 0) \
- return n_buckets; \
- n_buckets = 1U<<h->bits; \
- } /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
- mask = n_buckets - 1; \
- i = last = __kh_h2b(__hash_fn(*key), h->bits); \
- while (__kh_used(h->used, i) && !__hash_eq(h->keys[i], *key)) { \
- i = (i + 1U) & mask; \
- if (i == last) break; \
- } \
- if (!__kh_used(h->used, i)) { /* not present at all */ \
- h->keys[i] = *key; \
- __kh_set_used(h->used, i); \
- ++h->count; \
- *absent = 1; \
- } else *absent = 0; /* Don't touch h->keys[i] if present */ \
- return i; \
- } \
- SCOPE khint_t prefix##_put(HType *h, khkey_t key, int *absent) { return prefix##_putp(h, &key, absent); }
-
-#define __KHASHL_IMPL_DEL(SCOPE, HType, prefix, khkey_t, __hash_fn) \
- SCOPE int prefix##_del(HType *h, khint_t i) { \
- khint_t j = i, k, mask, n_buckets; \
- if (h->keys == 0) return 0; \
- n_buckets = 1U<<h->bits; \
- mask = n_buckets - 1U; \
- while (1) { \
- j = (j + 1U) & mask; \
- if (j == i || !__kh_used(h->used, j)) break; /* j==i only when the table is completely full */ \
- k = __kh_h2b(__hash_fn(h->keys[j]), h->bits); \
- if ((j > i && (k <= i || k > j)) || (j < i && (k <= i && k > j))) \
- h->keys[i] = h->keys[j], i = j; \
- } \
- __kh_set_unused(h->used, i); \
- --h->count; \
- return 1; \
- }
-
-#define KHASHL_DECLARE(HType, prefix, khkey_t) \
- __KHASHL_TYPE(HType, khkey_t) \
- __KHASHL_PROTOTYPES(HType, prefix, khkey_t)
-
-#define KHASHL_INIT(SCOPE, HType, prefix, khkey_t, __hash_fn, __hash_eq) \
- __KHASHL_TYPE(HType, khkey_t) \
- __KHASHL_IMPL_BASIC(SCOPE, HType, prefix) \
- __KHASHL_IMPL_GET(SCOPE, HType, prefix, khkey_t, __hash_fn, __hash_eq) \
- __KHASHL_IMPL_RESIZE(SCOPE, HType, prefix, khkey_t, __hash_fn, __hash_eq) \
- __KHASHL_IMPL_PUT(SCOPE, HType, prefix, khkey_t, __hash_fn, __hash_eq) \
- __KHASHL_IMPL_DEL(SCOPE, HType, prefix, khkey_t, __hash_fn)
-
-/*****************************
- * More convenient interface *
- *****************************/
-
-#define __kh_packed __attribute__ ((__packed__))
-#define __kh_cached_hash(x) ((x).hash)
-
-#define KHASHL_SET_INIT(SCOPE, HType, prefix, khkey_t, __hash_fn, __hash_eq) \
- typedef struct { khkey_t key; } __kh_packed HType##_s_bucket_t; \
- static kh_inline khint_t prefix##_s_hash(HType##_s_bucket_t x) { return __hash_fn(x.key); } \
- static kh_inline int prefix##_s_eq(HType##_s_bucket_t x, HType##_s_bucket_t y) { return __hash_eq(x.key, y.key); } \
- KHASHL_INIT(KH_LOCAL, HType, prefix##_s, HType##_s_bucket_t, prefix##_s_hash, prefix##_s_eq) \
- SCOPE HType *prefix##_init(void) { return prefix##_s_init(); } \
- SCOPE void prefix##_destroy(HType *h) { prefix##_s_destroy(h); } \
- SCOPE khint_t prefix##_get(const HType *h, khkey_t key) { HType##_s_bucket_t t; t.key = key; return prefix##_s_getp(h, &t); } \
- SCOPE int prefix##_del(HType *h, khint_t k) { return prefix##_s_del(h, k); } \
- SCOPE khint_t prefix##_put(HType *h, khkey_t key, int *absent) { HType##_s_bucket_t t; t.key = key; return prefix##_s_putp(h, &t, absent); }
-
-#define KHASHL_MAP_INIT(SCOPE, HType, prefix, khkey_t, kh_val_t, __hash_fn, __hash_eq) \
- typedef struct { khkey_t key; kh_val_t val; } __kh_packed HType##_m_bucket_t; \
- static kh_inline khint_t prefix##_m_hash(HType##_m_bucket_t x) { return __hash_fn(x.key); } \
- static kh_inline int prefix##_m_eq(HType##_m_bucket_t x, HType##_m_bucket_t y) { return __hash_eq(x.key, y.key); } \
- KHASHL_INIT(KH_LOCAL, HType, prefix##_m, HType##_m_bucket_t, prefix##_m_hash, prefix##_m_eq) \
- SCOPE HType *prefix##_init(void) { return prefix##_m_init(); } \
- SCOPE void prefix##_destroy(HType *h) { prefix##_m_destroy(h); } \
- SCOPE khint_t prefix##_get(const HType *h, khkey_t key) { HType##_m_bucket_t t; t.key = key; return prefix##_m_getp(h, &t); } \
- SCOPE int prefix##_del(HType *h, khint_t k) { return prefix##_m_del(h, k); } \
- SCOPE khint_t prefix##_put(HType *h, khkey_t key, int *absent) { HType##_m_bucket_t t; t.key = key; return prefix##_m_putp(h, &t, absent); }
-
-#define KHASHL_CSET_INIT(SCOPE, HType, prefix, khkey_t, __hash_fn, __hash_eq) \
- typedef struct { khkey_t key; khint_t hash; } __kh_packed HType##_cs_bucket_t; \
- static kh_inline int prefix##_cs_eq(HType##_cs_bucket_t x, HType##_cs_bucket_t y) { return x.hash == y.hash && __hash_eq(x.key, y.key); } \
- KHASHL_INIT(KH_LOCAL, HType, prefix##_cs, HType##_cs_bucket_t, __kh_cached_hash, prefix##_cs_eq) \
- SCOPE HType *prefix##_init(void) { return prefix##_cs_init(); } \
- SCOPE void prefix##_destroy(HType *h) { prefix##_cs_destroy(h); } \
- SCOPE khint_t prefix##_get(const HType *h, khkey_t key) { HType##_cs_bucket_t t; t.key = key; t.hash = __hash_fn(key); return prefix##_cs_getp(h, &t); } \
- SCOPE int prefix##_del(HType *h, khint_t k) { return prefix##_cs_del(h, k); } \
- SCOPE khint_t prefix##_put(HType *h, khkey_t key, int *absent) { HType##_cs_bucket_t t; t.key = key, t.hash = __hash_fn(key); return prefix##_cs_putp(h, &t, absent); }
-
-#define KHASHL_CMAP_INIT(SCOPE, HType, prefix, khkey_t, kh_val_t, __hash_fn, __hash_eq) \
- typedef struct { khkey_t key; kh_val_t val; khint_t hash; } __kh_packed HType##_cm_bucket_t; \
- static kh_inline int prefix##_cm_eq(HType##_cm_bucket_t x, HType##_cm_bucket_t y) { return x.hash == y.hash && __hash_eq(x.key, y.key); } \
- KHASHL_INIT(KH_LOCAL, HType, prefix##_cm, HType##_cm_bucket_t, __kh_cached_hash, prefix##_cm_eq) \
- SCOPE HType *prefix##_init(void) { return prefix##_cm_init(); } \
- SCOPE void prefix##_destroy(HType *h) { prefix##_cm_destroy(h); } \
- SCOPE khint_t prefix##_get(const HType *h, khkey_t key) { HType##_cm_bucket_t t; t.key = key; t.hash = __hash_fn(key); return prefix##_cm_getp(h, &t); } \
- SCOPE int prefix##_del(HType *h, khint_t k) { return prefix##_cm_del(h, k); } \
- SCOPE khint_t prefix##_put(HType *h, khkey_t key, int *absent) { HType##_cm_bucket_t t; t.key = key, t.hash = __hash_fn(key); return prefix##_cm_putp(h, &t, absent); }
-
-/**************************
- * Public macro functions *
- **************************/
-
-#define kh_bucket(h, x) ((h)->keys[x])
-#define kh_size(h) ((h)->count)
-#define kh_capacity(h) ((h)->keys? 1U<<(h)->bits : 0U)
-#define kh_end(h) kh_capacity(h)
-
-#define kh_key(h, x) ((h)->keys[x].key)
-#define kh_val(h, x) ((h)->keys[x].val)
-
-/**************************************
- * Common hash and equality functions *
- **************************************/
-
-#define kh_eq_generic(a, b) ((a) == (b))
-#define kh_eq_str(a, b) (strcmp((a), (b)) == 0)
-#define kh_hash_dummy(x) ((khint_t)(x))
-
-static kh_inline khint_t kh_hash_uint32(khint_t key) {
- key += ~(key << 15);
- key ^= (key >> 10);
- key += (key << 3);
- key ^= (key >> 6);
- key += ~(key << 11);
- key ^= (key >> 16);
- return key;
-}
-
-static kh_inline khint_t kh_hash_uint64(khint64_t key) {
- key = ~key + (key << 21);
- key = key ^ key >> 24;
- key = (key + (key << 3)) + (key << 8);
- key = key ^ key >> 14;
- key = (key + (key << 2)) + (key << 4);
- key = key ^ key >> 28;
- key = key + (key << 31);
- return (khint_t)key;
-}
-
-static kh_inline khint_t kh_hash_str(const char *s) {
- khint_t h = (khint_t)*s;
- if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s;
- return h;
-}
-
-#endif /* __AC_KHASHL_H */
diff --git a/deps/yopt.h b/deps/yopt.h
deleted file mode 100644
index f1a3b7e..0000000
--- a/deps/yopt.h
+++ /dev/null
@@ -1,198 +0,0 @@
-/* Copyright (c) 2012-2013 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-/* This is a simple command-line option parser. Operation is similar to
- * getopt_long(), except with a cleaner API.
- *
- * This is implemented in a single header file, as it's pretty small and you
- * generally only use an option parser in a single .c file in your program.
- *
- * Supports (examples from GNU tar(1)):
- * "--gzip"
- * "--file <arg>"
- * "--file=<arg>"
- * "-z"
- * "-f <arg>"
- * "-f<arg>"
- * "-zf <arg>"
- * "-zf<arg>"
- * "--" (To stop looking for further options)
- * "<arg>" (Non-option arguments)
- *
- * Issues/non-features:
- * - An option either requires an argument or it doesn't.
- * - No way to specify how often an option can/should be used.
- * - No way to specify the type of an argument (filename/integer/enum/whatever)
- */
-
-
-#ifndef YOPT_H
-#define YOPT_H
-
-
-#include <string.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-
-typedef struct {
- /* Value yopt_next() will return for this option */
- int val;
- /* Whether this option needs an argument */
- int needarg;
- /* Name(s) of this option, prefixed with '-' or '--' and separated by a
- * comma. E.g. "-z", "--gzip", "-z,--gzip".
- * An option can have any number of aliases.
- */
- const char *name;
-} yopt_opt_t;
-
-
-typedef struct {
- int argc;
- int cur;
- int argsep; /* '--' found */
- char **argv;
- char *sh;
- const yopt_opt_t *opts;
- char errbuf[128];
-} yopt_t;
-
-
-/* opts must be an array of options, terminated with an option with val=0 */
-static inline void yopt_init(yopt_t *o, int argc, char **argv, const yopt_opt_t *opts) {
- o->argc = argc;
- o->argv = argv;
- o->opts = opts;
- o->cur = 0;
- o->argsep = 0;
- o->sh = NULL;
-}
-
-
-static inline const yopt_opt_t *_yopt_find(const yopt_opt_t *o, const char *v) {
- const char *tn, *tv;
-
- for(; o->val; o++) {
- tn = o->name;
- while(*tn) {
- tv = v;
- while(*tn && *tn != ',' && *tv && *tv != '=' && *tn == *tv) {
- tn++;
- tv++;
- }
- if(!(*tn && *tn != ',') && !(*tv && *tv != '='))
- return o;
- while(*tn && *tn != ',')
- tn++;
- while(*tn == ',')
- tn++;
- }
- }
-
- return NULL;
-}
-
-
-static inline int _yopt_err(yopt_t *o, char **val, const char *fmt, ...) {
- va_list va;
- va_start(va, fmt);
- vsnprintf(o->errbuf, sizeof(o->errbuf), fmt, va);
- va_end(va);
- *val = o->errbuf;
- return -2;
-}
-
-
-/* Return values:
- * 0 -> Non-option argument, val is its value
- * -1 -> Last argument has been processed
- * -2 -> Error, val will contain the error message.
- * x -> Option with val = x found. If the option requires an argument, its
- * value will be in val.
- */
-static inline int yopt_next(yopt_t *o, char **val) {
- const yopt_opt_t *opt;
- char sh[3];
-
- *val = NULL;
- if(o->sh)
- goto inshort;
-
- if(++o->cur >= o->argc)
- return -1;
-
- if(!o->argsep && o->argv[o->cur][0] == '-' && o->argv[o->cur][1] == '-' && o->argv[o->cur][2] == 0) {
- o->argsep = 1;
- if(++o->cur >= o->argc)
- return -1;
- }
-
- if(o->argsep || *o->argv[o->cur] != '-') {
- *val = o->argv[o->cur];
- return 0;
- }
-
- if(o->argv[o->cur][1] != '-') {
- o->sh = o->argv[o->cur]+1;
- goto inshort;
- }
-
- /* Now we're supposed to have a long option */
- if(!(opt = _yopt_find(o->opts, o->argv[o->cur])))
- return _yopt_err(o, val, "Unknown option '%s'", o->argv[o->cur]);
- if((*val = strchr(o->argv[o->cur], '=')) != NULL)
- (*val)++;
- if(!opt->needarg && *val)
- return _yopt_err(o, val, "Option '%s' does not accept an argument", o->argv[o->cur]);
- if(opt->needarg && !*val) {
- if(o->cur+1 >= o->argc)
- return _yopt_err(o, val, "Option '%s' requires an argument", o->argv[o->cur]);
- *val = o->argv[++o->cur];
- }
- return opt->val;
-
- /* And here we're supposed to have a short option */
-inshort:
- sh[0] = '-';
- sh[1] = *o->sh;
- sh[2] = 0;
- if(!(opt = _yopt_find(o->opts, sh)))
- return _yopt_err(o, val, "Unknown option '%s'", sh);
- o->sh++;
- if(opt->needarg && *o->sh)
- *val = o->sh;
- else if(opt->needarg) {
- if(++o->cur >= o->argc)
- return _yopt_err(o, val, "Option '%s' requires an argument", sh);
- *val = o->argv[o->cur];
- }
- if(!*o->sh || opt->needarg)
- o->sh = NULL;
- return opt->val;
-}
-
-
-#endif
-
-/* vim: set noet sw=4 ts=4: */
diff --git a/src/browser.c b/src/browser.c
deleted file mode 100644
index a223b84..0000000
--- a/src/browser.c
+++ /dev/null
@@ -1,567 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#include "global.h"
-
-#include <string.h>
-#include <stdlib.h>
-#include <ncurses.h>
-#include <time.h>
-
-
-static int graph = 1, show_as = 0, info_show = 0, info_page = 0, info_start = 0, show_items = 0, show_mtime = 0;
-static const char *message = NULL;
-
-
-
-static void browse_draw_info(struct dir *dr) {
- struct dir *t;
- struct dir_ext *e = dir_ext_ptr(dr);
- char mbuf[46];
- int i;
-
- nccreate(11, 60, "Item info");
-
- if(dr->hlnk) {
- nctab(41, info_page == 0, 1, "Info");
- nctab(50, info_page == 1, 2, "Links");
- }
-
- switch(info_page) {
- case 0:
- attron(A_BOLD);
- ncaddstr(2, 3, "Name:");
- ncaddstr(3, 3, "Path:");
- if(!e)
- ncaddstr(4, 3, "Type:");
- else {
- ncaddstr(4, 3, "Mode:");
- ncaddstr(4, 21, "UID:");
- ncaddstr(4, 33, "GID:");
- ncaddstr(5, 3, "Last modified:");
- }
- ncaddstr(6, 3, " Disk usage:");
- ncaddstr(7, 3, "Apparent size:");
- attroff(A_BOLD);
-
- ncaddstr(2, 9, cropstr(dr->name, 49));
- ncaddstr(3, 9, cropstr(getpath(dr->parent), 49));
- ncaddstr(4, 9, dr->flags & FF_DIR ? "Directory" : dr->flags & FF_FILE ? "File" : "Other");
-
- if(e) {
- ncaddstr(4, 9, fmtmode(e->mode));
- ncprint(4, 26, "%d", e->uid);
- ncprint(4, 38, "%d", e->gid);
- time_t t = (time_t)e->mtime;
- strftime(mbuf, sizeof(mbuf), "%Y-%m-%d %H:%M:%S %z", localtime(&t));
- ncaddstr(5, 18, mbuf);
- }
-
- ncmove(6, 18);
- printsize(UIC_DEFAULT, dr->size);
- addstrc(UIC_DEFAULT, " (");
- addstrc(UIC_NUM, fullsize(dr->size));
- addstrc(UIC_DEFAULT, " B)");
-
- ncmove(7, 18);
- printsize(UIC_DEFAULT, dr->asize);
- addstrc(UIC_DEFAULT, " (");
- addstrc(UIC_NUM, fullsize(dr->asize));
- addstrc(UIC_DEFAULT, " B)");
- break;
-
- case 1:
- for(i=0,t=dr->hlnk; t!=dr; t=t->hlnk,i++) {
- if(info_start > i)
- continue;
- if(i-info_start > 5)
- break;
- ncaddstr(2+i-info_start, 3, cropstr(getpath(t), 54));
- }
- if(t!=dr)
- ncaddstr(8, 25, "-- more --");
- break;
- }
-
- ncaddstr(9, 31, "Press ");
- addchc(UIC_KEY, 'i');
- addstrc(UIC_DEFAULT, " to hide this window");
-}
-
-
-static void browse_draw_flag(struct dir *n, int *x) {
- addchc(n->flags & FF_BSEL ? UIC_FLAG_SEL : UIC_FLAG,
- n == dirlist_parent ? ' ' :
- n->flags & FF_EXL ? '<' :
- n->flags & FF_ERR ? '!' :
- n->flags & FF_SERR ? '.' :
- n->flags & FF_OTHFS ? '>' :
- n->flags & FF_KERNFS ? '^' :
- n->flags & FF_FRMLNK ? 'F' :
- n->flags & FF_HLNKC ? 'H' :
- !(n->flags & FF_FILE
- || n->flags & FF_DIR) ? '@' :
- n->flags & FF_DIR
- && n->sub == NULL ? 'e' :
- ' ');
- *x += 2;
-}
-
-
-static void browse_draw_graph(struct dir *n, int *x) {
- float pc = 0.0f;
- int o, i, bar_size = wincols/7 > 10 ? wincols/7 : 10;
- enum ui_coltype c = n->flags & FF_BSEL ? UIC_SEL : UIC_DEFAULT;
-
- if(!graph)
- return;
-
- *x += graph == 1 ? (bar_size + 3) : graph == 2 ? 9 : (bar_size + 10);
- if(n == dirlist_parent)
- return;
-
- addchc(c, '[');
-
- /* percentage (6 columns) */
- if(graph == 2 || graph == 3) {
- pc = (float)(show_as ? n->parent->asize : n->parent->size);
- if(pc < 1)
- pc = 1.0f;
- uic_set(c == UIC_SEL ? UIC_NUM_SEL : UIC_NUM);
- printw("%5.1f", ((float)(show_as ? n->asize : n->size) / pc) * 100.0f);
- addchc(c, '%');
- }
-
- if(graph == 3)
- addch(' ');
-
- /* graph (10+ columns) */
- if(graph == 1 || graph == 3) {
- uic_set(c == UIC_SEL ? UIC_GRAPH_SEL : UIC_GRAPH);
- o = (int)((float)bar_size*(float)(show_as ? n->asize : n->size) / (float)(show_as ? dirlist_maxa : dirlist_maxs));
- for(i=0; i<bar_size; i++)
- addch(i < o ? '#' : ' ');
- }
-
- addchc(c, ']');
-}
-
-
-static void browse_draw_items(struct dir *n, int *x) {
- enum ui_coltype c = n->flags & FF_BSEL ? UIC_SEL : UIC_DEFAULT;
- enum ui_coltype cn = c == UIC_SEL ? UIC_NUM_SEL : UIC_NUM;
-
- if(!show_items)
- return;
- *x += 7;
-
- if(!n->items)
- return;
- else if(n->items < 100*1000) {
- uic_set(cn);
- printw("%6s", fullsize(n->items));
- } else if(n->items < 1000*1000) {
- uic_set(cn);
- printw("%5.1f", n->items / 1000.0);
- addstrc(c, "k");
- } else if(n->items < 1000*1000*1000) {
- uic_set(cn);
- printw("%5.1f", n->items / 1e6);
- addstrc(c, "M");
- } else {
- addstrc(c, " > ");
- addstrc(cn, "1");
- addchc(c, 'B');
- }
-}
-
-
-static void browse_draw_mtime(struct dir *n, int *x) {
- enum ui_coltype c = n->flags & FF_BSEL ? UIC_SEL : UIC_DEFAULT;
- char mbuf[26];
- struct dir_ext *e;
- time_t t;
-
- if (n->flags & FF_EXT) {
- e = dir_ext_ptr(n);
- } else if (!strcmp(n->name, "..") && (n->parent->flags & FF_EXT)) {
- e = dir_ext_ptr(n->parent);
- } else {
- snprintf(mbuf, sizeof(mbuf), "no mtime");
- goto no_mtime;
- }
- t = (time_t)e->mtime;
-
- strftime(mbuf, sizeof(mbuf), "%Y-%m-%d %H:%M:%S %z", localtime(&t));
- uic_set(c == UIC_SEL ? UIC_NUM_SEL : UIC_NUM);
-no_mtime:
- printw("%26s", mbuf);
- *x += 27;
-}
-
-
-static void browse_draw_item(struct dir *n, int row) {
- int x = 0;
-
- enum ui_coltype c = n->flags & FF_BSEL ? UIC_SEL : UIC_DEFAULT;
- uic_set(c);
- mvhline(row, 0, ' ', wincols);
- move(row, 0);
-
- browse_draw_flag(n, &x);
- move(row, x);
-
- if(n != dirlist_parent)
- printsize(c, show_as ? n->asize : n->size);
- x += 10;
- move(row, x);
-
- browse_draw_graph(n, &x);
- move(row, x);
-
- browse_draw_items(n, &x);
- move(row, x);
-
- if (extended_info && show_mtime) {
- browse_draw_mtime(n, &x);
- move(row, x);
- }
-
- if(n->flags & FF_DIR)
- c = c == UIC_SEL ? UIC_DIR_SEL : UIC_DIR;
- addchc(c, n->flags & FF_DIR ? '/' : ' ');
- addstrc(c, cropstr(n->name, wincols-x-1));
-}
-
-
-void browse_draw() {
- struct dir *t;
- const char *tmp;
- int selected = 0, i;
-
- erase();
- t = dirlist_get(0);
-
- /* top line - basic info */
- uic_set(UIC_HD);
- mvhline(0, 0, ' ', wincols);
- mvprintw(0,0,"%s %s ~ Use the arrow keys to navigate, press ", PACKAGE_NAME, PACKAGE_VERSION);
- addchc(UIC_KEY_HD, '?');
- addstrc(UIC_HD, " for help");
- if(dir_import_active)
- mvaddstr(0, wincols-10, "[imported]");
- else if(read_only)
- mvaddstr(0, wincols-11, "[read-only]");
-
- /* second line - the path */
- mvhlinec(UIC_DEFAULT, 1, 0, '-', wincols);
- if(dirlist_par) {
- mvaddchc(UIC_DEFAULT, 1, 3, ' ');
- tmp = getpath(dirlist_par);
- mvaddstrc(UIC_DIR, 1, 4, cropstr(tmp, wincols-8));
- mvaddchc(UIC_DEFAULT, 1, 4+((int)strlen(tmp) > wincols-8 ? wincols-8 : (int)strlen(tmp)), ' ');
- }
-
- /* bottom line - stats */
- uic_set(UIC_HD);
- mvhline(winrows-1, 0, ' ', wincols);
- if(t) {
- mvaddstr(winrows-1, 0, " Total disk usage: ");
- printsize(UIC_HD, t->parent->size);
- addstrc(UIC_HD, " Apparent size: ");
- uic_set(UIC_NUM_HD);
- printsize(UIC_HD, t->parent->asize);
- addstrc(UIC_HD, " Items: ");
- uic_set(UIC_NUM_HD);
- printw("%d", t->parent->items);
- } else
- mvaddstr(winrows-1, 0, " No items to display.");
- uic_set(UIC_DEFAULT);
-
- /* nothing to display? stop here. */
- if(!t)
- return;
-
- /* get start position */
- t = dirlist_top(0);
-
- /* print the list to the screen */
- for(i=0; t && i<winrows-3; t=dirlist_next(t),i++) {
- browse_draw_item(t, 2+i);
- /* save the selected row number for later */
- if(t->flags & FF_BSEL)
- selected = i;
- }
-
- /* draw message window */
- if(message) {
- nccreate(6, 60, "Message");
- ncaddstr(2, 2, message);
- ncaddstr(4, 34, "Press any key to continue");
- }
-
- /* draw information window */
- t = dirlist_get(0);
- if(!message && info_show && t != dirlist_parent)
- browse_draw_info(t);
-
- /* move cursor to selected row for accessibility */
- move(selected+2, 0);
-}
-
-
-int browse_key(int ch) {
- struct dir *t, *sel;
- int i, catch = 0;
-
- /* message window overwrites all keys */
- if(message) {
- message = NULL;
- return 0;
- }
-
- sel = dirlist_get(0);
-
- /* info window overwrites a few keys */
- if(info_show && sel)
- switch(ch) {
- case '1':
- info_page = 0;
- break;
- case '2':
- if(sel->hlnk)
- info_page = 1;
- break;
- case KEY_RIGHT:
- case 'l':
- if(sel->hlnk) {
- info_page = 1;
- catch++;
- }
- break;
- case KEY_LEFT:
- case 'h':
- if(sel->hlnk) {
- info_page = 0;
- catch++;
- }
- break;
- case KEY_UP:
- case 'k':
- if(sel->hlnk && info_page == 1) {
- if(info_start > 0)
- info_start--;
- catch++;
- }
- break;
- case KEY_DOWN:
- case 'j':
- case ' ':
- if(sel->hlnk && info_page == 1) {
- for(i=0,t=sel->hlnk; t!=sel; t=t->hlnk)
- i++;
- if(i > info_start+6)
- info_start++;
- catch++;
- }
- break;
- }
-
- if(!catch)
- switch(ch) {
- /* selecting items */
- case KEY_UP:
- case 'k':
- dirlist_select(dirlist_get(-1));
- dirlist_top(-1);
- info_start = 0;
- break;
- case KEY_DOWN:
- case 'j':
- dirlist_select(dirlist_get(1));
- dirlist_top(1);
- info_start = 0;
- break;
- case KEY_HOME:
- dirlist_select(dirlist_next(NULL));
- dirlist_top(2);
- info_start = 0;
- break;
- case KEY_LL:
- case KEY_END:
- dirlist_select(dirlist_get(1<<30));
- dirlist_top(1);
- info_start = 0;
- break;
- case KEY_PPAGE:
- dirlist_select(dirlist_get(-1*(winrows-3)));
- dirlist_top(-1);
- info_start = 0;
- break;
- case KEY_NPAGE:
- dirlist_select(dirlist_get(winrows-3));
- dirlist_top(1);
- info_start = 0;
- break;
-
- /* sorting items */
- case 'n':
- dirlist_set_sort(DL_COL_NAME, dirlist_sort_col == DL_COL_NAME ? !dirlist_sort_desc : 0, DL_NOCHANGE);
- info_show = 0;
- break;
- case 's':
- i = show_as ? DL_COL_ASIZE : DL_COL_SIZE;
- dirlist_set_sort(i, dirlist_sort_col == i ? !dirlist_sort_desc : 1, DL_NOCHANGE);
- info_show = 0;
- break;
- case 'C':
- dirlist_set_sort(DL_COL_ITEMS, dirlist_sort_col == DL_COL_ITEMS ? !dirlist_sort_desc : 1, DL_NOCHANGE);
- info_show = 0;
- break;
- case 'M':
- if (extended_info) {
- dirlist_set_sort(DL_COL_MTIME, dirlist_sort_col == DL_COL_MTIME ? !dirlist_sort_desc : 1, DL_NOCHANGE);
- info_show = 0;
- }
- break;
- case 'e':
- dirlist_set_hidden(!dirlist_hidden);
- info_show = 0;
- break;
- case 't':
- dirlist_set_sort(DL_NOCHANGE, DL_NOCHANGE, !dirlist_sort_df);
- info_show = 0;
- break;
- case 'a':
- show_as = !show_as;
- if(dirlist_sort_col == DL_COL_ASIZE || dirlist_sort_col == DL_COL_SIZE)
- dirlist_set_sort(show_as ? DL_COL_ASIZE : DL_COL_SIZE, DL_NOCHANGE, DL_NOCHANGE);
- info_show = 0;
- break;
-
- /* browsing */
- case 10:
- case KEY_RIGHT:
- case 'l':
- if(sel != NULL && sel->flags & FF_DIR) {
- dirlist_open(sel == dirlist_parent ? dirlist_par->parent : sel);
- dirlist_top(-3);
- }
- info_show = 0;
- break;
- case KEY_LEFT:
- case KEY_BACKSPACE:
- case 'h':
- case '<':
- if(dirlist_par && dirlist_par->parent != NULL) {
- dirlist_open(dirlist_par->parent);
- dirlist_top(-3);
- }
- info_show = 0;
- break;
-
- /* and other stuff */
- case 'r':
- if(dir_import_active) {
- message = "Directory imported from file, won't refresh.";
- break;
- }
- if(dirlist_par) {
- dir_ui = 2;
- dir_mem_init(dirlist_par);
- dir_scan_init(getpath(dirlist_par));
- }
- info_show = 0;
- break;
- case 'q':
- if(info_show)
- info_show = 0;
- else
- if (confirm_quit)
- quit_init();
- else return 1;
- break;
- case 'g':
- if(++graph > 3)
- graph = 0;
- info_show = 0;
- break;
- case 'c':
- show_items = !show_items;
- break;
- case 'm':
- if (extended_info)
- show_mtime = !show_mtime;
- break;
- case 'i':
- info_show = !info_show;
- break;
- case '?':
- help_init();
- info_show = 0;
- break;
- case 'd':
- if(read_only >= 1 || dir_import_active) {
- message = read_only >= 1
- ? "File deletion disabled in read-only mode."
- : "File deletion not available for imported directories.";
- break;
- }
- if(sel == NULL || sel == dirlist_parent)
- break;
- info_show = 0;
- if((t = dirlist_get(1)) == sel)
- if((t = dirlist_get(-1)) == sel || t == dirlist_parent)
- t = NULL;
- delete_init(sel, t);
- break;
- case 'b':
- if(read_only >= 2 || dir_import_active) {
- message = read_only >= 2
- ? "Shell feature disabled in read-only mode."
- : "Shell feature not available for imported directories.";
- break;
- }
- shell_init();
- break;
- }
-
- /* make sure the info_* options are correct */
- sel = dirlist_get(0);
- if(!info_show || sel == dirlist_parent)
- info_show = info_page = info_start = 0;
- else if(sel && !sel->hlnk)
- info_page = info_start = 0;
-
- return 0;
-}
-
-
-void browse_init(struct dir *par) {
- pstate = ST_BROWSE;
- message = NULL;
- dirlist_open(par);
-}
-
diff --git a/src/browser.h b/src/browser.h
deleted file mode 100644
index 9f1579c..0000000
--- a/src/browser.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#ifndef _browser_h
-#define _browser_h
-
-#include "global.h"
-
-int browse_key(int);
-void browse_draw(void);
-void browse_init(struct dir *);
-
-
-#endif
-
diff --git a/src/delete.c b/src/delete.c
deleted file mode 100644
index d60ee6c..0000000
--- a/src/delete.c
+++ /dev/null
@@ -1,253 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-
-#include "global.h"
-
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-
-
-#define DS_CONFIRM 0
-#define DS_PROGRESS 1
-#define DS_FAILED 2
-
-
-static struct dir *root, *nextsel, *curdir;
-static char noconfirm = 0, ignoreerr = 0, state;
-static signed char seloption;
-static int lasterrno;
-
-
-static void delete_draw_confirm(void) {
- nccreate(6, 60, "Confirm delete");
-
- ncprint(1, 2, "Are you sure you want to delete \"%s\"%c",
- cropstr(root->name, 21), root->flags & FF_DIR ? ' ' : '?');
- if(root->flags & FF_DIR && root->sub != NULL)
- ncprint(2, 18, "and all of its contents?");
-
- if(seloption == 0)
- attron(A_REVERSE);
- ncaddstr(4, 15, "yes");
- attroff(A_REVERSE);
- if(seloption == 1)
- attron(A_REVERSE);
- ncaddstr(4, 24, "no");
- attroff(A_REVERSE);
- if(seloption == 2)
- attron(A_REVERSE);
- ncaddstr(4, 31, "don't ask me again");
- attroff(A_REVERSE);
-
- ncmove(4, seloption == 0 ? 15 : seloption == 1 ? 24 : 31);
-}
-
-
-static void delete_draw_progress(void) {
- nccreate(6, 60, "Deleting...");
-
- ncaddstr(1, 2, cropstr(getpath(curdir), 47));
- ncaddstr(4, 41, "Press ");
- addchc(UIC_KEY, 'q');
- addstrc(UIC_DEFAULT, " to abort");
-}
-
-
-static void delete_draw_error(void) {
- nccreate(6, 60, "Error!");
-
- ncprint(1, 2, "Can't delete %s:", cropstr(getpath(curdir), 42));
- ncaddstr(2, 4, strerror(lasterrno));
-
- if(seloption == 0)
- attron(A_REVERSE);
- ncaddstr(4, 14, "abort");
- attroff(A_REVERSE);
- if(seloption == 1)
- attron(A_REVERSE);
- ncaddstr(4, 23, "ignore");
- attroff(A_REVERSE);
- if(seloption == 2)
- attron(A_REVERSE);
- ncaddstr(4, 33, "ignore all");
- attroff(A_REVERSE);
-}
-
-
-void delete_draw() {
- browse_draw();
- switch(state) {
- case DS_CONFIRM: delete_draw_confirm(); break;
- case DS_PROGRESS: delete_draw_progress(); break;
- case DS_FAILED: delete_draw_error(); break;
- }
-}
-
-
-int delete_key(int ch) {
- /* confirm */
- if(state == DS_CONFIRM)
- switch(ch) {
- case KEY_LEFT:
- case 'h':
- if(--seloption < 0)
- seloption = 0;
- break;
- case KEY_RIGHT:
- case 'l':
- if(++seloption > 2)
- seloption = 2;
- break;
- case '\n':
- if(seloption == 1)
- return 1;
- if(seloption == 2)
- noconfirm++;
- state = DS_PROGRESS;
- break;
- case 'q':
- return 1;
- }
- /* processing deletion */
- else if(state == DS_PROGRESS)
- switch(ch) {
- case 'q':
- return 1;
- }
- /* error */
- else if(state == DS_FAILED)
- switch(ch) {
- case KEY_LEFT:
- case 'h':
- if(--seloption < 0)
- seloption = 0;
- break;
- case KEY_RIGHT:
- case 'l':
- if(++seloption > 2)
- seloption = 2;
- break;
- case 10:
- if(seloption == 0)
- return 1;
- if(seloption == 2)
- ignoreerr++;
- state = DS_PROGRESS;
- break;
- case 'q':
- return 1;
- }
-
- return 0;
-}
-
-
-static int delete_dir(struct dir *dr) {
- struct dir *nxt, *cur;
- int r;
-
- /* check for input or screen resizes */
- curdir = dr;
- if(input_handle(1))
- return 1;
-
- /* do the actual deleting */
- if(dr->flags & FF_DIR) {
- if((r = chdir(dr->name)) < 0)
- goto delete_nxt;
- if(dr->sub != NULL) {
- nxt = dr->sub;
- while(nxt != NULL) {
- cur = nxt;
- nxt = cur->next;
- if(delete_dir(cur))
- return 1;
- }
- }
- if((r = chdir("..")) < 0)
- goto delete_nxt;
- r = dr->sub == NULL ? rmdir(dr->name) : 0;
- } else
- r = unlink(dr->name);
-
-delete_nxt:
- /* error occurred, ask user what to do */
- if(r == -1 && !ignoreerr) {
- state = DS_FAILED;
- lasterrno = errno;
- curdir = dr;
- while(state == DS_FAILED)
- if(input_handle(0))
- return 1;
- } else if(!(dr->flags & FF_DIR && dr->sub != NULL)) {
- freedir(dr);
- return 0;
- }
- return root == dr ? 1 : 0;
-}
-
-
-void delete_process() {
- struct dir *par;
-
- /* confirm */
- seloption = 1;
- while(state == DS_CONFIRM && !noconfirm)
- if(input_handle(0)) {
- browse_init(root->parent);
- return;
- }
-
- /* chdir */
- if(path_chdir(getpath(root->parent)) < 0) {
- state = DS_FAILED;
- lasterrno = errno;
- while(state == DS_FAILED)
- if(input_handle(0))
- return;
- }
-
- /* delete */
- seloption = 0;
- state = DS_PROGRESS;
- par = root->parent;
- delete_dir(root);
- if(nextsel)
- nextsel->flags |= FF_BSEL;
- browse_init(par);
- if(nextsel)
- dirlist_top(-4);
-}
-
-
-void delete_init(struct dir *dr, struct dir *s) {
- state = DS_CONFIRM;
- root = curdir = dr;
- pstate = ST_DEL;
- nextsel = s;
-}
-
diff --git a/src/delete.h b/src/delete.h
deleted file mode 100644
index 6abb431..0000000
--- a/src/delete.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#ifndef _delete_h
-#define _delete_h
-
-#include "global.h"
-
-void delete_process(void);
-int delete_key(int);
-void delete_draw(void);
-void delete_init(struct dir *, struct dir *);
-
-
-#endif
diff --git a/src/dir.h b/src/dir.h
deleted file mode 100644
index fc33eb0..0000000
--- a/src/dir.h
+++ /dev/null
@@ -1,141 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#ifndef _dir_h
-#define _dir_h
-
-/* The dir_* functions and files implement the SCAN state and are organized as
- * follows:
- *
- * Input:
- * Responsible for getting a directory structure into ncdu. Will call the
- * Output functions for data and the UI functions for feedback. Currently
- * there is only one input implementation: dir_scan.c
- * Output:
- * Called by the Input handling code when there's some new file/directory
- * information. The Output code is responsible for doing something with it
- * and determines what action should follow after the Input is done.
- * Currently there is only one output implementation: dir_mem.c.
- * Common:
- * Utility functions and UI code for use by the Input handling code to draw
- * progress/error information on the screen, handle any user input and misc.
- * stuff.
- */
-
-
-/* "Interface" that Input code should call and Output code should implement. */
-struct dir_output {
- /* Called when there is new file/dir info. Call stack for an example
- * directory structure:
- * / item('/')
- * /subdir item('subdir')
- * /subdir/f item('f')
- * .. item(NULL)
- * /abc item('abc')
- * .. item(NULL)
- * Every opened dir is followed by a call to NULL. There is only one top-level
- * dir item. The name of the top-level dir item is the absolute path to the
- * scanned directory.
- *
- * The *item struct has the following fields set when item() is called:
- * size, asize, ino, dev, flags (only DIR,FILE,ERR,OTHFS,EXL,HLNKC).
- * All other fields/flags should be initialized to NULL or 0.
- * The name and dir_ext fields are given separately.
- * All pointers may be overwritten or freed in subsequent calls, so this
- * function should make a copy if necessary.
- *
- * The function should return non-zero on error, at which point errno is
- * assumed to be set to something sensible.
- */
- int (*item)(struct dir *, const char *, struct dir_ext *);
-
- /* Finalizes the output to go to the next program state or exit ncdu. Called
- * after item(NULL) has been called for the root item or before any item()
- * has been called at all.
- * Argument indicates success (0) or failure (1).
- * Failure happens when the root directory couldn't be opened, chdir, lstat,
- * read, when it is empty, or when the user aborted the operation.
- * Return value should be 0 to continue running ncdu, 1 to exit.
- */
- int (*final)(int);
-
- /* The output code is responsible for updating these stats. Can be 0 when not
- * available. */
- int64_t size;
- int items;
-};
-
-
-/* Initializes the SCAN state and dir_output for immediate browsing.
- * On success:
- * If a dir item is given, overwrites it with the new dir struct.
- * Then calls browse_init(new_dir_struct->sub).
- * On failure:
- * If a dir item is given, will just call browse_init(orig).
- * Otherwise, will exit ncdu.
- */
-void dir_mem_init(struct dir *);
-
-/* Initializes the SCAN state and dir_output for exporting to a file. */
-int dir_export_init(const char *fn);
-
-
-/* Function set by input code. Returns dir_output.final(). */
-extern int (*dir_process)(void);
-
-/* Scanning a live directory */
-extern int dir_scan_smfs;
-void dir_scan_init(const char *path);
-
-/* Importing a file */
-extern int dir_import_active;
-int dir_import_init(const char *fn);
-
-#if HAVE_LINUX_MAGIC_H && HAVE_SYS_STATFS_H && HAVE_STATFS
-extern int exclude_kernfs;
-#endif
-
-
-/* The currently configured output functions. */
-extern struct dir_output dir_output;
-
-/* Current path that we're working with. These are defined in dir_common.c. */
-extern char *dir_curpath;
-void dir_curpath_set(const char *);
-void dir_curpath_enter(const char *);
-void dir_curpath_leave(void);
-
-/* Sets the path where the last error occurred, or reset on NULL. */
-void dir_setlasterr(const char *);
-
-/* Error message on fatal error, or NULL if there hasn't been a fatal error yet. */
-extern char *dir_fatalerr;
-void dir_seterr(const char *, ...);
-
-extern int dir_ui;
-int dir_key(int);
-void dir_draw(void);
-
-#endif
diff --git a/src/dir_common.c b/src/dir_common.c
deleted file mode 100644
index 9eb06e8..0000000
--- a/src/dir_common.c
+++ /dev/null
@@ -1,232 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#include "global.h"
-
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-
-
-int (*dir_process)(void);
-char *dir_curpath; /* Full path of the last seen item. */
-struct dir_output dir_output;
-char *dir_fatalerr; /* Error message on a fatal error. (NULL if there was no fatal error) */
-int dir_ui; /* User interface to use */
-static int confirm_quit_while_scanning_stage_1_passed; /* Additional check before quitting */
-static char *lasterr; /* Path where the last error occurred. */
-static int curpathl; /* Allocated length of dir_curpath */
-static int lasterrl; /* ^ of lasterr */
-
-
-static void curpath_resize(int s) {
- if(curpathl < s) {
- curpathl = s < 128 ? 128 : s < curpathl*2 ? curpathl*2 : s;
- dir_curpath = xrealloc(dir_curpath, curpathl);
- }
-}
-
-
-void dir_curpath_set(const char *path) {
- curpath_resize(strlen(path)+1);
- strcpy(dir_curpath, path);
-}
-
-
-void dir_curpath_enter(const char *name) {
- curpath_resize(strlen(dir_curpath)+strlen(name)+2);
- if(dir_curpath[1])
- strcat(dir_curpath, "/");
- strcat(dir_curpath, name);
-}
-
-
-/* removes last component from dir_curpath */
-void dir_curpath_leave() {
- char *tmp;
- if((tmp = strrchr(dir_curpath, '/')) == NULL)
- strcpy(dir_curpath, "/");
- else if(tmp != dir_curpath)
- tmp[0] = 0;
- else
- tmp[1] = 0;
-}
-
-
-void dir_setlasterr(const char *path) {
- if(!path) {
- free(lasterr);
- lasterr = NULL;
- lasterrl = 0;
- return;
- }
- int req = strlen(path)+1;
- if(lasterrl < req) {
- lasterrl = req;
- lasterr = xrealloc(lasterr, lasterrl);
- }
- strcpy(lasterr, path);
-}
-
-
-void dir_seterr(const char *fmt, ...) {
- free(dir_fatalerr);
- dir_fatalerr = NULL;
- if(!fmt)
- return;
-
- va_list va;
- va_start(va, fmt);
- dir_fatalerr = xmalloc(1024); /* Should be enough for everything... */
- vsnprintf(dir_fatalerr, 1023, fmt, va);
- dir_fatalerr[1023] = 0;
- va_end(va);
-}
-
-
-static void draw_progress(void) {
- static const char scantext[] = "Scanning...";
- static const char loadtext[] = "Loading...";
- static size_t anpos = 0;
- const char *antext = dir_import_active ? loadtext : scantext;
- char ani[16] = {0};
- size_t i;
- int width = wincols-5;
-
- nccreate(10, width, antext);
-
- ncaddstr(2, 2, "Total items: ");
- uic_set(UIC_NUM);
- printw("%-9d", dir_output.items);
-
- if(dir_output.size) {
- ncaddstrc(UIC_DEFAULT, 2, 24, "size: ");
- printsize(UIC_DEFAULT, dir_output.size);
- }
-
- uic_set(UIC_DEFAULT);
- ncprint(3, 2, "Current item: %s", cropstr(dir_curpath, width-18));
- if(confirm_quit_while_scanning_stage_1_passed) {
- ncaddstr(8, width-26, "Press ");
- addchc(UIC_KEY, 'y');
- addstrc(UIC_DEFAULT, " to confirm abort");
- } else {
- ncaddstr(8, width-18, "Press ");
- addchc(UIC_KEY, 'q');
- addstrc(UIC_DEFAULT, " to abort");
- }
-
- /* show warning if we couldn't open a dir */
- if(lasterr) {
- attron(A_BOLD);
- ncaddstr(5, 2, "Warning:");
- attroff(A_BOLD);
- ncprint(5, 11, "error scanning %-32s", cropstr(lasterr, width-28));
- ncaddstr(6, 3, "some directory sizes may not be correct");
- }
-
- /* animation - but only if the screen refreshes more than or once every second */
- if(update_delay <= 1000) {
- if(++anpos == strlen(antext)*2)
- anpos = 0;
- memset(ani, ' ', strlen(antext));
- if(anpos < strlen(antext))
- for(i=0; i<=anpos; i++)
- ani[i] = antext[i];
- else
- for(i=strlen(antext)-1; i>anpos-strlen(antext); i--)
- ani[i] = antext[i];
- } else
- strcpy(ani, antext);
- ncaddstr(8, 3, ani);
-}
-
-
-static void draw_error(char *cur, char *msg) {
- int width = wincols-5;
- nccreate(7, width, "Error!");
-
- attron(A_BOLD);
- ncaddstr(2, 2, "Error:");
- attroff(A_BOLD);
-
- ncprint(2, 9, "could not open %s", cropstr(cur, width-26));
- ncprint(3, 4, "%s", cropstr(msg, width-8));
- ncaddstr(5, width-30, "press any key to continue...");
-}
-
-
-void dir_draw() {
- float f;
- const char *unit;
-
- switch(dir_ui) {
- case 0:
- if(dir_fatalerr)
- fprintf(stderr, "%s.\n", dir_fatalerr);
- break;
- case 1:
- if(dir_fatalerr)
- fprintf(stderr, "\r%s.\n", dir_fatalerr);
- else if(dir_output.size) {
- f = formatsize(dir_output.size, &unit);
- fprintf(stderr, "\r%-55s %8d files /%5.1f %s",
- cropstr(dir_curpath, 55), dir_output.items, f, unit);
- } else
- fprintf(stderr, "\r%-65s %8d files", cropstr(dir_curpath, 65), dir_output.items);
- break;
- case 2:
- browse_draw();
- if(dir_fatalerr)
- draw_error(dir_curpath, dir_fatalerr);
- else
- draw_progress();
- break;
- }
-}
-
-
-/* This function can't be called unless dir_ui == 2
- * (Doesn't really matter either way). */
-int dir_key(int ch) {
- if(dir_fatalerr)
- return 1;
- if(confirm_quit && confirm_quit_while_scanning_stage_1_passed) {
- if (ch == 'y'|| ch == 'Y') {
- return 1;
- } else {
- confirm_quit_while_scanning_stage_1_passed = 0;
- return 0;
- }
- } else if(ch == 'q') {
- if(confirm_quit) {
- confirm_quit_while_scanning_stage_1_passed = 1;
- return 0;
- } else
- return 1;
- }
- return 0;
-}
diff --git a/src/dir_export.c b/src/dir_export.c
deleted file mode 100644
index dd96721..0000000
--- a/src/dir_export.c
+++ /dev/null
@@ -1,194 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#include "global.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
-
-
-static FILE *stream;
-
-/* Stack of device IDs, also used to keep track of the level of nesting */
-static struct stack {
- uint64_t *list;
- int size, top;
-} stack;
-
-
-static void output_string(const char *str) {
- for(; *str; str++) {
- switch(*str) {
- case '\n': fputs("\\n", stream); break;
- case '\r': fputs("\\r", stream); break;
- case '\b': fputs("\\b", stream); break;
- case '\t': fputs("\\t", stream); break;
- case '\f': fputs("\\f", stream); break;
- case '\\': fputs("\\\\", stream); break;
- case '"': fputs("\\\"", stream); break;
- default:
- if((unsigned char)*str <= 31 || (unsigned char)*str == 127)
- fprintf(stream, "\\u00%02x", *str);
- else
- fputc(*str, stream);
- break;
- }
- }
-}
-
-
-static void output_int(uint64_t n) {
- char tmp[20];
- int i = 0;
-
- do
- tmp[i++] = n % 10;
- while((n /= 10) > 0);
-
- while(i--)
- fputc(tmp[i]+'0', stream);
-}
-
-
-static void output_info(struct dir *d, const char *name, struct dir_ext *e) {
- if(!extended_info || !(d->flags & FF_EXT))
- e = NULL;
-
- fputs("{\"name\":\"", stream);
- output_string(name);
- fputc('"', stream);
-
- /* No need for asize/dsize if they're 0 (which happens with excluded or failed-to-stat files) */
- if(d->asize) {
- fputs(",\"asize\":", stream);
- output_int((uint64_t)d->asize);
- }
- if(d->size) {
- fputs(",\"dsize\":", stream);
- output_int((uint64_t)d->size);
- }
-
- if(d->dev != nstack_top(&stack, 0)) {
- fputs(",\"dev\":", stream);
- output_int(d->dev);
- }
- fputs(",\"ino\":", stream);
- output_int(d->ino);
-
- if(e) {
- fputs(",\"uid\":", stream);
- output_int(e->uid);
- fputs(",\"gid\":", stream);
- output_int(e->gid);
- fputs(",\"mode\":", stream);
- output_int(e->mode);
- fputs(",\"mtime\":", stream);
- output_int(e->mtime);
- }
-
- /* TODO: Including the actual number of links would be nicer. */
- if(d->flags & FF_HLNKC)
- fputs(",\"hlnkc\":true", stream);
- if(d->flags & FF_ERR)
- fputs(",\"read_error\":true", stream);
- /* excluded/error'd files are "unknown" with respect to the "notreg" field. */
- if(!(d->flags & (FF_DIR|FF_FILE|FF_ERR|FF_EXL|FF_OTHFS|FF_KERNFS|FF_FRMLNK)))
- fputs(",\"notreg\":true", stream);
- if(d->flags & FF_EXL)
- fputs(",\"excluded\":\"pattern\"", stream);
- else if(d->flags & FF_OTHFS)
- fputs(",\"excluded\":\"othfs\"", stream);
- else if(d->flags & FF_KERNFS)
- fputs(",\"excluded\":\"kernfs\"", stream);
- else if(d->flags & FF_FRMLNK)
- fputs(",\"excluded\":\"frmlnk\"", stream);
-
- fputc('}', stream);
-}
-
-
-/* Note on error handling: For convenience, we just keep writing to *stream
- * without checking the return values of the functions. Only at the and of each
- * item() call do we check for ferror(). This greatly simplifies the code, but
- * assumes that calls to fwrite()/fput./etc don't do any weird stuff when
- * called with a stream that's in an error state. */
-static int item(struct dir *item, const char *name, struct dir_ext *ext) {
- if(!item) {
- nstack_pop(&stack);
- if(!stack.top) { /* closing of the root item */
- fputs("]]", stream);
- return fclose(stream);
- } else /* closing of a regular directory item */
- fputs("]", stream);
- return ferror(stream);
- }
-
- dir_output.items++;
-
- /* File header.
- * TODO: Add scan options? */
- if(!stack.top) {
- fputs("[1,1,{\"progname\":\""PACKAGE"\",\"progver\":\""PACKAGE_VERSION"\",\"timestamp\":", stream);
- output_int((uint64_t)time(NULL));
- fputc('}', stream);
- }
-
- fputs(",\n", stream);
- if(item->flags & FF_DIR)
- fputc('[', stream);
-
- output_info(item, name, ext);
-
- if(item->flags & FF_DIR)
- nstack_push(&stack, item->dev);
-
- return ferror(stream);
-}
-
-
-static int final(int fail) {
- nstack_free(&stack);
- return fail ? 1 : 1; /* Silences -Wunused-parameter */
-}
-
-
-int dir_export_init(const char *fn) {
- if(strcmp(fn, "-") == 0)
- stream = stdout;
- else if((stream = fopen(fn, "w")) == NULL)
- return 1;
-
- nstack_init(&stack);
-
- pstate = ST_CALC;
- dir_output.item = item;
- dir_output.final = final;
- dir_output.size = 0;
- dir_output.items = 0;
- return 0;
-}
-
diff --git a/src/dir_import.c b/src/dir_import.c
deleted file mode 100644
index bde822c..0000000
--- a/src/dir_import.c
+++ /dev/null
@@ -1,615 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-/* This JSON parser has the following limitations:
- * - No support for character encodings incompatible with ASCII (e.g.
- * UTF-16/32)
- * - Doesn't validate UTF-8 correctness (in fact, besides the ASCII part this
- * parser doesn't know anything about encoding).
- * - Doesn't validate that there are no duplicate keys in JSON objects.
- * - Isn't very strict with validating non-integer numbers.
- */
-
-#include "global.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <limits.h>
-
-
-/* Max. length of any JSON string we're interested in. A string may of course
- * be larger, we're not going to read more than MAX_VAL in memory. If a string
- * we're interested in (e.g. a file name) is longer than this, reading the
- * import will results in an error. */
-#define MAX_VAL (32*1024)
-
-/* Minimum number of bytes we request from fread() */
-#define MIN_READ_SIZE 1024
-
-/* Read buffer size. Must be at least 2*MIN_READ_SIZE, everything larger
- * improves performance. */
-#define READ_BUF_SIZE (32*1024)
-
-
-int dir_import_active = 0;
-
-
-/* Use a struct for easy batch-allocation and deallocation of state data. */
-static struct ctx {
- FILE *stream;
-
- int line;
- int byte;
- int eof;
- int items;
- char *buf; /* points into readbuf, always zero-terminated. */
- char *lastfill; /* points into readbuf, location of the zero terminator. */
-
- /* scratch space */
- struct dir *buf_dir;
- struct dir_ext buf_ext[1];
-
- char buf_name[MAX_VAL];
- char val[MAX_VAL];
- char readbuf[READ_BUF_SIZE];
-} *ctx;
-
-
-/* Fills readbuf with data from the stream. *buf will have at least n (<
- * READ_BUF_SIZE) bytes available, unless the stream reached EOF or an error
- * occurred. If the file data contains a null-type, this is considered an error.
- * Returns 0 on success, non-zero on error. */
-static int fill(int n) {
- int r;
-
- if(ctx->eof)
- return 0;
-
- r = READ_BUF_SIZE-(ctx->lastfill - ctx->readbuf); /* number of bytes left in the buffer */
- if(n < r)
- n = r-1;
- if(n < MIN_READ_SIZE) {
- r = ctx->lastfill - ctx->buf; /* number of unread bytes left in the buffer */
- memcpy(ctx->readbuf, ctx->buf, r);
- ctx->lastfill = ctx->readbuf + r;
- ctx->buf = ctx->readbuf;
- n = READ_BUF_SIZE-r-1;
- }
-
- do {
- r = fread(ctx->lastfill, 1, n, ctx->stream);
- if(r != n) {
- if(feof(ctx->stream))
- ctx->eof = 1;
- else if(ferror(ctx->stream) && errno != EINTR) {
- dir_seterr("Read error: %s", strerror(errno));
- return 1;
- }
- }
-
- ctx->lastfill[r] = 0;
- if(strlen(ctx->lastfill) != (size_t)r) {
- dir_seterr("Zero-byte found in JSON stream");
- return 1;
- }
- ctx->lastfill += r;
- n -= r;
- } while(!ctx->eof && n > MIN_READ_SIZE);
-
- return 0;
-}
-
-
-/* Two macros that break function calling behaviour, but are damn convenient */
-#define E(_x, _m) do {\
- if(_x) {\
- if(!dir_fatalerr)\
- dir_seterr("Line %d byte %d: %s", ctx->line, ctx->byte, _m);\
- return 1;\
- }\
- } while(0)
-
-#define C(_x) do {\
- if(_x)\
- return 1;\
- } while(0)
-
-
-/* Require at least n bytes in the buffer, throw an error on early EOF.
- * (Macro to quickly handle the common case) */
-#define rfill1 (!*ctx->buf && _rfill(1))
-#define rfill(_n) ((ctx->lastfill - ctx->buf < (_n)) && _rfill(_n))
-
-static int _rfill(int n) {
- C(fill(n));
- E(ctx->lastfill - ctx->buf < n, "Unexpected EOF");
- return 0;
-}
-
-
-/* Consumes n bytes from the buffer. */
-static inline void con(int n) {
- ctx->buf += n;
- ctx->byte += n;
-}
-
-
-/* Consumes any whitespace. If *ctx->buf == 0 after this function, we've reached EOF. */
-static int cons(void) {
- while(1) {
- C(!*ctx->buf && fill(1));
-
- switch(*ctx->buf) {
- case 0x0A:
- /* Special-case the newline-character with respect to consuming stuff
- * from the buffer. This is the only function which *can* consume the
- * newline character, so it's more efficient to handle it in here rather
- * than in the more general con(). */
- ctx->buf++;
- ctx->line++;
- ctx->byte = 0;
- break;
- case 0x20:
- case 0x09:
- case 0x0D:
- con(1);
- break;
- default:
- return 0;
- }
- }
-}
-
-
-static int rstring_esc(char **dest, int *destlen) {
- unsigned int n;
-
- C(rfill1);
-
-#define ap(c) if(*destlen > 1) { *((*dest)++) = c; (*destlen)--; }
- switch(*ctx->buf) {
- case '"': ap('"'); con(1); break;
- case '\\': ap('\\'); con(1); break;
- case '/': ap('/'); con(1); break;
- case 'b': ap(0x08); con(1); break;
- case 'f': ap(0x0C); con(1); break;
- case 'n': ap(0x0A); con(1); break;
- case 'r': ap(0x0D); con(1); break;
- case 't': ap(0x09); con(1); break;
- case 'u':
- C(rfill(5));
-#define hn(n) (n >= '0' && n <= '9' ? n-'0' : n >= 'A' && n <= 'F' ? n-'A'+10 : n >= 'a' && n <= 'f' ? n-'a'+10 : 1<<16)
- n = (hn(ctx->buf[1])<<12) + (hn(ctx->buf[2])<<8) + (hn(ctx->buf[3])<<4) + hn(ctx->buf[4]);
-#undef hn
- if(n <= 0x007F) {
- ap(n);
- } else if(n <= 0x07FF) {
- ap(0xC0 | (n>>6));
- ap(0x80 | (n & 0x3F));
- } else if(n <= 0xFFFF) {
- ap(0xE0 | (n>>12));
- ap(0x80 | ((n>>6) & 0x3F));
- ap(0x80 | (n & 0x3F));
- } else /* this happens if there was an invalid character (n >= (1<<16)) */
- E(1, "Invalid character in \\u escape");
- con(5);
- break;
- default:
- E(1, "Invalid escape sequence");
- }
-#undef ap
- return 0;
-}
-
-
-/* Parse a JSON string and write it to *dest (max. destlen). Consumes but
- * otherwise ignores any characters if the string is longer than destlen. *dest
- * will be null-terminated, dest[destlen-1] = 0 if the string was cut just long
- * enough of was cut off. That byte will be left untouched if the string is
- * small enough. */
-static int rstring(char *dest, int destlen) {
- C(rfill1);
- E(*ctx->buf != '"', "Expected string");
- con(1);
-
- while(1) {
- C(rfill1);
- if(*ctx->buf == '"')
- break;
- if(*ctx->buf == '\\') {
- con(1);
- C(rstring_esc(&dest, &destlen));
- continue;
- }
- E((unsigned char)*ctx->buf <= 0x1F || (unsigned char)*ctx->buf == 0x7F, "Invalid character");
- if(destlen > 1) {
- *(dest++) = *ctx->buf;
- destlen--;
- }
- con(1);
- }
- con(1);
- if(destlen > 0)
- *dest = 0;
- return 0;
-}
-
-
-/* Parse and consume a JSON integer. Throws an error if the value does not fit
- * in an uint64_t, is not an integer or is larger than 'max'. */
-static int rint64(uint64_t *val, uint64_t max) {
- uint64_t v;
- int haschar = 0;
- *val = 0;
- while(1) {
- C(!*ctx->buf && fill(1));
- if(*ctx->buf == '0' && !haschar) {
- con(1);
- break;
- }
- if(*ctx->buf >= '0' && *ctx->buf <= '9') {
- haschar = 1;
- v = (*val)*10 + (*ctx->buf-'0');
- E(v < *val, "Invalid (positive) integer");
- *val = v;
- con(1);
- continue;
- }
- E(!haschar, "Invalid (positive) integer");
- break;
- }
- E(*val > max, "Integer out of range");
- return 0;
-}
-
-
-/* Parse and consume a JSON number. The result is discarded.
- * TODO: Improve validation. */
-static int rnum(void) {
- int haschar = 0;
- C(rfill1);
- while(1) {
- C(!*ctx->buf && fill(1));
- if(*ctx->buf == 'e' || *ctx->buf == 'E' || *ctx->buf == '-' || *ctx->buf == '+' || *ctx->buf == '.' || (*ctx->buf >= '0' && *ctx->buf <= '9')) {
- haschar = 1;
- con(1);
- } else {
- E(!haschar, "Invalid JSON value");
- break;
- }
- }
- return 0;
-}
-
-
-static int rlit(const char *v, int len) {
- C(rfill(len));
- E(strncmp(ctx->buf, v, len) != 0, "Invalid JSON value");
- con(len);
- return 0;
-}
-
-
-/* Parse the "<space> <string> <space> : <space>" part of an object key. */
-static int rkey(char *dest, int destlen) {
- C(cons() || rstring(dest, destlen) || cons());
- E(*ctx->buf != ':', "Expected ':'");
- con(1);
- return cons();
-}
-
-
-/* (Recursively) parse and consume any JSON value. The result is discarded. */
-static int rval(void) {
- C(rfill1);
- switch(*ctx->buf) {
- case 't': /* true */
- C(rlit("true", 4));
- break;
- case 'f': /* false */
- C(rlit("false", 5));
- break;
- case 'n': /* null */
- C(rlit("null", 4));
- break;
- case '"': /* string */
- C(rstring(NULL, 0));
- break;
- case '{': /* object */
- con(1);
- while(1) {
- C(cons());
- if(*ctx->buf == '}')
- break;
- C(rkey(NULL, 0) || rval() || cons());
- if(*ctx->buf == '}')
- break;
- E(*ctx->buf != ',', "Expected ',' or '}'");
- con(1);
- }
- con(1);
- break;
- case '[': /* array */
- con(1);
- while(1) {
- C(cons());
- if(*ctx->buf == ']')
- break;
- C(cons() || rval() || cons());
- if(*ctx->buf == ']')
- break;
- E(*ctx->buf != ',', "Expected ',' or ']'");
- con(1);
- }
- con(1);
- break;
- default: /* assume number */
- C(rnum());
- break;
- }
-
- return 0;
-}
-
-
-/* Consumes everything up to the root item, and checks that this item is a dir. */
-static int header(void) {
- uint64_t v;
-
- C(cons());
- E(*ctx->buf != '[', "Expected JSON array");
- con(1);
- C(cons() || rint64(&v, 10000) || cons());
- E(v != 1, "Incompatible major format version");
- E(*ctx->buf != ',', "Expected ','");
- con(1);
- C(cons() || rint64(&v, 10000) || cons()); /* Ignore the minor version for now */
- E(*ctx->buf != ',', "Expected ','");
- con(1);
- /* Metadata block is currently ignored */
- C(cons() || rval() || cons());
- E(*ctx->buf != ',', "Expected ','");
- con(1);
-
- C(cons());
- E(*ctx->buf != '[', "Top-level item must be a directory");
-
- return 0;
-}
-
-
-static int item(uint64_t);
-
-/* Read and add dir contents */
-static int itemdir(uint64_t dev) {
- while(1) {
- C(cons());
- if(*ctx->buf == ']')
- break;
- E(*ctx->buf != ',', "Expected ',' or ']'");
- con(1);
- C(cons() || item(dev));
- }
- con(1);
- C(cons());
- return 0;
-}
-
-
-/* Reads a JSON object representing a struct dir/dir_ext item. Writes to
- * ctx->buf_dir, ctx->buf_ext and ctx->buf_name. */
-static int iteminfo(void) {
- uint64_t iv;
-
- E(*ctx->buf != '{', "Expected JSON object");
- con(1);
-
- while(1) {
- C(rkey(ctx->val, MAX_VAL));
- /* TODO: strcmp() in this fashion isn't very fast. */
- if(strcmp(ctx->val, "name") == 0) { /* name */
- ctx->val[MAX_VAL-1] = 1;
- C(rstring(ctx->val, MAX_VAL));
- E(ctx->val[MAX_VAL-1] != 1, "Too large string value");
- strcpy(ctx->buf_name, ctx->val);
- } else if(strcmp(ctx->val, "asize") == 0) { /* asize */
- C(rint64(&iv, INT64_MAX));
- ctx->buf_dir->asize = iv;
- } else if(strcmp(ctx->val, "dsize") == 0) { /* dsize */
- C(rint64(&iv, INT64_MAX));
- ctx->buf_dir->size = iv;
- } else if(strcmp(ctx->val, "dev") == 0) { /* dev */
- C(rint64(&iv, UINT64_MAX));
- ctx->buf_dir->dev = iv;
- } else if(strcmp(ctx->val, "ino") == 0) { /* ino */
- C(rint64(&iv, UINT64_MAX));
- ctx->buf_dir->ino = iv;
- } else if(strcmp(ctx->val, "uid") == 0) { /* uid */
- C(rint64(&iv, INT32_MAX));
- ctx->buf_dir->flags |= FF_EXT;
- ctx->buf_ext->uid = iv;
- } else if(strcmp(ctx->val, "gid") == 0) { /* gid */
- C(rint64(&iv, INT32_MAX));
- ctx->buf_dir->flags |= FF_EXT;
- ctx->buf_ext->gid = iv;
- } else if(strcmp(ctx->val, "mode") == 0) { /* mode */
- C(rint64(&iv, UINT16_MAX));
- ctx->buf_dir->flags |= FF_EXT;
- ctx->buf_ext->mode = iv;
- } else if(strcmp(ctx->val, "mtime") == 0) { /* mtime */
- C(rint64(&iv, UINT64_MAX));
- ctx->buf_dir->flags |= FF_EXT;
- ctx->buf_ext->mtime = iv;
- } else if(strcmp(ctx->val, "hlnkc") == 0) { /* hlnkc */
- if(*ctx->buf == 't') {
- C(rlit("true", 4));
- ctx->buf_dir->flags |= FF_HLNKC;
- } else
- C(rlit("false", 5));
- } else if(strcmp(ctx->val, "read_error") == 0) { /* read_error */
- if(*ctx->buf == 't') {
- C(rlit("true", 4));
- ctx->buf_dir->flags |= FF_ERR;
- } else
- C(rlit("false", 5));
- } else if(strcmp(ctx->val, "excluded") == 0) { /* excluded */
- C(rstring(ctx->val, 8));
- if(strcmp(ctx->val, "otherfs") == 0)
- ctx->buf_dir->flags |= FF_OTHFS;
- else if(strcmp(ctx->val, "kernfs") == 0)
- ctx->buf_dir->flags |= FF_KERNFS;
- else if(strcmp(ctx->val, "frmlnk") == 0)
- ctx->buf_dir->flags |= FF_FRMLNK;
- else
- ctx->buf_dir->flags |= FF_EXL;
- } else if(strcmp(ctx->val, "notreg") == 0) { /* notreg */
- if(*ctx->buf == 't') {
- C(rlit("true", 4));
- ctx->buf_dir->flags &= ~FF_FILE;
- } else
- C(rlit("false", 5));
- } else
- C(rval());
-
- C(cons());
- if(*ctx->buf == '}')
- break;
- E(*ctx->buf != ',', "Expected ',' or '}'");
- con(1);
- }
- con(1);
-
- E(!*ctx->buf_name, "No name field present in item information object");
- ctx->items++;
- /* Only call input_handle() once for every 32 items. Importing items is so
- * fast that the time spent in input_handle() dominates when called every
- * time. Don't set this value too high, either, as feedback should still be
- * somewhat responsive when our import data comes from a slow-ish source. */
- return !(ctx->items & 31) ? input_handle(1) : 0;
-}
-
-
-/* Recursively reads a file or directory item */
-static int item(uint64_t dev) {
- int isdir = 0;
- int isroot = ctx->items == 0;
-
- if(*ctx->buf == '[') {
- isdir = 1;
- con(1);
- C(cons());
- }
-
- memset(ctx->buf_dir, 0, offsetof(struct dir, name));
- memset(ctx->buf_ext, 0, sizeof(struct dir_ext));
- *ctx->buf_name = 0;
- ctx->buf_dir->flags |= isdir ? FF_DIR : FF_FILE;
- ctx->buf_dir->dev = dev;
-
- C(iteminfo());
- dev = ctx->buf_dir->dev;
-
- if(isroot)
- dir_curpath_set(ctx->buf_name);
- else
- dir_curpath_enter(ctx->buf_name);
-
- if(isdir) {
- if(dir_output.item(ctx->buf_dir, ctx->buf_name, ctx->buf_ext)) {
- dir_seterr("Output error: %s", strerror(errno));
- return 1;
- }
- C(itemdir(dev));
- if(dir_output.item(NULL, 0, NULL)) {
- dir_seterr("Output error: %s", strerror(errno));
- return 1;
- }
- } else if(dir_output.item(ctx->buf_dir, ctx->buf_name, ctx->buf_ext)) {
- dir_seterr("Output error: %s", strerror(errno));
- return 1;
- }
-
- if(!isroot)
- dir_curpath_leave();
-
- return 0;
-}
-
-
-static int footer(void) {
- C(cons());
- E(*ctx->buf != ']', "Expected ']'");
- con(1);
- C(cons());
- E(*ctx->buf, "Trailing garbage");
- return 0;
-}
-
-
-static int process(void) {
- int fail = 0;
-
- header();
-
- if(!dir_fatalerr)
- fail = item(0);
-
- if(!dir_fatalerr && !fail)
- footer();
-
- if(fclose(ctx->stream) && !dir_fatalerr && !fail)
- dir_seterr("Error closing file: %s", strerror(errno));
- free(ctx->buf_dir);
- free(ctx);
-
- while(dir_fatalerr && !input_handle(0))
- ;
- return dir_output.final(dir_fatalerr || fail);
-}
-
-
-int dir_import_init(const char *fn) {
- FILE *stream;
- if(strcmp(fn, "-") == 0)
- stream = stdin;
- else if((stream = fopen(fn, "r")) == NULL)
- return 1;
-
- ctx = xmalloc(sizeof(struct ctx));
- ctx->stream = stream;
- ctx->line = 1;
- ctx->byte = ctx->eof = ctx->items = 0;
- ctx->buf = ctx->lastfill = ctx->readbuf;
- ctx->buf_dir = xmalloc(dir_memsize(""));
- ctx->readbuf[0] = 0;
-
- dir_curpath_set(fn);
- dir_process = process;
- dir_import_active = 1;
- return 0;
-}
-
diff --git a/src/dir_mem.c b/src/dir_mem.c
deleted file mode 100644
index d1dce8d..0000000
--- a/src/dir_mem.c
+++ /dev/null
@@ -1,215 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#include "global.h"
-
-#include <string.h>
-#include <stdlib.h>
-
-#include <khashl.h>
-
-
-static struct dir *root; /* root directory struct we're scanning */
-static struct dir *curdir; /* directory item that we're currently adding items to */
-static struct dir *orig; /* original directory, when refreshing an already scanned dir */
-
-/* Table of struct dir items with more than one link (in order to detect hard links) */
-#define hlink_hash(d) (kh_hash_uint64((khint64_t)d->dev) ^ kh_hash_uint64((khint64_t)d->ino))
-#define hlink_equal(a, b) ((a)->dev == (b)->dev && (a)->ino == (b)->ino)
-KHASHL_SET_INIT(KH_LOCAL, hl_t, hl, struct dir *, hlink_hash, hlink_equal)
-static hl_t *links = NULL;
-
-
-/* recursively checks a dir structure for hard links and fills the lookup array */
-static void hlink_init(struct dir *d) {
- struct dir *t;
-
- for(t=d->sub; t!=NULL; t=t->next)
- hlink_init(t);
-
- if(!(d->flags & FF_HLNKC))
- return;
- int r;
- hl_put(links, d, &r);
-}
-
-
-/* checks an individual file for hard links and updates its cicrular linked
- * list, also updates the sizes of the parent dirs */
-static void hlink_check(struct dir *d) {
- struct dir *t, *pt, *par;
- int i;
-
- /* add to links table */
- khint_t k = hl_put(links, d, &i);
-
- /* found in the table? update hlnk */
- if(!i) {
- t = kh_key(links, k);
- d->hlnk = t->hlnk == NULL ? t : t->hlnk;
- t->hlnk = d;
- }
-
- /* now update the sizes of the parent directories,
- * This works by only counting this file in the parent directories where this
- * file hasn't been counted yet, which can be determined from the hlnk list.
- * XXX: This may not be the most efficient algorithm to do this */
- for(i=1,par=d->parent; i&&par; par=par->parent) {
- if(d->hlnk)
- for(t=d->hlnk; i&&t!=d; t=t->hlnk)
- for(pt=t->parent; i&&pt; pt=pt->parent)
- if(pt==par)
- i=0;
- if(i) {
- par->size = adds64(par->size, d->size);
- par->asize = adds64(par->asize, d->asize);
- }
- }
-}
-
-
-/* Add item to the correct place in the memory structure */
-static void item_add(struct dir *item) {
- if(!root) {
- root = item;
- /* Make sure that the *root appears to be part of the same dir structure as
- * *orig, otherwise the directory size calculation will be incorrect in the
- * case of hard links. */
- if(orig)
- root->parent = orig->parent;
- } else {
- item->parent = curdir;
- item->next = curdir->sub;
- if(item->next)
- item->next->prev = item;
- curdir->sub = item;
- }
-}
-
-
-static int item(struct dir *dir, const char *name, struct dir_ext *ext) {
- struct dir *t, *item;
-
- /* Go back to parent dir */
- if(!dir) {
- curdir = curdir->parent;
- return 0;
- }
-
- if(!root && orig)
- name = orig->name;
-
- if(!extended_info)
- dir->flags &= ~FF_EXT;
- item = xmalloc(dir->flags & FF_EXT ? dir_ext_memsize(name) : dir_memsize(name));
- memcpy(item, dir, offsetof(struct dir, name));
- strcpy(item->name, name);
- if(dir->flags & FF_EXT)
- memcpy(dir_ext_ptr(item), ext, sizeof(struct dir_ext));
-
- item_add(item);
-
- /* Ensure that any next items will go to this directory */
- if(item->flags & FF_DIR)
- curdir = item;
-
- /* Special-case the name of the root item to be empty instead of "/". This is
- * what getpath() expects. */
- if(item == root && strcmp(item->name, "/") == 0)
- item->name[0] = 0;
-
- /* Update stats of parents. Don't update the size/asize fields if this is a
- * possible hard link, because hlnk_check() will take care of it in that
- * case. */
- if(item->flags & FF_HLNKC) {
- addparentstats(item->parent, 0, 0, 0, 1);
- hlink_check(item);
- } else if(item->flags & FF_EXT) {
- addparentstats(item->parent, item->size, item->asize, dir_ext_ptr(item)->mtime, 1);
- } else {
- addparentstats(item->parent, item->size, item->asize, 0, 1);
- }
-
- /* propagate ERR and SERR back up to the root */
- if(item->flags & FF_SERR || item->flags & FF_ERR)
- for(t=item->parent; t; t=t->parent)
- t->flags |= FF_SERR;
-
- dir_output.size = root->size;
- dir_output.items = root->items;
-
- return 0;
-}
-
-
-static int final(int fail) {
- hl_destroy(links);
- links = NULL;
-
- if(fail) {
- freedir(root);
- if(orig) {
- browse_init(orig);
- return 0;
- } else
- return 1;
- }
-
- /* success, update references and free original item */
- if(orig) {
- root->next = orig->next;
- root->prev = orig->prev;
- if(root->parent && root->parent->sub == orig)
- root->parent->sub = root;
- if(root->prev)
- root->prev->next = root;
- if(root->next)
- root->next->prev = root;
- orig->next = orig->prev = NULL;
- freedir(orig);
- }
-
- browse_init(root);
- dirlist_top(-3);
- return 0;
-}
-
-
-void dir_mem_init(struct dir *_orig) {
- orig = _orig;
- root = curdir = NULL;
- pstate = ST_CALC;
-
- dir_output.item = item;
- dir_output.final = final;
- dir_output.size = 0;
- dir_output.items = 0;
-
- /* Init hash table for hard link detection */
- links = hl_init();
- if(orig)
- hlink_init(getroot(orig));
-}
-
diff --git a/src/dir_scan.c b/src/dir_scan.c
deleted file mode 100644
index 03a582b..0000000
--- a/src/dir_scan.c
+++ /dev/null
@@ -1,405 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#include "global.h"
-
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <dirent.h>
-
-#if HAVE_SYS_ATTR_H && HAVE_GETATTRLIST && HAVE_DECL_ATTR_CMNEXT_NOFIRMLINKPATH
-#include <sys/attr.h>
-#endif
-
-#if HAVE_LINUX_MAGIC_H && HAVE_SYS_STATFS_H && HAVE_STATFS
-#include <sys/statfs.h>
-#include <linux/magic.h>
-#endif
-
-
-/* set S_BLKSIZE if not defined already in sys/stat.h */
-#ifndef S_BLKSIZE
-# define S_BLKSIZE 512
-#endif
-
-
-int dir_scan_smfs; /* Stay on the same filesystem */
-
-static uint64_t curdev; /* current device we're scanning on */
-
-/* scratch space */
-static struct dir *buf_dir;
-static struct dir_ext buf_ext[1];
-
-
-#if HAVE_LINUX_MAGIC_H && HAVE_SYS_STATFS_H && HAVE_STATFS
-int exclude_kernfs; /* Exclude Linux pseudo filesystems */
-
-static int is_kernfs(unsigned long type) {
- if(
-#ifdef BINFMTFS_MAGIC
- type == BINFMTFS_MAGIC ||
-#endif
-#ifdef BPF_FS_MAGIC
- type == BPF_FS_MAGIC ||
-#endif
-#ifdef CGROUP_SUPER_MAGIC
- type == CGROUP_SUPER_MAGIC ||
-#endif
-#ifdef CGROUP2_SUPER_MAGIC
- type == CGROUP2_SUPER_MAGIC||
-#endif
-#ifdef DEBUGFS_MAGIC
- type == DEBUGFS_MAGIC ||
-#endif
-#ifdef DEVPTS_SUPER_MAGIC
- type == DEVPTS_SUPER_MAGIC ||
-#endif
-#ifdef PROC_SUPER_MAGIC
- type == PROC_SUPER_MAGIC ||
-#endif
-#ifdef PSTOREFS_MAGIC
- type == PSTOREFS_MAGIC ||
-#endif
-#ifdef SECURITYFS_MAGIC
- type == SECURITYFS_MAGIC ||
-#endif
-#ifdef SELINUX_MAGIC
- type == SELINUX_MAGIC ||
-#endif
-#ifdef SYSFS_MAGIC
- type == SYSFS_MAGIC ||
-#endif
-#ifdef TRACEFS_MAGIC
- type == TRACEFS_MAGIC ||
-#endif
- 0
- )
- return 1;
-
- return 0;
-}
-#endif
-
-/* Populates the buf_dir and buf_ext with information from the stat struct.
- * Sets everything necessary for output_dir.item() except FF_ERR and FF_EXL. */
-static void stat_to_dir(struct stat *fs) {
- buf_dir->flags |= FF_EXT; /* We always read extended data because it doesn't have an additional cost */
- buf_dir->ino = (uint64_t)fs->st_ino;
- buf_dir->dev = (uint64_t)fs->st_dev;
-
- if(S_ISREG(fs->st_mode))
- buf_dir->flags |= FF_FILE;
- else if(S_ISDIR(fs->st_mode))
- buf_dir->flags |= FF_DIR;
-
- if(!S_ISDIR(fs->st_mode) && fs->st_nlink > 1)
- buf_dir->flags |= FF_HLNKC;
-
- if(dir_scan_smfs && curdev != buf_dir->dev)
- buf_dir->flags |= FF_OTHFS;
-
- if(!(buf_dir->flags & (FF_OTHFS|FF_EXL|FF_KERNFS))) {
- buf_dir->size = fs->st_blocks * S_BLKSIZE;
- buf_dir->asize = fs->st_size;
- }
-
- buf_ext->mode = fs->st_mode;
- buf_ext->mtime = fs->st_mtime;
- buf_ext->uid = (int)fs->st_uid;
- buf_ext->gid = (int)fs->st_gid;
-}
-
-
-/* Reads all filenames in the currently chdir'ed directory and stores it as a
- * nul-separated list of filenames. The list ends with an empty filename (i.e.
- * two nuls). . and .. are not included. Returned memory should be freed. *err
- * is set to 1 if some error occurred. Returns NULL if that error was fatal.
- * The reason for reading everything in memory first and then walking through
- * the list is to avoid eating too many file descriptors in a deeply recursive
- * directory. */
-static char *dir_read(int *err) {
- DIR *dir;
- struct dirent *item;
- char *buf = NULL;
- size_t buflen = 512;
- size_t off = 0;
-
- if((dir = opendir(".")) == NULL) {
- *err = 1;
- return NULL;
- }
-
- buf = xmalloc(buflen);
- errno = 0;
-
- while((item = readdir(dir)) != NULL) {
- if(item->d_name[0] == '.' && (item->d_name[1] == 0 || (item->d_name[1] == '.' && item->d_name[2] == 0)))
- continue;
- size_t req = off+3+strlen(item->d_name);
- if(req > buflen) {
- buflen = req < buflen*2 ? buflen*2 : req;
- buf = xrealloc(buf, buflen);
- }
- strcpy(buf+off, item->d_name);
- off += strlen(item->d_name)+1;
- }
- if(errno)
- *err = 1;
- if(closedir(dir) < 0)
- *err = 1;
-
- buf[off] = 0;
- buf[off+1] = 0;
- return buf;
-}
-
-
-static int dir_walk(char *);
-
-
-/* Tries to recurse into the current directory item (buf_dir is assumed to be the current dir) */
-static int dir_scan_recurse(const char *name) {
- int fail = 0;
- char *dir;
-
- if(chdir(name)) {
- dir_setlasterr(dir_curpath);
- buf_dir->flags |= FF_ERR;
- if(dir_output.item(buf_dir, name, buf_ext) || dir_output.item(NULL, 0, NULL)) {
- dir_seterr("Output error: %s", strerror(errno));
- return 1;
- }
- return 0;
- }
-
- if((dir = dir_read(&fail)) == NULL) {
- dir_setlasterr(dir_curpath);
- buf_dir->flags |= FF_ERR;
- if(dir_output.item(buf_dir, name, buf_ext) || dir_output.item(NULL, 0, NULL)) {
- dir_seterr("Output error: %s", strerror(errno));
- return 1;
- }
- if(chdir("..")) {
- dir_seterr("Error going back to parent directory: %s", strerror(errno));
- return 1;
- } else
- return 0;
- }
-
- /* readdir() failed halfway, not fatal. */
- if(fail)
- buf_dir->flags |= FF_ERR;
-
- if(dir_output.item(buf_dir, name, buf_ext)) {
- dir_seterr("Output error: %s", strerror(errno));
- return 1;
- }
- fail = dir_walk(dir);
- if(dir_output.item(NULL, 0, NULL)) {
- dir_seterr("Output error: %s", strerror(errno));
- return 1;
- }
-
- /* Not being able to chdir back is fatal */
- if(!fail && chdir("..")) {
- dir_seterr("Error going back to parent directory: %s", strerror(errno));
- return 1;
- }
-
- return fail;
-}
-
-
-/* Scans and adds a single item. Recurses into dir_walk() again if this is a
- * directory. Assumes we're chdir'ed in the directory in which this item
- * resides. */
-static int dir_scan_item(const char *name) {
- static struct stat st, stl;
- int fail = 0;
-
-#ifdef __CYGWIN__
- /* /proc/registry names may contain slashes */
- if(strchr(name, '/') || strchr(name, '\\')) {
- buf_dir->flags |= FF_ERR;
- dir_setlasterr(dir_curpath);
- }
-#endif
-
- if(exclude_match(dir_curpath))
- buf_dir->flags |= FF_EXL;
-
- if(!(buf_dir->flags & (FF_ERR|FF_EXL)) && lstat(name, &st)) {
- buf_dir->flags |= FF_ERR;
- dir_setlasterr(dir_curpath);
- }
-
-#if HAVE_LINUX_MAGIC_H && HAVE_SYS_STATFS_H && HAVE_STATFS
- if(exclude_kernfs && !(buf_dir->flags & (FF_ERR|FF_EXL)) && S_ISDIR(st.st_mode)) {
- struct statfs fst;
- if(statfs(name, &fst)) {
- buf_dir->flags |= FF_ERR;
- dir_setlasterr(dir_curpath);
- } else if(is_kernfs(fst.f_type))
- buf_dir->flags |= FF_KERNFS;
- }
-#endif
-
-#if HAVE_SYS_ATTR_H && HAVE_GETATTRLIST && HAVE_DECL_ATTR_CMNEXT_NOFIRMLINKPATH
- if(!follow_firmlinks) {
- struct attrlist list = {
- .bitmapcount = ATTR_BIT_MAP_COUNT,
- .forkattr = ATTR_CMNEXT_NOFIRMLINKPATH,
- };
- struct {
- uint32_t length;
- attrreference_t reference;
- char extra[PATH_MAX];
- } __attribute__((aligned(4), packed)) attributes;
- if (getattrlist(name, &list, &attributes, sizeof(attributes), FSOPT_ATTR_CMN_EXTENDED) == -1) {
- buf_dir->flags |= FF_ERR;
- dir_setlasterr(dir_curpath);
- } else if (strcmp(dir_curpath, (char *)&attributes.reference + attributes.reference.attr_dataoffset))
- buf_dir->flags |= FF_FRMLNK;
- }
-#endif
-
- if(!(buf_dir->flags & (FF_ERR|FF_EXL))) {
- if(follow_symlinks && S_ISLNK(st.st_mode) && !stat(name, &stl) && !S_ISDIR(stl.st_mode))
- stat_to_dir(&stl);
- else
- stat_to_dir(&st);
- }
-
- if(cachedir_tags && (buf_dir->flags & FF_DIR) && !(buf_dir->flags & (FF_ERR|FF_EXL|FF_OTHFS|FF_KERNFS|FF_FRMLNK)))
- if(has_cachedir_tag(name)) {
- buf_dir->flags |= FF_EXL;
- buf_dir->size = buf_dir->asize = 0;
- }
-
- /* Recurse into the dir or output the item */
- if(buf_dir->flags & FF_DIR && !(buf_dir->flags & (FF_ERR|FF_EXL|FF_OTHFS|FF_KERNFS|FF_FRMLNK)))
- fail = dir_scan_recurse(name);
- else if(buf_dir->flags & FF_DIR) {
- if(dir_output.item(buf_dir, name, buf_ext) || dir_output.item(NULL, 0, NULL)) {
- dir_seterr("Output error: %s", strerror(errno));
- fail = 1;
- }
- } else if(dir_output.item(buf_dir, name, buf_ext)) {
- dir_seterr("Output error: %s", strerror(errno));
- fail = 1;
- }
-
- return fail || input_handle(1);
-}
-
-
-/* Walks through the directory that we're currently chdir'ed to. *dir contains
- * the filenames as returned by dir_read(), and will be freed automatically by
- * this function. */
-static int dir_walk(char *dir) {
- int fail = 0;
- char *cur;
-
- fail = 0;
- for(cur=dir; !fail&&cur&&*cur; cur+=strlen(cur)+1) {
- dir_curpath_enter(cur);
- memset(buf_dir, 0, offsetof(struct dir, name));
- memset(buf_ext, 0, sizeof(struct dir_ext));
- fail = dir_scan_item(cur);
- dir_curpath_leave();
- }
-
- free(dir);
- return fail;
-}
-
-
-static int process(void) {
- char *path;
- char *dir;
- int fail = 0;
- struct stat fs;
-
- memset(buf_dir, 0, offsetof(struct dir, name));
- memset(buf_ext, 0, sizeof(struct dir_ext));
-
- if((path = path_real(dir_curpath)) == NULL)
- dir_seterr("Error obtaining full path: %s", strerror(errno));
- else {
- dir_curpath_set(path);
- free(path);
- }
-
- if(!dir_fatalerr && path_chdir(dir_curpath) < 0)
- dir_seterr("Error changing directory: %s", strerror(errno));
-
- /* Can these even fail after a chdir? */
- if(!dir_fatalerr && lstat(".", &fs) != 0)
- dir_seterr("Error obtaining directory information: %s", strerror(errno));
- if(!dir_fatalerr && !S_ISDIR(fs.st_mode))
- dir_seterr("Not a directory");
-
- if(!dir_fatalerr && !(dir = dir_read(&fail)))
- dir_seterr("Error reading directory: %s", strerror(errno));
-
- if(!dir_fatalerr) {
- curdev = (uint64_t)fs.st_dev;
- if(fail)
- buf_dir->flags |= FF_ERR;
- stat_to_dir(&fs);
-
- if(dir_output.item(buf_dir, dir_curpath, buf_ext)) {
- dir_seterr("Output error: %s", strerror(errno));
- fail = 1;
- }
- if(!fail)
- fail = dir_walk(dir);
- if(!fail && dir_output.item(NULL, 0, NULL)) {
- dir_seterr("Output error: %s", strerror(errno));
- fail = 1;
- }
- }
-
- while(dir_fatalerr && !input_handle(0))
- ;
- return dir_output.final(dir_fatalerr || fail);
-}
-
-
-void dir_scan_init(const char *path) {
- dir_curpath_set(path);
- dir_setlasterr(NULL);
- dir_seterr(NULL);
- dir_process = process;
- if (!buf_dir)
- buf_dir = xmalloc(dir_memsize(""));
- pstate = ST_CALC;
-}
diff --git a/src/dirlist.c b/src/dirlist.c
deleted file mode 100644
index bf2ea01..0000000
--- a/src/dirlist.c
+++ /dev/null
@@ -1,398 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#include "global.h"
-
-#include <string.h>
-#include <stdlib.h>
-
-
-/* public variables */
-struct dir *dirlist_parent = NULL,
- *dirlist_par = NULL;
-int64_t dirlist_maxs = 0,
- dirlist_maxa = 0;
-
-int dirlist_sort_desc = 1,
- dirlist_sort_col = DL_COL_SIZE,
- dirlist_sort_df = 0,
- dirlist_hidden = 0;
-
-/* private state vars */
-static struct dir *parent_alloc, *head, *head_real, *selected, *top = NULL;
-
-
-
-#define ISHIDDEN(d) (dirlist_hidden && (d) != dirlist_parent && (\
- (d)->flags & FF_EXL || (d)->name[0] == '.' || (d)->name[strlen((d)->name)-1] == '~'\
- ))
-
-
-static inline int cmp_mtime(struct dir *x, struct dir*y) {
- int64_t x_mtime = 0, y_mtime = 0;
- if (x->flags & FF_EXT)
- x_mtime = dir_ext_ptr(x)->mtime;
- if (y->flags & FF_EXT)
- y_mtime = dir_ext_ptr(y)->mtime;
- return (x_mtime > y_mtime ? 1 : (x_mtime == y_mtime ? 0 : -1));
-}
-
-static int dirlist_cmp(struct dir *x, struct dir *y) {
- int r;
-
- /* dirs are always before files when that option is set */
- if(dirlist_sort_df) {
- if(y->flags & FF_DIR && !(x->flags & FF_DIR))
- return 1;
- else if(!(y->flags & FF_DIR) && x->flags & FF_DIR)
- return -1;
- }
-
- /* sort columns:
- * 1 -> 2 -> 3 -> 4
- * NAME: name -> size -> asize -> items
- * SIZE: size -> asize -> name -> items
- * ASIZE: asize -> size -> name -> items
- * ITEMS: items -> size -> asize -> name
- *
- * Note that the method used below is supposed to be fast, not readable :-)
- */
-#define CMP_NAME strcmp(x->name, y->name)
-#define CMP_SIZE (x->size > y->size ? 1 : (x->size == y->size ? 0 : -1))
-#define CMP_ASIZE (x->asize > y->asize ? 1 : (x->asize == y->asize ? 0 : -1))
-#define CMP_ITEMS (x->items > y->items ? 1 : (x->items == y->items ? 0 : -1))
-
- /* try 1 */
- r = dirlist_sort_col == DL_COL_NAME ? CMP_NAME :
- dirlist_sort_col == DL_COL_SIZE ? CMP_SIZE :
- dirlist_sort_col == DL_COL_ASIZE ? CMP_ASIZE :
- dirlist_sort_col == DL_COL_ITEMS ? CMP_ITEMS :
- cmp_mtime(x, y);
- /* try 2 */
- if(!r)
- r = dirlist_sort_col == DL_COL_SIZE ? CMP_ASIZE : CMP_SIZE;
- /* try 3 */
- if(!r)
- r = (dirlist_sort_col == DL_COL_NAME || dirlist_sort_col == DL_COL_ITEMS) ?
- CMP_ASIZE : CMP_NAME;
- /* try 4 */
- if(!r)
- r = dirlist_sort_col == DL_COL_ITEMS ? CMP_NAME : CMP_ITEMS;
-
- /* reverse when sorting in descending order */
- if(dirlist_sort_desc && r != 0)
- r = r < 0 ? 1 : -1;
-
- return r;
-}
-
-
-static struct dir *dirlist_sort(struct dir *list) {
- struct dir *p, *q, *e, *tail;
- int insize, nmerges, psize, qsize, i;
-
- insize = 1;
- while(1) {
- p = list;
- list = NULL;
- tail = NULL;
- nmerges = 0;
- while(p) {
- nmerges++;
- q = p;
- psize = 0;
- for(i=0; i<insize; i++) {
- psize++;
- q = q->next;
- if(!q) break;
- }
- qsize = insize;
- while(psize > 0 || (qsize > 0 && q)) {
- if(psize == 0) {
- e = q; q = q->next; qsize--;
- } else if(qsize == 0 || !q) {
- e = p; p = p->next; psize--;
- } else if(dirlist_cmp(p,q) <= 0) {
- e = p; p = p->next; psize--;
- } else {
- e = q; q = q->next; qsize--;
- }
- if(tail) tail->next = e;
- else list = e;
- e->prev = tail;
- tail = e;
- }
- p = q;
- }
- tail->next = NULL;
- if(nmerges <= 1) {
- if(list->parent)
- list->parent->sub = list;
- return list;
- }
- insize *= 2;
- }
-}
-
-
-/* passes through the dir listing once and:
- * - makes sure one, and only one, visible item is selected
- * - updates the dirlist_(maxs|maxa) values
- * - makes sure that the FF_BSEL bits are correct */
-static void dirlist_fixup(void) {
- struct dir *t;
-
- /* we're going to determine the selected items from the list itself, so reset this one */
- selected = NULL;
-
- for(t=head; t; t=t->next) {
- /* not visible? not selected! */
- if(ISHIDDEN(t))
- t->flags &= ~FF_BSEL;
- else {
- /* visible and selected? make sure only one item is selected */
- if(t->flags & FF_BSEL) {
- if(!selected)
- selected = t;
- else
- t->flags &= ~FF_BSEL;
- }
- }
-
- /* update dirlist_(maxs|maxa) */
- if(t->size > dirlist_maxs)
- dirlist_maxs = t->size;
- if(t->asize > dirlist_maxa)
- dirlist_maxa = t->asize;
- }
-
- /* no selected items found after one pass? select the first visible item */
- if(!selected)
- if((selected = dirlist_next(NULL)))
- selected->flags |= FF_BSEL;
-}
-
-
-void dirlist_open(struct dir *d) {
- dirlist_par = d;
-
- /* set the head of the list */
- head_real = head = d == NULL ? NULL : d->sub;
-
- /* reset internal status */
- dirlist_maxs = dirlist_maxa = 0;
-
- /* stop if this is not a directory list we can work with */
- if(d == NULL) {
- dirlist_parent = NULL;
- return;
- }
-
- /* sort the dir listing */
- if(head)
- head_real = head = dirlist_sort(head);
-
- /* set the reference to the parent dir */
- if(d->parent) {
- if(!parent_alloc)
- parent_alloc = xcalloc(1, dir_memsize(".."));
- dirlist_parent = parent_alloc;
- strcpy(dirlist_parent->name, "..");
- dirlist_parent->next = head;
- dirlist_parent->parent = d;
- dirlist_parent->sub = d;
- dirlist_parent->flags = FF_DIR;
- head = dirlist_parent;
- } else
- dirlist_parent = NULL;
-
- dirlist_fixup();
-}
-
-
-struct dir *dirlist_next(struct dir *d) {
- if(!head)
- return NULL;
- if(!d) {
- if(!ISHIDDEN(head))
- return head;
- else
- d = head;
- }
- while((d = d->next)) {
- if(!ISHIDDEN(d))
- return d;
- }
- return NULL;
-}
-
-
-static struct dir *dirlist_prev(struct dir *d) {
- if(!head || !d)
- return NULL;
- while((d = d->prev)) {
- if(!ISHIDDEN(d))
- return d;
- }
- if(dirlist_parent)
- return dirlist_parent;
- return NULL;
-}
-
-
-struct dir *dirlist_get(int i) {
- struct dir *t = selected, *d;
-
- if(!head)
- return NULL;
-
- if(ISHIDDEN(selected)) {
- selected = dirlist_next(NULL);
- return selected;
- }
-
- /* i == 0? return the selected item */
- if(!i)
- return selected;
-
- /* positive number? simply move forward */
- while(i > 0) {
- d = dirlist_next(t);
- if(!d)
- return t;
- t = d;
- if(!--i)
- return t;
- }
-
- /* otherwise, backward */
- while(1) {
- d = dirlist_prev(t);
- if(!d)
- return t;
- t = d;
- if(!++i)
- return t;
- }
-}
-
-
-void dirlist_select(struct dir *d) {
- if(!d || !head || ISHIDDEN(d) || d->parent != head->parent)
- return;
-
- selected->flags &= ~FF_BSEL;
- selected = d;
- selected->flags |= FF_BSEL;
-}
-
-
-
-/* We need a hint in order to figure out which item should be on top:
- * 0 = only get the current top, don't set anything
- * 1 = selected has moved down
- * -1 = selected has moved up
- * -2 = selected = first item in the list (faster version of '1')
- * -3 = top should be considered as invalid (after sorting or opening another dir)
- * -4 = an item has been deleted
- * -5 = hidden flag has been changed
- *
- * Actions:
- * hint = -1 or -4 -> top = selected_is_visible ? top : selected
- * hint = -2 or -3 -> top = selected-(winrows-3)/2
- * hint = 1 -> top = selected_is_visible ? top : selected-(winrows-4)
- * hint = 0 or -5 -> top = selected_is_visible ? top : selected-(winrows-3)/2
- *
- * Regardless of the hint, the returned top will always be chosen such that the
- * selected item is visible.
- */
-struct dir *dirlist_top(int hint) {
- struct dir *t;
- int i, visible = 0;
-
- if(hint == -2 || hint == -3)
- top = NULL;
-
- /* check whether the current selected item is within the visible window */
- if(top) {
- i = winrows-3;
- t = dirlist_get(0);
- while(t && i--) {
- if(t == top) {
- visible++;
- break;
- }
- t = dirlist_prev(t);
- }
- }
-
- /* otherwise, get a new top */
- if(!visible)
- top = hint == -1 || hint == -4 ? dirlist_get(0) :
- hint == 1 ? dirlist_get(-1*(winrows-4)) :
- dirlist_get(-1*(winrows-3)/2);
-
- /* also make sure that if the list is longer than the window and the last
- * item is visible, that this last item is also the last on the window */
- t = top;
- i = winrows-3;
- while(t && i--)
- t = dirlist_next(t);
- t = top;
- do {
- top = t;
- t = dirlist_prev(t);
- } while(t && i-- > 0);
-
- return top;
-}
-
-
-void dirlist_set_sort(int col, int desc, int df) {
- /* update config */
- if(col != DL_NOCHANGE)
- dirlist_sort_col = col;
- if(desc != DL_NOCHANGE)
- dirlist_sort_desc = desc;
- if(df != DL_NOCHANGE)
- dirlist_sort_df = df;
-
- /* sort the list (excluding the parent, which is always on top) */
- if(head_real)
- head_real = dirlist_sort(head_real);
- if(dirlist_parent)
- dirlist_parent->next = head_real;
- else
- head = head_real;
- dirlist_top(-3);
-}
-
-
-void dirlist_set_hidden(int hidden) {
- dirlist_hidden = hidden;
- dirlist_fixup();
- dirlist_top(-5);
-}
-
diff --git a/src/dirlist.h b/src/dirlist.h
deleted file mode 100644
index e833b01..0000000
--- a/src/dirlist.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-/* Note: all functions below include a 'reference to parent dir' node at the
- * top of the list. */
-
-#ifndef _dirlist_h
-#define _dirlist_h
-
-#include "global.h"
-
-
-#define DL_NOCHANGE -1
-#define DL_COL_NAME 0
-#define DL_COL_SIZE 1
-#define DL_COL_ASIZE 2
-#define DL_COL_ITEMS 3
-#define DL_COL_MTIME 4
-
-
-void dirlist_open(struct dir *);
-
-/* Get the next non-hidden item,
- * NULL = get first non-hidden item */
-struct dir *dirlist_next(struct dir *);
-
-/* Get the struct dir item relative to the selected item, or the item nearest to the requested item
- * i = 0 get selected item
- * hidden items aren't considered */
-struct dir *dirlist_get(int i);
-
-/* Get/set the first visible item in the list on the screen */
-struct dir *dirlist_top(int hint);
-
-/* Set selected dir (must be in the currently opened directory, obviously) */
-void dirlist_select(struct dir *);
-
-/* Change sort column (arguments should have a NO_CHANGE option) */
-void dirlist_set_sort(int column, int desc, int df);
-
-/* Set the hidden thingy */
-void dirlist_set_hidden(int hidden);
-
-
-/* DO NOT WRITE TO ANY OF THE BELOW VARIABLES FROM OUTSIDE OF dirlist.c! */
-
-/* The 'reference to parent dir' */
-extern struct dir *dirlist_parent;
-
-/* The actual parent dir */
-extern struct dir *dirlist_par;
-
-/* current sorting configuration (set with dirlist_set_sort()) */
-extern int dirlist_sort_desc, dirlist_sort_col, dirlist_sort_df;
-
-/* set with dirlist_set_hidden() */
-extern int dirlist_hidden;
-
-/* maximum size of an item in the opened dir */
-extern int64_t dirlist_maxs, dirlist_maxa;
-
-
-#endif
-
diff --git a/src/exclude.c b/src/exclude.c
deleted file mode 100644
index 572335f..0000000
--- a/src/exclude.c
+++ /dev/null
@@ -1,139 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#include "global.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <fnmatch.h>
-
-
-static struct exclude {
- char *pattern;
- struct exclude *next;
-} *excludes = NULL;
-
-
-
-void exclude_add(char *pat) {
- struct exclude **n;
-
- n = &excludes;
- while(*n != NULL)
- n = &((*n)->next);
-
- *n = (struct exclude *) xcalloc(1, sizeof(struct exclude));
- (*n)->pattern = (char *) xmalloc(strlen(pat)+1);
- strcpy((*n)->pattern, pat);
-}
-
-
-int exclude_addfile(char *file) {
- FILE *f;
- char buf[256];
- int len;
-
- if((f = fopen(file, "r")) == NULL)
- return 1;
-
- while(fgets(buf, 256, f) != NULL) {
- len = strlen(buf)-1;
- while(len >=0 && (buf[len] == '\r' || buf[len] == '\n'))
- buf[len--] = '\0';
- if(len < 0)
- continue;
- exclude_add(buf);
- }
-
- len = ferror(f);
- fclose(f);
- return len;
-}
-
-
-int exclude_match(char *path) {
- struct exclude *n;
- char *c;
-
- for(n=excludes; n!=NULL; n=n->next) {
- if(!fnmatch(n->pattern, path, 0))
- return 1;
- for(c = path; *c; c++)
- if(*c == '/' && c[1] != '/' && !fnmatch(n->pattern, c+1, 0))
- return 1;
- }
- return 0;
-}
-
-
-void exclude_clear() {
- struct exclude *n, *l;
-
- for(n=excludes; n!=NULL; n=l) {
- l = n->next;
- free(n->pattern);
- free(n);
- }
- excludes = NULL;
-}
-
-
-/*
- * Exclusion of directories that contain only cached information.
- * See http://www.brynosaurus.com/cachedir/
- */
-#define CACHEDIR_TAG_FILENAME "CACHEDIR.TAG"
-#define CACHEDIR_TAG_SIGNATURE "Signature: 8a477f597d28d172789f06886806bc55"
-
-int has_cachedir_tag(const char *name) {
- static int path_l = 1024;
- static char *path = NULL;
- int l;
- char buf[sizeof CACHEDIR_TAG_SIGNATURE - 1];
- FILE *f;
- int match = 0;
-
- /* Compute the required length for `path`. */
- l = strlen(name) + sizeof CACHEDIR_TAG_FILENAME + 2;
- if(l > path_l || path == NULL) {
- path_l = path_l * 2;
- if(path_l < l)
- path_l = l;
- /* We don't need to copy the content of `path`, so it's more efficient to
- * use `free` + `malloc`. */
- free(path);
- path = xmalloc(path_l);
- }
- snprintf(path, path_l, "%s/%s", name, CACHEDIR_TAG_FILENAME);
- f = fopen(path, "rb");
-
- if(f != NULL) {
- match = ((fread(buf, 1, sizeof buf, f) == sizeof buf) &&
- !memcmp(buf, CACHEDIR_TAG_SIGNATURE, sizeof buf));
- fclose(f);
- }
- return match;
-}
diff --git a/src/exclude.h b/src/exclude.h
deleted file mode 100644
index 72eff50..0000000
--- a/src/exclude.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#ifndef _exclude_h
-#define _exclude_h
-
-void exclude_add(char *);
-int exclude_addfile(char *);
-int exclude_match(char *);
-void exclude_clear(void);
-int has_cachedir_tag(const char *name);
-
-#endif
diff --git a/src/global.h b/src/global.h
deleted file mode 100644
index afb9c5e..0000000
--- a/src/global.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#ifndef _global_h
-#define _global_h
-
-#include "config.h"
-#include <stdio.h>
-#include <stddef.h>
-#include <limits.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#ifdef HAVE_INTTYPES_H
-# include <inttypes.h>
-#endif
-#ifdef HAVE_STDINT_H
-# include <stdint.h>
-#endif
-
-/* File Flags (struct dir -> flags) */
-#define FF_DIR 0x01
-#define FF_FILE 0x02
-#define FF_ERR 0x04 /* error while reading this item */
-#define FF_OTHFS 0x08 /* excluded because it was another filesystem */
-#define FF_EXL 0x10 /* excluded using exclude patterns */
-#define FF_SERR 0x20 /* error in subdirectory */
-#define FF_HLNKC 0x40 /* hard link candidate (file with st_nlink > 1) */
-#define FF_BSEL 0x80 /* selected */
-#define FF_EXT 0x100 /* extended struct available */
-#define FF_KERNFS 0x200 /* excluded because it was a Linux pseudo filesystem */
-#define FF_FRMLNK 0x400 /* excluded because it was a firmlink */
-
-/* Program states */
-#define ST_CALC 0
-#define ST_BROWSE 1
-#define ST_DEL 2
-#define ST_HELP 3
-#define ST_SHELL 4
-#define ST_QUIT 5
-
-
-/* structure representing a file or directory */
-struct dir {
- int64_t size, asize;
- uint64_t ino, dev;
- struct dir *parent, *next, *prev, *sub, *hlnk;
- int items;
- unsigned short flags;
- char name[];
-};
-
-/* A note on the ino and dev fields above: ino is usually represented as ino_t,
- * which POSIX specifies to be an unsigned integer. dev is usually represented
- * as dev_t, which may be either a signed or unsigned integer, and in practice
- * both are used. dev represents an index / identifier of a device or
- * filesystem, and I'm unsure whether a negative value has any meaning in that
- * context. Hence my choice of using an unsigned integer. Negative values, if
- * we encounter them, will just get typecasted into a positive value. No
- * information is lost in this conversion, and the semantics remain the same.
- */
-
-/* Extended information for a struct dir. This struct is stored in the same
- * memory region as struct dir, placed after the name field. See util.h for
- * macros to help manage this. */
-struct dir_ext {
- uint64_t mtime;
- int uid, gid;
- unsigned short mode;
-};
-
-
-/* program state */
-extern int pstate;
-/* read-only flag, 1+ = disable deletion, 2+ = also disable shell */
-extern int read_only;
-/* minimum screen update interval when calculating, in ms */
-extern long update_delay;
-/* filter directories with CACHEDIR.TAG */
-extern int cachedir_tags;
-/* flag if we should ask for confirmation when quitting */
-extern int confirm_quit;
-/* flag whether we want to enable use of struct dir_ext */
-extern int extended_info;
-/* flag whether we want to follow symlinks */
-extern int follow_symlinks;
-/* flag whether we want to follow firmlinks */
-extern int follow_firmlinks;
-
-/* handle input from keyboard and update display */
-int input_handle(int);
-
-/* de-initialize ncurses */
-void close_nc(void);
-
-
-/* import all other global functions and variables */
-#include "browser.h"
-#include "delete.h"
-#include "dir.h"
-#include "dirlist.h"
-#include "exclude.h"
-#include "help.h"
-#include "path.h"
-#include "util.h"
-#include "shell.h"
-#include "quit.h"
-
-#endif
diff --git a/src/help.c b/src/help.c
deleted file mode 100644
index 4ceeebc..0000000
--- a/src/help.c
+++ /dev/null
@@ -1,212 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#include "global.h"
-
-#include <ncurses.h>
-#include <string.h>
-
-
-static int page, start;
-
-
-#define KEYS 19
-static const char *keys[KEYS*2] = {
-/*|----key----| |----------------description----------------|*/
- "up, k", "Move cursor up",
- "down, j", "Move cursor down",
- "right/enter", "Open selected directory",
- "left, <, h", "Open parent directory",
- "n", "Sort by name (ascending/descending)",
- "s", "Sort by size (ascending/descending)",
- "C", "Sort by items (ascending/descending)",
- "M", "Sort by mtime (-e flag)",
- "d", "Delete selected file or directory",
- "t", "Toggle dirs before files when sorting",
- "g", "Show percentage and/or graph",
- "a", "Toggle between apparent size and disk usage",
- "c", "Toggle display of child item counts",
- "m", "Toggle display of latest mtime (-e flag)",
- "e", "Show/hide hidden or excluded files",
- "i", "Show information about selected item",
- "r", "Recalculate the current directory",
- "b", "Spawn shell in current directory",
- "q", "Quit ncdu"
-};
-
-
-#define FLAGS 9
-static const char *flags[FLAGS*2] = {
- "!", "An error occurred while reading this directory",
- ".", "An error occurred while reading a subdirectory",
- "<", "File or directory is excluded from the statistics",
- "e", "Empty directory",
- ">", "Directory was on another filesystem",
- "@", "This is not a file nor a dir (symlink, socket, ...)",
- "^", "Excluded Linux pseudo-filesystem",
- "H", "Same file was already counted (hard link)",
- "F", "Excluded firmlink",
-};
-
-void help_draw() {
- int i, line;
-
- browse_draw();
-
- nccreate(15, 60, "ncdu help");
- ncaddstr(13, 42, "Press ");
- uic_set(UIC_KEY);
- addch('q');
- uic_set(UIC_DEFAULT);
- addstr(" to close");
-
- nctab(30, page == 1, 1, "Keys");
- nctab(39, page == 2, 2, "Format");
- nctab(50, page == 3, 3, "About");
-
- switch(page) {
- case 1:
- line = 1;
- for(i=start*2; i<start*2+20; i+=2) {
- uic_set(UIC_KEY);
- ncaddstr(++line, 13-strlen(keys[i]), keys[i]);
- uic_set(UIC_DEFAULT);
- ncaddstr(line, 15, keys[i+1]);
- }
- if(start != KEYS-10)
- ncaddstr(12, 25, "-- more --");
- break;
- case 2:
- attron(A_BOLD);
- ncaddstr(2, 3, "X [size] [graph] [file or directory]");
- attroff(A_BOLD);
- ncaddstr(3, 4, "The X is only present in the following cases:");
- line = 4;
- for(i=start*2; i<start*2+14; i+=2) {
- uic_set(UIC_FLAG);
- ncaddstr(++line, 4, flags[i]);
- uic_set(UIC_DEFAULT);
- ncaddstr(line, 7, flags[i+1]);
- }
- if(start != FLAGS-7)
- ncaddstr(12, 25, "-- more --");
- break;
- case 3:
- /* Indeed, too much spare time */
- attron(A_REVERSE);
-#define x 12
-#define y 3
- /* N */
- ncaddstr(y+0, x+0, " ");
- ncaddstr(y+1, x+0, " ");
- ncaddstr(y+2, x+0, " ");
- ncaddstr(y+3, x+0, " ");
- ncaddstr(y+4, x+0, " ");
- ncaddstr(y+1, x+4, " ");
- ncaddstr(y+2, x+4, " ");
- ncaddstr(y+3, x+4, " ");
- ncaddstr(y+4, x+4, " ");
- /* C */
- ncaddstr(y+0, x+8, " ");
- ncaddstr(y+1, x+8, " ");
- ncaddstr(y+2, x+8, " ");
- ncaddstr(y+3, x+8, " ");
- ncaddstr(y+4, x+8, " ");
- /* D */
- ncaddstr(y+0, x+19, " ");
- ncaddstr(y+1, x+19, " ");
- ncaddstr(y+2, x+15, " ");
- ncaddstr(y+3, x+15, " ");
- ncaddstr(y+3, x+19, " ");
- ncaddstr(y+4, x+15, " ");
- /* U */
- ncaddstr(y+0, x+23, " ");
- ncaddstr(y+1, x+23, " ");
- ncaddstr(y+2, x+23, " ");
- ncaddstr(y+3, x+23, " ");
- ncaddstr(y+0, x+27, " ");
- ncaddstr(y+1, x+27, " ");
- ncaddstr(y+2, x+27, " ");
- ncaddstr(y+3, x+27, " ");
- ncaddstr(y+4, x+23, " ");
- attroff(A_REVERSE);
- ncaddstr(y+0, x+30, "NCurses");
- ncaddstr(y+1, x+30, "Disk");
- ncaddstr(y+2, x+30, "Usage");
- ncprint( y+4, x+30, "%s", PACKAGE_VERSION);
- ncaddstr( 9, 7, "Written by Yoran Heling <projects@yorhel.nl>");
- ncaddstr(10, 16, "https://dev.yorhel.nl/ncdu/");
- break;
- }
-}
-
-
-int help_key(int ch) {
- switch(ch) {
- case '1':
- case '2':
- case '3':
- page = ch-'0';
- start = 0;
- break;
- case KEY_RIGHT:
- case KEY_NPAGE:
- case 'l':
- if(++page > 3)
- page = 3;
- start = 0;
- break;
- case KEY_LEFT:
- case KEY_PPAGE:
- case 'h':
- if(--page < 1)
- page = 1;
- start = 0;
- break;
- case KEY_DOWN:
- case ' ':
- case 'j':
- if((page == 1 && start < KEYS-10) || (page == 2 && start < FLAGS-7))
- start++;
- break;
- case KEY_UP:
- case 'k':
- if(start > 0)
- start--;
- break;
- default:
- pstate = ST_BROWSE;
- }
- return 0;
-}
-
-
-void help_init() {
- page = 1;
- start = 0;
- pstate = ST_HELP;
-}
-
-
diff --git a/src/help.h b/src/help.h
deleted file mode 100644
index 09fd553..0000000
--- a/src/help.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#ifndef _help_h
-#define _help_h
-
-#include "global.h"
-
-int help_key(int);
-void help_draw(void);
-void help_init(void);
-
-
-#endif
-
diff --git a/src/main.c b/src/main.c
deleted file mode 100644
index 6e85c52..0000000
--- a/src/main.c
+++ /dev/null
@@ -1,359 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#include "global.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-
-#include <unistd.h>
-#include <sys/time.h>
-
-#include <yopt.h>
-
-
-int pstate;
-int read_only = 0;
-long update_delay = 100;
-int cachedir_tags = 0;
-int extended_info = 0;
-int follow_symlinks = 0;
-int follow_firmlinks = 1;
-int confirm_quit = 0;
-
-static int min_rows = 17, min_cols = 60;
-static int ncurses_init = 0;
-static int ncurses_tty = 0; /* Explicitly open /dev/tty instead of using stdio */
-static long lastupdate = 999;
-
-
-static void screen_draw(void) {
- switch(pstate) {
- case ST_CALC: dir_draw(); break;
- case ST_BROWSE: browse_draw(); break;
- case ST_HELP: help_draw(); break;
- case ST_SHELL: shell_draw(); break;
- case ST_DEL: delete_draw(); break;
- case ST_QUIT: quit_draw(); break;
- }
-}
-
-
-/* wait:
- * -1: non-blocking, always draw screen
- * 0: blocking wait for input and always draw screen
- * 1: non-blocking, draw screen only if a configured delay has passed or after keypress
- */
-int input_handle(int wait) {
- int ch;
- struct timeval tv;
-
- if(wait != 1)
- screen_draw();
- else {
- gettimeofday(&tv, NULL);
- tv.tv_usec = (1000*(tv.tv_sec % 1000) + (tv.tv_usec / 1000)) / update_delay;
- if(lastupdate != tv.tv_usec) {
- screen_draw();
- lastupdate = tv.tv_usec;
- }
- }
-
- /* No actual input handling is done if ncurses hasn't been initialized yet. */
- if(!ncurses_init)
- return wait == 0 ? 1 : 0;
-
- nodelay(stdscr, wait?1:0);
- errno = 0;
- while((ch = getch()) != ERR) {
- if(ch == KEY_RESIZE) {
- if(ncresize(min_rows, min_cols))
- min_rows = min_cols = 0;
- /* ncresize() may change nodelay state, make sure to revert it. */
- nodelay(stdscr, wait?1:0);
- screen_draw();
- continue;
- }
- switch(pstate) {
- case ST_CALC: return dir_key(ch);
- case ST_BROWSE: return browse_key(ch);
- case ST_HELP: return help_key(ch);
- case ST_DEL: return delete_key(ch);
- case ST_QUIT: return quit_key(ch);
- }
- screen_draw();
- }
- if(errno == EPIPE || errno == EBADF || errno == EIO)
- return 1;
- return 0;
-}
-
-
-/* parse command line */
-static void argv_parse(int argc, char **argv) {
- yopt_t yopt;
- int v;
- char *val;
- char *export = NULL;
- char *import = NULL;
- char *dir = NULL;
-
- static yopt_opt_t opts[] = {
- { 'h', 0, "-h,-?,--help" },
- { 'q', 0, "-q" },
- { 'v', 0, "-v,-V,--version" },
- { 'x', 0, "-x" },
- { 'e', 0, "-e" },
- { 'r', 0, "-r" },
- { 'o', 1, "-o" },
- { 'f', 1, "-f" },
- { '0', 0, "-0" },
- { '1', 0, "-1" },
- { '2', 0, "-2" },
- { 1, 1, "--exclude" },
- { 'X', 1, "-X,--exclude-from" },
- { 'L', 0, "-L,--follow-symlinks" },
- { 'C', 0, "--exclude-caches" },
- { 2, 0, "--exclude-kernfs" },
- { 3, 0, "--follow-firmlinks" }, /* undocumented, this behavior is the current default */
- { 4, 0, "--exclude-firmlinks" },
- { 's', 0, "--si" },
- { 'Q', 0, "--confirm-quit" },
- { 'c', 1, "--color" },
- {0,0,NULL}
- };
-
- dir_ui = -1;
- si = 0;
-
- yopt_init(&yopt, argc, argv, opts);
- while((v = yopt_next(&yopt, &val)) != -1) {
- switch(v) {
- case 0 : dir = val; break;
- case 'h':
- printf("ncdu <options> <directory>\n\n");
- printf(" -h,--help This help message\n");
- printf(" -q Quiet mode, refresh interval 2 seconds\n");
- printf(" -v,-V,--version Print version\n");
- printf(" -x Same filesystem\n");
- printf(" -e Enable extended information\n");
- printf(" -r Read only\n");
- printf(" -o FILE Export scanned directory to FILE\n");
- printf(" -f FILE Import scanned directory from FILE\n");
- printf(" -0,-1,-2 UI to use when scanning (0=none,2=full ncurses)\n");
- printf(" --si Use base 10 (SI) prefixes instead of base 2\n");
- printf(" --exclude PATTERN Exclude files that match PATTERN\n");
- printf(" -X, --exclude-from FILE Exclude files that match any pattern in FILE\n");
- printf(" -L, --follow-symlinks Follow symbolic links (excluding directories)\n");
- printf(" --exclude-caches Exclude directories containing CACHEDIR.TAG\n");
-#if HAVE_LINUX_MAGIC_H && HAVE_SYS_STATFS_H && HAVE_STATFS
- printf(" --exclude-kernfs Exclude Linux pseudo filesystems (procfs,sysfs,cgroup,...)\n");
-#endif
-#if HAVE_SYS_ATTR_H && HAVE_GETATTRLIST && HAVE_DECL_ATTR_CMNEXT_NOFIRMLINKPATH
- printf(" --exclude-firmlinks Exclude firmlinks on macOS\n");
-#endif
- printf(" --confirm-quit Confirm quitting ncdu\n");
- printf(" --color SCHEME Set color scheme (off/dark)\n");
- exit(0);
- case 'q': update_delay = 2000; break;
- case 'v':
- printf("ncdu %s\n", PACKAGE_VERSION);
- exit(0);
- case 'x': dir_scan_smfs = 1; break;
- case 'e': extended_info = 1; break;
- case 'r': read_only++; break;
- case 's': si = 1; break;
- case 'o': export = val; break;
- case 'f': import = val; break;
- case '0': dir_ui = 0; break;
- case '1': dir_ui = 1; break;
- case '2': dir_ui = 2; break;
- case 'Q': confirm_quit = 1; break;
- case 1 : exclude_add(val); break; /* --exclude */
- case 'X':
- if(exclude_addfile(val)) {
- fprintf(stderr, "Can't open %s: %s\n", val, strerror(errno));
- exit(1);
- }
- break;
- case 'L': follow_symlinks = 1; break;
- case 'C':
- cachedir_tags = 1;
- break;
-
- case 2 : /* --exclude-kernfs */
-#if HAVE_LINUX_MAGIC_H && HAVE_SYS_STATFS_H && HAVE_STATFS
- exclude_kernfs = 1; break;
-#else
- fprintf(stderr, "This feature is not supported on your platform\n");
- exit(1);
-#endif
- case 3 : /* --follow-firmlinks */
-#if HAVE_SYS_ATTR_H && HAVE_GETATTRLIST && HAVE_DECL_ATTR_CMNEXT_NOFIRMLINKPATH
- follow_firmlinks = 1; break;
-#else
- fprintf(stderr, "This feature is not supported on your platform\n");
- exit(1);
-#endif
- case 4 : /* --exclude-firmlinks */
-#if HAVE_SYS_ATTR_H && HAVE_GETATTRLIST && HAVE_DECL_ATTR_CMNEXT_NOFIRMLINKPATH
- follow_firmlinks = 0; break;
-#else
- fprintf(stderr, "This feature is not supported on your platform\n");
- exit(1);
-#endif
- case 'c':
- if(strcmp(val, "off") == 0) { uic_theme = 0; }
- else if(strcmp(val, "dark") == 0) { uic_theme = 1; }
- else {
- fprintf(stderr, "Unknown --color option: %s\n", val);
- exit(1);
- }
- break;
- case -2:
- fprintf(stderr, "ncdu: %s.\n", val);
- exit(1);
- }
- }
-
- if(export) {
- if(dir_export_init(export)) {
- fprintf(stderr, "Can't open %s: %s\n", export, strerror(errno));
- exit(1);
- }
- if(strcmp(export, "-") == 0)
- ncurses_tty = 1;
- } else
- dir_mem_init(NULL);
-
- if(import) {
- if(dir_import_init(import)) {
- fprintf(stderr, "Can't open %s: %s\n", import, strerror(errno));
- exit(1);
- }
- if(strcmp(import, "-") == 0)
- ncurses_tty = 1;
- } else
- dir_scan_init(dir ? dir : ".");
-
- /* Use the single-line scan feedback by default when exporting to file, no
- * feedback when exporting to stdout. */
- if(dir_ui == -1)
- dir_ui = export && strcmp(export, "-") == 0 ? 0 : export ? 1 : 2;
-}
-
-
-/* Initializes ncurses only when not done yet. */
-static void init_nc(void) {
- int ok = 0;
- FILE *tty;
- SCREEN *term;
-
- if(ncurses_init)
- return;
- ncurses_init = 1;
-
- if(ncurses_tty) {
- tty = fopen("/dev/tty", "r+");
- if(!tty) {
- fprintf(stderr, "Error opening /dev/tty: %s\n", strerror(errno));
- exit(1);
- }
- term = newterm(NULL, tty, tty);
- if(term)
- set_term(term);
- ok = !!term;
- } else {
- /* Make sure the user doesn't accidentally pipe in data to ncdu's standard
- * input without using "-f -". An annoying input sequence could result in
- * the deletion of your files, which we want to prevent at all costs. */
- if(!isatty(0)) {
- fprintf(stderr, "Standard input is not a TTY. Did you mean to import a file using '-f -'?\n");
- exit(1);
- }
- ok = !!initscr();
- }
-
- if(!ok) {
- fprintf(stderr, "Error while initializing ncurses.\n");
- exit(1);
- }
-
- uic_init();
- cbreak();
- noecho();
- curs_set(0);
- keypad(stdscr, TRUE);
- if(ncresize(min_rows, min_cols))
- min_rows = min_cols = 0;
-}
-
-
-void close_nc() {
- if(ncurses_init) {
- erase();
- refresh();
- endwin();
- }
-}
-
-
-/* main program */
-int main(int argc, char **argv) {
- read_locale();
- argv_parse(argc, argv);
-
- if(dir_ui == 2)
- init_nc();
-
- while(1) {
- /* We may need to initialize/clean up the screen when switching from the
- * (sometimes non-ncurses) CALC state to something else. */
- if(pstate != ST_CALC) {
- if(dir_ui == 1)
- fputc('\n', stderr);
- init_nc();
- }
-
- if(pstate == ST_CALC) {
- if(dir_process()) {
- if(dir_ui == 1)
- fputc('\n', stderr);
- break;
- }
- } else if(pstate == ST_DEL)
- delete_process();
- else if(input_handle(0))
- break;
- }
-
- close_nc();
- exclude_clear();
-
- return 0;
-}
-
diff --git a/src/main.zig b/src/main.zig
new file mode 100644
index 0000000..65ab30d
--- /dev/null
+++ b/src/main.zig
@@ -0,0 +1,72 @@
+const std = @import("std");
+const model = @import("model.zig");
+const scan = @import("scan.zig");
+
+var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
+pub const allocator = &general_purpose_allocator.allocator;
+
+pub const Config = struct {
+ same_fs: bool = true,
+ extended: bool = false,
+ exclude_caches: bool = false,
+ follow_symlinks: bool = false,
+ exclude_kernfs: bool = false,
+ // TODO: exclude patterns
+
+ update_delay: u32 = 100,
+ si: bool = false,
+ // TODO: color scheme
+
+ read_only: bool = false,
+ can_shell: bool = true,
+ confirm_quit: bool = false,
+};
+
+pub var config = Config{};
+
+// For debugging
+fn writeTree(out: anytype, e: *model.Entry, indent: u32) @TypeOf(out).Error!void {
+ var i: u32 = 0;
+ while (i<indent) {
+ try out.writeByte(' ');
+ i += 1;
+ }
+ try out.print("{s} blocks={d} size={d}", .{ e.name(), e.blocks, e.size });
+
+ if (e.dir()) |d| {
+ try out.print(" blocks={d}-{d} size={d}-{d} items={d}-{d} dev={x}", .{
+ d.total_blocks, d.shared_blocks,
+ d.total_size, d.shared_size,
+ d.total_items, d.shared_items, d.dev
+ });
+ if (d.err) try out.writeAll(" err");
+ if (d.suberr) try out.writeAll(" suberr");
+ } else if (e.file()) |f| {
+ if (f.err) try out.writeAll(" err");
+ if (f.excluded) try out.writeAll(" excluded");
+ if (f.other_fs) try out.writeAll(" other_fs");
+ if (f.kernfs) try out.writeAll(" kernfs");
+ if (f.notreg) try out.writeAll(" notreg");
+ } else if (e.link()) |l| {
+ try out.print(" ino={x} nlinks={d}", .{ l.ino, l.nlink });
+ }
+
+ try out.writeByte('\n');
+ if (e.dir()) |d| {
+ var s = d.sub;
+ while (s) |sub| {
+ try writeTree(out, sub, indent+4);
+ s = sub.next;
+ }
+ }
+}
+
+pub fn main() anyerror!void {
+ std.log.info("align={}, Entry={}, Dir={}, Link={}, File={}.",
+ .{@alignOf(model.Dir), @sizeOf(model.Entry), @sizeOf(model.Dir), @sizeOf(model.Link), @sizeOf(model.File)});
+ try scan.scanRoot("/");
+
+ //var out = std.io.bufferedWriter(std.io.getStdOut().writer());
+ //try writeTree(out.writer(), &model.root.entry, 0);
+ //try out.flush();
+}
diff --git a/src/model.zig b/src/model.zig
new file mode 100644
index 0000000..9782c4f
--- /dev/null
+++ b/src/model.zig
@@ -0,0 +1,350 @@
+const std = @import("std");
+const main = @import("main.zig");
+
+// While an arena allocator is optimimal for almost all scenarios in which ncdu
+// is used, it doesn't allow for re-using deleted nodes after doing a delete or
+// refresh operation, so a long-running ncdu session with regular refreshes
+// will leak memory, but I'd say that's worth the efficiency gains.
+// (TODO: Measure, though. Might as well use a general purpose allocator if the
+// memory overhead turns out to be insignificant.)
+var allocator = std.heap.ArenaAllocator.init(std.heap.page_allocator);
+
+pub const EType = packed enum(u2) { dir, link, file };
+
+// Memory layout:
+// Dir + name (+ alignment + Ext)
+// or: Link + name (+ alignment + Ext)
+// or: File + name (+ alignment + Ext)
+//
+// Entry is always the first part of Dir, Link and File, so a pointer cast to
+// *Entry is always safe and an *Entry can be casted to the full type.
+// (TODO: What are the aliassing rules for Zig? There is a 'noalias' keyword,
+// but does that mean all unmarked pointers are allowed to alias?)
+// (TODO: The 'alignment' in the layout above is a lie, none of these structs
+// or fields have any sort of alignment. This is great for saving memory but
+// perhaps not very great for code size or performance. Might want to
+// experiment with setting some alignment and measure the impact)
+// (TODO: Putting Ext before the Entry pointer may be a little faster; removes
+// the need to iterate over the name)
+pub const Entry = packed struct {
+ etype: EType,
+ isext: bool,
+ blocks: u61, // 512-byte blocks
+ size: u64,
+ next: ?*Entry,
+
+ const Self = @This();
+
+ pub fn dir(self: *Self) ?*Dir {
+ return if (self.etype == .dir) @ptrCast(*Dir, self) else null;
+ }
+
+ pub fn link(self: *Self) ?*Link {
+ return if (self.etype == .link) @ptrCast(*Link, self) else null;
+ }
+
+ pub fn file(self: *Self) ?*File {
+ return if (self.etype == .file) @ptrCast(*File, self) else null;
+ }
+
+ fn name_offset(etype: EType) usize {
+ return switch (etype) {
+ .dir => @byteOffsetOf(Dir, "name"),
+ .link => @byteOffsetOf(Link, "name"),
+ .file => @byteOffsetOf(File, "name"),
+ };
+ }
+
+ pub fn name(self: *const Self) [:0]const u8 {
+ const ptr = @intToPtr([*:0]u8, @ptrToInt(self) + name_offset(self.etype));
+ return ptr[0..std.mem.lenZ(ptr) :0];
+ }
+
+ pub fn ext(self: *Self) ?*Ext {
+ if (!self.isext) return null;
+ const n = self.name();
+ return @intToPtr(*Ext, std.mem.alignForward(@ptrToInt(self) + name_offset(self.etype) + n.len + 1, @alignOf(Ext)));
+ }
+
+ pub fn create(etype: EType, isext: bool, ename: []const u8) !*Entry {
+ const base_size = name_offset(etype) + ename.len + 1;
+ const size = (if (isext) std.mem.alignForward(base_size, @alignOf(Ext))+@sizeOf(Ext) else base_size);
+ var ptr = try allocator.allocator.allocWithOptions(u8, size, @alignOf(Entry), null);
+ std.mem.set(u8, ptr, 0); // kind of ugly, but does the trick
+ var e = @ptrCast(*Entry, ptr);
+ e.etype = etype;
+ e.isext = isext;
+ var name_ptr = @intToPtr([*]u8, @ptrToInt(e) + name_offset(etype));
+ std.mem.copy(u8, name_ptr[0..ename.len], ename);
+ //std.debug.warn("{any}\n", .{ @ptrCast([*]u8, e)[0..size] });
+ return e;
+ }
+
+ // Set the 'err' flag on Dirs and Files, propagating 'suberr' to parents.
+ pub fn set_err(self: *Self, parents: *const Parents) void {
+ if (self.dir()) |d| d.err = true
+ else if (self.file()) |f| f.err = true
+ else unreachable;
+ var it = parents.iter();
+ if (&parents.top().entry == self) _ = it.next();
+ while (it.next()) |p| {
+ if (p.suberr) break;
+ p.suberr = true;
+ }
+ }
+
+ // Insert this entry into the tree at the given directory, updating parent sizes and item counts.
+ // (TODO: This function creates an unrecoverable mess on OOM, need to do something better)
+ pub fn insert(self: *Entry, parents: *const Parents) !void {
+ self.next = parents.top().sub;
+ parents.top().sub = self;
+ if (self.dir()) |d| std.debug.assert(d.sub == null);
+
+ const dev = parents.top().dev;
+ // Set if this is the first time we've found this hardlink in the bottom-most directory of the given dev.
+ // Means we should count it for other-dev parent dirs, too.
+ var new_hl = false;
+
+ // TODO: Saturating add/substract
+ var it = parents.iter();
+ while(it.next()) |p| {
+ var add_total = false;
+
+ // Hardlink in a subdirectory with a different device, only count it the first time.
+ if (self.link() != null and dev != p.dev) {
+ add_total = new_hl;
+
+ } else if (self.link()) |l| {
+ const n = HardlinkNode{ .ino = l.ino, .dir = p, .num_files = 1 };
+ var d = try devices.items[dev].hardlinks.getOrPut(n);
+ new_hl = !d.found_existing;
+ if (d.found_existing) d.entry.key.num_files += 1;
+ // First time we encounter this file in this dir, count it.
+ if (d.entry.key.num_files == 1) {
+ add_total = true;
+ p.shared_size += self.size;
+ p.shared_blocks += self.blocks;
+ p.shared_items += 1;
+ // Encountered this file in this dir the same number of times as its link count, meaning it's not shared with other dirs.
+ } else if(d.entry.key.num_files == l.nlink) {
+ p.shared_size -= self.size;
+ p.shared_blocks -= self.blocks;
+ p.shared_items -= 1;
+ }
+ } else {
+ add_total = true;
+ }
+ if(add_total) {
+ p.total_size += self.size;
+ p.total_blocks += self.blocks;
+ p.total_items += 1;
+ }
+ }
+ }
+};
+
+const DevId = u30; // Can be reduced to make room for more flags in Dir.
+
+pub const Dir = packed struct {
+ entry: Entry,
+
+ sub: ?*Entry,
+
+ // total_*: Total size of all unique files + dirs. Non-shared hardlinks are counted only once.
+ // (i.e. the space you'll need if you created a filesystem with only this dir)
+ // shared_*: Unique hardlinks that still have references outside of this directory.
+ // (i.e. the space you won't reclaim by deleting this dir)
+ // (space reclaimed by deleting a dir =~ total_ - shared_)
+ total_blocks: u64,
+ shared_blocks: u64,
+ total_size: u64,
+ shared_size: u64,
+ total_items: u32,
+ shared_items: u32,
+ // TODO: ncdu1 only keeps track of a total item count including duplicate hardlinks.
+ // That number seems useful, too. Include it somehow?
+
+ // Indexes into the global 'devices' array
+ dev: DevId,
+
+ err: bool,
+ suberr: bool,
+
+ // Only used to find the @byteOffsetOff, the name is written at this point as a 0-terminated string.
+ // (Old C habits die hard)
+ name: u8,
+};
+
+// File that's been hardlinked (i.e. nlink > 1)
+pub const Link = packed struct {
+ entry: Entry,
+ ino: u64,
+ // dev is inherited from the parent Dir
+ nlink: u32,
+ name: u8,
+};
+
+// Anything that's not an (indexed) directory or hardlink. Excluded directories are also "Files".
+pub const File = packed struct {
+ entry: Entry,
+
+ err: bool,
+ excluded: bool,
+ other_fs: bool,
+ kernfs: bool,
+ notreg: bool,
+ _pad: u3,
+
+ name: u8,
+};
+
+pub const Ext = packed struct {
+ mtime: u64,
+ uid: i32,
+ gid: i32,
+ mode: u16,
+};
+
+
+// Hardlink handling:
+//
+// Global lookup table of dev -> (ino,*Dir) -> num_files
+//
+// num_files is how many times the file has been found in the particular dir.
+// num_links is the file's st_nlink count.
+//
+// Adding a hardlink: O(parents)
+//
+// for dir in file.parents:
+// add to dir.total_* if it's not yet in the lookup table
+// add to num_files in the lookup table
+// add to dir.shared_* where num_files == 1
+//
+// Removing a hardlink: O(parents)
+//
+// for dir in file.parents:
+// subtract from num_files in the lookup table
+// subtract from dir.total_* if num_files == 0
+// subtract from dir.shared_* if num_files == num_links-1
+// remove from lookup table if num_files == 0
+//
+// Re-calculating full hardlink stats (only possible when also storing sizes):
+//
+// reset total_* and shared_* for all dirs
+// for (file,dir) in lookup_table:
+// dir.total_* += file
+// if file.num_links != dir.num_files:
+// dir.shared_* += file
+//
+// Problem: num_links is not available in ncdu JSON dumps, will have to assume
+// that there are no shared hardlinks outside of the given dump.
+//
+// Problem: This data structure does not provide a way to easily list all paths
+// with the same dev,ino. ncdu provides this list in the info window. Doesn't
+// seem too commonly used, can still be provided by a slow full scan of the
+// tree.
+
+
+// 20 bytes per hardlink/Dir entry, everything in a single allocation.
+// (Should really be aligned to 8 bytes and hence take up 24 bytes, but let's see how this works out)
+//
+// getEntry() allows modification of the key without re-insertion (this is unsafe in the general case, but works fine for modifying num_files)
+//
+// Potential problem: HashMap uses a 32bit item counter, which may be exceeded in extreme scenarios.
+// (ncdu itself doesn't support more than 31bit-counted files, but this table is hardlink_count*parent_dirs and may grow a bit)
+
+const HardlinkNode = packed struct {
+ ino: u64,
+ dir: *Dir,
+ num_files: u32,
+
+ const Self = @This();
+
+ // hash() assumes a struct layout, hence the 'packed struct'
+ fn hash(self: Self) u64 { return std.hash.Wyhash.hash(0, @ptrCast([*]const u8, &self)[0..@byteOffsetOf(Self, "dir")+@sizeOf(*Dir)]); }
+ fn eql(a: Self, b: Self) bool { return a.ino == b.ino and a.dir == b.dir; }
+};
+
+// Device entry, this is used for two reasons:
+// 1. st_dev ids are 64-bit, but in a typical filesystem there's only a few
+// unique ids, hence we can save RAM by only storing smaller DevId's in Dir
+// entries and using that as an index to a lookup table.
+// 2. Keeping track of hardlink counts for each dir and inode, as described above.
+//
+// (Device entries are never deallocated)
+const Device = struct {
+ dev: u64,
+ hardlinks: Hardlinks = Hardlinks.init(main.allocator),
+
+ const Hardlinks = std.HashMap(HardlinkNode, void, HardlinkNode.hash, HardlinkNode.eql, 80);
+};
+
+var devices: std.ArrayList(Device) = std.ArrayList(Device).init(main.allocator);
+var dev_lookup: std.AutoHashMap(u64, DevId) = std.AutoHashMap(u64, DevId).init(main.allocator);
+
+pub fn getDevId(dev: u64) !DevId {
+ var d = try dev_lookup.getOrPut(dev);
+ if (!d.found_existing) {
+ errdefer dev_lookup.removeAssertDiscard(dev);
+ d.entry.value = @intCast(DevId, devices.items.len);
+ try devices.append(.{ .dev = dev });
+ }
+ return d.entry.value;
+}
+
+pub fn getDev(id: DevId) u64 {
+ return devices.items[id].dev;
+}
+
+pub var root: *Dir = undefined;
+
+// Stack of parent directories, convenient helper when constructing and traversing the tree.
+// The 'root' node is always implicitely at the bottom of the stack.
+pub const Parents = struct {
+ stack: std.ArrayList(*Dir) = std.ArrayList(*Dir).init(main.allocator),
+
+ const Self = @This();
+
+ pub fn push(self: *Self, dir: *Dir) !void {
+ return self.stack.append(dir);
+ }
+
+ // Attempting to remove the root node is considered a bug.
+ pub fn pop(self: *Self) void {
+ _ = self.stack.pop();
+ }
+
+ pub fn top(self: *const Self) *Dir {
+ return if (self.stack.items.len == 0) root else self.stack.items[self.stack.items.len-1];
+ }
+
+ pub const Iterator = struct {
+ lst: *const Self,
+ index: usize = 0, // 0 = top of the stack, counts upwards to go down
+
+ pub fn next(it: *Iterator) ?*Dir {
+ const len = it.lst.stack.items.len;
+ if (it.index > len) return null;
+ it.index += 1;
+ return if (it.index > len) root else it.lst.stack.items[len-it.index];
+ }
+ };
+
+ // Iterate from top to bottom of the stack.
+ pub fn iter(self: *const Self) Iterator {
+ return .{ .lst = self };
+ }
+};
+
+test "name offsets" {
+ std.testing.expectEqual(@bitOffsetOf(Dir, "name") % 8, 0);
+ std.testing.expectEqual(@bitOffsetOf(Link, "name") % 8, 0);
+ std.testing.expectEqual(@bitOffsetOf(File, "name") % 8, 0);
+}
+
+test "entry" {
+ var e = Entry.create(.file, false, "hello") catch unreachable;
+ std.debug.assert(e.etype == .file);
+ std.debug.assert(!e.isext);
+ std.testing.expectEqualStrings(e.name(), "hello");
+}
diff --git a/src/path.c b/src/path.c
deleted file mode 100644
index 8ccdfa9..0000000
--- a/src/path.c
+++ /dev/null
@@ -1,246 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#include "global.h"
-
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <unistd.h>
-#include <limits.h>
-
-#ifndef LINK_MAX
-# ifdef _POSIX_LINK_MAX
-# define LINK_MAX _POSIX_LINK_MAX
-# else
-# define LINK_MAX 32
-# endif
-#endif
-
-
-#define RPATH_CNKSZ 256
-
-
-/* splits a path into components and does a bit of cannonicalization.
- a pointer to a reversed array of components is stored in res and the
- number of components is returned.
- cur is modified, and res has to be free()d after use */
-static int path_split(char *cur, char ***res) {
- char **old;
- int i, j, n;
-
- cur += strspn(cur, "/");
- n = strlen(cur);
-
- /* replace slashes with zeros */
- for(i=j=0; i<n; i++)
- if(cur[i] == '/') {
- cur[i] = 0;
- if(cur[i-1] != 0)
- j++;
- }
-
- /* create array of the components */
- old = xmalloc((j+1)*sizeof(char *));
- *res = xmalloc((j+1)*sizeof(char *));
- for(i=j=0; i<n; i++)
- if(i == 0 || (cur[i-1] == 0 && cur[i] != 0))
- old[j++] = cur+i;
-
- /* re-order and remove parts */
- for(i=n=0; --j>=0; ) {
- if(!strcmp(old[j], "..")) {
- n++;
- continue;
- }
- if(!strcmp(old[j], "."))
- continue;
- if(n) {
- n--;
- continue;
- }
- (*res)[i++] = old[j];
- }
- free(old);
- return i;
-}
-
-
-/* copies path and prepends cwd if needed, to ensure an absolute path
- return value has to be free()'d manually */
-static char *path_absolute(const char *path) {
- int i, n;
- char *ret;
-
- /* not an absolute path? prepend cwd */
- if(path[0] != '/') {
- n = RPATH_CNKSZ;
- ret = xmalloc(n);
- errno = 0;
- while(!getcwd(ret, n) && errno == ERANGE) {
- n += RPATH_CNKSZ;
- ret = xrealloc(ret, n);
- errno = 0;
- }
- if(errno) {
- free(ret);
- return NULL;
- }
-
- i = strlen(path) + strlen(ret) + 2;
- if(i > n)
- ret = xrealloc(ret, i);
- strcat(ret, "/");
- strcat(ret, path);
- /* otherwise, just make a copy */
- } else {
- ret = xmalloc(strlen(path)+1);
- strcpy(ret, path);
- }
- return ret;
-}
-
-
-/* NOTE: cwd and the memory cur points to are unreliable after calling this
- * function.
- * TODO: This code is rather fragile and inefficient. A rewrite is in order.
- */
-static char *path_real_rec(char *cur, int *links) {
- int i, n, tmpl, lnkl = 0;
- char **arr, *tmp, *lnk = NULL, *ret = NULL;
-
- tmpl = strlen(cur)+1;
- tmp = xmalloc(tmpl);
-
- /* split path */
- i = path_split(cur, &arr);
-
- /* re-create full path */
- strcpy(tmp, "/");
- if(i > 0) {
- lnkl = RPATH_CNKSZ;
- lnk = xmalloc(lnkl);
- if(chdir("/") < 0)
- goto path_real_done;
- }
-
- while(--i>=0) {
- if(arr[i][0] == 0)
- continue;
- /* check for symlink */
- while((n = readlink(arr[i], lnk, lnkl)) == lnkl || (n < 0 && errno == ERANGE)) {
- lnkl += RPATH_CNKSZ;
- lnk = xrealloc(lnk, lnkl);
- }
- if(n < 0 && errno != EINVAL)
- goto path_real_done;
- if(n > 0) {
- if(++*links > LINK_MAX) {
- errno = ELOOP;
- goto path_real_done;
- }
- lnk[n++] = 0;
- /* create new path */
- if(lnk[0] != '/')
- n += strlen(tmp);
- if(tmpl < n) {
- tmpl = n;
- tmp = xrealloc(tmp, tmpl);
- }
- if(lnk[0] != '/')
- strcat(tmp, lnk);
- else
- strcpy(tmp, lnk);
- /* append remaining directories */
- while(--i>=0) {
- n += strlen(arr[i])+1;
- if(tmpl < n) {
- tmpl = n;
- tmp = xrealloc(tmp, tmpl);
- }
- strcat(tmp, "/");
- strcat(tmp, arr[i]);
- }
- /* call path_real_rec() with the new path */
- ret = path_real_rec(tmp, links);
- goto path_real_done;
- }
- /* not a symlink, append component and go to the next part */
- strcat(tmp, arr[i]);
- if(i) {
- if(chdir(arr[i]) < 0)
- goto path_real_done;
- strcat(tmp, "/");
- }
- }
- ret = tmp;
-
-path_real_done:
- if(ret != tmp)
- free(tmp);
- if(lnkl > 0)
- free(lnk);
- free(arr);
- return ret;
-}
-
-
-char *path_real(const char *orig) {
- int links = 0;
- char *tmp, *ret;
-
- if(orig == NULL)
- return NULL;
-
- if((tmp = path_absolute(orig)) == NULL)
- return NULL;
- ret = path_real_rec(tmp, &links);
- free(tmp);
- return ret;
-}
-
-
-int path_chdir(const char *path) {
- char **arr, *cur;
- int i, r = -1;
-
- if((cur = path_absolute(path)) == NULL)
- return -1;
-
- i = path_split(cur, &arr);
- if(chdir("/") < 0)
- goto path_chdir_done;
- while(--i >= 0)
- if(chdir(arr[i]) < 0)
- goto path_chdir_done;
- r = 0;
-
-path_chdir_done:
- free(cur);
- free(arr);
- return r;
-}
-
diff --git a/src/path.h b/src/path.h
deleted file mode 100644
index 773da9d..0000000
--- a/src/path.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-/*
- path.c reimplements realpath() and chdir(), both functions accept
- arbitrary long path names not limited by PATH_MAX.
-
- Caveats/bugs:
- - path_real uses chdir(), so it's not thread safe
- - Process requires +x access for all directory components
- - Potentionally slow
- - path_real doesn't check for the existence of the last component
- - cwd is unreliable after path_real
-*/
-
-#ifndef _path_h
-#define _path_h
-
-/* path_real reimplements realpath(). The returned string is allocated
- by malloc() and should be manually free()d by the programmer. */
-extern char *path_real(const char *);
-
-/* works exactly the same as chdir() */
-extern int path_chdir(const char *);
-
-#endif
diff --git a/src/quit.c b/src/quit.c
deleted file mode 100644
index 154e319..0000000
--- a/src/quit.c
+++ /dev/null
@@ -1,50 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2015-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#include "global.h"
-
-#include <ncurses.h>
-
-int quit_key(int ch) {
- switch(ch) {
- case 'y':
- case 'Y':
- return 1;
- default:
- pstate = ST_BROWSE;
- }
- return 0;
-}
-
-void quit_draw() {
- browse_draw();
-
- nccreate(4,30, "ncdu confirm quit");
- ncaddstr(2,2, "Really quit? (y/N)");
-}
-
-void quit_init() {
- pstate = ST_QUIT;
-}
diff --git a/src/quit.h b/src/quit.h
deleted file mode 100644
index 6c50d5e..0000000
--- a/src/quit.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2015-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#ifndef _quit_h
-#define _quit_h
-
-#include "global.h"
-
-int quit_key(int);
-void quit_draw(void);
-void quit_init(void);
-
-
-#endif
-
diff --git a/src/scan.zig b/src/scan.zig
new file mode 100644
index 0000000..dacb7ff
--- /dev/null
+++ b/src/scan.zig
@@ -0,0 +1,138 @@
+const std = @import("std");
+const main = @import("main.zig");
+const model = @import("model.zig");
+
+
+// Concise stat struct for fields we're interested in, with the types used by the model.
+const Stat = struct {
+ blocks: u61,
+ size: u64,
+ dev: u64,
+ ino: u64,
+ nlink: u32,
+ dir: bool,
+ reg: bool,
+ ext: model.Ext,
+};
+
+// Cast any integer type to the target type, clamping the
+// value to the supported maximum if necessary.
+fn castClamp(comptime T: type, x: anytype) T {
+ // (adapted from std.math.cast)
+ if (std.math.maxInt(@TypeOf(x)) > std.math.maxInt(T) and x > std.math.maxInt(T)) {
+ return std.math.maxInt(T);
+ } else if (std.math.minInt(@TypeOf(x)) < std.math.minInt(T) and x < std.math.minInt(T)) {
+ return std.math.minInt(T);
+ } else {
+ return @intCast(T, x);
+ }
+}
+
+// Cast any integer type to the unsigned target type, wrapping/truncating as necessary.
+fn castWrap(comptime T: type, x: anytype) T {
+ return @intCast(T, x); // TODO
+}
+
+fn clamp(comptime T: type, comptime field: anytype, x: anytype) std.meta.fieldInfo(T, field).field_type {
+ return castClamp(std.meta.fieldInfo(T, field).field_type, x);
+}
+
+fn wrap(comptime T: type, comptime field: anytype, x: anytype) std.meta.fieldInfo(T, field).field_type {
+ return castWrap(std.meta.fieldInfo(T, field).field_type, x);
+}
+
+fn readStat(parent: std.fs.Dir, name: [:0]const u8) !Stat {
+ const stat = try std.os.fstatatZ(parent.fd, name, 0);
+ return Stat{
+ .blocks = clamp(Stat, .blocks, stat.blocks),
+ .size = clamp(Stat, .size, stat.size),
+ .dev = wrap(Stat, .dev, stat.dev),
+ .ino = wrap(Stat, .ino, stat.ino),
+ .nlink = clamp(Stat, .nlink, stat.nlink),
+ .dir = std.os.system.S_ISDIR(stat.mode),
+ .reg = std.os.system.S_ISREG(stat.mode),
+ .ext = .{
+ .mtime = clamp(model.Ext, .mtime, stat.mtime().tv_sec),
+ .uid = wrap(model.Ext, .uid, stat.uid),
+ .gid = wrap(model.Ext, .gid, stat.gid),
+ .mode = clamp(model.Ext, .mode, stat.mode & 0xffff),
+ },
+ };
+}
+
+// Read and index entries of the dir identified by parent/parents.top().
+// (TODO: shouldn't error on OOM but instead call a function that waits or something)
+fn scanDir(parents: *model.Parents, parent: std.fs.Dir) std.mem.Allocator.Error!void {
+ var dir = parent.openDirZ(parents.top().entry.name(), .{ .access_sub_paths = true, .iterate = true, .no_follow = true }) catch {
+ parents.top().entry.set_err(parents);
+ return;
+ };
+ defer dir.close();
+
+ var it = dir.iterate();
+ while(true) {
+ const entry = it.next() catch {
+ parents.top().entry.set_err(parents);
+ return;
+ } orelse break;
+
+ // TODO: Check for exclude patterns
+
+ // XXX: Surely the name already has a trailing \0 in the buffer received by the OS?
+ const name_z = std.os.toPosixPath(entry.name) catch undefined;
+ const stat = readStat(dir, &name_z) catch {
+ var e = try model.Entry.create(.file, false, entry.name);
+ e.insert(parents) catch unreachable;
+ e.set_err(parents);
+ continue;
+ };
+
+ if (main.config.same_fs and stat.dev != model.getDev(parents.top().dev)) {
+ var e = try model.Entry.create(.file, false, entry.name);
+ e.file().?.other_fs = true;
+ e.insert(parents) catch unreachable;
+ continue;
+ }
+
+ // TODO Check for kernfs
+ // TODO Follow symlink if that option is enabled
+ // TODO Check for CACHEDIR.TAG if that option is enabled and this is a dir
+
+ const etype = if (stat.dir) model.EType.dir else if (stat.nlink > 1) model.EType.link else model.EType.file;
+ var e = try model.Entry.create(etype, main.config.extended, entry.name);
+ e.blocks = stat.blocks;
+ e.size = stat.size;
+ if (e.dir()) |d| {
+ d.dev = try model.getDevId(stat.dev);
+ // The dir entry itself also counts.
+ d.total_blocks = stat.blocks;
+ d.total_size = stat.size;
+ d.total_items = 1;
+ }
+ if (e.ext()) |ext| ext.* = stat.ext;
+ if (e.link()) |l| {
+ l.ino = stat.ino;
+ l.nlink = stat.nlink;
+ }
+ try e.insert(parents);
+
+ if (e.dir()) |d| {
+ try parents.push(d);
+ try scanDir(parents, dir);
+ parents.pop();
+ }
+ }
+}
+
+pub fn scanRoot(path: [:0]const u8) !void {
+ const stat = try readStat(std.fs.cwd(), path);
+ if (!stat.dir) return error.NotADirectory;
+ model.root = (try model.Entry.create(.dir, false, path)).dir().?;
+ model.root.entry.blocks = stat.blocks;
+ model.root.entry.size = stat.size;
+ model.root.dev = try model.getDevId(stat.dev);
+ if (model.root.entry.ext()) |ext| ext.* = stat.ext;
+
+ var parents = model.Parents{};
+ try scanDir(&parents, std.fs.cwd());
+}
diff --git a/src/shell.c b/src/shell.c
deleted file mode 100644
index fd589e6..0000000
--- a/src/shell.c
+++ /dev/null
@@ -1,82 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
- Shell support: Copyright (c) 2014 Thomas Jarosch
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#include "config.h"
-#include "global.h"
-#include "dirlist.h"
-#include "util.h"
-
-#include <ncurses.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/wait.h>
-
-void shell_draw() {
- const char *full_path;
- int res;
-
- /* suspend ncurses mode */
- def_prog_mode();
- endwin();
-
- full_path = getpath(dirlist_par);
- res = chdir(full_path);
- if (res != 0) {
- reset_prog_mode();
- clear();
- printw("ERROR: Can't change directory: %s (errcode: %d)\n"
- "\n"
- "Press any key to continue.",
- full_path, res);
- } else {
- const char *shell = getenv("NCDU_SHELL");
- if (shell == NULL) {
- shell = getenv("SHELL");
- if (shell == NULL)
- shell = DEFAULT_SHELL;
- }
-
- res = system(shell);
-
- /* resume ncurses mode */
- reset_prog_mode();
-
- if (res == -1 || !WIFEXITED(res) || WEXITSTATUS(res) == 127) {
- clear();
- printw("ERROR: Can't execute shell interpreter: %s\n"
- "\n"
- "Press any key to continue.",
- shell);
- }
- }
-
- refresh();
- pstate = ST_BROWSE;
-}
-
-void shell_init() {
- pstate = ST_SHELL;
-}
diff --git a/src/shell.h b/src/shell.h
deleted file mode 100644
index b0e53b7..0000000
--- a/src/shell.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
- Shell support: Copyright (c) 2014 Thomas Jarosch
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#ifndef _shell_h
-#define _shell_h
-
-#include "global.h"
-
-void shell_draw(void);
-void shell_init(void);
-
-#endif
diff --git a/src/util.c b/src/util.c
deleted file mode 100644
index 2d92718..0000000
--- a/src/util.c
+++ /dev/null
@@ -1,434 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#include "util.h"
-
-#include <string.h>
-#include <stdlib.h>
-#include <ncurses.h>
-#include <stdarg.h>
-#include <unistd.h>
-#ifdef HAVE_LOCALE_H
-#include <locale.h>
-#endif
-
-int uic_theme;
-int winrows, wincols;
-int subwinr, subwinc;
-int si;
-static char thou_sep;
-
-
-char *cropstr(const char *from, int s) {
- static char dat[4096];
- int i, j, o = strlen(from);
- if(o < s) {
- strcpy(dat, from);
- return dat;
- }
- j=s/2-3;
- for(i=0; i<j; i++)
- dat[i] = from[i];
- dat[i] = '.';
- dat[++i] = '.';
- dat[++i] = '.';
- j=o-s;
- while(++i<s)
- dat[i] = from[j+i];
- dat[s] = '\0';
- return dat;
-}
-
-
-float formatsize(int64_t from, const char **unit) {
- float r = from;
- if (si) {
- if(r < 1000.0f) { *unit = " B"; }
- else if(r < 1e6f) { *unit = "KB"; r/=1e3f; }
- else if(r < 1e9f) { *unit = "MB"; r/=1e6f; }
- else if(r < 1e12f){ *unit = "GB"; r/=1e9f; }
- else if(r < 1e15f){ *unit = "TB"; r/=1e12f; }
- else if(r < 1e18f){ *unit = "PB"; r/=1e15f; }
- else { *unit = "EB"; r/=1e18f; }
- }
- else {
- if(r < 1000.0f) { *unit = " B"; }
- else if(r < 1023e3f) { *unit = "KiB"; r/=1024.0f; }
- else if(r < 1023e6f) { *unit = "MiB"; r/=1048576.0f; }
- else if(r < 1023e9f) { *unit = "GiB"; r/=1073741824.0f; }
- else if(r < 1023e12f){ *unit = "TiB"; r/=1099511627776.0f; }
- else if(r < 1023e15f){ *unit = "PiB"; r/=1125899906842624.0f; }
- else { *unit = "EiB"; r/=1152921504606846976.0f; }
- }
- return r;
-}
-
-
-void printsize(enum ui_coltype t, int64_t from) {
- const char *unit;
- float r = formatsize(from, &unit);
- uic_set(t == UIC_HD ? UIC_NUM_HD : t == UIC_SEL ? UIC_NUM_SEL : UIC_NUM);
- printw("%5.1f", r);
- addchc(t, ' ');
- addstrc(t, unit);
-}
-
-
-char *fullsize(int64_t from) {
- static char dat[26]; /* max: 9.223.372.036.854.775.807 (= 2^63-1) */
- char tmp[26];
- int64_t n = from;
- int i, j;
-
- /* the K&R method - more portable than sprintf with %lld */
- i = 0;
- do {
- tmp[i++] = n % 10 + '0';
- } while((n /= 10) > 0);
- tmp[i] = '\0';
-
- /* reverse and add thousand separators */
- j = 0;
- while(i--) {
- dat[j++] = tmp[i];
- if(i != 0 && i%3 == 0)
- dat[j++] = thou_sep;
- }
- dat[j] = '\0';
-
- return dat;
-}
-
-
-char *fmtmode(unsigned short mode) {
- static char buf[11];
- unsigned short ft = mode & S_IFMT;
- buf[0] = ft == S_IFDIR ? 'd'
- : ft == S_IFREG ? '-'
- : ft == S_IFLNK ? 'l'
- : ft == S_IFIFO ? 'p'
- : ft == S_IFSOCK ? 's'
- : ft == S_IFCHR ? 'c'
- : ft == S_IFBLK ? 'b' : '?';
- buf[1] = mode & 0400 ? 'r' : '-';
- buf[2] = mode & 0200 ? 'w' : '-';
- buf[3] = mode & 0100 ? 'x' : '-';
- buf[4] = mode & 0040 ? 'r' : '-';
- buf[5] = mode & 0020 ? 'w' : '-';
- buf[6] = mode & 0010 ? 'x' : '-';
- buf[7] = mode & 0004 ? 'r' : '-';
- buf[8] = mode & 0002 ? 'w' : '-';
- buf[9] = mode & 0001 ? 'x' : '-';
- buf[10] = 0;
- return buf;
-}
-
-
-void read_locale() {
- thou_sep = '.';
-#ifdef HAVE_LOCALE_H
- setlocale(LC_ALL, "");
- char *locale_thou_sep = localeconv()->thousands_sep;
- if(locale_thou_sep && 1 == strlen(locale_thou_sep))
- thou_sep = locale_thou_sep[0];
-#endif
-}
-
-
-int ncresize(int minrows, int mincols) {
- int ch;
-
- getmaxyx(stdscr, winrows, wincols);
- while((minrows && winrows < minrows) || (mincols && wincols < mincols)) {
- erase();
- mvaddstr(0, 0, "Warning: terminal too small,");
- mvaddstr(1, 1, "please either resize your terminal,");
- mvaddstr(2, 1, "press i to ignore, or press q to quit.");
- refresh();
- nodelay(stdscr, 0);
- ch = getch();
- getmaxyx(stdscr, winrows, wincols);
- if(ch == 'q') {
- erase();
- refresh();
- endwin();
- exit(0);
- }
- if(ch == 'i')
- return 1;
- }
- erase();
- return 0;
-}
-
-
-void nccreate(int height, int width, const char *title) {
- int i;
-
- uic_set(UIC_DEFAULT);
- subwinr = winrows/2-height/2;
- subwinc = wincols/2-width/2;
-
- /* clear window */
- for(i=0; i<height; i++)
- mvhline(subwinr+i, subwinc, ' ', width);
-
- /* box() only works around curses windows, so create our own */
- move(subwinr, subwinc);
- addch(ACS_ULCORNER);
- for(i=0; i<width-2; i++)
- addch(ACS_HLINE);
- addch(ACS_URCORNER);
-
- move(subwinr+height-1, subwinc);
- addch(ACS_LLCORNER);
- for(i=0; i<width-2; i++)
- addch(ACS_HLINE);
- addch(ACS_LRCORNER);
-
- mvvline(subwinr+1, subwinc, ACS_VLINE, height-2);
- mvvline(subwinr+1, subwinc+width-1, ACS_VLINE, height-2);
-
- /* title */
- uic_set(UIC_BOX_TITLE);
- mvaddstr(subwinr, subwinc+4, title);
- uic_set(UIC_DEFAULT);
-}
-
-
-void ncprint(int r, int c, const char *fmt, ...) {
- va_list arg;
- va_start(arg, fmt);
- move(subwinr+r, subwinc+c);
- vw_printw(stdscr, fmt, arg);
- va_end(arg);
-}
-
-
-void nctab(int c, int sel, int num, const char *str) {
- uic_set(sel ? UIC_KEY_HD : UIC_KEY);
- ncprint(0, c, "%d", num);
- uic_set(sel ? UIC_HD : UIC_DEFAULT);
- addch(':');
- addstr(str);
- uic_set(UIC_DEFAULT);
-}
-
-
-static int colors[] = {
-#define C(name, ...) 0,
- UI_COLORS
-#undef C
- 0
-};
-static int lastcolor = 0;
-
-
-static const struct {
- short fg, bg;
- int attr;
-} color_defs[] = {
-#define C(name, off_fg, off_bg, off_a, dark_fg, dark_bg, dark_a) \
- {off_fg, off_bg, off_a}, \
- {dark_fg, dark_bg, dark_a},
- UI_COLORS
-#undef C
- {0,0,0}
-};
-
-void uic_init() {
- size_t i, j;
-
- start_color();
- use_default_colors();
- for(i=0; i<sizeof(colors)/sizeof(*colors)-1; i++) {
- j = i*2 + uic_theme;
- init_pair(i+1, color_defs[j].fg, color_defs[j].bg);
- colors[i] = color_defs[j].attr | COLOR_PAIR(i+1);
- }
-}
-
-void uic_set(enum ui_coltype c) {
- attroff(lastcolor);
- lastcolor = colors[(int)c];
- attron(lastcolor);
-}
-
-
-
-/* removes item from the hlnk circular linked list and size counts of the parents */
-static void freedir_hlnk(struct dir *d) {
- struct dir *t, *par, *pt;
- int i;
-
- if(!(d->flags & FF_HLNKC))
- return;
-
- /* remove size from parents.
- * This works the same as with adding: only the parents in which THIS is the
- * only occurrence of the hard link will be modified, if the same file still
- * exists within the parent it shouldn't get removed from the count.
- * XXX: Same note as for dir_mem.c / hlink_check():
- * this is probably not the most efficient algorithm */
- for(i=1,par=d->parent; i&&par; par=par->parent) {
- if(d->hlnk)
- for(t=d->hlnk; i&&t!=d; t=t->hlnk)
- for(pt=t->parent; i&&pt; pt=pt->parent)
- if(pt==par)
- i=0;
- if(i) {
- par->size = adds64(par->size, -d->size);
- par->asize = adds64(par->size, -d->asize);
- }
- }
-
- /* remove from hlnk */
- if(d->hlnk) {
- for(t=d->hlnk; t->hlnk!=d; t=t->hlnk)
- ;
- t->hlnk = d->hlnk;
- }
-}
-
-
-static void freedir_rec(struct dir *dr) {
- struct dir *tmp, *tmp2;
- tmp2 = dr;
- while((tmp = tmp2) != NULL) {
- freedir_hlnk(tmp);
- /* remove item */
- if(tmp->sub) freedir_rec(tmp->sub);
- tmp2 = tmp->next;
- free(tmp);
- }
-}
-
-
-void freedir(struct dir *dr) {
- if(!dr)
- return;
-
- /* free dr->sub recursively */
- if(dr->sub)
- freedir_rec(dr->sub);
-
- /* update references */
- if(dr->parent && dr->parent->sub == dr)
- dr->parent->sub = dr->next;
- if(dr->prev)
- dr->prev->next = dr->next;
- if(dr->next)
- dr->next->prev = dr->prev;
-
- freedir_hlnk(dr);
-
- /* update sizes of parent directories if this isn't a hard link.
- * If this is a hard link, freedir_hlnk() would have done so already
- *
- * mtime is 0 here because recalculating the maximum at every parent
- * dir is expensive, but might be good feature to add later if desired */
- addparentstats(dr->parent, dr->flags & FF_HLNKC ? 0 : -dr->size, dr->flags & FF_HLNKC ? 0 : -dr->asize, 0, -(dr->items+1));
-
- free(dr);
-}
-
-
-const char *getpath(struct dir *cur) {
- static char *dat;
- static int datl = 0;
- struct dir *d, **list;
- int c, i;
-
- if(!cur->name[0])
- return "/";
-
- c = i = 1;
- for(d=cur; d!=NULL; d=d->parent) {
- i += strlen(d->name)+1;
- c++;
- }
-
- if(datl == 0) {
- datl = i;
- dat = xmalloc(i);
- } else if(datl < i) {
- datl = i;
- dat = xrealloc(dat, i);
- }
- list = xmalloc(c*sizeof(struct dir *));
-
- c = 0;
- for(d=cur; d!=NULL; d=d->parent)
- list[c++] = d;
-
- dat[0] = '\0';
- while(c--) {
- if(list[c]->parent)
- strcat(dat, "/");
- strcat(dat, list[c]->name);
- }
- free(list);
- return dat;
-}
-
-
-struct dir *getroot(struct dir *d) {
- while(d && d->parent)
- d = d->parent;
- return d;
-}
-
-
-void addparentstats(struct dir *d, int64_t size, int64_t asize, uint64_t mtime, int items) {
- struct dir_ext *e;
- while(d) {
- d->size = adds64(d->size, size);
- d->asize = adds64(d->asize, asize);
- d->items += items;
- if (d->flags & FF_EXT) {
- e = dir_ext_ptr(d);
- e->mtime = (e->mtime > mtime) ? e->mtime : mtime;
- }
- d = d->parent;
- }
-}
-
-
-/* Apparently we can just resume drawing after endwin() and ncurses will pick
- * up where it left. Probably not very portable... */
-#define oom_msg "\nOut of memory, press enter to try again or Ctrl-C to give up.\n"
-#define wrap_oom(f) \
- void *ptr;\
- char buf[128];\
- while((ptr = f) == NULL) {\
- close_nc();\
- write(2, oom_msg, sizeof(oom_msg));\
- read(0, buf, sizeof(buf));\
- }\
- return ptr;
-
-void *xmalloc(size_t size) { wrap_oom(malloc(size)) }
-void *xcalloc(size_t n, size_t size) { wrap_oom(calloc(n, size)) }
-void *xrealloc(void *mem, size_t size) { wrap_oom(realloc(mem, size)) }
diff --git a/src/util.h b/src/util.h
deleted file mode 100644
index 2c3e5ed..0000000
--- a/src/util.h
+++ /dev/null
@@ -1,195 +0,0 @@
-/* ncdu - NCurses Disk Usage
-
- Copyright (c) 2007-2020 Yoran Heling
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#ifndef _util_h
-#define _util_h
-
-#include "global.h"
-#include <ncurses.h>
-
-
-/* UI colors: (foreground, background, attrs)
- * NAME OFF DARK
- */
-#define UI_COLORS \
- C(DEFAULT, -1,-1,0 , -1, -1, 0 )\
- C(BOX_TITLE, -1,-1,A_BOLD , COLOR_BLUE, -1, A_BOLD)\
- C(HD, -1,-1,A_REVERSE , COLOR_BLACK, COLOR_CYAN, 0 ) /* header & footer */\
- C(SEL, -1,-1,A_REVERSE , COLOR_WHITE, COLOR_GREEN,A_BOLD)\
- C(NUM, -1,-1,0 , COLOR_YELLOW, -1, A_BOLD)\
- C(NUM_HD, -1,-1,A_REVERSE , COLOR_YELLOW, COLOR_CYAN, A_BOLD)\
- C(NUM_SEL, -1,-1,A_REVERSE , COLOR_YELLOW, COLOR_GREEN,A_BOLD)\
- C(KEY, -1,-1,A_BOLD , COLOR_YELLOW, -1, A_BOLD)\
- C(KEY_HD, -1,-1,A_BOLD|A_REVERSE, COLOR_YELLOW, COLOR_CYAN, A_BOLD)\
- C(DIR, -1,-1,0 , COLOR_BLUE, -1, A_BOLD)\
- C(DIR_SEL, -1,-1,A_REVERSE , COLOR_BLUE, COLOR_GREEN,A_BOLD)\
- C(FLAG, -1,-1,0 , COLOR_RED, -1, 0 )\
- C(FLAG_SEL, -1,-1,A_REVERSE , COLOR_RED, COLOR_GREEN,0 )\
- C(GRAPH, -1,-1,0 , COLOR_MAGENTA,-1, 0 )\
- C(GRAPH_SEL, -1,-1,A_REVERSE , COLOR_MAGENTA,COLOR_GREEN,0 )
-
-enum ui_coltype {
-#define C(name, ...) UIC_##name,
- UI_COLORS
-#undef C
- UIC_NONE
-};
-
-/* Color & attribute manipulation */
-extern int uic_theme;
-
-void uic_init(void);
-void uic_set(enum ui_coltype);
-
-
-/* updated when window is resized */
-extern int winrows, wincols;
-
-/* used by the nc* functions and macros */
-extern int subwinr, subwinc;
-
-/* used by formatsize to choose between base 2 or 10 prefixes */
-extern int si;
-
-
-/* Macros/functions for managing struct dir and struct dir_ext */
-
-#define dir_memsize(n) (offsetof(struct dir, name)+1+strlen(n))
-#define dir_ext_offset(n) ((dir_memsize(n) + 7) & ~7)
-#define dir_ext_memsize(n) (dir_ext_offset(n) + sizeof(struct dir_ext))
-
-static inline struct dir_ext *dir_ext_ptr(struct dir *d) {
- return d->flags & FF_EXT
- ? (struct dir_ext *) ( ((char *)d) + dir_ext_offset(d->name) )
- : NULL;
-}
-
-
-/* Instead of using several ncurses windows, we only draw to stdscr.
- * the functions nccreate, ncprint and the macros ncaddstr and ncaddch
- * mimic the behaviour of ncurses windows.
- * This works better than using ncurses windows when all windows are
- * created in the correct order: it paints directly on stdscr, so
- * wrefresh, wnoutrefresh and other window-specific functions are not
- * necessary.
- * Also, this method doesn't require any window objects, as you can
- * only create one window at a time.
-*/
-
-/* updates winrows, wincols, and displays a warning when the terminal
- * is smaller than the specified minimum size. */
-int ncresize(int, int);
-
-/* creates a new centered window with border */
-void nccreate(int, int, const char *);
-
-/* printf something somewhere in the last created window */
-void ncprint(int, int, const char *, ...);
-
-/* Add a "tab" to a window */
-void nctab(int, int, int, const char *);
-
-/* same as the w* functions of ncurses, with a color */
-#define ncaddstr(r, c, s) mvaddstr(subwinr+(r), subwinc+(c), s)
-#define ncaddch(r, c, s) mvaddch(subwinr+(r), subwinc+(c), s)
-#define ncmove(r, c) move(subwinr+(r), subwinc+(c))
-
-/* add stuff with a color */
-#define mvaddstrc(t, r, c, s) do { uic_set(t); mvaddstr(r, c, s); } while(0)
-#define mvaddchc(t, r, c, s) do { uic_set(t); mvaddch(r, c, s); } while(0)
-#define addstrc(t, s) do { uic_set(t); addstr( s); } while(0)
-#define addchc(t, s) do { uic_set(t); addch( s); } while(0)
-#define ncaddstrc(t, r, c, s) do { uic_set(t); ncaddstr(r, c, s); } while(0)
-#define ncaddchc(t, r, c, s) do { uic_set(t); ncaddch(r, c, s); } while(0)
-#define mvhlinec(t, r, c, s, n) do { uic_set(t); mvhline(r, c, s, n); } while(0)
-
-/* crops a string into the specified length */
-char *cropstr(const char *, int);
-
-/* Converts the given size in bytes into a float (0 <= f < 1000) and a unit string */
-float formatsize(int64_t, const char **);
-
-/* print size in the form of xxx.x XB */
-void printsize(enum ui_coltype, int64_t);
-
-/* int2string with thousand separators */
-char *fullsize(int64_t);
-
-/* format's a file mode as a ls -l string */
-char *fmtmode(unsigned short);
-
-/* read locale information from the environment */
-void read_locale(void);
-
-/* recursively free()s a directory tree */
-void freedir(struct dir *);
-
-/* generates full path from a dir item,
- returned pointer will be overwritten with a subsequent call */
-const char *getpath(struct dir *);
-
-/* returns the root element of the given dir struct */
-struct dir *getroot(struct dir *);
-
-/* Add two signed 64-bit integers. Returns INT64_MAX if the result would
- * overflow, or 0 if it would be negative. At least one of the integers must be
- * positive.
- * I use uint64_t's to detect the overflow, as (a + b < 0) relies on undefined
- * behaviour, and (INT64_MAX - b >= a) didn't work for some reason. */
-#define adds64(a, b) ((a) > 0 && (b) > 0\
- ? ((uint64_t)(a) + (uint64_t)(b) > (uint64_t)INT64_MAX ? INT64_MAX : (a)+(b))\
- : (a)+(b) < 0 ? 0 : (a)+(b))
-
-/* Adds a value to the size, asize and items fields of *d and its parents */
-void addparentstats(struct dir *, int64_t, int64_t, uint64_t, int);
-
-
-/* A simple stack implemented in macros */
-#define nstack_init(_s) do {\
- (_s)->size = 10;\
- (_s)->top = 0;\
- (_s)->list = xmalloc(10*sizeof(*(_s)->list));\
- } while(0)
-
-#define nstack_push(_s, _v) do {\
- if((_s)->size <= (_s)->top) {\
- (_s)->size *= 2;\
- (_s)->list = xrealloc((_s)->list, (_s)->size*sizeof(*(_s)->list));\
- }\
- (_s)->list[(_s)->top++] = _v;\
- } while(0)
-
-#define nstack_pop(_s) (_s)->top--
-#define nstack_top(_s, _d) ((_s)->top > 0 ? (_s)->list[(_s)->top-1] : (_d))
-#define nstack_free(_s) free((_s)->list)
-
-
-/* Malloc wrappers that exit on OOM */
-void *xmalloc(size_t);
-void *xcalloc(size_t, size_t);
-void *xrealloc(void *, size_t);
-
-#endif
-
diff --git a/static/build.sh b/static/build.sh
deleted file mode 100755
index 37cfabf..0000000
--- a/static/build.sh
+++ /dev/null
@@ -1,130 +0,0 @@
-#!/bin/sh
-
-# This script is based on static/build.sh from the ncdc git repo.
-# Only i486 and arm arches are supported. i486 should perform well enough, so
-# x86_64 isn't really necessary. I can't test any other arches.
-#
-# This script assumes that you have the musl-cross cross compilers installed in
-# $MUSL_CROSS_PATH.
-#
-# Usage:
-# ./build.sh $arch
-# where $arch = 'arm', 'i486' or 'x86_64'
-
-MUSL_CROSS_PATH=/opt/cross
-NCURSES_VERSION=6.0
-
-export CFLAGS="-O3 -g -static"
-
-# (The variables below are automatically set by the functions, they're defined
-# here to make sure they have global scope and for documentation purposes.)
-
-# This is the arch we're compiling for, e.g. arm/mipsel.
-TARGET=
-# This is the name of the toolchain we're using, and thus the value we should
-# pass to autoconf's --host argument.
-HOST=
-# Installation prefix.
-PREFIX=
-# Path of the extracted source code of the package we're currently building.
-srcdir=
-
-mkdir -p tarballs
-
-
-# "Fetch, Extract, Move"
-fem() { # base-url name targerdir extractdir
- echo "====== Fetching and extracting $1 $2"
- cd tarballs
- if [ -n "$4" ]; then
- EDIR="$4"
- else
- EDIR=$(basename $(basename $(basename $2 .tar.bz2) .tar.gz) .tar.xz)
- fi
- if [ ! -e "$2" ]; then
- wget "$1$2" || exit
- fi
- if [ ! -d "$3" ]; then
- tar -xvf "$2" || exit
- mv "$EDIR" "$3"
- fi
- cd ..
-}
-
-
-prebuild() { # dirname
- if [ -e "$TARGET/$1/_built" ]; then
- echo "====== Skipping build for $TARGET/$1 (assumed to be done)"
- return 1
- fi
- echo "====== Starting build for $TARGET/$1"
- rm -rf "$TARGET/$1"
- mkdir -p "$TARGET/$1"
- cd "$TARGET/$1"
- srcdir="../../tarballs/$1"
- return 0
-}
-
-
-postbuild() {
- touch _built
- cd ../..
-}
-
-
-getncurses() {
- fem http://ftp.gnu.org/pub/gnu/ncurses/ ncurses-$NCURSES_VERSION.tar.gz ncurses
- prebuild ncurses || return
- $srcdir/configure --prefix=$PREFIX\
- --without-cxx --without-cxx-binding --without-ada --without-manpages --without-progs\
- --without-tests --without-curses-h --without-pkg-config --without-shared --without-debug\
- --without-gpm --without-sysmouse --enable-widec --with-default-terminfo-dir=/usr/share/terminfo\
- --with-terminfo-dirs=/usr/share/terminfo:/lib/terminfo:/usr/local/share/terminfo\
- --with-fallbacks="screen linux vt100 xterm xterm-256color" --host=$HOST\
- CPPFLAGS=-D_GNU_SOURCE || exit
- make || exit
- make install.libs || exit
- postbuild
-}
-
-
-getncdu() {
- prebuild ncdu || return
- srcdir=../../..
- $srcdir/configure --host=$HOST --with-ncursesw PKG_CONFIG=false\
- CPPFLAGS="-I$PREFIX/include -I$PREFIX/include/ncursesw"\
- LDFLAGS="-static -L$PREFIX/lib -lncursesw" CFLAGS="$CFLAGS -Wall -Wextra" || exit
- make || exit
-
- VER=`cd '../../..' && git describe --abbrev=5 --dirty= | sed s/^v//`
- tar -czf ../../ncdu-linux-$TARGET-$VER-unstripped.tar.gz ncdu
- $HOST-strip ncdu
- tar -czf ../../ncdu-linux-$TARGET-$VER.tar.gz ncdu
- echo "====== ncdu-linux-$TARGET-$VER.tar.gz and -unstripped created."
-
- postbuild
-}
-
-
-buildarch() {
- TARGET=$1
- case $TARGET in
- arm) HOST=arm-linux-musleabi DIR=arm-linux-musleabi ;;
- aarch64)HOST=aarch64-linux-musl DIR=aarch64-linux-musl ;;
- i486) HOST=i486-linux-musl DIR=i486-linux-musl ;;
- x86_64) HOST=x86_64-linux-musl DIR=x86_64-linux-musl ;;
- *) echo "Unknown target: $TARGET" ;;
- esac
- PREFIX="`pwd`/$TARGET/inst"
- mkdir -p $TARGET $PREFIX
- ln -s lib $PREFIX/lib64
-
- OLDPATH="$PATH"
- export PATH="$PATH:$MUSL_CROSS_PATH/$DIR/bin"
- getncurses
- getncdu
- PATH="$OLDPATH"
-}
-
-
-buildarch $1