mirror of
https://code.blicky.net/yorhel/ncdu.git
synced 2026-01-13 09:18:40 -09:00
Compare commits
No commits in common. "zig" and "v2.9" have entirely different histories.
13 changed files with 163 additions and 209 deletions
|
|
@ -1,14 +1,6 @@
|
||||||
# SPDX-FileCopyrightText: Yorhel <projects@yorhel.nl>
|
# SPDX-FileCopyrightText: Yorhel <projects@yorhel.nl>
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
2.9.2 - 2025-10-24
|
|
||||||
- Still requires Zig 0.14 or 0.15
|
|
||||||
- Fix hang on loading config file when compiled with Zig 0.15.2
|
|
||||||
|
|
||||||
2.9.1 - 2025-08-21
|
|
||||||
- Add support for building with Zig 0.15
|
|
||||||
- Zig 0.14 is still supported
|
|
||||||
|
|
||||||
2.9 - 2025-08-16
|
2.9 - 2025-08-16
|
||||||
- Still requires Zig 0.14
|
- Still requires Zig 0.14
|
||||||
- Add --delete-command option to replace the built-in file deletion
|
- Add --delete-command option to replace the built-in file deletion
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ C version (1.x).
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- Zig 0.14 or 0.15
|
- Zig 0.14
|
||||||
- Some sort of POSIX-like OS
|
- Some sort of POSIX-like OS
|
||||||
- ncurses
|
- ncurses
|
||||||
- libzstd
|
- libzstd
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ const c = @import("c.zig").c;
|
||||||
|
|
||||||
pub const global = struct {
|
pub const global = struct {
|
||||||
var fd: std.fs.File = undefined;
|
var fd: std.fs.File = undefined;
|
||||||
var index: std.ArrayListUnmanaged(u8) = .empty;
|
var index = std.ArrayList(u8).init(main.allocator);
|
||||||
var file_off: u64 = 0;
|
var file_off: u64 = 0;
|
||||||
var lock: std.Thread.Mutex = .{};
|
var lock: std.Thread.Mutex = .{};
|
||||||
var root_itemref: u64 = 0;
|
var root_itemref: u64 = 0;
|
||||||
|
|
@ -105,11 +105,11 @@ pub const Thread = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn createBlock(t: *Thread) std.ArrayListUnmanaged(u8) {
|
fn createBlock(t: *Thread) std.ArrayList(u8) {
|
||||||
var out: std.ArrayListUnmanaged(u8) = .empty;
|
var out = std.ArrayList(u8).init(main.allocator);
|
||||||
if (t.block_num == std.math.maxInt(u32) or t.off == 0) return out;
|
if (t.block_num == std.math.maxInt(u32) or t.off == 0) return out;
|
||||||
|
|
||||||
out.ensureTotalCapacityPrecise(main.allocator, 12 + @as(usize, @intCast(c.ZSTD_COMPRESSBOUND(@as(c_int, @intCast(t.off)))))) catch unreachable;
|
out.ensureTotalCapacityPrecise(12 + @as(usize, @intCast(c.ZSTD_COMPRESSBOUND(@as(c_int, @intCast(t.off)))))) catch unreachable;
|
||||||
out.items.len = out.capacity;
|
out.items.len = out.capacity;
|
||||||
const bodylen = compressZstd(t.buf[0..t.off], out.items[8..]);
|
const bodylen = compressZstd(t.buf[0..t.off], out.items[8..]);
|
||||||
out.items.len = 12 + bodylen;
|
out.items.len = 12 + bodylen;
|
||||||
|
|
@ -122,8 +122,8 @@ pub const Thread = struct {
|
||||||
|
|
||||||
fn flush(t: *Thread, expected_len: usize) void {
|
fn flush(t: *Thread, expected_len: usize) void {
|
||||||
@branchHint(.unlikely);
|
@branchHint(.unlikely);
|
||||||
var block = createBlock(t);
|
const block = createBlock(t);
|
||||||
defer block.deinit(main.allocator);
|
defer block.deinit();
|
||||||
|
|
||||||
global.lock.lock();
|
global.lock.lock();
|
||||||
defer global.lock.unlock();
|
defer global.lock.unlock();
|
||||||
|
|
@ -141,7 +141,7 @@ pub const Thread = struct {
|
||||||
|
|
||||||
t.off = 0;
|
t.off = 0;
|
||||||
t.block_num = @intCast((global.index.items.len - 4) / 8);
|
t.block_num = @intCast((global.index.items.len - 4) / 8);
|
||||||
global.index.appendSlice(main.allocator, &[1]u8{0}**8) catch unreachable;
|
global.index.appendSlice(&[1]u8{0}**8) catch unreachable;
|
||||||
if (global.index.items.len + 12 >= (1<<28)) ui.die("Too many data blocks, please report a bug.\n", .{});
|
if (global.index.items.len + 12 >= (1<<28)) ui.die("Too many data blocks, please report a bug.\n", .{});
|
||||||
|
|
||||||
const newsize = blockSize(t.block_num);
|
const newsize = blockSize(t.block_num);
|
||||||
|
|
@ -447,12 +447,12 @@ pub fn done(threads: []sink.Thread) void {
|
||||||
|
|
||||||
while (std.mem.endsWith(u8, global.index.items, &[1]u8{0}**8))
|
while (std.mem.endsWith(u8, global.index.items, &[1]u8{0}**8))
|
||||||
global.index.shrinkRetainingCapacity(global.index.items.len - 8);
|
global.index.shrinkRetainingCapacity(global.index.items.len - 8);
|
||||||
global.index.appendSlice(main.allocator, &bigu64(global.root_itemref)) catch unreachable;
|
global.index.appendSlice(&bigu64(global.root_itemref)) catch unreachable;
|
||||||
global.index.appendSlice(main.allocator, &blockHeader(1, @intCast(global.index.items.len + 4))) catch unreachable;
|
global.index.appendSlice(&blockHeader(1, @intCast(global.index.items.len + 4))) catch unreachable;
|
||||||
global.index.items[0..4].* = blockHeader(1, @intCast(global.index.items.len));
|
global.index.items[0..4].* = blockHeader(1, @intCast(global.index.items.len));
|
||||||
global.fd.writeAll(global.index.items) catch |e|
|
global.fd.writeAll(global.index.items) catch |e|
|
||||||
ui.die("Error writing to file: {s}.\n", .{ ui.errorString(e) });
|
ui.die("Error writing to file: {s}.\n", .{ ui.errorString(e) });
|
||||||
global.index.clearAndFree(main.allocator);
|
global.index.clearAndFree();
|
||||||
|
|
||||||
global.fd.close();
|
global.fd.close();
|
||||||
}
|
}
|
||||||
|
|
@ -464,5 +464,5 @@ pub fn setupOutput(fd: std.fs.File) void {
|
||||||
global.file_off = 8;
|
global.file_off = 8;
|
||||||
|
|
||||||
// Placeholder for the index block header.
|
// Placeholder for the index block header.
|
||||||
global.index.appendSlice(main.allocator, "aaaa") catch unreachable;
|
global.index.appendSlice("aaaa") catch unreachable;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,16 +15,16 @@ const util = @import("util.zig");
|
||||||
// Currently opened directory.
|
// Currently opened directory.
|
||||||
pub var dir_parent: *model.Dir = undefined;
|
pub var dir_parent: *model.Dir = undefined;
|
||||||
pub var dir_path: [:0]u8 = undefined;
|
pub var dir_path: [:0]u8 = undefined;
|
||||||
var dir_parents: std.ArrayListUnmanaged(model.Ref) = .empty;
|
var dir_parents = std.ArrayList(model.Ref).init(main.allocator);
|
||||||
var dir_alloc = std.heap.ArenaAllocator.init(main.allocator);
|
var dir_alloc = std.heap.ArenaAllocator.init(main.allocator);
|
||||||
|
|
||||||
// Used to keep track of which dir is which ref, so we can enter it.
|
// Used to keep track of which dir is which ref, so we can enter it.
|
||||||
// Only used for binreader browsing.
|
// Only used for binreader browsing.
|
||||||
var dir_refs: std.ArrayListUnmanaged(struct { ptr: *model.Dir, ref: u64 }) = .empty;
|
var dir_refs = std.ArrayList(struct { ptr: *model.Dir, ref: u64 }).init(main.allocator);
|
||||||
|
|
||||||
// Sorted list of all items in the currently opened directory.
|
// Sorted list of all items in the currently opened directory.
|
||||||
// (first item may be null to indicate the "parent directory" item)
|
// (first item may be null to indicate the "parent directory" item)
|
||||||
var dir_items: std.ArrayListUnmanaged(?*model.Entry) = .empty;
|
var dir_items = std.ArrayList(?*model.Entry).init(main.allocator);
|
||||||
|
|
||||||
var dir_max_blocks: u64 = 0;
|
var dir_max_blocks: u64 = 0;
|
||||||
var dir_max_size: u64 = 0;
|
var dir_max_size: u64 = 0;
|
||||||
|
|
@ -146,7 +146,7 @@ pub fn loadDir(next_sel: u64) void {
|
||||||
dir_has_shared = false;
|
dir_has_shared = false;
|
||||||
|
|
||||||
if (dir_parents.items.len > 1)
|
if (dir_parents.items.len > 1)
|
||||||
dir_items.append(main.allocator, null) catch unreachable;
|
dir_items.append(null) catch unreachable;
|
||||||
var ref = dir_parent.sub;
|
var ref = dir_parent.sub;
|
||||||
while (!ref.isNull()) {
|
while (!ref.isNull()) {
|
||||||
const e =
|
const e =
|
||||||
|
|
@ -164,10 +164,10 @@ pub fn loadDir(next_sel: u64) void {
|
||||||
break :blk !excl and name[0] != '.' and name[name.len-1] != '~';
|
break :blk !excl and name[0] != '.' and name[name.len-1] != '~';
|
||||||
};
|
};
|
||||||
if (shown) {
|
if (shown) {
|
||||||
dir_items.append(main.allocator, e) catch unreachable;
|
dir_items.append(e) catch unreachable;
|
||||||
if (e.dir()) |d| {
|
if (e.dir()) |d| {
|
||||||
if (d.shared_blocks > 0 or d.shared_size > 0) dir_has_shared = true;
|
if (d.shared_blocks > 0 or d.shared_size > 0) dir_has_shared = true;
|
||||||
if (main.config.binreader) dir_refs.append(main.allocator, .{ .ptr = d, .ref = ref.ref }) catch unreachable;
|
if (main.config.binreader) dir_refs.append(.{ .ptr = d, .ref = ref.ref }) catch unreachable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -185,10 +185,10 @@ pub fn initRoot() void {
|
||||||
if (main.config.binreader) {
|
if (main.config.binreader) {
|
||||||
const ref = bin_reader.getRoot();
|
const ref = bin_reader.getRoot();
|
||||||
dir_parent = bin_reader.get(ref, main.allocator).dir() orelse ui.die("Invalid import\n", .{});
|
dir_parent = bin_reader.get(ref, main.allocator).dir() orelse ui.die("Invalid import\n", .{});
|
||||||
dir_parents.append(main.allocator, .{ .ref = ref }) catch unreachable;
|
dir_parents.append(.{ .ref = ref }) catch unreachable;
|
||||||
} else {
|
} else {
|
||||||
dir_parent = model.root;
|
dir_parent = model.root;
|
||||||
dir_parents.append(main.allocator, .{ .ptr = &dir_parent.entry }) catch unreachable;
|
dir_parents.append(.{ .ptr = &dir_parent.entry }) catch unreachable;
|
||||||
}
|
}
|
||||||
dir_path = main.allocator.dupeZ(u8, dir_parent.entry.name()) catch unreachable;
|
dir_path = main.allocator.dupeZ(u8, dir_parent.entry.name()) catch unreachable;
|
||||||
loadDir(0);
|
loadDir(0);
|
||||||
|
|
@ -202,10 +202,10 @@ fn enterSub(e: *model.Dir) void {
|
||||||
};
|
};
|
||||||
dir_parent.entry.destroy(main.allocator);
|
dir_parent.entry.destroy(main.allocator);
|
||||||
dir_parent = bin_reader.get(ref, main.allocator).dir() orelse unreachable;
|
dir_parent = bin_reader.get(ref, main.allocator).dir() orelse unreachable;
|
||||||
dir_parents.append(main.allocator, .{ .ref = ref }) catch unreachable;
|
dir_parents.append(.{ .ref = ref }) catch unreachable;
|
||||||
} else {
|
} else {
|
||||||
dir_parent = e;
|
dir_parent = e;
|
||||||
dir_parents.append(main.allocator, .{ .ptr = &e.entry }) catch unreachable;
|
dir_parents.append(.{ .ptr = &e.entry }) catch unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newpath = std.fs.path.joinZ(main.allocator, &[_][]const u8{ dir_path, e.entry.name() }) catch unreachable;
|
const newpath = std.fs.path.joinZ(main.allocator, &[_][]const u8{ dir_path, e.entry.name() }) catch unreachable;
|
||||||
|
|
@ -430,7 +430,7 @@ const info = struct {
|
||||||
|
|
||||||
var tab: Tab = .info;
|
var tab: Tab = .info;
|
||||||
var entry: ?*model.Entry = null;
|
var entry: ?*model.Entry = null;
|
||||||
var links: ?std.ArrayListUnmanaged(*model.Link) = null;
|
var links: ?std.ArrayList(*model.Link) = null;
|
||||||
var links_top: usize = 0;
|
var links_top: usize = 0;
|
||||||
var links_idx: usize = 0;
|
var links_idx: usize = 0;
|
||||||
|
|
||||||
|
|
@ -445,7 +445,7 @@ const info = struct {
|
||||||
// Set the displayed entry to the currently selected item and open the tab.
|
// Set the displayed entry to the currently selected item and open the tab.
|
||||||
fn set(e: ?*model.Entry, t: Tab) void {
|
fn set(e: ?*model.Entry, t: Tab) void {
|
||||||
if (e != entry) {
|
if (e != entry) {
|
||||||
if (links) |*l| l.deinit(main.allocator);
|
if (links) |*l| l.deinit();
|
||||||
links = null;
|
links = null;
|
||||||
links_top = 0;
|
links_top = 0;
|
||||||
links_idx = 0;
|
links_idx = 0;
|
||||||
|
|
@ -458,10 +458,10 @@ const info = struct {
|
||||||
state = .info;
|
state = .info;
|
||||||
tab = t;
|
tab = t;
|
||||||
if (tab == .links and links == null and !main.config.binreader) {
|
if (tab == .links and links == null and !main.config.binreader) {
|
||||||
var list: std.ArrayListUnmanaged(*model.Link) = .empty;
|
var list = std.ArrayList(*model.Link).init(main.allocator);
|
||||||
var l = e.?.link().?;
|
var l = e.?.link().?;
|
||||||
while (true) {
|
while (true) {
|
||||||
list.append(main.allocator, l) catch unreachable;
|
list.append(l) catch unreachable;
|
||||||
l = l.next;
|
l = l.next;
|
||||||
if (&l.entry == e)
|
if (&l.entry == e)
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -136,19 +136,19 @@ pub fn delete() ?*model.Entry {
|
||||||
if (it.* == entry)
|
if (it.* == entry)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
var path: std.ArrayListUnmanaged(u8) = .empty;
|
var path = std.ArrayList(u8).init(main.allocator);
|
||||||
defer path.deinit(main.allocator);
|
defer path.deinit();
|
||||||
parent.fmtPath(main.allocator, true, &path);
|
parent.fmtPath(true, &path);
|
||||||
if (path.items.len == 0 or path.items[path.items.len-1] != '/')
|
if (path.items.len == 0 or path.items[path.items.len-1] != '/')
|
||||||
path.append(main.allocator, '/') catch unreachable;
|
path.append('/') catch unreachable;
|
||||||
path.appendSlice(main.allocator, entry.name()) catch unreachable;
|
path.appendSlice(entry.name()) catch unreachable;
|
||||||
|
|
||||||
if (main.config.delete_command.len == 0) {
|
if (main.config.delete_command.len == 0) {
|
||||||
_ = deleteItem(std.fs.cwd(), util.arrayListBufZ(&path, main.allocator), it);
|
_ = deleteItem(std.fs.cwd(), util.arrayListBufZ(&path), it);
|
||||||
model.inodes.addAllStats();
|
model.inodes.addAllStats();
|
||||||
return if (it.* == e) e else next_sel;
|
return if (it.* == e) e else next_sel;
|
||||||
} else {
|
} else {
|
||||||
const isdel = deleteCmd(util.arrayListBufZ(&path, main.allocator), it);
|
const isdel = deleteCmd(util.arrayListBufZ(&path), it);
|
||||||
model.inodes.addAllStats();
|
model.inodes.addAllStats();
|
||||||
return if (isdel) next_sel else it.*;
|
return if (isdel) next_sel else it.*;
|
||||||
}
|
}
|
||||||
|
|
@ -197,17 +197,17 @@ fn drawConfirm() void {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drawProgress() void {
|
fn drawProgress() void {
|
||||||
var path: std.ArrayListUnmanaged(u8) = .empty;
|
var path = std.ArrayList(u8).init(main.allocator);
|
||||||
defer path.deinit(main.allocator);
|
defer path.deinit();
|
||||||
parent.fmtPath(main.allocator, false, &path);
|
parent.fmtPath(false, &path);
|
||||||
path.append(main.allocator, '/') catch unreachable;
|
path.append('/') catch unreachable;
|
||||||
path.appendSlice(main.allocator, entry.name()) catch unreachable;
|
path.appendSlice(entry.name()) catch unreachable;
|
||||||
|
|
||||||
// TODO: Item counts and progress bar would be nice.
|
// TODO: Item counts and progress bar would be nice.
|
||||||
|
|
||||||
const box = ui.Box.create(6, 60, "Deleting...");
|
const box = ui.Box.create(6, 60, "Deleting...");
|
||||||
box.move(2, 2);
|
box.move(2, 2);
|
||||||
ui.addstr(ui.shorten(ui.toUtf8(util.arrayListBufZ(&path, main.allocator)), 56));
|
ui.addstr(ui.shorten(ui.toUtf8(util.arrayListBufZ(&path)), 56));
|
||||||
box.move(4, 41);
|
box.move(4, 41);
|
||||||
ui.addstr("Press ");
|
ui.addstr("Press ");
|
||||||
ui.style(.key);
|
ui.style(.key);
|
||||||
|
|
@ -217,16 +217,16 @@ fn drawProgress() void {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drawErr() void {
|
fn drawErr() void {
|
||||||
var path: std.ArrayListUnmanaged(u8) = .empty;
|
var path = std.ArrayList(u8).init(main.allocator);
|
||||||
defer path.deinit(main.allocator);
|
defer path.deinit();
|
||||||
parent.fmtPath(main.allocator, false, &path);
|
parent.fmtPath(false, &path);
|
||||||
path.append(main.allocator, '/') catch unreachable;
|
path.append('/') catch unreachable;
|
||||||
path.appendSlice(main.allocator, entry.name()) catch unreachable;
|
path.appendSlice(entry.name()) catch unreachable;
|
||||||
|
|
||||||
const box = ui.Box.create(6, 60, "Error");
|
const box = ui.Box.create(6, 60, "Error");
|
||||||
box.move(1, 2);
|
box.move(1, 2);
|
||||||
ui.addstr("Error deleting ");
|
ui.addstr("Error deleting ");
|
||||||
ui.addstr(ui.shorten(ui.toUtf8(util.arrayListBufZ(&path, main.allocator)), 41));
|
ui.addstr(ui.shorten(ui.toUtf8(util.arrayListBufZ(&path)), 41));
|
||||||
box.move(2, 4);
|
box.move(2, 4);
|
||||||
ui.addstr(ui.errorString(error_code));
|
ui.addstr(ui.errorString(error_code));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@ test "parse" {
|
||||||
fn PatternList(comptime withsub: bool) type {
|
fn PatternList(comptime withsub: bool) type {
|
||||||
return struct {
|
return struct {
|
||||||
literals: std.HashMapUnmanaged(*const Pattern, Val, Ctx, 80) = .{},
|
literals: std.HashMapUnmanaged(*const Pattern, Val, Ctx, 80) = .{},
|
||||||
wild: std.ArrayListUnmanaged(*const Pattern) = .empty,
|
wild: std.ArrayListUnmanaged(*const Pattern) = .{},
|
||||||
|
|
||||||
// Not a fan of the map-of-arrays approach in the 'withsub' case, it
|
// Not a fan of the map-of-arrays approach in the 'withsub' case, it
|
||||||
// has a lot of extra allocations. Linking the Patterns together in a
|
// has a lot of extra allocations. Linking the Patterns together in a
|
||||||
|
|
|
||||||
54
src/main.zig
54
src/main.zig
|
|
@ -1,7 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: Yorhel <projects@yorhel.nl>
|
// SPDX-FileCopyrightText: Yorhel <projects@yorhel.nl>
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
pub const program_version = "2.9.2";
|
pub const program_version = "2.9";
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const model = @import("model.zig");
|
const model = @import("model.zig");
|
||||||
|
|
@ -80,6 +80,7 @@ pub const config = struct {
|
||||||
pub var follow_symlinks: bool = false;
|
pub var follow_symlinks: bool = false;
|
||||||
pub var exclude_caches: bool = false;
|
pub var exclude_caches: bool = false;
|
||||||
pub var exclude_kernfs: bool = false;
|
pub var exclude_kernfs: bool = false;
|
||||||
|
pub var exclude_patterns: std.ArrayList([:0]const u8) = std.ArrayList([:0]const u8).init(allocator);
|
||||||
pub var threads: usize = 1;
|
pub var threads: usize = 1;
|
||||||
pub var complevel: u8 = 4;
|
pub var complevel: u8 = 4;
|
||||||
pub var compress: bool = false;
|
pub var compress: bool = false;
|
||||||
|
|
@ -118,9 +119,6 @@ pub const config = struct {
|
||||||
|
|
||||||
pub var state: enum { scan, browse, refresh, shell, delete } = .scan;
|
pub var state: enum { scan, browse, refresh, shell, delete } = .scan;
|
||||||
|
|
||||||
const stdin = if (@hasDecl(std.io, "getStdIn")) std.io.getStdIn() else std.fs.File.stdin();
|
|
||||||
const stdout = if (@hasDecl(std.io, "getStdOut")) std.io.getStdOut() else std.fs.File.stdout();
|
|
||||||
|
|
||||||
// Simple generic argument parser, supports getopt_long() style arguments.
|
// Simple generic argument parser, supports getopt_long() style arguments.
|
||||||
const Args = struct {
|
const Args = struct {
|
||||||
lst: []const [:0]const u8,
|
lst: []const [:0]const u8,
|
||||||
|
|
@ -330,13 +328,19 @@ fn tryReadArgsFile(path: [:0]const u8) void {
|
||||||
};
|
};
|
||||||
defer f.close();
|
defer f.close();
|
||||||
|
|
||||||
var line_buf: [4096]u8 = undefined;
|
var rd_ = std.io.bufferedReader(f.reader());
|
||||||
var line_rd = util.LineReader.init(f, &line_buf);
|
const rd = rd_.reader();
|
||||||
|
|
||||||
while (true) {
|
var line_buf: [4096]u8 = undefined;
|
||||||
const line_ = (line_rd.read() catch |e|
|
var line_fbs = std.io.fixedBufferStream(&line_buf);
|
||||||
ui.die("Error reading from {s}: {s}\nRun with --ignore-config to skip reading config files.\n", .{ path, ui.errorString(e) })
|
const line_writer = line_fbs.writer();
|
||||||
) orelse break;
|
|
||||||
|
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) }),
|
||||||
|
};
|
||||||
|
const line_ = line_fbs.getWritten();
|
||||||
|
|
||||||
var argc: usize = 0;
|
var argc: usize = 0;
|
||||||
var ignerror = false;
|
var ignerror = false;
|
||||||
|
|
@ -371,11 +375,13 @@ fn tryReadArgsFile(path: [:0]const u8) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn version() noreturn {
|
fn version() noreturn {
|
||||||
|
const stdout = std.io.getStdOut();
|
||||||
stdout.writeAll("ncdu " ++ program_version ++ "\n") catch {};
|
stdout.writeAll("ncdu " ++ program_version ++ "\n") catch {};
|
||||||
std.process.exit(0);
|
std.process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn help() noreturn {
|
fn help() noreturn {
|
||||||
|
const stdout = std.io.getStdOut();
|
||||||
stdout.writeAll(
|
stdout.writeAll(
|
||||||
\\ncdu <options> <directory>
|
\\ncdu <options> <directory>
|
||||||
\\
|
\\
|
||||||
|
|
@ -438,9 +444,19 @@ fn readExcludeFile(path: [:0]const u8) !void {
|
||||||
const f = try std.fs.cwd().openFileZ(path, .{});
|
const f = try std.fs.cwd().openFileZ(path, .{});
|
||||||
defer f.close();
|
defer f.close();
|
||||||
|
|
||||||
|
var rd_ = std.io.bufferedReader(f.reader());
|
||||||
|
const rd = rd_.reader();
|
||||||
|
|
||||||
var line_buf: [4096]u8 = undefined;
|
var line_buf: [4096]u8 = undefined;
|
||||||
var line_rd = util.LineReader.init(f, &line_buf);
|
var line_fbs = std.io.fixedBufferStream(&line_buf);
|
||||||
while (try line_rd.read()) |line| {
|
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)
|
if (line.len > 0)
|
||||||
exclude.addPattern(line);
|
exclude.addPattern(line);
|
||||||
}
|
}
|
||||||
|
|
@ -448,12 +464,12 @@ fn readExcludeFile(path: [:0]const u8) !void {
|
||||||
|
|
||||||
fn readImport(path: [:0]const u8) !void {
|
fn readImport(path: [:0]const u8) !void {
|
||||||
const fd =
|
const fd =
|
||||||
if (std.mem.eql(u8, "-", path)) stdin
|
if (std.mem.eql(u8, "-", path)) std.io.getStdIn()
|
||||||
else try std.fs.cwd().openFileZ(path, .{});
|
else try std.fs.cwd().openFileZ(path, .{});
|
||||||
errdefer fd.close();
|
errdefer fd.close();
|
||||||
|
|
||||||
var buf: [8]u8 = undefined;
|
var buf: [8]u8 = undefined;
|
||||||
if (8 != try fd.readAll(&buf)) return error.EndOfStream;
|
try fd.reader().readNoEof(&buf);
|
||||||
if (std.mem.eql(u8, &buf, bin_export.SIGNATURE)) {
|
if (std.mem.eql(u8, &buf, bin_export.SIGNATURE)) {
|
||||||
try bin_reader.open(fd);
|
try bin_reader.open(fd);
|
||||||
config.binreader = true;
|
config.binreader = true;
|
||||||
|
|
@ -535,6 +551,8 @@ pub fn main() void {
|
||||||
if (@import("builtin").os.tag != .linux and config.exclude_kernfs)
|
if (@import("builtin").os.tag != .linux and config.exclude_kernfs)
|
||||||
ui.die("The --exclude-kernfs flag is currently only supported on Linux.\n", .{});
|
ui.die("The --exclude-kernfs flag is currently only supported on Linux.\n", .{});
|
||||||
|
|
||||||
|
const stdin = std.io.getStdIn();
|
||||||
|
const stdout = std.io.getStdOut();
|
||||||
const out_tty = stdout.isTty();
|
const out_tty = stdout.isTty();
|
||||||
const in_tty = stdin.isTty();
|
const in_tty = stdin.isTty();
|
||||||
if (config.scan_ui == null) {
|
if (config.scan_ui == null) {
|
||||||
|
|
@ -592,10 +610,10 @@ pub fn main() void {
|
||||||
while (true) {
|
while (true) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
.refresh => {
|
.refresh => {
|
||||||
var full_path: std.ArrayListUnmanaged(u8) = .empty;
|
var full_path = std.ArrayList(u8).init(allocator);
|
||||||
defer full_path.deinit(allocator);
|
defer full_path.deinit();
|
||||||
mem_sink.global.root.?.fmtPath(allocator, true, &full_path);
|
mem_sink.global.root.?.fmtPath(true, &full_path);
|
||||||
scan.scan(util.arrayListBufZ(&full_path, allocator)) catch {
|
scan.scan(util.arrayListBufZ(&full_path)) catch {
|
||||||
sink.global.last_error = allocator.dupeZ(u8, full_path.items) catch unreachable;
|
sink.global.last_error = allocator.dupeZ(u8, full_path.items) catch unreachable;
|
||||||
sink.global.state = .err;
|
sink.global.state = .err;
|
||||||
while (state == .refresh) handleEvent(true, true);
|
while (state == .refresh) handleEvent(true, true);
|
||||||
|
|
|
||||||
|
|
@ -60,10 +60,10 @@ pub fn run(d: *model.Dir) void {
|
||||||
.sink = &sink_threads[0],
|
.sink = &sink_threads[0],
|
||||||
.stat = toStat(&d.entry),
|
.stat = toStat(&d.entry),
|
||||||
};
|
};
|
||||||
var buf: std.ArrayListUnmanaged(u8) = .empty;
|
var buf = std.ArrayList(u8).init(main.allocator);
|
||||||
d.fmtPath(main.allocator, true, &buf);
|
d.fmtPath(true, &buf);
|
||||||
const root = sink.createRoot(buf.items, &ctx.stat);
|
const root = sink.createRoot(buf.items, &ctx.stat);
|
||||||
buf.deinit(main.allocator);
|
buf.deinit();
|
||||||
|
|
||||||
var it = d.sub.ptr;
|
var it = d.sub.ptr;
|
||||||
while (it) |e| : (it = e.next.ptr) rec(&ctx, root, e);
|
while (it) |e| : (it = e.next.ptr) rec(&ctx, root, e);
|
||||||
|
|
|
||||||
|
|
@ -109,8 +109,7 @@ pub const Entry = extern struct {
|
||||||
fn alloc(comptime T: type, allocator: std.mem.Allocator, etype: EType, isext: bool, ename: []const u8) *Entry {
|
fn alloc(comptime T: type, allocator: std.mem.Allocator, etype: EType, isext: bool, ename: []const u8) *Entry {
|
||||||
const size = (if (isext) @as(usize, @sizeOf(Ext)) else 0) + @sizeOf(T) + ename.len + 1;
|
const size = (if (isext) @as(usize, @sizeOf(Ext)) else 0) + @sizeOf(T) + ename.len + 1;
|
||||||
var ptr = blk: while (true) {
|
var ptr = blk: while (true) {
|
||||||
const alignment = if (@typeInfo(@TypeOf(std.mem.Allocator.allocWithOptions)).@"fn".params[3].type == ?u29) 1 else std.mem.Alignment.@"1";
|
if (allocator.allocWithOptions(u8, size, 1, null)) |p| break :blk p
|
||||||
if (allocator.allocWithOptions(u8, size, alignment, null)) |p| break :blk p
|
|
||||||
else |_| {}
|
else |_| {}
|
||||||
ui.oom();
|
ui.oom();
|
||||||
};
|
};
|
||||||
|
|
@ -218,20 +217,19 @@ pub const Dir = extern struct {
|
||||||
suberr: bool = false,
|
suberr: bool = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn fmtPath(self: *const @This(), alloc: std.mem.Allocator, withRoot: bool, out: *std.ArrayListUnmanaged(u8)) void {
|
pub fn fmtPath(self: *const @This(), withRoot: bool, out: *std.ArrayList(u8)) void {
|
||||||
if (!withRoot and self.parent == null) return;
|
if (!withRoot and self.parent == null) return;
|
||||||
var components: std.ArrayListUnmanaged([:0]const u8) = .empty;
|
var components = std.ArrayList([:0]const u8).init(main.allocator);
|
||||||
defer components.deinit(main.allocator);
|
defer components.deinit();
|
||||||
var it: ?*const @This() = self;
|
var it: ?*const @This() = self;
|
||||||
while (it) |e| : (it = e.parent)
|
while (it) |e| : (it = e.parent)
|
||||||
if (withRoot or e.parent != null)
|
if (withRoot or e.parent != null)
|
||||||
components.append(main.allocator, e.entry.name()) catch unreachable;
|
components.append(e.entry.name()) catch unreachable;
|
||||||
|
|
||||||
var i: usize = components.items.len-1;
|
var i: usize = components.items.len-1;
|
||||||
while (true) {
|
while (true) {
|
||||||
if (i != components.items.len-1 and !(out.items.len != 0 and out.items[out.items.len-1] == '/'))
|
if (i != components.items.len-1 and !(out.items.len != 0 and out.items[out.items.len-1] == '/')) out.append('/') catch unreachable;
|
||||||
out.append(main.allocator, '/') catch unreachable;
|
out.appendSlice(components.items[i]) catch unreachable;
|
||||||
out.appendSlice(alloc, components.items[i]) catch unreachable;
|
|
||||||
if (i == 0) break;
|
if (i == 0) break;
|
||||||
i -= 1;
|
i -= 1;
|
||||||
}
|
}
|
||||||
|
|
@ -273,11 +271,11 @@ pub const Link = extern struct {
|
||||||
|
|
||||||
// Return value should be freed with main.allocator.
|
// Return value should be freed with main.allocator.
|
||||||
pub fn path(self: *const @This(), withRoot: bool) [:0]const u8 {
|
pub fn path(self: *const @This(), withRoot: bool) [:0]const u8 {
|
||||||
var out: std.ArrayListUnmanaged(u8) = .empty;
|
var out = std.ArrayList(u8).init(main.allocator);
|
||||||
self.parent.fmtPath(main.allocator, withRoot, &out);
|
self.parent.fmtPath(withRoot, &out);
|
||||||
out.append(main.allocator, '/') catch unreachable;
|
out.append('/') catch unreachable;
|
||||||
out.appendSlice(main.allocator, self.entry.name()) catch unreachable;
|
out.appendSlice(self.entry.name()) catch unreachable;
|
||||||
return out.toOwnedSliceSentinel(main.allocator, 0) catch unreachable;
|
return out.toOwnedSliceSentinel(0) catch unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add this link to the inodes map and mark it as 'uncounted'.
|
// Add this link to the inodes map and mark it as 'uncounted'.
|
||||||
|
|
@ -352,7 +350,7 @@ pub const Ext = extern struct {
|
||||||
pub const devices = struct {
|
pub const devices = struct {
|
||||||
var lock = std.Thread.Mutex{};
|
var lock = std.Thread.Mutex{};
|
||||||
// id -> dev
|
// id -> dev
|
||||||
pub var list: std.ArrayListUnmanaged(u64) = .empty;
|
pub var list = std.ArrayList(u64).init(main.allocator);
|
||||||
// dev -> id
|
// dev -> id
|
||||||
var lookup = std.AutoHashMap(u64, DevId).init(main.allocator);
|
var lookup = std.AutoHashMap(u64, DevId).init(main.allocator);
|
||||||
|
|
||||||
|
|
@ -363,7 +361,7 @@ pub const devices = struct {
|
||||||
if (!d.found_existing) {
|
if (!d.found_existing) {
|
||||||
if (list.items.len >= std.math.maxInt(DevId)) ui.die("Maximum number of device identifiers exceeded.\n", .{});
|
if (list.items.len >= std.math.maxInt(DevId)) ui.die("Maximum number of device identifiers exceeded.\n", .{});
|
||||||
d.value_ptr.* = @as(DevId, @intCast(list.items.len));
|
d.value_ptr.* = @as(DevId, @intCast(list.items.len));
|
||||||
list.append(main.allocator, dev) catch unreachable;
|
list.append(dev) catch unreachable;
|
||||||
}
|
}
|
||||||
return d.value_ptr.*;
|
return d.value_ptr.*;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
src/scan.zig
10
src/scan.zig
|
|
@ -91,7 +91,7 @@ fn isCacheDir(dir: std.fs.Dir) bool {
|
||||||
const f = dir.openFileZ("CACHEDIR.TAG", .{}) catch return false;
|
const f = dir.openFileZ("CACHEDIR.TAG", .{}) catch return false;
|
||||||
defer f.close();
|
defer f.close();
|
||||||
var buf: [sig.len]u8 = undefined;
|
var buf: [sig.len]u8 = undefined;
|
||||||
const len = f.readAll(&buf) catch return false;
|
const len = f.reader().readAll(&buf) catch return false;
|
||||||
return len == sig.len and std.mem.eql(u8, &buf, sig);
|
return len == sig.len and std.mem.eql(u8, &buf, sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -184,7 +184,7 @@ const Thread = struct {
|
||||||
thread_num: usize,
|
thread_num: usize,
|
||||||
sink: *sink.Thread,
|
sink: *sink.Thread,
|
||||||
state: *State,
|
state: *State,
|
||||||
stack: std.ArrayListUnmanaged(*Dir) = .empty,
|
stack: std.ArrayList(*Dir) = std.ArrayList(*Dir).init(main.allocator),
|
||||||
thread: std.Thread = undefined,
|
thread: std.Thread = undefined,
|
||||||
namebuf: [4096]u8 = undefined,
|
namebuf: [4096]u8 = undefined,
|
||||||
|
|
||||||
|
|
@ -265,13 +265,13 @@ const Thread = struct {
|
||||||
const s = dir.sink.addDir(t.sink, name, &stat);
|
const s = dir.sink.addDir(t.sink, name, &stat);
|
||||||
const ndir = Dir.create(edir, stat.dev, dir.pat.enter(name), s);
|
const ndir = Dir.create(edir, stat.dev, dir.pat.enter(name), s);
|
||||||
if (main.config.threads == 1 or !t.state.tryPush(ndir))
|
if (main.config.threads == 1 or !t.state.tryPush(ndir))
|
||||||
t.stack.append(main.allocator, ndir) catch unreachable;
|
t.stack.append(ndir) catch unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(t: *Thread) void {
|
fn run(t: *Thread) void {
|
||||||
defer t.stack.deinit(main.allocator);
|
defer t.stack.deinit();
|
||||||
while (t.state.waitPop()) |dir| {
|
while (t.state.waitPop()) |dir| {
|
||||||
t.stack.append(main.allocator, dir) catch unreachable;
|
t.stack.append(dir) catch unreachable;
|
||||||
|
|
||||||
while (t.stack.items.len > 0) {
|
while (t.stack.items.len > 0) {
|
||||||
const d = t.stack.items[t.stack.items.len - 1];
|
const d = t.stack.items[t.stack.items.len - 1];
|
||||||
|
|
|
||||||
58
src/sink.zig
58
src/sink.zig
|
|
@ -140,21 +140,20 @@ pub const Dir = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path(d: *Dir) [:0]u8 {
|
fn path(d: *Dir) [:0]u8 {
|
||||||
var components: std.ArrayListUnmanaged([]const u8) = .empty;
|
var components = std.ArrayList([]const u8).init(main.allocator);
|
||||||
defer components.deinit(main.allocator);
|
defer components.deinit();
|
||||||
var it: ?*Dir = d;
|
var it: ?*Dir = d;
|
||||||
while (it) |e| : (it = e.parent) components.append(main.allocator, e.name) catch unreachable;
|
while (it) |e| : (it = e.parent) components.append(e.name) catch unreachable;
|
||||||
|
|
||||||
var out: std.ArrayListUnmanaged(u8) = .empty;
|
var out = std.ArrayList(u8).init(main.allocator);
|
||||||
var i: usize = components.items.len-1;
|
var i: usize = components.items.len-1;
|
||||||
while (true) {
|
while (true) {
|
||||||
if (i != components.items.len-1 and !(out.items.len != 0 and out.items[out.items.len-1] == '/'))
|
if (i != components.items.len-1 and !(out.items.len != 0 and out.items[out.items.len-1] == '/')) out.append('/') catch unreachable;
|
||||||
out.append(main.allocator, '/') catch unreachable;
|
out.appendSlice(components.items[i]) catch unreachable;
|
||||||
out.appendSlice(main.allocator, components.items[i]) catch unreachable;
|
|
||||||
if (i == 0) break;
|
if (i == 0) break;
|
||||||
i -= 1;
|
i -= 1;
|
||||||
}
|
}
|
||||||
return out.toOwnedSliceSentinel(main.allocator, 0) catch unreachable;
|
return out.toOwnedSliceSentinel(0) catch unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ref(d: *Dir) void {
|
fn ref(d: *Dir) void {
|
||||||
|
|
@ -292,7 +291,7 @@ fn drawConsole() void {
|
||||||
var ansi: ?bool = null;
|
var ansi: ?bool = null;
|
||||||
var lines_written: usize = 0;
|
var lines_written: usize = 0;
|
||||||
};
|
};
|
||||||
const stderr = if (@hasDecl(std.io, "getStdErr")) std.io.getStdErr() else std.fs.File.stderr();
|
const stderr = std.io.getStdErr();
|
||||||
const ansi = st.ansi orelse blk: {
|
const ansi = st.ansi orelse blk: {
|
||||||
const t = stderr.supportsAnsiEscapeCodes();
|
const t = stderr.supportsAnsiEscapeCodes();
|
||||||
st.ansi = t;
|
st.ansi = t;
|
||||||
|
|
@ -451,28 +450,25 @@ pub fn draw() void {
|
||||||
switch (main.config.scan_ui.?) {
|
switch (main.config.scan_ui.?) {
|
||||||
.none => {},
|
.none => {},
|
||||||
.line => drawConsole(),
|
.line => drawConsole(),
|
||||||
.full => {
|
.full => switch (global.state) {
|
||||||
ui.init();
|
.done => {},
|
||||||
switch (global.state) {
|
.err => drawError(),
|
||||||
.done => {},
|
.zeroing => {
|
||||||
.err => drawError(),
|
const box = ui.Box.create(4, ui.cols -| 5, "Initializing");
|
||||||
.zeroing => {
|
box.move(2, 2);
|
||||||
const box = ui.Box.create(4, ui.cols -| 5, "Initializing");
|
ui.addstr("Clearing directory counts...");
|
||||||
box.move(2, 2);
|
},
|
||||||
ui.addstr("Clearing directory counts...");
|
.hlcnt => {
|
||||||
},
|
const box = ui.Box.create(4, ui.cols -| 5, "Finalizing");
|
||||||
.hlcnt => {
|
box.move(2, 2);
|
||||||
const box = ui.Box.create(4, ui.cols -| 5, "Finalizing");
|
ui.addstr("Counting hardlinks... ");
|
||||||
box.move(2, 2);
|
if (model.inodes.add_total > 0) {
|
||||||
ui.addstr("Counting hardlinks... ");
|
ui.addnum(.default, model.inodes.add_done);
|
||||||
if (model.inodes.add_total > 0) {
|
ui.addstr(" / ");
|
||||||
ui.addnum(.default, model.inodes.add_done);
|
ui.addnum(.default, model.inodes.add_total);
|
||||||
ui.addstr(" / ");
|
}
|
||||||
ui.addnum(.default, model.inodes.add_total);
|
},
|
||||||
}
|
.running => drawProgress(),
|
||||||
},
|
|
||||||
.running => drawProgress(),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
59
src/ui.zig
59
src/ui.zig
|
|
@ -17,7 +17,8 @@ pub var cols: u32 = undefined;
|
||||||
|
|
||||||
pub fn die(comptime fmt: []const u8, args: anytype) noreturn {
|
pub fn die(comptime fmt: []const u8, args: anytype) noreturn {
|
||||||
deinit();
|
deinit();
|
||||||
std.debug.print(fmt, args);
|
const stderr = std.io.getStdErr();
|
||||||
|
stderr.writer().print(fmt, args) catch {};
|
||||||
std.process.exit(1);
|
std.process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -26,8 +27,6 @@ pub fn quit() noreturn {
|
||||||
std.process.exit(0);
|
std.process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const sleep = if (@hasDecl(std.time, "sleep")) std.time.sleep else std.Thread.sleep;
|
|
||||||
|
|
||||||
// Should be called when malloc fails. Will show a message to the user, wait
|
// Should be called when malloc fails. Will show a message to the user, wait
|
||||||
// for a second and return to give it another try.
|
// for a second and return to give it another try.
|
||||||
// Glitch: this function may be called while we're in the process of drawing
|
// Glitch: this function may be called while we're in the process of drawing
|
||||||
|
|
@ -42,13 +41,14 @@ pub fn oom() void {
|
||||||
if (main_thread == std.Thread.getCurrentId()) {
|
if (main_thread == std.Thread.getCurrentId()) {
|
||||||
const haveui = inited;
|
const haveui = inited;
|
||||||
deinit();
|
deinit();
|
||||||
std.debug.print("\x1b7\x1b[JOut of memory, trying again in 1 second. Hit Ctrl-C to abort.\x1b8", .{});
|
const stderr = std.io.getStdErr();
|
||||||
sleep(std.time.ns_per_s);
|
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)
|
if (haveui)
|
||||||
init();
|
init();
|
||||||
} else {
|
} else {
|
||||||
_ = oom_threads.fetchAdd(1, .monotonic);
|
_ = oom_threads.fetchAdd(1, .monotonic);
|
||||||
sleep(std.time.ns_per_s);
|
std.time.sleep(std.time.ns_per_s);
|
||||||
_ = oom_threads.fetchSub(1, .monotonic);
|
_ = oom_threads.fetchSub(1, .monotonic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -80,7 +80,7 @@ pub fn errorString(e: anyerror) [:0]const u8 {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var to_utf8_buf: std.ArrayListUnmanaged(u8) = .empty;
|
var to_utf8_buf = std.ArrayList(u8).init(main.allocator);
|
||||||
|
|
||||||
fn toUtf8BadChar(ch: u8) bool {
|
fn toUtf8BadChar(ch: u8) bool {
|
||||||
return switch (ch) {
|
return switch (ch) {
|
||||||
|
|
@ -107,19 +107,19 @@ pub fn toUtf8(in: [:0]const u8) [:0]const u8 {
|
||||||
if (std.unicode.utf8ByteSequenceLength(in[i])) |cp_len| {
|
if (std.unicode.utf8ByteSequenceLength(in[i])) |cp_len| {
|
||||||
if (!toUtf8BadChar(in[i]) and i + cp_len <= in.len) {
|
if (!toUtf8BadChar(in[i]) and i + cp_len <= in.len) {
|
||||||
if (std.unicode.utf8Decode(in[i .. i + cp_len])) |_| {
|
if (std.unicode.utf8Decode(in[i .. i + cp_len])) |_| {
|
||||||
to_utf8_buf.appendSlice(main.allocator, in[i .. i + cp_len]) catch unreachable;
|
to_utf8_buf.appendSlice(in[i .. i + cp_len]) catch unreachable;
|
||||||
i += cp_len;
|
i += cp_len;
|
||||||
continue;
|
continue;
|
||||||
} else |_| {}
|
} else |_| {}
|
||||||
}
|
}
|
||||||
} else |_| {}
|
} else |_| {}
|
||||||
to_utf8_buf.writer(main.allocator).print("\\x{X:0>2}", .{in[i]}) catch unreachable;
|
to_utf8_buf.writer().print("\\x{X:0>2}", .{in[i]}) catch unreachable;
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
return util.arrayListBufZ(&to_utf8_buf, main.allocator);
|
return util.arrayListBufZ(&to_utf8_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
var shorten_buf: std.ArrayListUnmanaged(u8) = .empty;
|
var shorten_buf = std.ArrayList(u8).init(main.allocator);
|
||||||
|
|
||||||
// Shorten the given string to fit in the given number of columns.
|
// Shorten the given string to fit in the given number of columns.
|
||||||
// If the string is too long, only the prefix and suffix will be printed, with '...' in between.
|
// If the string is too long, only the prefix and suffix will be printed, with '...' in between.
|
||||||
|
|
@ -150,8 +150,8 @@ pub fn shorten(in: [:0]const u8, max_width: u32) [:0] const u8 {
|
||||||
if (total_width <= max_width) return in;
|
if (total_width <= max_width) return in;
|
||||||
|
|
||||||
shorten_buf.shrinkRetainingCapacity(0);
|
shorten_buf.shrinkRetainingCapacity(0);
|
||||||
shorten_buf.appendSlice(main.allocator, in[0..prefix_end]) catch unreachable;
|
shorten_buf.appendSlice(in[0..prefix_end]) catch unreachable;
|
||||||
shorten_buf.appendSlice(main.allocator, "...") catch unreachable;
|
shorten_buf.appendSlice("...") catch unreachable;
|
||||||
|
|
||||||
var start_width: u32 = prefix_width;
|
var start_width: u32 = prefix_width;
|
||||||
var start_len: u32 = prefix_end;
|
var start_len: u32 = prefix_end;
|
||||||
|
|
@ -163,11 +163,11 @@ pub fn shorten(in: [:0]const u8, max_width: u32) [:0] const u8 {
|
||||||
start_width += cp_width;
|
start_width += cp_width;
|
||||||
start_len += cp_len;
|
start_len += cp_len;
|
||||||
if (total_width - start_width <= max_width - prefix_width - 3) {
|
if (total_width - start_width <= max_width - prefix_width - 3) {
|
||||||
shorten_buf.appendSlice(main.allocator, in[start_len..]) catch unreachable;
|
shorten_buf.appendSlice(in[start_len..]) catch unreachable;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return util.arrayListBufZ(&shorten_buf, main.allocator);
|
return util.arrayListBufZ(&shorten_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shortenTest(in: [:0]const u8, max_width: u32, out: [:0]const u8) !void {
|
fn shortenTest(in: [:0]const u8, max_width: u32, out: [:0]const u8) !void {
|
||||||
|
|
@ -335,7 +335,8 @@ fn updateSize() void {
|
||||||
fn clearScr() void {
|
fn clearScr() void {
|
||||||
// Send a "clear from cursor to end of screen" instruction, to clear a
|
// Send a "clear from cursor to end of screen" instruction, to clear a
|
||||||
// potential line left behind from scanning in -1 mode.
|
// potential line left behind from scanning in -1 mode.
|
||||||
std.debug.print("\x1b[J", .{});
|
const stderr = std.io.getStdErr();
|
||||||
|
stderr.writeAll("\x1b[J") catch {};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init() void {
|
pub fn init() void {
|
||||||
|
|
@ -633,7 +634,7 @@ pub fn getch(block: bool) i32 {
|
||||||
}
|
}
|
||||||
if (ch == c.ERR) {
|
if (ch == c.ERR) {
|
||||||
if (!block) return 0;
|
if (!block) return 0;
|
||||||
sleep(10*std.time.ns_per_ms);
|
std.time.sleep(10*std.time.ns_per_ms);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return ch;
|
return ch;
|
||||||
|
|
@ -642,15 +643,6 @@ pub fn getch(block: bool) i32 {
|
||||||
.{ c.strerror(@intFromEnum(std.posix.errno(-1))) });
|
.{ c.strerror(@intFromEnum(std.posix.errno(-1))) });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn waitInput() void {
|
|
||||||
if (@hasDecl(std.io, "getStdIn")) {
|
|
||||||
std.io.getStdIn().reader().skipUntilDelimiterOrEof('\n') catch unreachable;
|
|
||||||
} else {
|
|
||||||
var buf: [512]u8 = undefined;
|
|
||||||
var rd = std.fs.File.stdin().reader(&buf);
|
|
||||||
_ = rd.interface.discardDelimiterExclusive('\n') catch unreachable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn runCmd(cmd: []const []const u8, cwd: ?[]const u8, env: *std.process.EnvMap, reporterr: bool) void {
|
pub fn runCmd(cmd: []const []const u8, cwd: ?[]const u8, env: *std.process.EnvMap, reporterr: bool) void {
|
||||||
deinit();
|
deinit();
|
||||||
|
|
@ -670,9 +662,14 @@ pub fn runCmd(cmd: []const []const u8, cwd: ?[]const u8, env: *std.process.EnvMa
|
||||||
child.cwd = cwd;
|
child.cwd = cwd;
|
||||||
child.env_map = env;
|
child.env_map = env;
|
||||||
|
|
||||||
|
const stdin = std.io.getStdIn();
|
||||||
|
const stderr = std.io.getStdErr();
|
||||||
const term = child.spawnAndWait() catch |e| blk: {
|
const term = child.spawnAndWait() catch |e| blk: {
|
||||||
std.debug.print("Error running command: {s}\n\nPress enter to continue.\n", .{ ui.errorString(e) });
|
stderr.writer().print(
|
||||||
waitInput();
|
"Error running command: {s}\n\nPress enter to continue.\n",
|
||||||
|
.{ ui.errorString(e) }
|
||||||
|
) catch {};
|
||||||
|
stdin.reader().skipUntilDelimiterOrEof('\n') catch unreachable;
|
||||||
break :blk std.process.Child.Term{ .Exited = 0 };
|
break :blk std.process.Child.Term{ .Exited = 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -684,7 +681,9 @@ pub fn runCmd(cmd: []const []const u8, cwd: ?[]const u8, env: *std.process.EnvMa
|
||||||
};
|
};
|
||||||
const v = switch (term) { inline else => |v| v };
|
const v = switch (term) { inline else => |v| v };
|
||||||
if (term != .Exited or (reporterr and v != 0)) {
|
if (term != .Exited or (reporterr and v != 0)) {
|
||||||
std.debug.print("\nCommand returned with {s} code {}.\nPress enter to continue.\n", .{ n, v });
|
stderr.writer().print(
|
||||||
waitInput();
|
"\nCommand returned with {s} code {}.\nPress enter to continue.\n", .{ n, v }
|
||||||
|
) catch {};
|
||||||
|
stdin.reader().skipUntilDelimiterOrEof('\n') catch unreachable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
55
src/util.zig
55
src/util.zig
|
|
@ -32,8 +32,8 @@ pub fn blocksToSize(b: u64) u64 {
|
||||||
// Ensure the given arraylist buffer gets zero-terminated and returns a slice
|
// Ensure the given arraylist buffer gets zero-terminated and returns a slice
|
||||||
// into the buffer. The returned buffer is invalidated whenever the arraylist
|
// into the buffer. The returned buffer is invalidated whenever the arraylist
|
||||||
// is freed or written to.
|
// is freed or written to.
|
||||||
pub fn arrayListBufZ(buf: *std.ArrayListUnmanaged(u8), alloc: std.mem.Allocator) [:0]const u8 {
|
pub fn arrayListBufZ(buf: *std.ArrayList(u8)) [:0]const u8 {
|
||||||
buf.append(alloc, 0) catch unreachable;
|
buf.append(0) catch unreachable;
|
||||||
defer buf.items.len -= 1;
|
defer buf.items.len -= 1;
|
||||||
return buf.items[0..buf.items.len-1:0];
|
return buf.items[0..buf.items.len-1:0];
|
||||||
}
|
}
|
||||||
|
|
@ -196,54 +196,5 @@ pub fn expanduser(path: []const u8, alloc: std.mem.Allocator) ![:0]u8 {
|
||||||
const home = std.mem.trimRight(u8, home_raw, "/");
|
const home = std.mem.trimRight(u8, home_raw, "/");
|
||||||
|
|
||||||
if (home.len == 0 and path.len == len) return alloc.dupeZ(u8, "/");
|
if (home.len == 0 and path.len == len) return alloc.dupeZ(u8, "/");
|
||||||
return try std.mem.concatWithSentinel(alloc, u8, &.{ home, path[len..] }, 0);
|
return try std.fmt.allocPrintZ(alloc, "{s}{s}", .{ home, path[len..] });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Silly abstraction to read a file one line at a time. Only exists to help
|
|
||||||
// with supporting both Zig 0.14 and 0.15, can be removed once 0.14 support is
|
|
||||||
// dropped.
|
|
||||||
pub const LineReader = if (@hasDecl(std.io, "bufferedReader")) struct {
|
|
||||||
rd: std.io.BufferedReader(4096, std.fs.File.Reader),
|
|
||||||
fbs: std.io.FixedBufferStream([]u8),
|
|
||||||
|
|
||||||
pub fn init(f: std.fs.File, buf: []u8) @This() {
|
|
||||||
return .{
|
|
||||||
.rd = std.io.bufferedReader(f.reader()),
|
|
||||||
.fbs = std.io.fixedBufferStream(buf),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read(s: *@This()) !?[]u8 {
|
|
||||||
s.fbs.reset();
|
|
||||||
s.rd.reader().streamUntilDelimiter(s.fbs.writer(), '\n', s.fbs.buffer.len) catch |err| switch (err) {
|
|
||||||
error.EndOfStream => if (s.fbs.getPos() catch unreachable == 0) return null,
|
|
||||||
else => |e| return e,
|
|
||||||
};
|
|
||||||
return s.fbs.getWritten();
|
|
||||||
}
|
|
||||||
|
|
||||||
} else struct {
|
|
||||||
rd: std.fs.File.Reader,
|
|
||||||
|
|
||||||
pub fn init(f: std.fs.File, buf: []u8) @This() {
|
|
||||||
return .{ .rd = f.readerStreaming(buf) };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read(s: *@This()) !?[]u8 {
|
|
||||||
// Can't use takeDelimiter() because that's not available in 0.15.1,
|
|
||||||
// Can't use takeDelimiterExclusive() because that changed behavior in 0.15.2.
|
|
||||||
const r = &s.rd.interface;
|
|
||||||
const result = r.peekDelimiterInclusive('\n') catch |err| switch (err) {
|
|
||||||
error.EndOfStream => {
|
|
||||||
const remaining = r.buffer[r.seek..r.end];
|
|
||||||
if (remaining.len == 0) return null;
|
|
||||||
r.toss(remaining.len);
|
|
||||||
return remaining;
|
|
||||||
},
|
|
||||||
else => |e| return e,
|
|
||||||
};
|
|
||||||
r.toss(result.len);
|
|
||||||
return result[0 .. result.len - 1];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue