zig: upgrade from 0.11 to 0.12.0
ci/woodpecker/push/woodpecker Pipeline was successful Details

mostly lots of language improvements and bugfixes, leading to better
code here, from the programming language point of view.

zig v0.12.0 release notes:
https://ziglang.org/download/0.12.0/release-notes.html
master^2
alex 8 months ago
parent e07b1557c7
commit 729af48569
Signed by: x1ddos
GPG Key ID: FDEFB4A63CBD8460

@ -9,27 +9,27 @@ clone:
recursive: false
pipeline:
lint:
image: git.qcode.ch/nakamochi/ci-zig0.11.0:v2
image: git.qcode.ch/nakamochi/ci-zig0.12.0:v1
commands:
- ./tools/fmt-check.sh
test:
image: git.qcode.ch/nakamochi/ci-zig0.11.0:v2
image: git.qcode.ch/nakamochi/ci-zig0.12.0:v1
commands:
- zig build test
sdl2:
image: git.qcode.ch/nakamochi/ci-zig0.11.0:v2
image: git.qcode.ch/nakamochi/ci-zig0.12.0:v1
commands:
- zig build -Ddriver=sdl2
x11:
image: git.qcode.ch/nakamochi/ci-zig0.11.0:v2
image: git.qcode.ch/nakamochi/ci-zig0.12.0:v1
commands:
- zig build -Ddriver=x11
aarch64:
image: git.qcode.ch/nakamochi/ci-zig0.11.0:v2
image: git.qcode.ch/nakamochi/ci-zig0.12.0:v1
commands:
- zig build -Ddriver=fbev -Dtarget=aarch64-linux-musl -Doptimize=ReleaseSafe -Dstrip
- sha256sum zig-out/bin/nd zig-out/bin/ngui
playground:
image: git.qcode.ch/nakamochi/ci-zig0.11.0:v2
image: git.qcode.ch/nakamochi/ci-zig0.12.0:v1
commands:
- zig build guiplay btcrpc lndhc

@ -61,17 +61,17 @@ to make a new image and switch the CI to use it, first modify the
[ci-containerfile](tools/ci-containerfile) and produce the image locally:
podman build --rm -t ndg-ci -f ./tools/ci-containerfile \
--build-arg ZIGURL=https://ziglang.org/download/0.11.0/zig-linux-x86_64-0.11.0.tar.xz
--build-arg ZIGURL=https://ziglang.org/download/0.12.0/zig-linux-x86_64-0.12.0.tar.xz
then tag it with the target URL, for example:
podman tag localhost/ndg-ci git.qcode.ch/nakamochi/ci-zig0.11.0:v2
podman tag localhost/ndg-ci git.qcode.ch/nakamochi/ci-zig0.12.0:v1
generate an [access token](https://git.qcode.ch/user/settings/applications),
login to the container registry and push the image to remote:
podman login git.qcode.ch
podman push git.qcode.ch/nakamochi/ci-zig0.11.0:v2
podman push git.qcode.ch/nakamochi/ci-zig0.12.0:v1
the image will be available at
https://git.qcode.ch/nakamochi/-/packages/

@ -11,18 +11,16 @@ pub fn build(b: *std.Build) void {
const inver = b.option([]const u8, "version", "semantic version of the build; must match git tag when available");
const buildopts = b.addOptions();
const buildopts_mod = buildopts.createModule();
buildopts.addOption(DriverTarget, "driver", drv);
const semver_step = VersionStep.create(b, buildopts, inver);
buildopts.step.dependOn(semver_step);
// network interface (nif) standalone library used by the daemon and tests.
const libnif_dep = b.anonymousDependency("lib/nif", @import("lib/nif/build.zig"), .{
.target = target,
.optimize = optimize,
});
const libnif_dep = b.lazyDependency("nif", .{ .target = target, .optimize = optimize }) orelse return;
const libnif = libnif_dep.artifact("nif");
// ini file format parser
const libini = b.addModule("ini", .{ .source_file = .{ .path = "lib/ini/src/ini.zig" } });
const libini_dep = b.lazyDependency("ini", .{ .target = target, .optimize = optimize }) orelse return;
const common_cflags = .{
"-Wall",
@ -35,16 +33,16 @@ pub fn build(b: *std.Build) void {
// gui build
const ngui = b.addExecutable(.{
.name = "ngui",
.root_source_file = .{ .path = "src/ngui.zig" },
.root_source_file = b.path("src/ngui.zig"),
.target = target,
.optimize = optimize,
.link_libc = true,
.strip = strip,
});
ngui.pie = true;
ngui.strip = strip;
ngui.addOptions("build_options", buildopts);
ngui.addIncludePath(.{ .path = "lib" });
ngui.addIncludePath(.{ .path = "src/ui/c" });
ngui.root_module.addImport("build_options", buildopts_mod);
ngui.addIncludePath(b.path("lib"));
ngui.addIncludePath(b.path("src/ui/c"));
const lvgl_flags = .{
"-std=c11",
@ -52,7 +50,7 @@ pub fn build(b: *std.Build) void {
"-Wformat",
"-Wformat-security",
} ++ common_cflags;
ngui.addCSourceFiles(lvgl_generic_src, &lvgl_flags);
ngui.addCSourceFiles(.{ .files = lvgl_generic_src, .flags = &lvgl_flags });
const ngui_cflags = .{
"-std=c11",
@ -60,39 +58,46 @@ pub fn build(b: *std.Build) void {
"-Wunused-parameter",
"-Werror",
} ++ common_cflags;
ngui.addCSourceFiles(&.{
"src/ui/c/ui.c",
"src/ui/c/lv_font_courierprimecode_14.c",
"src/ui/c/lv_font_courierprimecode_16.c",
"src/ui/c/lv_font_courierprimecode_24.c",
}, &ngui_cflags);
ngui.defineCMacroRaw(b.fmt("NM_DISP_HOR={}", .{disp_horiz}));
ngui.defineCMacroRaw(b.fmt("NM_DISP_VER={}", .{disp_vert}));
ngui.defineCMacro("LV_CONF_INCLUDE_SIMPLE", null);
ngui.addCSourceFiles(.{
.root = b.path("src/ui/c"),
.files = &.{
"ui.c",
"lv_font_courierprimecode_14.c",
"lv_font_courierprimecode_16.c",
"lv_font_courierprimecode_24.c",
},
.flags = &ngui_cflags,
});
ngui.root_module.addCMacro("NM_DISP_HOR", b.fmt("{d}", .{disp_horiz}));
ngui.root_module.addCMacro("NM_DISP_VER", b.fmt("{d}", .{disp_vert}));
ngui.defineCMacro("LV_CONF_INCLUDE_SIMPLE", "1");
ngui.defineCMacro("LV_LOG_LEVEL", lvgl_loglevel.text());
ngui.defineCMacro("LV_TICK_CUSTOM", "1");
ngui.defineCMacro("LV_TICK_CUSTOM_INCLUDE", "\"lv_custom_tick.h\"");
ngui.defineCMacro("LV_TICK_CUSTOM_SYS_TIME_EXPR", "(nm_get_curr_tick())");
switch (drv) {
.sdl2 => {
ngui.addCSourceFiles(lvgl_sdl2_src, &lvgl_flags);
ngui.addCSourceFile(.{ .file = .{ .path = "src/ui/c/drv_sdl2.c" }, .flags = &ngui_cflags });
ngui.addCSourceFiles(.{ .files = lvgl_sdl2_src, .flags = &lvgl_flags });
ngui.addCSourceFile(.{ .file = b.path("src/ui/c/drv_sdl2.c"), .flags = &ngui_cflags });
ngui.defineCMacro("USE_SDL", "1");
ngui.linkSystemLibrary("SDL2");
},
.x11 => {
ngui.addCSourceFiles(lvgl_x11_src, &lvgl_flags);
ngui.addCSourceFiles(&.{
"src/ui/c/drv_x11.c",
"src/ui/c/mouse_cursor_icon.c",
}, &ngui_cflags);
ngui.addCSourceFiles(.{ .files = lvgl_x11_src, .flags = &lvgl_flags });
ngui.addCSourceFiles(.{
.files = &.{
"src/ui/c/drv_x11.c",
"src/ui/c/mouse_cursor_icon.c",
},
.flags = &ngui_cflags,
});
ngui.defineCMacro("USE_X11", "1");
ngui.linkSystemLibrary("X11");
},
.fbev => {
ngui.addCSourceFiles(lvgl_fbev_src, &lvgl_flags);
ngui.addCSourceFile(.{ .file = .{ .path = "src/ui/c/drv_fbev.c" }, .flags = &ngui_cflags });
ngui.addCSourceFiles(.{ .files = lvgl_fbev_src, .flags = &lvgl_flags });
ngui.addCSourceFile(.{ .file = b.path("src/ui/c/drv_fbev.c"), .flags = &ngui_cflags });
ngui.defineCMacro("USE_FBDEV", "1");
ngui.defineCMacro("USE_EVDEV", "1");
},
@ -104,15 +109,15 @@ pub fn build(b: *std.Build) void {
// daemon build
const nd = b.addExecutable(.{
.name = "nd",
.root_source_file = .{ .path = "src/nd.zig" },
.root_source_file = b.path("src/nd.zig"),
.target = target,
.optimize = optimize,
.strip = strip,
});
nd.pie = true;
nd.strip = strip;
nd.addOptions("build_options", buildopts);
nd.addModule("nif", libnif_dep.module("nif"));
nd.addModule("ini", libini);
nd.root_module.addImport("build_options", buildopts_mod);
nd.root_module.addImport("nif", libnif_dep.module("nif"));
nd.root_module.addImport("ini", libini_dep.module("ini"));
nd.linkLibrary(libnif);
const nd_build_step = b.step("nd", "build nd (nakamochi daemon)");
@ -121,15 +126,15 @@ pub fn build(b: *std.Build) void {
// automated tests
{
const tests = b.addTest(.{
.root_source_file = .{ .path = "src/test.zig" },
.root_source_file = b.path("src/test.zig"),
.target = target,
.optimize = optimize,
.link_libc = true,
.filter = b.option([]const u8, "test-filter", "run tests matching the filter"),
});
tests.addOptions("build_options", buildopts);
tests.addModule("nif", libnif_dep.module("nif"));
tests.addModule("ini", libini);
tests.root_module.addImport("build_options", buildopts_mod);
tests.root_module.addImport("nif", libnif_dep.module("nif"));
tests.root_module.addImport("ini", libini_dep.module("ini"));
tests.linkLibrary(libnif);
const run_tests = b.addRunArtifact(tests);
@ -141,11 +146,11 @@ pub fn build(b: *std.Build) void {
{
const guiplay = b.addExecutable(.{
.name = "guiplay",
.root_source_file = .{ .path = "src/test/guiplay.zig" },
.root_source_file = b.path("src/test/guiplay.zig"),
.target = target,
.optimize = optimize,
});
guiplay.addModule("comm", b.createModule(.{ .source_file = .{ .path = "src/comm.zig" } }));
guiplay.root_module.addImport("comm", b.createModule(.{ .root_source_file = b.path("src/comm.zig") }));
const guiplay_build_step = b.step("guiplay", "build GUI playground");
guiplay_build_step.dependOn(&b.addInstallArtifact(guiplay, .{}).step);
@ -156,12 +161,12 @@ pub fn build(b: *std.Build) void {
{
const btcrpc = b.addExecutable(.{
.name = "btcrpc",
.root_source_file = .{ .path = "src/test/btcrpc.zig" },
.root_source_file = b.path("src/test/btcrpc.zig"),
.target = target,
.optimize = optimize,
.strip = strip,
});
btcrpc.strip = strip;
btcrpc.addModule("bitcoindrpc", b.createModule(.{ .source_file = .{ .path = "src/bitcoindrpc.zig" } }));
btcrpc.root_module.addImport("bitcoindrpc", b.createModule(.{ .root_source_file = b.path("src/bitcoindrpc.zig") }));
const btcrpc_build_step = b.step("btcrpc", "bitcoind RPC client playground");
btcrpc_build_step.dependOn(&b.addInstallArtifact(btcrpc, .{}).step);
@ -171,12 +176,12 @@ pub fn build(b: *std.Build) void {
{
const lndhc = b.addExecutable(.{
.name = "lndhc",
.root_source_file = .{ .path = "src/test/lndhc.zig" },
.root_source_file = b.path("src/test/lndhc.zig"),
.target = target,
.optimize = optimize,
.strip = strip,
});
lndhc.strip = strip;
lndhc.addModule("lightning", b.createModule(.{ .source_file = .{ .path = "src/lightning.zig" } }));
lndhc.root_module.addImport("lightning", b.createModule(.{ .root_source_file = b.path("src/lightning.zig") }));
const lndhc_build_step = b.step("lndhc", "lnd HTTP API client playground");
lndhc_build_step.dependOn(&b.addInstallArtifact(lndhc, .{}).step);
@ -423,7 +428,7 @@ const VersionStep = struct {
}
fn make(step: *std.Build.Step, _: *std.Progress.Node) anyerror!void {
const self = @fieldParentPtr(VersionStep, "step", step);
const self: *@This() = @fieldParentPtr("step", step);
const semver = try self.eval();
std.log.info("build version: {any}", .{semver});
self.buildopts.addOption(std.SemanticVersion, "semver", semver);
@ -460,7 +465,7 @@ const VersionStep = struct {
const matchTag = self.b.fmt("{s}*.*.*", .{prefix});
const cmd = [_][]const u8{ git, "-C", self.b.pathFromRoot("."), "describe", "--match", matchTag, "--tags", "--abbrev=8" };
var code: u8 = undefined;
const git_describe = self.b.execAllowFail(&cmd, &code, .Ignore) catch return null;
const git_describe = self.b.runAllowFail(&cmd, &code, .Ignore) catch return null;
const repotag = std.mem.trim(u8, git_describe, " \n\r")[prefix.len..];
return std.SemanticVersion.parse(repotag) catch |err| ret: {
std.log.err("unparsable git tag semver '{s}': {any}", .{ repotag, err });

@ -0,0 +1,17 @@
.{
.name = "ndg",
.version = "0.8.1",
.dependencies = .{
.nif = .{
.path = "lib/nif",
},
.ini = .{
.path = "lib/ini",
},
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

@ -0,0 +1,10 @@
.{
.name = "libini",
.version = "0.0.0",
.dependencies = .{},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

@ -1,30 +1,33 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
_ = b.addModule("nif", .{ .source_file = .{ .path = "nif.zig" } });
_ = b.addModule("nif", .{ .root_source_file = b.path("nif.zig") });
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const lib = b.addStaticLibrary(.{
.name = "nif",
.root_source_file = .{ .path = "nif.zig" },
.root_source_file = b.path("nif.zig"),
.target = target,
.optimize = optimize,
.link_libc = true,
});
lib.defineCMacro("CONFIG_CTRL_IFACE", null);
lib.defineCMacro("CONFIG_CTRL_IFACE_UNIX", null);
lib.addIncludePath(.{ .path = "wpa_supplicant" });
lib.addCSourceFiles(&.{
"wpa_supplicant/wpa_ctrl.c",
"wpa_supplicant/os_unix.c",
}, &.{
"-Wall",
"-Wextra",
"-Wshadow",
"-Wundef",
"-Wunused-parameter",
"-Werror",
lib.addIncludePath(b.path("wpa_supplicant"));
lib.addCSourceFiles(.{
.files = &.{
"wpa_supplicant/wpa_ctrl.c",
"wpa_supplicant/os_unix.c",
},
.flags = &.{
"-Wall",
"-Wextra",
"-Wshadow",
"-Wundef",
"-Wunused-parameter",
"-Werror",
},
});
b.installArtifact(lib);
}

@ -0,0 +1,10 @@
.{
.name = "libnif",
.version = "0.0.1",
.dependencies = .{},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

@ -1,7 +1,7 @@
const std = @import("std");
const mem = std.mem;
const net = std.net;
const os = std.os;
const posix = std.posix;
pub const wpa = @import("wpa.zig");
@ -12,11 +12,11 @@ const ifaddrs = extern struct {
next: ?*ifaddrs,
name: [*:0]const u8,
flags: c_uint, // see IFF_xxx SIOCGIFFLAGS in netdevice(7)
addr: ?*std.os.sockaddr,
netmask: ?*std.os.sockaddr,
addr: ?*std.posix.sockaddr,
netmask: ?*std.posix.sockaddr,
ifu: extern union {
broad: *os.sockaddr, // flags & IFF_BROADCAST
dst: *os.sockaddr, // flags & IFF_POINTOPOINT
broad: *posix.sockaddr, // flags & IFF_BROADCAST
dst: *posix.sockaddr, // flags & IFF_POINTOPOINT
},
data: ?*anyopaque,
};
@ -37,8 +37,8 @@ pub fn pubAddresses(allocator: mem.Allocator, ifname: ?[]const u8) ![]net.Addres
var list = std.ArrayList(net.Address).init(allocator);
var it: ?*ifaddrs = res;
while (it) |ifa| : (it = ifa.next) {
const sa: *os.sockaddr = ifa.addr orelse continue;
if (sa.family != os.AF.INET and sa.family != os.AF.INET6) {
const sa: *posix.sockaddr = ifa.addr orelse continue;
if (sa.family != posix.AF.INET and sa.family != posix.AF.INET6) {
// not an IP address
continue;
}
@ -47,7 +47,7 @@ pub fn pubAddresses(allocator: mem.Allocator, ifname: ?[]const u8) ![]net.Addres
continue;
}
const ipaddr = net.Address.initPosix(@alignCast(sa)); // initPosix makes a copy
if (ipaddr.any.family == os.AF.INET6 and ipaddr.in6.sa.scope_id > 0) {
if (ipaddr.any.family == posix.AF.INET6 and ipaddr.in6.sa.scope_id > 0) {
// want only global, with 0 scope
// non-zero scopes make sense for link-local addr only.
continue;

@ -2,7 +2,7 @@
const std = @import("std");
const ArenaAllocator = std.heap.ArenaAllocator;
const Atomic = std.atomic.Atomic;
const Atomic = std.atomic.Value;
const base64enc = std.base64.standard.Encoder;
const types = @import("types.zig");
@ -13,7 +13,7 @@ pub const Client = struct {
addr: []const u8 = "127.0.0.1",
port: u16 = 8332,
// each request gets a new ID with a value of reqid.fetchAdd(1, .Monotonic)
// each request gets a new ID with a value of reqid.fetchAdd(1, .monotonic)
reqid: Atomic(u64) = Atomic(u64).init(1),
pub const Method = enum {
@ -153,7 +153,7 @@ pub const Client = struct {
/// callers own returned value.
fn formatreq(self: *Client, comptime m: Method, args: MethodArgs(m)) ![]const u8 {
const req = RpcRequest(m){
.id = self.reqid.fetchAdd(1, .Monotonic),
.id = self.reqid.fetchAdd(1, .monotonic),
.method = @tagName(m),
.params = args,
};
@ -183,7 +183,7 @@ pub const Client = struct {
defer file.close();
const cookie = try file.readToEndAlloc(self.allocator, 1024);
defer self.allocator.free(cookie);
var auth = try self.allocator.alloc(u8, base64enc.calcSize(cookie.len));
const auth = try self.allocator.alloc(u8, base64enc.calcSize(cookie.len));
return base64enc.encode(auth, cookie);
}

@ -56,15 +56,15 @@ pub const MessageTag = enum(u16) {
ping = 0x01,
pong = 0x02,
poweroff = 0x03,
wifi_connect = 0x04,
network_report = 0x05,
get_network_report = 0x06,
// nd -> ngui: reports poweroff progress
poweroff_progress = 0x09,
// ngui -> nd: screen timeout, no user activity; no reply
standby = 0x07,
// ngui -> nd: resume screen due to user touch; no reply
wakeup = 0x08,
// nd -> ngui: reports poweroff progress
poweroff_progress = 0x09,
wifi_connect = 0x04,
network_report = 0x05,
get_network_report = 0x06,
// nd -> ngui: bitcoin core daemon status report
onchain_report = 0x0a,
// nd -> ngui: lnd status and stats report
@ -103,12 +103,12 @@ pub const Message = union(MessageTag) {
ping: void,
pong: void,
poweroff: void,
poweroff_progress: PoweroffProgress,
standby: void,
wakeup: void,
wifi_connect: WifiConnect,
network_report: NetworkReport,
get_network_report: GetNetworkReport,
poweroff_progress: PoweroffProgress,
onchain_report: OnchainReport,
lightning_report: LightningReport,
lightning_error: LightningError,
@ -294,8 +294,8 @@ pub const ParsedMessage = struct {
/// callers must deallocate resources with ParsedMessage.deinit when done.
pub fn read(allocator: mem.Allocator, reader: anytype) !ParsedMessage {
// alternative is @intToEnum(reader.ReadIntLittle(u16)) but it may panic.
const tag = try reader.readEnum(MessageTag, .Little);
const len = try reader.readIntLittle(u64);
const tag = try reader.readEnum(MessageTag, .little);
const len = try reader.readInt(u64, .little);
if (len == 0) {
return switch (tag) {
.lightning_get_ctrlconn => .{ .value = .lightning_get_ctrlconn },
@ -318,7 +318,7 @@ pub fn read(allocator: mem.Allocator, reader: anytype) !ParsedMessage {
.wakeup,
=> unreachable, // handled above
inline else => |t| {
var bytes = try allocator.alloc(u8, len);
const bytes = try allocator.alloc(u8, len);
defer allocator.free(bytes);
try reader.readNoEof(bytes);
@ -370,8 +370,8 @@ pub fn write(allocator: mem.Allocator, writer: anytype, msg: Message) !void {
return Error.CommWriteTooLarge;
}
try writer.writeIntLittle(u16, @intFromEnum(msg));
try writer.writeIntLittle(u64, data.items.len);
try writer.writeInt(u16, @intFromEnum(msg), .little);
try writer.writeInt(u64, data.items.len, .little);
try writer.writeAll(data.items);
}
@ -401,8 +401,8 @@ test "read" {
var buf = std.ArrayList(u8).init(t.allocator);
defer buf.deinit();
try buf.writer().writeIntLittle(u16, @intFromEnum(msg));
try buf.writer().writeIntLittle(u64, data.items.len);
try buf.writer().writeInt(u16, @intFromEnum(msg), .little);
try buf.writer().writeInt(u64, data.items.len, .little);
try buf.writer().writeAll(data.items);
var bs = std.io.fixedBufferStream(buf.items);
@ -424,8 +424,8 @@ test "write" {
const payload = "{\"ssid\":\"wlan\",\"password\":\"secret\"}";
var js = std.ArrayList(u8).init(t.allocator);
defer js.deinit();
try js.writer().writeIntLittle(u16, @intFromEnum(msg));
try js.writer().writeIntLittle(u64, payload.len);
try js.writer().writeInt(u16, @intFromEnum(msg), .little);
try js.writer().writeInt(u64, payload.len, .little);
try js.appendSlice(payload);
try t.expectEqualStrings(js.items, buf.items);
@ -442,8 +442,8 @@ test "write enum" {
const payload = "\"edge\"";
var js = std.ArrayList(u8).init(t.allocator);
defer js.deinit();
try js.writer().writeIntLittle(u16, @intFromEnum(msg));
try js.writer().writeIntLittle(u64, payload.len);
try js.writer().writeInt(u16, @intFromEnum(msg), .little);
try js.writer().writeInt(u64, payload.len, .little);
try js.appendSlice(payload);
try t.expectEqualStrings(js.items, buf.items);

@ -36,7 +36,7 @@ pub const Section = struct {
const vdup = try self.alloc.dupe(u8, value);
errdefer self.alloc.free(vdup);
var res = try self.props.getOrPut(try self.alloc.dupe(u8, key));
const res = try self.props.getOrPut(try self.alloc.dupe(u8, key));
if (!res.found_existing) {
res.value_ptr.* = .{ .str = vdup };
return;
@ -180,7 +180,7 @@ pub fn appendDefaultSection(self: *LndConf) !*Section {
/// the section name ascii is converted to lower case.
pub fn appendSection(self: *LndConf, name: []const u8) !*Section {
const alloc = self.arena.allocator();
var low_name = try std.ascii.allocLowerString(alloc, name);
const low_name = try std.ascii.allocLowerString(alloc, name);
try self.sections.append(.{
.name = low_name,
.props = std.StringArrayHashMap(PropValue).init(alloc),

@ -143,22 +143,29 @@ pub const Client = struct {
pub fn call(self: *Client, comptime apimethod: ApiMethod, args: MethodArgs(apimethod)) !Result(apimethod) {
const formatted = try self.formatreq(apimethod, args);
defer formatted.deinit();
var headersbuf: [8 * 1024]u8 = undefined;
const reqinfo = formatted.value;
const opt = std.http.Client.Options{ .handle_redirects = false }; // no redirects in REST API
var req = try self.httpClient.request(reqinfo.httpmethod, reqinfo.url, reqinfo.headers, opt);
const opt = std.http.Client.RequestOptions{
.redirect_behavior = .not_allowed, // no redirects in REST API
.headers = reqinfo.stdheaders,
.privileged_headers = reqinfo.xheaders,
.server_header_buffer = &headersbuf,
};
var req = try self.httpClient.open(reqinfo.httpmethod, reqinfo.url, opt);
defer req.deinit();
if (reqinfo.payload) |p| {
req.transfer_encoding = .{ .content_length = p.len };
}
try req.start();
try req.send();
if (reqinfo.payload) |p| {
req.writer().writeAll(p) catch return Error.LndPayloadWriteFail;
req.writeAll(p) catch return Error.LndPayloadWriteFail;
try req.finish();
}
try req.wait();
if (req.response.status.class() != .success) {
// a structured error reporting in lnd is in a less than desirable state.
// a structured error reporting in lnd is unclear:
// https://github.com/lightningnetwork/lnd/issues/5586
// TODO: return a more detailed error when the upstream improves.
return Error.LndHttpBadStatusCode;
@ -181,8 +188,9 @@ pub const Client = struct {
const HttpReqInfo = struct {
httpmethod: std.http.Method,
url: std.Uri,
headers: std.http.Headers,
payload: ?[]const u8,
stdheaders: std.http.Client.Request.Headers = .{}, // overridable standard headers
xheaders: []const std.http.Header = &.{}, // any extra headers
payload: ?[]const u8 = null,
};
fn formatreq(self: Client, comptime apimethod: ApiMethod, args: MethodArgs(apimethod)) !types.Deinitable(HttpReqInfo) {
@ -194,12 +202,10 @@ pub const Client = struct {
.genseed, .walletstatus => |m| .{
.httpmethod = .GET,
.url = try std.Uri.parse(try std.fmt.allocPrint(arena, "{s}/{s}", .{ self.apibase, m.apipath() })),
.headers = std.http.Headers{ .allocator = arena },
.payload = null,
},
.initwallet => |m| blk: {
const payload = p: {
var params: struct {
const params: struct {
wallet_password: []const u8, // base64
cipher_seed_mnemonic: []const []const u8,
aezeed_passphrase: ?[]const u8 = null, // base64
@ -215,13 +221,12 @@ pub const Client = struct {
break :blk .{
.httpmethod = .POST,
.url = try std.Uri.parse(try std.fmt.allocPrint(arena, "{s}/{s}", .{ self.apibase, m.apipath() })),
.headers = std.http.Headers{ .allocator = arena },
.payload = payload,
};
},
.unlockwallet => |m| blk: {
const payload = p: {
var params: struct {
const params: struct {
wallet_password: []const u8, // base64
} = .{
.wallet_password = try base64EncodeAlloc(arena, args.unlock_password),
@ -233,20 +238,19 @@ pub const Client = struct {
break :blk .{
.httpmethod = .POST,
.url = try std.Uri.parse(try std.fmt.allocPrint(arena, "{s}/{s}", .{ self.apibase, m.apipath() })),
.headers = std.http.Headers{ .allocator = arena },
.payload = payload,
};
},
.feereport, .getinfo, .getnetworkinfo, .pendingchannels, .walletbalance => |m| .{
.httpmethod = .GET,
.url = try std.Uri.parse(try std.fmt.allocPrint(arena, "{s}/{s}", .{ self.apibase, m.apipath() })),
.headers = blk: {
.xheaders = blk: {
if (self.macaroon.readonly == null) {
return Error.LndHttpMissingMacaroon;
}
var h = std.http.Headers{ .allocator = arena };
try h.append(authHeaderName, self.macaroon.readonly.?);
break :blk h;
var h = std.ArrayList(std.http.Header).init(arena);
try h.append(.{ .name = authHeaderName, .value = self.macaroon.readonly.? });
break :blk try h.toOwnedSlice();
},
.payload = null,
},
@ -270,13 +274,13 @@ pub const Client = struct {
}
break :blk try std.Uri.parse(buf.items); // uri point to the original buf
},
.headers = blk: {
.xheaders = blk: {
if (self.macaroon.readonly == null) {
return Error.LndHttpMissingMacaroon;
}
var h = std.http.Headers{ .allocator = arena };
try h.append(authHeaderName, self.macaroon.readonly.?);
break :blk h;
var h = std.ArrayList(std.http.Header).init(arena);
try h.append(.{ .name = authHeaderName, .value = self.macaroon.readonly.? });
break :blk try h.toOwnedSlice();
},
.payload = null,
},
@ -299,7 +303,7 @@ pub const Client = struct {
}
fn base64EncodeAlloc(gpa: std.mem.Allocator, v: []const u8) ![]const u8 {
var buf = try gpa.alloc(u8, base64enc.calcSize(v.len));
const buf = try gpa.alloc(u8, base64enc.calcSize(v.len));
return base64enc.encode(buf, v); // always returns a slice of buf.len
}
};

@ -1,7 +1,6 @@
const buildopts = @import("build_options");
const std = @import("std");
const os = std.os;
const sys = os.system;
const posix = std.posix;
const time = std.time;
const Address = std.net.Address;
@ -137,7 +136,7 @@ fn sighandler(sig: c_int) callconv(.C) void {
return;
}
switch (sig) {
os.SIG.INT, os.SIG.TERM => sigquit.set(),
posix.SIG.INT, posix.SIG.TERM => sigquit.set(),
else => {},
}
}
@ -220,13 +219,13 @@ pub fn main() !void {
try nd.start();
// graceful shutdown; see sigaction(2)
const sa = os.Sigaction{
const sa = posix.Sigaction{
.handler = .{ .handler = sighandler },
.mask = os.empty_sigset,
.mask = posix.empty_sigset,
.flags = 0,
};
try os.sigaction(os.SIG.INT, &sa, null);
try os.sigaction(os.SIG.TERM, &sa, null);
try posix.sigaction(posix.SIG.INT, &sa, null);
try posix.sigaction(posix.SIG.TERM, &sa, null);
sigquit.wait();
logger.info("sigquit: terminating ...", .{});

@ -145,7 +145,7 @@ fn inferStaticData(allocator: std.mem.Allocator) !StaticData {
}
fn inferLndTorHostname(allocator: std.mem.Allocator) ![]const u8 {
var raw = try std.fs.cwd().readFileAlloc(allocator, TOR_DATA_DIR ++ "/lnd/hostname", 1024);
const raw = try std.fs.cwd().readFileAlloc(allocator, TOR_DATA_DIR ++ "/lnd/hostname", 1024);
const hostname = std.mem.trim(u8, raw, &std.ascii.whitespace);
logger.info("inferred lnd tor hostname: [{s}]", .{hostname});
return hostname;
@ -156,7 +156,7 @@ fn inferBitcoindRpcPass(allocator: std.mem.Allocator) ![]const u8 {
// the password was placed on a separate comment line, preceding another comment
// line containing "rpcauth.py".
// TODO: get rid of the hack; do something more robust
var conf = try std.fs.cwd().readFileAlloc(allocator, BITCOIND_CONFIG_PATH, 1024 * 1024);
const conf = try std.fs.cwd().readFileAlloc(allocator, BITCOIND_CONFIG_PATH, 1024 * 1024);
var it = std.mem.tokenizeScalar(u8, conf, '\n');
var next_is_pass = false;
while (it.next()) |line| {
@ -346,7 +346,7 @@ fn genSysupdatesCronScript(self: Config) !void {
///
/// the caller must serialize this function calls.
fn runSysupdates(allocator: std.mem.Allocator, scriptpath: []const u8) !void {
const res = try std.ChildProcess.exec(.{ .allocator = allocator, .argv = &.{scriptpath} });
const res = try std.ChildProcess.run(.{ .allocator = allocator, .argv = &.{scriptpath} });
defer {
allocator.free(res.stdout);
allocator.free(res.stderr);
@ -383,7 +383,7 @@ pub fn lndConnectWaitMacaroonFile(self: Config, allocator: std.mem.Allocator, ty
defer allocator.free(macaroon);
const base64enc = std.base64.url_safe_no_pad.Encoder;
var buf = try allocator.alloc(u8, base64enc.calcSize(macaroon.len));
const buf = try allocator.alloc(u8, base64enc.calcSize(macaroon.len));
defer allocator.free(buf);
const macaroon_b64 = base64enc.encode(buf, macaroon);
const port: u16 = switch (typ) {
@ -599,7 +599,7 @@ test "ndconfig: switch sysupdates with .run=true" {
const tt = @import("../test.zig");
// no arena deinit here: expecting Config to auto-deinit.
var conf_arena = try std.testing.allocator.create(std.heap.ArenaAllocator);
const conf_arena = try std.testing.allocator.create(std.heap.ArenaAllocator);
conf_arena.* = std.heap.ArenaAllocator.init(std.testing.allocator);
var tmp = try tt.TempDir.create();
defer tmp.cleanup();
@ -644,7 +644,7 @@ test "ndconfig: genLndConfig" {
const tt = @import("../test.zig");
// Config auto-deinits the arena.
var conf_arena = try std.testing.allocator.create(std.heap.ArenaAllocator);
const conf_arena = try std.testing.allocator.create(std.heap.ArenaAllocator);
conf_arena.* = std.heap.ArenaAllocator.init(std.testing.allocator);
var tmp = try tt.TempDir.create();
defer tmp.cleanup();
@ -695,7 +695,7 @@ test "ndconfig: mutate LndConf" {
const tt = @import("../test.zig");
// Config auto-deinits the arena.
var conf_arena = try std.testing.allocator.create(std.heap.ArenaAllocator);
const conf_arena = try std.testing.allocator.create(std.heap.ArenaAllocator);
conf_arena.* = std.heap.ArenaAllocator.init(t.allocator);
var tmp = try tt.TempDir.create();
defer tmp.cleanup();
@ -737,7 +737,7 @@ test "ndconfig: screen lock" {
const tt = @import("../test.zig");
// Config auto-deinits the arena.
var conf_arena = try std.testing.allocator.create(std.heap.ArenaAllocator);
const conf_arena = try std.testing.allocator.create(std.heap.ArenaAllocator);
conf_arena.* = std.heap.ArenaAllocator.init(t.allocator);
var tmp = try tt.TempDir.create();
defer tmp.cleanup();

@ -548,7 +548,9 @@ fn unlockScreen(self: *Daemon, pincode: []const u8) !void {
defer self.allocator.free(pindup);
// TODO: slow down
self.conf.verifySlockPin(pindup) catch |err| {
logger.err("verifySlockPin: {!}", .{err});
if (!builtin.is_test) { // logging err makes some tests fail
logger.err("verifySlockPin: {!}", .{err});
}
const errmsg: comm.Message = .{ .screen_unlock_result = .{
.ok = false,
.err = if (err == error.IncorrectSlockPin) "incorrect pin code" else "unlock failed",
@ -562,7 +564,7 @@ fn unlockScreen(self: *Daemon, pincode: []const u8) !void {
/// sends poweroff progress to uiwriter in comm.Message.PoweroffProgress format.
fn sendPoweroffReport(self: *Daemon) !void {
var svstat = try self.allocator.alloc(comm.Message.PoweroffProgress.Service, self.services.list.len);
const svstat = try self.allocator.alloc(comm.Message.PoweroffProgress.Service, self.services.list.len);
defer self.allocator.free(svstat);
for (self.services.list, svstat) |*sv, *stat| {
stat.* = .{
@ -962,7 +964,7 @@ fn processLndReportError(self: *Daemon, err: anyerror) !void {
error.FileNotFound, // tls cert file missing, not re-generated by lnd yet
=> return comm.write(self.allocator, self.uiwriter, msg_starting),
// old tls cert, refused by our http client
std.http.Client.ConnectUnproxiedError.TlsInitializationFailed => {
std.http.Client.ConnectTcpError.TlsInitializationFailed => {
try self.resetLndTlsUnguarded();
return error.LndReportRetryLater;
},
@ -1006,7 +1008,7 @@ fn sendLightningPairingConn(self: *Daemon) !void {
defer self.allocator.free(tor_rpc);
const tor_http = try self.conf.lndConnectWaitMacaroonFile(self.allocator, .tor_http);
defer self.allocator.free(tor_http);
var conn: comm.Message.LightningCtrlConn = &.{
const conn: comm.Message.LightningCtrlConn = &.{
.{ .url = tor_rpc, .typ = .lnd_rpc, .perm = .admin },
.{ .url = tor_http, .typ = .lnd_http, .perm = .admin },
};
@ -1257,7 +1259,7 @@ fn setNodenameInternal(self: *Daemon, newname: []const u8) !void {
/// replaces whitespace with space literal and ignores ascii control chars.
/// caller owns returned value.
fn allocSanitizeNodename(allocator: std.mem.Allocator, name: []const u8) ![]const u8 {
if (name.len == 0 or try std.unicode.utf8CountCodepoints(name) > std.os.HOST_NAME_MAX) {
if (name.len == 0 or try std.unicode.utf8CountCodepoints(name) > std.posix.HOST_NAME_MAX) {
return error.InvalidNodenameLength;
}
var sanitized = try std.ArrayList(u8).initCapacity(allocator, name.len);
@ -1448,7 +1450,7 @@ test "daemon: screen unlock" {
fn dummyTestConfig() !Config {
const talloc = std.testing.allocator;
var arena = try talloc.create(std.heap.ArenaAllocator);
const arena = try talloc.create(std.heap.ArenaAllocator);
arena.* = std.heap.ArenaAllocator.init(talloc);
return Config{
.arena = arena,

@ -86,7 +86,7 @@ pub fn sendReport(gpa: mem.Allocator, wpa_ctrl: *types.WpaControl, w: anytype) !
};
// fetch available wifi networks from scan results using WPA ctrl
var wifi_networks: ?types.StringList = if (queryWifiScanResults(arena, wpa_ctrl)) |v| v else |err| blk: {
const wifi_networks: ?types.StringList = if (queryWifiScanResults(arena, wpa_ctrl)) |v| v else |err| blk: {
logger.err("queryWifiScanResults: {any}", .{err});
break :blk null;
};

@ -1,6 +1,6 @@
const buildopts = @import("build_options");
const std = @import("std");
const os = std.os;
const posix = std.posix;
const time = std.time;
const comm = @import("comm.zig");
@ -291,7 +291,7 @@ fn commThreadLoopCycle() !void {
fn uiThreadLoop() void {
while (true) {
ui_mutex.lock();
var till_next_ms = lvgl.loopCycle(); // UI loop
const till_next_ms = lvgl.loopCycle(); // UI loop
const do_state = state;
ui_mutex.unlock();
@ -391,7 +391,7 @@ fn sighandler(sig: c_int) callconv(.C) void {
return;
}
switch (sig) {
os.SIG.INT, os.SIG.TERM => sigquit.set(),
posix.SIG.INT, posix.SIG.TERM => sigquit.set(),
else => {},
}
}
@ -440,13 +440,13 @@ pub fn main() anyerror!void {
}
// set up a sigterm handler for clean exit.
const sa = os.Sigaction{
const sa = posix.Sigaction{
.handler = .{ .handler = sighandler },
.mask = os.empty_sigset,
.mask = posix.empty_sigset,
.flags = 0,
};
try os.sigaction(os.SIG.INT, &sa, null);
try os.sigaction(os.SIG.TERM, &sa, null);
try posix.sigaction(posix.SIG.INT, &sa, null);
try posix.sigaction(posix.SIG.TERM, &sa, null);
sigquit.wait();
logger.info("sigquit: terminating ...", .{});

@ -5,8 +5,8 @@ const types = @import("../types.zig");
/// caller owns memory; must dealloc using `allocator`.
pub fn hostname(allocator: std.mem.Allocator) ![]const u8 {
var buf: [std.os.HOST_NAME_MAX]u8 = undefined;
const name = try std.os.gethostname(&buf);
var buf: [std.posix.HOST_NAME_MAX]u8 = undefined;
const name = try std.posix.gethostname(&buf);
return allocator.dupe(u8, name);
}
@ -40,8 +40,8 @@ pub fn setHostname(allocator: std.mem.Allocator, name: []const u8) !void {
const newname = sanitized.items;
// need not continue if current name matches the new one.
var buf: [std.os.HOST_NAME_MAX]u8 = undefined;
const currname = try std.os.gethostname(&buf);
var buf: [std.posix.HOST_NAME_MAX]u8 = undefined;
const currname = try std.posix.gethostname(&buf);
if (std.mem.eql(u8, currname, newname)) {
return;
}

@ -37,7 +37,7 @@ pub fn initGlobal() void {
fn initGlobalFn() void {
global_gpa_state = std.heap.GeneralPurposeAllocator(.{}){};
global_gpa = global_gpa_state.allocator();
var pipe = types.IoPipe.create() catch |err| {
const pipe = types.IoPipe.create() catch |err| {
std.debug.panic("IoPipe.create: {any}", .{err});
};
comm.initPipe(global_gpa, pipe);
@ -118,7 +118,7 @@ pub const TestChildProcess = struct {
argv: []const []const u8,
pub fn init(argv: []const []const u8, allocator: std.mem.Allocator) TestChildProcess {
var adup = allocator.alloc([]u8, argv.len) catch unreachable;
const adup = allocator.alloc([]u8, argv.len) catch unreachable;
for (argv, adup) |v, *dup| {
dup.* = allocator.dupe(u8, v) catch unreachable;
}
@ -242,7 +242,7 @@ pub fn expectDeepEqual(expected: anytype, actual: @TypeOf(expected)) !void {
.Slice => {
switch (@typeInfo(p.child)) {
.Pointer, .Struct, .Optional, .Union => {
var err: ?anyerror = blk: {
const err: ?anyerror = blk: {
if (expected.len != actual.len) {
std.debug.print("expected.len = {d}, actual.len = {d}\n", .{ expected.len, actual.len });
break :blk error.ExpectDeepEqual;
@ -331,6 +331,7 @@ test {
_ = @import("ngui.zig");
_ = @import("lightning.zig");
_ = @import("sys.zig");
_ = @import("xfmt.zig");
std.testing.refAllDecls(@This());
}

@ -1,6 +1,6 @@
const std = @import("std");
const time = std.time;
const os = std.os;
const posix = std.posix;
const comm = @import("comm");
const types = @import("../types.zig");
@ -12,9 +12,9 @@ var ngui_proc: std.ChildProcess = undefined;
var sigquit: std.Thread.ResetEvent = .{};
fn sighandler(sig: c_int) callconv(.C) void {
logger.info("received signal {} (TERM={} INT={})", .{ sig, os.SIG.TERM, os.SIG.INT });
logger.info("received signal {} (TERM={} INT={})", .{ sig, posix.SIG.TERM, posix.SIG.INT });
switch (sig) {
os.SIG.INT, os.SIG.TERM => sigquit.set(),
posix.SIG.INT, posix.SIG.TERM => sigquit.set(),
else => {},
}
}
@ -79,7 +79,7 @@ fn parseArgs(gpa: std.mem.Allocator) !Flags {
/// global vars for comm read/write threads
var state: struct {
mu: std.Thread.Mutex = .{},
nodename: types.BufTrimString(std.os.HOST_NAME_MAX) = .{},
nodename: types.BufTrimString(std.posix.HOST_NAME_MAX) = .{},
slock_pincode: ?[]const u8 = null, // disabled when null
settings_sent: bool = false,
@ -114,7 +114,7 @@ fn commReadThread(gpa: std.mem.Allocator, r: anytype, w: anytype) void {
},
.poweroff => {
logger.info("sending poweroff status1", .{});
var s1: comm.Message.PoweroffProgress = .{ .services = &.{
const s1: comm.Message.PoweroffProgress = .{ .services = &.{
.{ .name = "lnd", .stopped = false, .err = null },
.{ .name = "bitcoind", .stopped = false, .err = null },
} };
@ -122,7 +122,7 @@ fn commReadThread(gpa: std.mem.Allocator, r: anytype, w: anytype) void {
time.sleep(2 * time.ns_per_s);
logger.info("sending poweroff status2", .{});
var s2: comm.Message.PoweroffProgress = .{ .services = &.{
const s2: comm.Message.PoweroffProgress = .{ .services = &.{
.{ .name = "lnd", .stopped = true, .err = null },
.{ .name = "bitcoind", .stopped = false, .err = null },
} };
@ -130,7 +130,7 @@ fn commReadThread(gpa: std.mem.Allocator, r: anytype, w: anytype) void {
time.sleep(3 * time.ns_per_s);
logger.info("sending poweroff status3", .{});
var s3: comm.Message.PoweroffProgress = .{ .services = &.{
const s3: comm.Message.PoweroffProgress = .{ .services = &.{
.{ .name = "lnd", .stopped = true, .err = null },
.{ .name = "bitcoind", .stopped = true, .err = null },
} };
@ -149,7 +149,7 @@ fn commReadThread(gpa: std.mem.Allocator, r: anytype, w: anytype) void {
time.sleep(3 * time.ns_per_s);
},
.lightning_get_ctrlconn => {
var conn: comm.Message.LightningCtrlConn = &.{
const conn: comm.Message.LightningCtrlConn = &.{
.{ .url = "lndconnect://adfkjhadwaepoijsadflkjtrpoijawokjafulkjsadfkjhgjfdskjszd.onion:10009?macaroon=Adasjsadkfljhfjhasdpiuhfiuhawfffoihgpoiadsfjharpoiuhfdsgpoihafdsgpoiheafoiuhasdfhisdufhiuhfewiuhfiuhrfl6prrx", .typ = .lnd_rpc, .perm = .admin },
.{ .url = "lndconnect://adfkjhadwaepoijsadflkjtrpoijawokjafulkjsadfkjhgjfdskjszd.onion:10010?macaroon=Adasjsadkfljhfjhasdpiuhfiuhawfffoihgpoiadsfjharpoiuhfdsgpoihafdsgpoiheafoiuhasdfhisdufhiuhfewiuhfiuhrfl6prrx", .typ = .lnd_http, .perm = .admin },
};
@ -374,13 +374,13 @@ pub fn main() !void {
const th2 = try std.Thread.spawn(.{}, commWriteThread, .{ gpa, uiwriter });
th2.detach();
const sa = os.Sigaction{
const sa = posix.Sigaction{
.handler = .{ .handler = sighandler },
.mask = os.empty_sigset,
.mask = posix.empty_sigset,
.flags = 0,
};
try os.sigaction(os.SIG.INT, &sa, null);
try os.sigaction(os.SIG.TERM, &sa, null);
try posix.sigaction(posix.SIG.INT, &sa, null);
try posix.sigaction(posix.SIG.TERM, &sa, null);
sigquit.wait();
logger.info("killing ngui", .{});

@ -40,8 +40,8 @@ pub const IoPipe = struct {
w: std.fs.File,
/// a pipe must be close'ed when done.
pub fn create() std.os.PipeError!IoPipe {
const fds = try std.os.pipe();
pub fn create() std.posix.PipeError!IoPipe {
const fds = try std.posix.pipe();
return .{
.r = std.fs.File{ .handle = fds[0] },
.w = std.fs.File{ .handle = fds[1] },
@ -138,7 +138,7 @@ pub fn Deinitable(comptime T: type) type {
const Self = @This();
pub fn init(allocator: std.mem.Allocator) !Self {
var res = Self{
const res = Self{
.arena = try allocator.create(std.heap.ArenaAllocator),
.value = undefined,
};

@ -46,9 +46,9 @@ pub usingnamespace switch (buildopts.driver) {
}
},
.fbev => struct {
extern "c" fn nm_open_evdev_nonblock() std.os.fd_t;
extern "c" fn nm_close_evdev(fd: std.os.fd_t) void;
extern "c" fn nm_consume_input_events(fd: std.os.fd_t) bool;
extern "c" fn nm_open_evdev_nonblock() std.posix.fd_t;
extern "c" fn nm_close_evdev(fd: std.posix.fd_t) void;
extern "c" fn nm_consume_input_events(fd: std.posix.fd_t) bool;
pub fn InputWatcher() !EvdevWatcher {
const fd = nm_open_evdev_nonblock();
@ -59,7 +59,7 @@ pub usingnamespace switch (buildopts.driver) {
}
pub const EvdevWatcher = struct {
evdev_fd: std.os.fd_t,
evdev_fd: std.posix.fd_t,
pub fn consume(self: @This()) bool {
return nm_consume_input_events(self.evdev_fd);

@ -99,7 +99,7 @@ var tab: struct {
} = null,
fn initSetup(self: *@This(), topwin: lvgl.Window) !void {
var arena = try self.allocator.create(std.heap.ArenaAllocator);
const arena = try self.allocator.create(std.heap.ArenaAllocator);
arena.* = std.heap.ArenaAllocator.init(tab.allocator);
self.seed_setup = .{ .arena = arena, .topwin = topwin };
}

@ -719,7 +719,7 @@ pub const Label = struct {
/// the text value is copied into a heap-allocated alloc.
pub fn new(parent: anytype, text: ?[*:0]const u8, opt: Opt) !Label {
var lv_label = lv_label_create(parent.lvobj) orelse return error.OutOfMemory;
const lv_label = lv_label_create(parent.lvobj) orelse return error.OutOfMemory;
if (text) |s| {
lv_label_set_text(lv_label, s);
}
@ -757,7 +757,7 @@ pub const Label = struct {
/// formats a new label text and passes it on to `setText`.
/// the buffer can be dropped once the function returns.
pub fn setTextFmt(self: Label, buf: []u8, comptime format: []const u8, args: anytype) !void {
var s = try std.fmt.bufPrintZ(buf, format, args);
const s = try std.fmt.bufPrintZ(buf, format, args);
self.setText(s);
}

@ -75,7 +75,7 @@ var tab: struct {
var state: struct {
// node name
nodename_change_inprogress: bool = false,
curr_nodename: types.BufTrimString(std.os.HOST_NAME_MAX) = .{},
curr_nodename: types.BufTrimString(std.posix.HOST_NAME_MAX) = .{},
// screenlock
slock_pin_input1: ?[]const u8 = null, // verified against a second time input
// sysupdates channel
@ -111,7 +111,7 @@ pub fn initNodenamePanel(cont: lvgl.Container) !lvgl.Card {
right.setPad(0, .column, .{});
tab.nodename.textarea = try lvgl.TextArea.new(right, .{
.maxlen = std.os.HOST_NAME_MAX,
.maxlen = std.posix.HOST_NAME_MAX,
.oneline = true,
});
tab.nodename.textarea.setWidth(lvgl.sizePercent(100));

@ -41,12 +41,11 @@ fn formatUnix(sec: u64, comptime fmt: []const u8, opts: std.fmt.FormatOptions, w
}
fn formatMetricI(value: i64, comptime fmt: []const u8, opts: std.fmt.FormatOptions, w: anytype) !void {
const uval: u64 = std.math.absCast(value);
const uval: u64 = @abs(value);
const base: u64 = 1000;
if (uval < base) {
return std.fmt.formatIntValue(value, fmt, opts, w);
}
if (value < 0) {
try w.writeByte('-');
}
@ -64,8 +63,60 @@ fn formatMetricU(value: u64, comptime fmt: []const u8, opts: std.fmt.FormatOptio
const mags_si = " kMGTPEZY";
const log2 = std.math.log2(value);
const m = @min(log2 / comptime std.math.log2(base), mags_si.len - 1);
const newval = lossyCast(f64, value) / std.math.pow(f64, lossyCast(f64, base), lossyCast(f64, m));
const suffix = mags_si[m];
try std.fmt.formatFloatDecimal(newval, opts, w);
const newval: f64 = lossyCast(f64, value) / std.math.pow(f64, lossyCast(f64, base), lossyCast(f64, m));
try std.fmt.formatType(newval, "d", opts, w, 0);
try w.writeByte(suffix);
}
test "unix" {
const t = std.testing;
var buf: [1024]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buf);
try std.fmt.format(fbs.writer(), "{}", .{unix(1136239445)});
try t.expectEqualStrings("2006-01-02 22:04:05 UTC", fbs.getWritten());
}
test "imetric" {
const t = std.testing;
var buf: [1024]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buf);
const table: []const struct { val: i64, str: []const u8 } = &.{
.{ .val = 0, .str = "0" },
.{ .val = -13, .str = "-13" },
.{ .val = 1000, .str = "1k" },
.{ .val = -1234, .str = "-1.234k" },
.{ .val = 12340, .str = "12.34k" },
.{ .val = -123400, .str = "-123.4k" },
.{ .val = 1234000, .str = "1.234M" },
.{ .val = -1234000000, .str = "-1.234G" },
};
for (table) |item| {
fbs.reset();
try std.fmt.format(fbs.writer(), "{}", .{imetric(item.val)});
try t.expectEqualStrings(item.str, fbs.getWritten());
}
}
test "umetric" {
const t = std.testing;
var buf: [1024]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buf);
const table: []const struct { val: u64, str: []const u8 } = &.{
.{ .val = 0, .str = "0" },
.{ .val = 13, .str = "13" },
.{ .val = 1000, .str = "1k" },
.{ .val = 1234, .str = "1.234k" },
.{ .val = 12340, .str = "12.34k" },
.{ .val = 123400, .str = "123.4k" },
.{ .val = 1234000, .str = "1.234M" },
.{ .val = 1234000000, .str = "1.234G" },
};
for (table) |item| {
fbs.reset();
try std.fmt.format(fbs.writer(), "{}", .{umetric(item.val)});
try t.expectEqualStrings(item.str, fbs.getWritten());
}
}

@ -1,9 +1,9 @@
# ci container file for compiling and testing zig projects.
# requires a ZIGURL build arg. for instance:
# podman build --rm -t ci-zig0.11.0 -f ci-containerfile \
# --build-arg ZIGURL=https://ziglang.org/download/0.11.0/zig-linux-x86_64-0.11.0.tar.xz
# podman build --rm -t ci-zig0.12.0 -f ci-containerfile \
# --build-arg ZIGURL=https://ziglang.org/download/0.12.0/zig-linux-x86_64-0.12.0.tar.xz
FROM alpine:3.18.3
FROM alpine:3.18.6
ARG ZIGURL
RUN apk add --no-cache git curl xz libc-dev sdl2-dev clang16-extra-tools && \