mirror of
https://code.blicky.net/yorhel/ncdu.git
synced 2026-01-12 17:08:39 -09:00
Compare commits
14 commits
2e5c767d4c
...
2b4c1ca03e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b4c1ca03e | ||
|
|
af7163acf6 | ||
|
|
5438312440 | ||
|
|
0918096301 | ||
|
|
ee1d80da6a | ||
|
|
93a81a3898 | ||
|
|
cf3a8f3043 | ||
|
|
f7fe61194b | ||
|
|
456cde16df | ||
|
|
3c77dc458a | ||
|
|
ce9921846c | ||
|
|
e0ab5d40c7 | ||
|
|
607b07a30e | ||
|
|
b4dc9f1d4d |
14 changed files with 40 additions and 32 deletions
|
|
@ -1,6 +1,12 @@
|
|||
# SPDX-FileCopyrightText: Yorhel <projects@yorhel.nl>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
2.8 - 2025-03-05
|
||||
- Now requires Zig 0.14
|
||||
- Add support for @-prefixed lines to ignore errors in config file
|
||||
- List all supported options in `--help`
|
||||
- Use `kB` instead of `KB` in `--si` mode
|
||||
|
||||
2.7 - 2024-11-19
|
||||
- Still requires Zig 0.12 or 0.13
|
||||
- Support transparent reading/writing of zstandard-compressed JSON
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ C version (1.x).
|
|||
|
||||
## Requirements
|
||||
|
||||
- Zig 0.12 or 0.13.
|
||||
- Zig 0.14
|
||||
- Some sort of POSIX-like OS
|
||||
- ncurses
|
||||
- libzstd
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ pub fn build(b: *std.Build) void {
|
|||
exe.pie = pie;
|
||||
exe.root_module.linkSystemLibrary("ncursesw", .{});
|
||||
exe.root_module.linkSystemLibrary("libzstd", .{});
|
||||
// https://github.com/ziglang/zig/blob/b52be973dfb7d1408218b8e75800a2da3dc69108/build.zig#L551-L554
|
||||
if (target.result.isDarwin()) {
|
||||
// https://github.com/ziglang/zig/blob/faccd79ca5debbe22fe168193b8de54393257604/build.zig#L745-L748
|
||||
if (target.result.os.tag.isDarwin()) {
|
||||
// useful for package maintainers
|
||||
exe.headerpad_max_install_names = true;
|
||||
}
|
||||
|
|
|
|||
2
ncdu.1
2
ncdu.1
|
|
@ -1,6 +1,6 @@
|
|||
.\" SPDX-FileCopyrightText: Yorhel <projects@yorhel.nl>
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.Dd November 19, 2024
|
||||
.Dd March 5, 2025
|
||||
.Dt NCDU 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ pub const ItemKey = enum(u5) {
|
|||
|
||||
// Pessimistic upper bound on the encoded size of an item, excluding the name field.
|
||||
// 2 bytes for map start/end, 11 per field (2 for the key, 9 for a full u64).
|
||||
const MAX_ITEM_LEN = 2 + 11 * @typeInfo(ItemKey).Enum.fields.len;
|
||||
const MAX_ITEM_LEN = 2 + 11 * @typeInfo(ItemKey).@"enum".fields.len;
|
||||
|
||||
pub const CborMajor = enum(u3) { pos, neg, bytes, text, array, map, tag, simple };
|
||||
|
||||
|
|
@ -118,7 +118,7 @@ pub const Thread = struct {
|
|||
}
|
||||
|
||||
fn flush(t: *Thread, expected_len: usize) void {
|
||||
@setCold(true);
|
||||
@branchHint(.unlikely);
|
||||
const block = createBlock(t);
|
||||
defer block.deinit();
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ inline fn bigu32(v: [4]u8) u32 { return std.mem.bigToNative(u32, @bitCast(v)); }
|
|||
inline fn bigu64(v: [8]u8) u64 { return std.mem.bigToNative(u64, @bitCast(v)); }
|
||||
|
||||
fn die() noreturn {
|
||||
@setCold(true);
|
||||
@branchHint(.cold);
|
||||
if (global.lastitem) |e| ui.die("Error reading item {x} from file\n", .{e})
|
||||
else ui.die("Error reading from file\n", .{});
|
||||
}
|
||||
|
|
@ -338,7 +338,7 @@ const ItemParser = struct {
|
|||
// Skips over any fields that don't fit into an ItemKey.
|
||||
fn next(r: *ItemParser) ?Field {
|
||||
while (r.key()) |k| {
|
||||
if (k.major == .pos and k.arg <= std.math.maxInt(@typeInfo(ItemKey).Enum.tag_type)) return .{
|
||||
if (k.major == .pos and k.arg <= std.math.maxInt(@typeInfo(ItemKey).@"enum".tag_type)) return .{
|
||||
.key = @enumFromInt(k.arg),
|
||||
.val = r.r.next(),
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ pub const Writer = struct {
|
|||
dir_entry_open: bool = false,
|
||||
|
||||
fn flush(ctx: *Writer, bytes: usize) void {
|
||||
@setCold(true);
|
||||
@branchHint(.unlikely);
|
||||
// This can only really happen when the root path exceeds PATH_MAX,
|
||||
// in which case we would probably have error'ed out earlier anyway.
|
||||
if (bytes > ctx.buf.len) ui.die("Error writing JSON export: path too long.\n", .{});
|
||||
|
|
@ -126,14 +126,14 @@ pub const Writer = struct {
|
|||
var index: usize = buf.len;
|
||||
while (a >= 100) : (a = @divTrunc(a, 100)) {
|
||||
index -= 2;
|
||||
buf[index..][0..2].* = std.fmt.digits2(@as(usize, @intCast(a % 100)));
|
||||
buf[index..][0..2].* = std.fmt.digits2(@as(u8, @intCast(a % 100)));
|
||||
}
|
||||
if (a < 10) {
|
||||
index -= 1;
|
||||
buf[index] = '0' + @as(u8, @intCast(a));
|
||||
} else {
|
||||
index -= 2;
|
||||
buf[index..][0..2].* = std.fmt.digits2(@as(usize, @intCast(a)));
|
||||
buf[index..][0..2].* = std.fmt.digits2(@as(u8, @intCast(a)));
|
||||
}
|
||||
ctx.write(buf[index..]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,7 +83,6 @@ const Parser = struct {
|
|||
}
|
||||
|
||||
fn fill(p: *Parser) void {
|
||||
@setCold(true);
|
||||
p.rdoff = 0;
|
||||
p.rdsize = (if (p.zstd) |z| z.read(p.rd, &p.buf) else p.rd.read(&p.buf)) catch |e| switch (e) {
|
||||
error.IsDir => p.die("not a file"), // should be detected at open() time, but no flag for that...
|
||||
|
|
@ -98,6 +97,7 @@ const Parser = struct {
|
|||
// (Returning a '?u8' here is nicer but kills performance by about +30%)
|
||||
fn nextByte(p: *Parser) u8 {
|
||||
if (p.rdoff == p.rdsize) {
|
||||
@branchHint(.unlikely);
|
||||
p.fill();
|
||||
if (p.rdsize == 0) return 0;
|
||||
}
|
||||
|
|
@ -133,7 +133,7 @@ const Parser = struct {
|
|||
}
|
||||
|
||||
fn stringContentSlow(p: *Parser, buf: []u8, head: u8, off: usize) []u8 {
|
||||
@setCold(true);
|
||||
@branchHint(.unlikely);
|
||||
var b = head;
|
||||
var n = off;
|
||||
while (true) {
|
||||
|
|
|
|||
20
src/main.zig
20
src/main.zig
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-FileCopyrightText: Yorhel <projects@yorhel.nl>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pub const program_version = "2.7";
|
||||
pub const program_version = "2.8";
|
||||
|
||||
const std = @import("std");
|
||||
const model = @import("model.zig");
|
||||
|
|
@ -41,7 +41,7 @@ test "imports" {
|
|||
// This allocator never returns an error, it either succeeds or causes ncdu to quit.
|
||||
// (Which means you'll find a lot of "catch unreachable" sprinkled through the code,
|
||||
// they look scarier than they are)
|
||||
fn wrapAlloc(_: *anyopaque, len: usize, ptr_alignment: u8, return_address: usize) ?[*]u8 {
|
||||
fn wrapAlloc(_: *anyopaque, len: usize, ptr_alignment: std.mem.Alignment, return_address: usize) ?[*]u8 {
|
||||
while (true) {
|
||||
if (std.heap.c_allocator.vtable.alloc(undefined, len, ptr_alignment, return_address)) |r|
|
||||
return r
|
||||
|
|
@ -56,18 +56,20 @@ pub const allocator = std.mem.Allocator{
|
|||
.alloc = wrapAlloc,
|
||||
// AFAIK, all uses of resize() to grow an allocation will fall back to alloc() on failure.
|
||||
.resize = std.heap.c_allocator.vtable.resize,
|
||||
.remap = std.heap.c_allocator.vtable.remap,
|
||||
.free = std.heap.c_allocator.vtable.free,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
// Custom panic impl to reset the terminal before spewing out an error message.
|
||||
pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, ret_addr: ?usize) noreturn {
|
||||
@setCold(true);
|
||||
pub const panic = std.debug.FullPanic(struct {
|
||||
pub fn panicFn(msg: []const u8, first_trace_addr: ?usize) noreturn {
|
||||
@branchHint(.cold);
|
||||
ui.deinit();
|
||||
std.debug.panicImpl(error_return_trace, ret_addr orelse @returnAddress(), msg);
|
||||
std.debug.defaultPanic(msg, first_trace_addr);
|
||||
}
|
||||
|
||||
}.panicFn);
|
||||
|
||||
pub const config = struct {
|
||||
pub const SortCol = enum { name, blocks, size, items, mtime };
|
||||
|
|
@ -637,7 +639,7 @@ pub fn main() void {
|
|||
if (config.binreader and (export_json != null or export_bin != null))
|
||||
bin_reader.import();
|
||||
} else {
|
||||
var buf = [_]u8{0} ** (std.fs.MAX_PATH_BYTES+1);
|
||||
var buf: [std.fs.max_path_bytes+1]u8 = @splat(0);
|
||||
const path =
|
||||
if (std.posix.realpathZ(scan_dir orelse ".", buf[0..buf.len-1])) |p| buf[0..p.len:0]
|
||||
else |_| (scan_dir orelse ".");
|
||||
|
|
@ -725,13 +727,13 @@ test "argument parser" {
|
|||
const T = struct {
|
||||
a: Args,
|
||||
fn opt(self: *@This(), isopt: bool, val: []const u8) !void {
|
||||
const o = self.a.next().?;
|
||||
const o = (self.a.next() catch unreachable).?;
|
||||
try std.testing.expectEqual(isopt, o.opt);
|
||||
try std.testing.expectEqualStrings(val, o.val);
|
||||
try std.testing.expectEqual(o.is(val), isopt);
|
||||
}
|
||||
fn arg(self: *@This(), val: []const u8) !void {
|
||||
try std.testing.expectEqualStrings(val, self.a.arg());
|
||||
try std.testing.expectEqualStrings(val, self.a.arg() catch unreachable);
|
||||
}
|
||||
};
|
||||
var t = T{ .a = Args.init(&lst) };
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ fn rec(ctx: *Ctx, dir: *sink.Dir, entry: *model.Entry) void {
|
|||
pub fn run(d: *model.Dir) void {
|
||||
const sink_threads = sink.createThreads(1);
|
||||
|
||||
var ctx = .{
|
||||
var ctx: Ctx = .{
|
||||
.sink = &sink_threads[0],
|
||||
.stat = toStat(&d.entry),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ fn statAt(parent: std.fs.Dir, name: [:0]const u8, follow: bool, symlink: *bool)
|
|||
.hasgid = true,
|
||||
.hasmode = true,
|
||||
},
|
||||
.mtime = clamp(model.Ext, .mtime, stat.mtime().tv_sec),
|
||||
.mtime = clamp(model.Ext, .mtime, stat.mtime().sec),
|
||||
.uid = truncate(model.Ext, .uid, stat.uid),
|
||||
.gid = truncate(model.Ext, .gid, stat.gid),
|
||||
.mode = truncate(model.Ext, .mode, stat.mode),
|
||||
|
|
@ -276,7 +276,7 @@ const Thread = struct {
|
|||
if (entry) |e| t.scanOne(d, e.name)
|
||||
else {
|
||||
t.sink.setDir(null);
|
||||
t.stack.pop().destroy(t);
|
||||
t.stack.pop().?.destroy(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ pub const Dir = struct {
|
|||
|
||||
pub fn unref(d: *Dir, t: *Thread) void {
|
||||
if (d.refcnt.fetchSub(1, .release) != 1) return;
|
||||
d.refcnt.fence(.acquire);
|
||||
_ = d.refcnt.load(.acquire);
|
||||
|
||||
switch (d.out) {
|
||||
.mem => |*m| m.final(if (d.parent) |p| &p.out.mem else null),
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ pub fn quit() noreturn {
|
|||
// no clue if ncurses will consistently report OOM, but we're not handling that
|
||||
// right now.
|
||||
pub fn oom() void {
|
||||
@setCold(true);
|
||||
@branchHint(.cold);
|
||||
if (main_thread == std.Thread.getCurrentId()) {
|
||||
const haveui = inited;
|
||||
deinit();
|
||||
|
|
@ -288,7 +288,7 @@ pub const Style = lbl: {
|
|||
};
|
||||
}
|
||||
break :lbl @Type(.{
|
||||
.Enum = .{
|
||||
.@"enum" = .{
|
||||
.tag_type = u8,
|
||||
.fields = &fields,
|
||||
.decls = &[_]std.builtin.Type.Declaration{},
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ pub fn castClamp(comptime T: type, x: anytype) T {
|
|||
|
||||
// Cast any integer type to the target type, truncating if necessary.
|
||||
pub fn castTruncate(comptime T: type, x: anytype) T {
|
||||
const Ti = @typeInfo(T).Int;
|
||||
const Xi = @typeInfo(@TypeOf(x)).Int;
|
||||
const Ti = @typeInfo(T).int;
|
||||
const Xi = @typeInfo(@TypeOf(x)).int;
|
||||
const nx: std.meta.Int(Ti.signedness, Xi.bits) = @bitCast(x);
|
||||
return if (Xi.bits > Ti.bits) @truncate(nx) else nx;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue