mirror of
https://code.blicky.net/yorhel/ncdu.git
synced 2026-01-13 01:08:41 -09:00
Update to Zig 0.11.0
Signed-off-by: Eric Joldasov <bratishkaerik@getgoogleoff.me>
This commit is contained in:
parent
0d99781c67
commit
ab6dc5be75
10 changed files with 175 additions and 151 deletions
4
Makefile
4
Makefile
|
|
@ -9,7 +9,7 @@ ZIG ?= zig
|
|||
PREFIX ?= /usr/local
|
||||
BINDIR ?= ${PREFIX}/bin
|
||||
MANDIR ?= ${PREFIX}/share/man/man1
|
||||
ZIG_FLAGS ?= -Drelease-fast
|
||||
ZIG_FLAGS ?= -Doptimize=ReleaseFast
|
||||
|
||||
NCDU_VERSION=$(shell grep 'program_version = "' src/main.zig | sed -e 's/^.*"\(.\+\)".*$$/\1/')
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ static-%.tar.gz:
|
|||
@# Alternative approach, bypassing zig-build
|
||||
cd static-$* && zig build-exe -target $*\
|
||||
-Iinst/include -Iinst/include/ncursesw -lc inst/lib/libncursesw.a\
|
||||
--cache-dir zig-cache -static --strip -O ReleaseFast ../src/main.zig ../src/ncurses_refs.c
|
||||
--cache-dir zig-cache -static -fstrip -O ReleaseFast ../src/main.zig ../src/ncurses_refs.c
|
||||
cd static-$* && mv main ncdu && tar -czf ../static-$*.tar.gz ncdu
|
||||
rm -rf static-$*
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ C version (1.x).
|
|||
|
||||
## Requirements
|
||||
|
||||
- Zig 0.10.0 or 0.10.1
|
||||
- Zig 0.11.0
|
||||
- Some sort of POSIX-like OS
|
||||
- ncurses libraries and header files
|
||||
|
||||
|
|
|
|||
47
build.zig
47
build.zig
|
|
@ -3,27 +3,29 @@
|
|||
|
||||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.build.Builder) void {
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const mode = b.standardReleaseOptions();
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const pie = b.option(bool, "pie", "Build with PIE support (by default false)") orelse false;
|
||||
|
||||
const exe = b.addExecutable("ncdu", "src/main.zig");
|
||||
exe.setTarget(target);
|
||||
exe.setBuildMode(mode);
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "ncdu",
|
||||
.root_source_file = .{ .path = "src/main.zig" },
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
// https://github.com/ziglang/zig/blob/b52be973dfb7d1408218b8e75800a2da3dc69108/build.zig#L551-L554
|
||||
if (exe.target.isDarwin()) {
|
||||
// useful for package maintainers
|
||||
exe.headerpad_max_install_names = true;
|
||||
}
|
||||
exe.addCSourceFile("src/ncurses_refs.c", &[_][]const u8{});
|
||||
exe.linkLibC();
|
||||
exe.linkSystemLibrary("ncursesw");
|
||||
linkNcurses(exe);
|
||||
exe.pie = pie;
|
||||
exe.install();
|
||||
b.installArtifact(exe);
|
||||
|
||||
const run_cmd = exe.run();
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
if (b.args) |args| {
|
||||
run_cmd.addArgs(args);
|
||||
|
|
@ -32,11 +34,22 @@ pub fn build(b: *std.build.Builder) void {
|
|||
const run_step = b.step("run", "Run the app");
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
|
||||
const tst = b.addTest("src/main.zig");
|
||||
tst.linkLibC();
|
||||
tst.linkSystemLibrary("ncursesw");
|
||||
tst.addCSourceFile("src/ncurses_refs.c", &[_][]const u8{});
|
||||
tst.pie = pie;
|
||||
const tst_step = b.step("test", "Run tests");
|
||||
tst_step.dependOn(&tst.step);
|
||||
const unit_tests = b.addTest(.{
|
||||
.root_source_file = .{ .path = "src/main.zig" },
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
linkNcurses(unit_tests);
|
||||
unit_tests.pie = pie;
|
||||
|
||||
const run_unit_tests = b.addRunArtifact(unit_tests);
|
||||
|
||||
const test_step = b.step("test", "Run unit tests");
|
||||
test_step.dependOn(&run_unit_tests.step);
|
||||
}
|
||||
|
||||
pub fn linkNcurses(compile_step: *std.Build.CompileStep) void {
|
||||
compile_step.linkSystemLibrary("ncursesw");
|
||||
compile_step.linkLibC();
|
||||
compile_step.addCSourceFile(.{ .file = .{ .path = "src/ncurses_refs.c" }, .flags = &.{} });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,15 +44,15 @@ const View = struct {
|
|||
fn save(self: *@This()) void {
|
||||
self.cursor_hash = if (dir_items.items.len == 0) 0
|
||||
else hashEntry(dir_items.items[cursor_idx]);
|
||||
opened_dir_views.put(@ptrToInt(dir_parent), self.*) catch {};
|
||||
opened_dir_views.put(@intFromPtr(dir_parent), self.*) catch {};
|
||||
}
|
||||
|
||||
// Should be called after dir_parent or dir_items has changed, will load the last saved view and find the proper cursor_idx.
|
||||
fn load(self: *@This(), sel: ?*const model.Entry) void {
|
||||
if (opened_dir_views.get(@ptrToInt(dir_parent))) |v| self.* = v
|
||||
if (opened_dir_views.get(@intFromPtr(dir_parent))) |v| self.* = v
|
||||
else self.* = @This(){};
|
||||
cursor_idx = 0;
|
||||
for (dir_items.items) |e, i| {
|
||||
for (dir_items.items, 0..) |e, i| {
|
||||
if (if (sel != null) e == sel else self.cursor_hash == hashEntry(e)) {
|
||||
cursor_idx = i;
|
||||
break;
|
||||
|
|
@ -64,7 +64,7 @@ const View = struct {
|
|||
var current_view = View{};
|
||||
|
||||
// Directories the user has browsed to before, and which item was last selected.
|
||||
// The key is the @ptrToInt() of the opened *Dir; An int because the pointer
|
||||
// The key is the @intFromPtr() of the opened *Dir; An int because the pointer
|
||||
// itself may have gone stale after deletion or refreshing. They're only for
|
||||
// lookups, not dereferencing.
|
||||
var opened_dir_views = std.AutoHashMap(usize, View).init(main.allocator);
|
||||
|
|
@ -117,7 +117,7 @@ fn sortDir(next_sel: ?*const model.Entry) void {
|
|||
// No need to sort the first item if that's the parent dir reference,
|
||||
// excluding that allows sortLt() to ignore null values.
|
||||
const lst = dir_items.items[(if (dir_items.items.len > 0 and dir_items.items[0] == null) @as(usize, 1) else 0)..];
|
||||
std.sort.sort(?*model.Entry, lst, @as(void, undefined), sortLt);
|
||||
std.mem.sort(?*model.Entry, lst, {}, sortLt);
|
||||
current_view.load(next_sel);
|
||||
}
|
||||
|
||||
|
|
@ -134,7 +134,7 @@ pub fn loadDir(next_sel: ?*const model.Entry) void {
|
|||
if (dir_parent != model.root)
|
||||
dir_items.append(null) catch unreachable;
|
||||
var it = dir_parent.sub;
|
||||
while (it) |e| {
|
||||
while (it) |e| : (it = e.next) {
|
||||
if (e.pack.blocks > dir_max_blocks) dir_max_blocks = e.pack.blocks;
|
||||
if (e.size > dir_max_size) dir_max_size = e.size;
|
||||
const shown = main.config.show_hidden or blk: {
|
||||
|
|
@ -146,7 +146,6 @@ pub fn loadDir(next_sel: ?*const model.Entry) void {
|
|||
dir_items.append(e) catch unreachable;
|
||||
if (e.dir()) |d| if (d.shared_blocks > 0 or d.shared_size > 0) { dir_has_shared = true; };
|
||||
}
|
||||
it = e.next;
|
||||
}
|
||||
sortDir(next_sel);
|
||||
}
|
||||
|
|
@ -203,7 +202,7 @@ const Row = struct {
|
|||
fn graph(self: *Self) void {
|
||||
if ((!main.config.show_graph and !main.config.show_percent) or self.col + 20 > ui.cols) return;
|
||||
|
||||
const bar_size = std.math.max(ui.cols/7, 10);
|
||||
const bar_size = @max(ui.cols/7, 10);
|
||||
defer self.col += 3
|
||||
+ (if (main.config.show_graph) bar_size else 0)
|
||||
+ (if (main.config.show_percent) @as(u32, 6) else 0)
|
||||
|
|
@ -216,8 +215,8 @@ const Row = struct {
|
|||
if (main.config.show_percent) {
|
||||
self.bg.fg(.num);
|
||||
ui.addprint("{d:>5.1}", .{ 100 *
|
||||
if (main.config.show_blocks) @intToFloat(f32, item.pack.blocks) / @intToFloat(f32, std.math.max(1, dir_parent.entry.pack.blocks))
|
||||
else @intToFloat(f32, item.size) / @intToFloat(f32, std.math.max(1, dir_parent.entry.size))
|
||||
if (main.config.show_blocks) @as(f32, @floatFromInt(item.pack.blocks)) / @as(f32, @floatFromInt(@max(1, dir_parent.entry.pack.blocks)))
|
||||
else @as(f32, @floatFromInt(item.size)) / @as(f32, @floatFromInt(@max(1, dir_parent.entry.size)))
|
||||
});
|
||||
self.bg.fg(.default);
|
||||
ui.addch('%');
|
||||
|
|
@ -230,11 +229,11 @@ const Row = struct {
|
|||
max *= bar_size;
|
||||
num *= bar_size;
|
||||
}
|
||||
|
||||
const perblock = std.math.divFloor(u64, max, bar_size) catch unreachable;
|
||||
var i: u32 = 0;
|
||||
self.bg.fg(.graph);
|
||||
while (i < bar_size) : (i += 1) {
|
||||
const frac = std.math.min(@as(usize, 8), (num *| 8) / perblock);
|
||||
for (0..bar_size) |_| {
|
||||
const frac = @min(@as(usize, 8), (num *| 8) / perblock);
|
||||
ui.addstr(switch (main.config.graph_style) {
|
||||
.hash => ([_][:0]const u8{ " ", " ", " ", " ", " ", " ", " ", " ", "#" })[frac],
|
||||
.half => ([_][:0]const u8{ " ", " ", " ", " ", "▌", "▌", "▌", "▌", "█" })[frac],
|
||||
|
|
@ -261,11 +260,11 @@ const Row = struct {
|
|||
} else if (n < 100_000)
|
||||
ui.addnum(self.bg, n)
|
||||
else if (n < 1000_000) {
|
||||
ui.addprint("{d:>5.1}", .{ @intToFloat(f32, n) / 1000 });
|
||||
ui.addprint("{d:>5.1}", .{ @as(f32, @floatFromInt(n)) / 1000 });
|
||||
self.bg.fg(.default);
|
||||
ui.addch('k');
|
||||
} else if (n < 1000_000_000) {
|
||||
ui.addprint("{d:>5.1}", .{ @intToFloat(f32, n) / 1000_000 });
|
||||
ui.addprint("{d:>5.1}", .{ @as(f32, @floatFromInt(n)) / 1000_000 });
|
||||
self.bg.fg(.default);
|
||||
ui.addch('M');
|
||||
} else {
|
||||
|
|
@ -384,8 +383,8 @@ const info = struct {
|
|||
// TODO: Zig's sort() implementation is type-generic and not very
|
||||
// small. I suspect we can get a good save on our binary size by using
|
||||
// a smaller or non-generic sort. This doesn't have to be very fast.
|
||||
std.sort.sort(*model.Link, list.items, @as(void, undefined), lt);
|
||||
for (list.items) |n,i| if (&n.entry == e) { links_idx = i; };
|
||||
std.mem.sort(*model.Link, list.items, {}, lt);
|
||||
for (list.items, 0..) |n,i| if (&n.entry == e) { links_idx = i; };
|
||||
links = list;
|
||||
}
|
||||
}
|
||||
|
|
@ -395,8 +394,7 @@ const info = struct {
|
|||
if (links_idx < links_top) links_top = links_idx;
|
||||
if (links_idx >= links_top + numrows) links_top = links_idx - numrows + 1;
|
||||
|
||||
var i: u32 = 0;
|
||||
while (i < numrows) : (i += 1) {
|
||||
for (0..numrows) |i| {
|
||||
if (i + links_top >= links.?.items.len) break;
|
||||
const e = links.?.items[i+links_top];
|
||||
ui.style(if (i+links_top == links_idx) .sel else .default);
|
||||
|
|
@ -625,7 +623,7 @@ const help = struct {
|
|||
var i = offset*2;
|
||||
while (i < (offset + keylines)*2) : (i += 2) {
|
||||
line += 1;
|
||||
box.move(line, 13 - @intCast(u32, keys[i].len));
|
||||
box.move(line, 13 - @as(u32, @intCast(keys[i].len)));
|
||||
ui.style(.key);
|
||||
ui.addstr(keys[i]);
|
||||
ui.style(.default);
|
||||
|
|
@ -657,15 +655,12 @@ const help = struct {
|
|||
}
|
||||
|
||||
fn drawAbout(box: ui.Box) void {
|
||||
for (logo) |s, n| {
|
||||
box.move(@intCast(u32, n)+3, 12);
|
||||
for (logo, 0..) |s, n| {
|
||||
box.move(@as(u32, @intCast(n+3)), 12);
|
||||
var i: u5 = 28;
|
||||
while (true) {
|
||||
while (i != 0) : (i -= 1) {
|
||||
ui.style(if (s & (@as(u29,1)<<i) > 0) .sel else .default);
|
||||
ui.addch(' ');
|
||||
if (i == 0)
|
||||
break;
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
ui.style(.default);
|
||||
|
|
@ -823,7 +818,7 @@ fn keyInputSelection(ch: i32, idx: *usize, len: usize, page: u32) bool {
|
|||
ui.c.KEY_HOME => idx.* = 0,
|
||||
ui.c.KEY_END, ui.c.KEY_LL => idx.* = len -| 1,
|
||||
ui.c.KEY_PPAGE => idx.* = idx.* -| page,
|
||||
ui.c.KEY_NPAGE => idx.* = std.math.min(len -| 1, idx.* + page),
|
||||
ui.c.KEY_NPAGE => idx.* = @min(len -| 1, idx.* + page),
|
||||
else => return false,
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
101
src/main.zig
101
src/main.zig
|
|
@ -13,15 +13,25 @@ const util = @import("util.zig");
|
|||
const exclude = @import("exclude.zig");
|
||||
const c = @cImport(@cInclude("locale.h"));
|
||||
|
||||
test "imports" {
|
||||
_ = model;
|
||||
_ = scan;
|
||||
_ = ui;
|
||||
_ = browser;
|
||||
_ = delete;
|
||||
_ = util;
|
||||
_ = exclude;
|
||||
}
|
||||
|
||||
// "Custom" allocator that wraps the libc allocator and calls ui.oom() on error.
|
||||
// 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, alignment: u29, len_align: u29, return_address: usize) error{OutOfMemory}![]u8 {
|
||||
fn wrapAlloc(_: *anyopaque, len: usize, ptr_alignment: u8, return_address: usize) ?[*]u8 {
|
||||
while (true) {
|
||||
if (std.heap.c_allocator.vtable.alloc(undefined, len, alignment, len_align, return_address)) |r|
|
||||
if (std.heap.c_allocator.vtable.alloc(undefined, len, ptr_alignment, return_address)) |r|
|
||||
return r
|
||||
else |_| {}
|
||||
else {}
|
||||
ui.oom();
|
||||
}
|
||||
}
|
||||
|
|
@ -264,19 +274,26 @@ fn tryReadArgsFile(path: [:0]const u8) void {
|
|||
defer f.close();
|
||||
|
||||
var arglist = std.ArrayList([:0]const u8).init(allocator);
|
||||
var rd_ = std.io.bufferedReader(f.reader());
|
||||
var rd = rd_.reader();
|
||||
var linebuf: [4096]u8 = undefined;
|
||||
|
||||
while (
|
||||
rd.readUntilDelimiterOrEof(&linebuf, '\n')
|
||||
catch |e| ui.die("Error reading from {s}: {s}\nRun with --ignore-config to skip reading config files.\n", .{ path, ui.errorString(e) })
|
||||
) |line_| {
|
||||
var line = std.mem.trim(u8, line_, &std.ascii.spaces);
|
||||
var rd_ = std.io.bufferedReader(f.reader());
|
||||
const rd = rd_.reader();
|
||||
|
||||
var line_buf: [4096]u8 = undefined;
|
||||
var line_fbs = std.io.fixedBufferStream(&line_buf);
|
||||
const line_writer = line_fbs.writer();
|
||||
|
||||
while (true) : (line_fbs.reset()) {
|
||||
rd.streamUntilDelimiter(line_writer, '\n', line_buf.len) catch |err| switch (err) {
|
||||
error.EndOfStream => if (line_fbs.getPos() catch unreachable == 0) break,
|
||||
else => |e| ui.die("Error reading from {s}: {s}\nRun with --ignore-config to skip reading config files.\n", .{ path, ui.errorString(e) }),
|
||||
};
|
||||
var line_ = line_fbs.getWritten();
|
||||
|
||||
var line = std.mem.trim(u8, line_, &std.ascii.whitespace);
|
||||
if (line.len == 0 or line[0] == '#') continue;
|
||||
if (std.mem.indexOfAny(u8, line, " \t=")) |i| {
|
||||
arglist.append(allocator.dupeZ(u8, line[0..i]) catch unreachable) catch unreachable;
|
||||
line = std.mem.trimLeft(u8, line[i+1..], &std.ascii.spaces);
|
||||
line = std.mem.trimLeft(u8, line[i+1..], &std.ascii.whitespace);
|
||||
}
|
||||
arglist.append(allocator.dupeZ(u8, line) catch unreachable) catch unreachable;
|
||||
}
|
||||
|
|
@ -291,12 +308,14 @@ fn tryReadArgsFile(path: [:0]const u8) void {
|
|||
}
|
||||
|
||||
fn version() noreturn {
|
||||
std.io.getStdOut().writer().writeAll("ncdu " ++ program_version ++ "\n") catch {};
|
||||
const stdout = std.io.getStdOut();
|
||||
stdout.writeAll("ncdu " ++ program_version ++ "\n") catch {};
|
||||
std.process.exit(0);
|
||||
}
|
||||
|
||||
fn help() noreturn {
|
||||
std.io.getStdOut().writer().writeAll(
|
||||
const stdout = std.io.getStdOut();
|
||||
stdout.writeAll(
|
||||
\\ncdu <options> <directory>
|
||||
\\
|
||||
\\Options:
|
||||
|
|
@ -339,7 +358,7 @@ fn spawnShell() void {
|
|||
// NCDU_LEVEL can only count to 9, keeps the implementation simple.
|
||||
if (env.get("NCDU_LEVEL")) |l|
|
||||
env.put("NCDU_LEVEL", if (l.len == 0) "1" else switch (l[0]) {
|
||||
'0'...'8' => @as([]const u8, &.{l[0]+1}),
|
||||
'0'...'8' => |d| &[1] u8{d+1},
|
||||
'9' => "9",
|
||||
else => "1"
|
||||
}) catch unreachable
|
||||
|
|
@ -347,17 +366,19 @@ fn spawnShell() void {
|
|||
env.put("NCDU_LEVEL", "1") catch unreachable;
|
||||
|
||||
const shell = std.os.getenvZ("NCDU_SHELL") orelse std.os.getenvZ("SHELL") orelse "/bin/sh";
|
||||
var child = std.ChildProcess.init(&.{shell}, allocator);
|
||||
var child = std.process.Child.init(&.{shell}, allocator);
|
||||
child.cwd = path.items;
|
||||
child.env_map = &env;
|
||||
|
||||
const stdin = std.io.getStdIn();
|
||||
const stderr = std.io.getStdErr();
|
||||
const term = child.spawnAndWait() catch |e| blk: {
|
||||
_ = std.io.getStdErr().writer().print(
|
||||
stderr.writer().print(
|
||||
"Error spawning shell: {s}\n\nPress enter to continue.\n",
|
||||
.{ ui.errorString(e) }
|
||||
) catch {};
|
||||
_ = std.io.getStdIn().reader().skipUntilDelimiterOrEof('\n') catch unreachable;
|
||||
break :blk std.ChildProcess.Term{ .Exited = 0 };
|
||||
stdin.reader().skipUntilDelimiterOrEof('\n') catch unreachable;
|
||||
break :blk std.process.Child.Term{ .Exited = 0 };
|
||||
};
|
||||
if (term != .Exited) {
|
||||
const n = switch (term) {
|
||||
|
|
@ -372,10 +393,10 @@ fn spawnShell() void {
|
|||
.Stopped => |v| v,
|
||||
.Unknown => |v| v,
|
||||
};
|
||||
_ = std.io.getStdErr().writer().print(
|
||||
stderr.writer().print(
|
||||
"Shell returned with {s} code {}.\n\nPress enter to continue.\n", .{ n, v }
|
||||
) catch {};
|
||||
_ = std.io.getStdIn().reader().skipUntilDelimiterOrEof('\n') catch unreachable;
|
||||
stdin.reader().skipUntilDelimiterOrEof('\n') catch unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -383,15 +404,22 @@ fn spawnShell() void {
|
|||
fn readExcludeFile(path: [:0]const u8) !void {
|
||||
const f = try std.fs.cwd().openFileZ(path, .{});
|
||||
defer f.close();
|
||||
|
||||
var rd_ = std.io.bufferedReader(f.reader());
|
||||
var rd = rd_.reader();
|
||||
var buf = std.ArrayList(u8).init(allocator);
|
||||
defer buf.deinit();
|
||||
while (true) {
|
||||
rd.readUntilDelimiterArrayList(&buf, '\n', 4096)
|
||||
catch |e| if (e != error.EndOfStream) return e else if (buf.items.len == 0) break;
|
||||
if (buf.items.len > 0)
|
||||
exclude.addPattern(util.arrayListBufZ(&buf));
|
||||
const rd = rd_.reader();
|
||||
|
||||
var line_buf: [4096]u8 = undefined;
|
||||
var line_fbs = std.io.fixedBufferStream(&line_buf);
|
||||
const line_writer = line_fbs.writer();
|
||||
|
||||
while (true) : (line_fbs.reset()) {
|
||||
rd.streamUntilDelimiter(line_writer, '\n', line_buf.len) catch |err| switch (err) {
|
||||
error.EndOfStream => if (line_fbs.getPos() catch unreachable == 0) break,
|
||||
else => |e| return e,
|
||||
};
|
||||
const line = line_fbs.getWritten();
|
||||
if (line.len > 0)
|
||||
exclude.addPattern(line);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -459,8 +487,10 @@ pub fn main() void {
|
|||
if (@import("builtin").os.tag != .linux and config.exclude_kernfs)
|
||||
ui.die("The --exclude-kernfs flag is currently only supported on Linux.\n", .{});
|
||||
|
||||
const out_tty = std.io.getStdOut().isTty();
|
||||
const in_tty = std.io.getStdIn().isTty();
|
||||
const stdin = std.io.getStdIn();
|
||||
const stdout = std.io.getStdOut();
|
||||
const out_tty = stdout.isTty();
|
||||
const in_tty = stdin.isTty();
|
||||
if (config.scan_ui == null) {
|
||||
if (export_file) |f| {
|
||||
if (!out_tty or std.mem.eql(u8, f, "-")) config.scan_ui = .none
|
||||
|
|
@ -475,7 +505,7 @@ pub fn main() void {
|
|||
defer ui.deinit();
|
||||
|
||||
var out_file = if (export_file) |f| (
|
||||
if (std.mem.eql(u8, f, "-")) std.io.getStdOut()
|
||||
if (std.mem.eql(u8, f, "-")) stdout
|
||||
else std.fs.cwd().createFileZ(f, .{})
|
||||
catch |e| ui.die("Error opening export file: {s}.\n", .{ui.errorString(e)})
|
||||
) else null;
|
||||
|
|
@ -554,13 +584,6 @@ pub fn handleEvent(block: bool, force_draw: bool) void {
|
|||
}
|
||||
}
|
||||
|
||||
test "imports" {
|
||||
_ = @import("model.zig");
|
||||
_ = @import("ui.zig");
|
||||
_ = @import("util.zig");
|
||||
_ = @import("exclude.zig");
|
||||
}
|
||||
|
||||
test "argument parser" {
|
||||
const lst = [_][:0]const u8{ "a", "-abcd=e", "--opt1=arg1", "--opt2", "arg2", "-x", "foo", "", "--", "--arg", "", "-", };
|
||||
const T = struct {
|
||||
|
|
|
|||
|
|
@ -50,15 +50,15 @@ pub const Entry = extern struct {
|
|||
const Self = @This();
|
||||
|
||||
pub fn dir(self: *Self) ?*Dir {
|
||||
return if (self.pack.etype == .dir) @ptrCast(*Dir, self) else null;
|
||||
return if (self.pack.etype == .dir) @ptrCast(self) else null;
|
||||
}
|
||||
|
||||
pub fn link(self: *Self) ?*Link {
|
||||
return if (self.pack.etype == .link) @ptrCast(*Link, self) else null;
|
||||
return if (self.pack.etype == .link) @ptrCast(self) else null;
|
||||
}
|
||||
|
||||
pub fn file(self: *Self) ?*File {
|
||||
return if (self.pack.etype == .file) @ptrCast(*File, self) else null;
|
||||
return if (self.pack.etype == .file) @ptrCast(self) else null;
|
||||
}
|
||||
|
||||
// Whether this entry should be displayed as a "directory".
|
||||
|
|
@ -68,17 +68,18 @@ pub const Entry = extern struct {
|
|||
}
|
||||
|
||||
pub fn name(self: *const Self) [:0]const u8 {
|
||||
const ptr = switch (self.pack.etype) {
|
||||
.dir => &@ptrCast(*const Dir, self).name,
|
||||
.link => &@ptrCast(*const Link, self).name,
|
||||
.file => &@ptrCast(*const File, self).name,
|
||||
const self_name = switch (self.pack.etype) {
|
||||
.dir => &@as(*const Dir, @ptrCast(self)).name,
|
||||
.link => &@as(*const Link, @ptrCast(self)).name,
|
||||
.file => &@as(*const File, @ptrCast(self)).name,
|
||||
};
|
||||
return std.mem.sliceTo(@ptrCast([*:0]const u8, ptr), 0);
|
||||
const name_ptr: [*:0]const u8 = @ptrCast(self_name);
|
||||
return std.mem.sliceTo(name_ptr, 0);
|
||||
}
|
||||
|
||||
pub fn ext(self: *Self) ?*Ext {
|
||||
if (!self.pack.isext) return null;
|
||||
return @ptrCast(*Ext, @ptrCast([*]Ext, self) - 1);
|
||||
return @ptrCast(@as([*]Ext, @ptrCast(self)) - 1);
|
||||
}
|
||||
|
||||
fn alloc(comptime T: type, etype: EType, isext: bool, ename: []const u8) *Entry {
|
||||
|
|
@ -89,13 +90,13 @@ pub const Entry = extern struct {
|
|||
ui.oom();
|
||||
};
|
||||
if (isext) {
|
||||
@ptrCast(*Ext, ptr).* = .{};
|
||||
@as(*Ext, @ptrCast(ptr)).* = .{};
|
||||
ptr = ptr[@sizeOf(Ext)..];
|
||||
}
|
||||
const e = @ptrCast(*T, ptr);
|
||||
const e: *T = @ptrCast(ptr);
|
||||
e.* = .{ .entry = .{ .pack = .{ .etype = etype, .isext = isext } } };
|
||||
const n = @ptrCast([*]u8, &e.name)[0..ename.len+1];
|
||||
std.mem.copy(u8, n, ename);
|
||||
const n = @as([*]u8, @ptrCast(&e.name))[0..ename.len+1];
|
||||
@memcpy(n[0..ename.len], ename);
|
||||
n[ename.len] = 0;
|
||||
return &e.entry;
|
||||
}
|
||||
|
|
@ -320,7 +321,7 @@ pub const devices = struct {
|
|||
pub fn getId(dev: u64) DevId {
|
||||
var d = lookup.getOrPut(dev) catch unreachable;
|
||||
if (!d.found_existing) {
|
||||
d.value_ptr.* = @intCast(DevId, list.items.len);
|
||||
d.value_ptr.* = @as(DevId, @intCast(list.items.len));
|
||||
list.append(dev) catch unreachable;
|
||||
}
|
||||
return d.value_ptr.*;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,3 @@ chtype ncdu_acs_urcorner() { return ACS_URCORNER; }
|
|||
chtype ncdu_acs_lrcorner() { return ACS_LRCORNER; }
|
||||
chtype ncdu_acs_hline() { return ACS_VLINE ; }
|
||||
chtype ncdu_acs_vline() { return ACS_HLINE ; }
|
||||
|
||||
/* https://github.com/ziglang/zig/issues/8947 */
|
||||
void ncdu_init_pair(int a, int b, int c) { init_pair(a,b,c); }
|
||||
|
|
|
|||
18
src/scan.zig
18
src/scan.zig
|
|
@ -23,12 +23,12 @@ const Stat = struct {
|
|||
symlink: bool = false,
|
||||
ext: model.Ext = .{},
|
||||
|
||||
fn clamp(comptime T: type, comptime field: anytype, x: anytype) std.meta.fieldInfo(T, field).field_type {
|
||||
return util.castClamp(std.meta.fieldInfo(T, field).field_type, x);
|
||||
fn clamp(comptime T: type, comptime field: anytype, x: anytype) std.meta.fieldInfo(T, field).type {
|
||||
return util.castClamp(std.meta.fieldInfo(T, field).type, x);
|
||||
}
|
||||
|
||||
fn truncate(comptime T: type, comptime field: anytype, x: anytype) std.meta.fieldInfo(T, field).field_type {
|
||||
return util.castTruncate(std.meta.fieldInfo(T, field).field_type, x);
|
||||
fn truncate(comptime T: type, comptime field: anytype, x: anytype) std.meta.fieldInfo(T, field).type {
|
||||
return util.castTruncate(std.meta.fieldInfo(T, field).type, x);
|
||||
}
|
||||
|
||||
fn read(parent: std.fs.Dir, name: [:0]const u8, follow: bool) !Stat {
|
||||
|
|
@ -787,7 +787,7 @@ const Import = struct {
|
|||
},
|
||||
'd' => {
|
||||
if (eq(u8, key, "dsize")) {
|
||||
self.ctx.stat.blocks = @intCast(model.Blocks, self.uint(u64)>>9);
|
||||
self.ctx.stat.blocks = @intCast(self.uint(u64)>>9);
|
||||
return;
|
||||
}
|
||||
if (eq(u8, key, "dev")) {
|
||||
|
|
@ -968,7 +968,7 @@ const Import = struct {
|
|||
};
|
||||
|
||||
pub fn importRoot(path: [:0]const u8, out: ?std.fs.File) void {
|
||||
var fd = if (std.mem.eql(u8, "-", path)) std.io.getStdIn()
|
||||
const fd = if (std.mem.eql(u8, "-", path)) std.io.getStdIn()
|
||||
else std.fs.cwd().openFileZ(path, .{})
|
||||
catch |e| ui.die("Error reading file: {s}.\n", .{ui.errorString(e)});
|
||||
defer fd.close();
|
||||
|
|
@ -1060,9 +1060,8 @@ fn drawBox() void {
|
|||
animation_pos += 1;
|
||||
if (animation_pos >= txt.len*2) animation_pos = 0;
|
||||
if (animation_pos < txt.len) {
|
||||
var i: u32 = 0;
|
||||
box.move(8, 2);
|
||||
while (i <= animation_pos) : (i += 1) ui.addch(txt[i]);
|
||||
for (txt[0..animation_pos + 1]) |t| ui.addch(t);
|
||||
} else {
|
||||
var i: u32 = txt.len-1;
|
||||
while (i > animation_pos-txt.len) : (i -= 1) {
|
||||
|
|
@ -1093,7 +1092,8 @@ pub fn draw() void {
|
|||
.{ ui.shorten(active_context.pathZ(), 51), active_context.items_seen, r.num(), r.unit }
|
||||
) catch return;
|
||||
}
|
||||
_ = std.io.getStdErr().write(line) catch {};
|
||||
const stderr = std.io.getStdErr();
|
||||
stderr.writeAll(line) catch {};
|
||||
},
|
||||
.full => drawBox(),
|
||||
}
|
||||
|
|
|
|||
54
src/ui.zig
54
src/ui.zig
|
|
@ -24,7 +24,8 @@ pub var cols: u32 = undefined;
|
|||
|
||||
pub fn die(comptime fmt: []const u8, args: anytype) noreturn {
|
||||
deinit();
|
||||
_ = std.io.getStdErr().writer().print(fmt, args) catch {};
|
||||
const stderr = std.io.getStdErr();
|
||||
stderr.writer().print(fmt, args) catch {};
|
||||
std.process.exit(1);
|
||||
}
|
||||
|
||||
|
|
@ -45,7 +46,8 @@ pub fn quit() noreturn {
|
|||
pub fn oom() void {
|
||||
const haveui = inited;
|
||||
deinit();
|
||||
_ = std.io.getStdErr().writer().writeAll("\x1b7\x1b[JOut of memory, trying again in 1 second. Hit Ctrl-C to abort.\x1b8") catch {};
|
||||
const stderr = std.io.getStdErr();
|
||||
stderr.writeAll("\x1b7\x1b[JOut of memory, trying again in 1 second. Hit Ctrl-C to abort.\x1b8") catch {};
|
||||
std.time.sleep(std.time.ns_per_s);
|
||||
if (haveui)
|
||||
init();
|
||||
|
|
@ -135,7 +137,7 @@ pub fn shorten(in: [:0]const u8, max_width: u32) [:0] const u8 {
|
|||
// (The "proper" way is to use mbtowc(), but I'd rather port the musl wcwidth implementation to Zig so that I *know* it'll be Unicode.
|
||||
// On the other hand, ncurses also use wcwidth() so that would cause duplicated code. Ugh)
|
||||
const cp_width_ = c.wcwidth(cp);
|
||||
const cp_width = @intCast(u32, if (cp_width_ < 0) 0 else cp_width_);
|
||||
const cp_width: u32 = @intCast(if (cp_width_ < 0) 0 else cp_width_);
|
||||
const cp_len = std.unicode.utf8CodepointSequenceLength(cp) catch unreachable;
|
||||
total_width += cp_width;
|
||||
if (!prefix_done and prefix_width + cp_width <= @divFloor(max_width-1, 2)-1) {
|
||||
|
|
@ -155,7 +157,7 @@ pub fn shorten(in: [:0]const u8, max_width: u32) [:0] const u8 {
|
|||
it = std.unicode.Utf8View.initUnchecked(in[prefix_end..]).iterator();
|
||||
while (it.nextCodepoint()) |cp| {
|
||||
const cp_width_ = c.wcwidth(cp);
|
||||
const cp_width = @intCast(u32, if (cp_width_ < 0) 0 else cp_width_);
|
||||
const cp_width: u32 = @intCast(if (cp_width_ < 0) 0 else cp_width_);
|
||||
const cp_len = std.unicode.utf8CodepointSequenceLength(cp) catch unreachable;
|
||||
start_width += cp_width;
|
||||
start_len += cp_len;
|
||||
|
|
@ -197,7 +199,6 @@ extern fn ncdu_acs_urcorner() c.chtype;
|
|||
extern fn ncdu_acs_lrcorner() c.chtype;
|
||||
extern fn ncdu_acs_hline() c.chtype;
|
||||
extern fn ncdu_acs_vline() c.chtype;
|
||||
extern fn ncdu_init_pair(idx: c_int, fg: c_int, bg: c_int) void;
|
||||
|
||||
const StyleAttr = struct { fg: i16, bg: i16, attr: u32 };
|
||||
const StyleDef = struct {
|
||||
|
|
@ -286,20 +287,18 @@ const styles = [_]StyleDef{
|
|||
};
|
||||
|
||||
pub const Style = lbl: {
|
||||
var fields: [styles.len]std.builtin.Type.EnumField = undefined;
|
||||
var decls = [_]std.builtin.Type.Declaration{};
|
||||
inline for (styles) |s, i| {
|
||||
fields[i] = .{
|
||||
comptime var fields: [styles.len]std.builtin.Type.EnumField = undefined;
|
||||
inline for (&fields, styles, 0..) |*field, s, i| {
|
||||
field.* = .{
|
||||
.name = s.name,
|
||||
.value = i,
|
||||
};
|
||||
}
|
||||
break :lbl @Type(.{
|
||||
.Enum = .{
|
||||
.layout = .Auto,
|
||||
.tag_type = u8,
|
||||
.fields = &fields,
|
||||
.decls = &decls,
|
||||
.decls = &[_]std.builtin.Type.Declaration{},
|
||||
.is_exhaustive = true,
|
||||
}
|
||||
});
|
||||
|
|
@ -336,14 +335,15 @@ pub const Bg = enum {
|
|||
|
||||
fn updateSize() void {
|
||||
// getmax[yx] macros are marked as "legacy", but Zig can't deal with the "proper" getmaxyx macro.
|
||||
rows = @intCast(u32, c.getmaxy(c.stdscr));
|
||||
cols = @intCast(u32, c.getmaxx(c.stdscr));
|
||||
rows = @intCast(c.getmaxy(c.stdscr));
|
||||
cols = @intCast(c.getmaxx(c.stdscr));
|
||||
}
|
||||
|
||||
fn clearScr() void {
|
||||
// Send a "clear from cursor to end of screen" instruction, to clear a
|
||||
// potential line left behind from scanning in -1 mode.
|
||||
_ = std.io.getStdErr().write("\x1b[J") catch {};
|
||||
const stderr = std.io.getStdErr();
|
||||
stderr.writeAll("\x1b[J") catch {};
|
||||
}
|
||||
|
||||
pub fn init() void {
|
||||
|
|
@ -351,7 +351,7 @@ pub fn init() void {
|
|||
clearScr();
|
||||
if (main.config.nc_tty) {
|
||||
var tty = c.fopen("/dev/tty", "r+");
|
||||
if (tty == null) die("Error opening /dev/tty: {s}.\n", .{ c.strerror(@enumToInt(std.c.getErrno(-1))) });
|
||||
if (tty == null) die("Error opening /dev/tty: {s}.\n", .{ c.strerror(@intFromEnum(std.c.getErrno(-1))) });
|
||||
var term = c.newterm(null, tty, tty);
|
||||
if (term == null) die("Error initializing ncurses.\n", .{});
|
||||
_ = c.set_term(term);
|
||||
|
|
@ -366,9 +366,8 @@ pub fn init() void {
|
|||
|
||||
_ = c.start_color();
|
||||
_ = c.use_default_colors();
|
||||
for (styles) |s, i| _ = ncdu_init_pair(@intCast(i16, i+1), s.style().fg, s.style().bg);
|
||||
_ = c.bkgd(@intCast(c.chtype, c.COLOR_PAIR(@enumToInt(Style.default)+1)));
|
||||
|
||||
for (styles, 0..) |s, i| _ = c.init_pair(@as(i16, @intCast(i+1)), s.style().fg, s.style().bg);
|
||||
_ = c.bkgd(@intCast(c.COLOR_PAIR(@intFromEnum(Style.default)+1)));
|
||||
inited = true;
|
||||
}
|
||||
|
||||
|
|
@ -384,11 +383,11 @@ pub fn deinit() void {
|
|||
}
|
||||
|
||||
pub fn style(s: Style) void {
|
||||
_ = c.attr_set(styles[@enumToInt(s)].style().attr, @enumToInt(s)+1, null);
|
||||
_ = c.attr_set(styles[@intFromEnum(s)].style().attr, @intFromEnum(s)+1, null);
|
||||
}
|
||||
|
||||
pub fn move(y: u32, x: u32) void {
|
||||
_ = c.move(@intCast(i32, y), @intCast(i32, x));
|
||||
_ = c.move(@as(i32, @intCast(y)), @as(i32, @intCast(x)));
|
||||
}
|
||||
|
||||
// Wraps to the next line if the text overflows, not sure how to disable that.
|
||||
|
|
@ -419,7 +418,7 @@ pub const FmtSize = struct {
|
|||
|
||||
pub fn fmt(v: u64) @This() {
|
||||
var r: @This() = undefined;
|
||||
var f = @intToFloat(f32, v);
|
||||
var f: f32 = @floatFromInt(v);
|
||||
if (main.config.si) {
|
||||
if(f < 1000.0) { r.unit = " B"; }
|
||||
else if(f < 1e6) { r.unit = " KB"; f /= 1e3; }
|
||||
|
|
@ -464,7 +463,7 @@ pub fn addnum(bg: Bg, v: u64) void {
|
|||
const s = std.fmt.bufPrint(&buf, "{d}", .{v}) catch unreachable;
|
||||
var f: [64:0]u8 = undefined;
|
||||
var i: usize = 0;
|
||||
for (s) |digit, n| {
|
||||
for (s, 0..) |digit, n| {
|
||||
if (n != 0 and (s.len - n) % 3 == 0) {
|
||||
for (main.config.thousands_sep) |ch| {
|
||||
f[i] = ch;
|
||||
|
|
@ -518,7 +517,7 @@ pub fn addts(bg: Bg, ts: u64) void {
|
|||
}
|
||||
|
||||
pub fn hline(ch: c.chtype, len: u32) void {
|
||||
_ = c.hline(ch, @intCast(i32, len));
|
||||
_ = c.hline(ch, @as(i32, @intCast(len)));
|
||||
}
|
||||
|
||||
// Draws a bordered box in the center of the screen.
|
||||
|
|
@ -586,20 +585,19 @@ pub fn getch(block: bool) i32 {
|
|||
// In non-blocking mode, we can only assume that ERR means "no input yet".
|
||||
// In blocking mode, give it 100 tries with a 10ms delay in between,
|
||||
// then just give up and die to avoid an infinite loop and unresponsive program.
|
||||
var attempts: u8 = 0;
|
||||
while (attempts < 100) : (attempts += 1) {
|
||||
var ch = c.getch();
|
||||
for (0..100) |_| {
|
||||
const ch = c.getch();
|
||||
if (ch == c.KEY_RESIZE) {
|
||||
updateSize();
|
||||
return -1;
|
||||
}
|
||||
if (ch == c.ERR) {
|
||||
if (!block) return 0;
|
||||
std.os.nanosleep(0, 10*std.time.ns_per_ms);
|
||||
std.time.sleep(10*std.time.ns_per_ms);
|
||||
continue;
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
die("Error reading keyboard input, assuming TTY has been lost.\n(Potentially nonsensical error message: {s})\n",
|
||||
.{ c.strerror(@enumToInt(std.c.getErrno(-1))) });
|
||||
.{ c.strerror(@intFromEnum(std.c.getErrno(-1))) });
|
||||
}
|
||||
|
|
|
|||
21
src/util.zig
21
src/util.zig
|
|
@ -11,7 +11,7 @@ pub fn castClamp(comptime T: type, x: anytype) 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);
|
||||
return @intCast(x);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -19,13 +19,13 @@ pub fn castClamp(comptime T: type, x: anytype) T {
|
|||
pub fn castTruncate(comptime T: type, x: anytype) T {
|
||||
const Ti = @typeInfo(T).Int;
|
||||
const Xi = @typeInfo(@TypeOf(x)).Int;
|
||||
const nx = if (Xi.signedness != Ti.signedness) @bitCast(std.meta.Int(Ti.signedness, Xi.bits), x) else x;
|
||||
return if (Xi.bits > Ti.bits) @truncate(T, nx) else nx;
|
||||
const nx: std.meta.Int(Ti.signedness, Xi.bits) = @bitCast(x);
|
||||
return if (Xi.bits > Ti.bits) @truncate(nx) else nx;
|
||||
}
|
||||
|
||||
// Multiplies by 512, saturating.
|
||||
pub fn blocksToSize(b: u64) u64 {
|
||||
return if (b & 0xFF80000000000000 > 0) std.math.maxInt(u64) else b << 9;
|
||||
return b *| 512;
|
||||
}
|
||||
|
||||
// Ensure the given arraylist buffer gets zero-terminated and returns a slice
|
||||
|
|
@ -44,8 +44,8 @@ pub fn strnatcmp(a: [:0]const u8, b: [:0]const u8) std.math.Order {
|
|||
var bi: usize = 0;
|
||||
const isDigit = std.ascii.isDigit;
|
||||
while (true) {
|
||||
while (std.ascii.isSpace(a[ai])) ai += 1;
|
||||
while (std.ascii.isSpace(b[bi])) bi += 1;
|
||||
while (std.ascii.isWhitespace(a[ai])) ai += 1;
|
||||
while (std.ascii.isWhitespace(b[bi])) bi += 1;
|
||||
|
||||
if (isDigit(a[ai]) and isDigit(b[bi])) {
|
||||
if (a[ai] == '0' or b[bi] == '0') { // compare_left
|
||||
|
|
@ -133,12 +133,9 @@ test "strnatcmp" {
|
|||
};
|
||||
// Test each string against each other string, simple and thorough.
|
||||
const eq = std.testing.expectEqual;
|
||||
var i: usize = 0;
|
||||
while (i < w.len) : (i += 1) {
|
||||
var j: usize = 0;
|
||||
for (0..w.len) |i| {
|
||||
try eq(strnatcmp(w[i], w[i]), .eq);
|
||||
while (j < i) : (j += 1) try eq(strnatcmp(w[i], w[j]), .gt);
|
||||
j += 1;
|
||||
while (j < w.len) : (j += 1) try eq(strnatcmp(w[i], w[j]), .lt);
|
||||
for (0..i) |j| try eq(strnatcmp(w[i], w[j]), .gt);
|
||||
for (i+1..w.len) |j| try eq(strnatcmp(w[i], w[j]), .lt);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue