aboutsummaryrefslogtreecommitdiff
path: root/src/env.zig
diff options
context:
space:
mode:
authortsne <tsne.dev@outlook.com>2025-01-01 14:58:28 +0100
committertsne <tsne.dev@outlook.com>2025-10-30 08:32:49 +0100
commit44e5ad763794a438ecfd50c8b7f6ea760ea82da5 (patch)
tree575a1fc72ceb1d7f052cf582abc1e038e27f69a3 /src/env.zig
downloadporteur-main.tar.gz
initial commitHEADmain
Diffstat (limited to 'src/env.zig')
-rw-r--r--src/env.zig200
1 files changed, 200 insertions, 0 deletions
diff --git a/src/env.zig b/src/env.zig
new file mode 100644
index 0000000..7e8b355
--- /dev/null
+++ b/src/env.zig
@@ -0,0 +1,200 @@
+const options = @import("options");
+const std = @import("std");
+const assert = std.debug.assert;
+
+const cli = @import("cli.zig");
+const fs = @import("fs.zig");
+const conf = @import("conf.zig");
+
+const Self = @This();
+
+allocator: std.mem.Allocator,
+prefix: []const u8,
+etc: []const u8, // ${PREFIX}/etc/
+args: *cli.Args,
+
+config: conf.Config,
+ports_dir: []const u8,
+distfiles_dir: []const u8,
+distfiles_history: usize,
+default_category: ?[]const u8,
+default_repo_branch: []const u8,
+editor_cmd: ?[]const u8,
+
+pub fn init(allocator: std.mem.Allocator, etc: []const u8, args: *cli.Args) Self {
+ const prefix = if (options.path_prefix[options.path_prefix.len - 1] == '/')
+ options.path_prefix[0 .. options.path_prefix.len - 1]
+ else
+ options.path_prefix;
+
+ const config = read_config(allocator, .join(.{ etc, "porteur/porteur.conf" }));
+ var self: Self = .{
+ .allocator = allocator,
+ .prefix = prefix,
+ .etc = etc,
+ .args = args,
+
+ .config = config,
+ .ports_dir = std.posix.getenvZ("PORTSDIR") orelse "/usr/ports",
+ .distfiles_dir = prefix ++ "/porteur/distfiles/{{TREENAME}}",
+ .distfiles_history = 1,
+ .default_category = null,
+ .default_repo_branch = "main",
+ .editor_cmd = null,
+ };
+
+ var iter = config.iterate();
+ while (iter.next()) |v| {
+ if (std.mem.eql(u8, v.key, "portsdir") and v.val.len > 0) {
+ self.ports_dir = v.val;
+ } else if (std.mem.eql(u8, v.key, "distfiles-dir") and v.val.len > 0) {
+ self.distfiles_dir = std.mem.trimEnd(u8, v.val, "/");
+ } else if (std.mem.eql(u8, v.key, "distfiles-history") and v.val.len > 0) {
+ self.distfiles_history = history: {
+ var res: usize = 0;
+ for (v.val) |c| {
+ if (c < '0' or c > '9') {
+ self.warn("invalid distfile history `{s}` (falling back to 1)", .{v.val});
+ break :history 1;
+ }
+ const m = @mulWithOverflow(res, @as(usize, 10));
+ const a = @addWithOverflow(m[0], @as(usize, @intCast(c - '0')));
+ if (m[1] != 0 or a[1] != 0) {
+ break :history std.math.maxInt(usize);
+ }
+ res = a[0];
+ }
+ break :history res;
+ };
+ } else if (std.mem.eql(u8, v.key, "category") and v.val.len > 0) {
+ self.default_category = v.val;
+ } else if (std.mem.eql(u8, v.key, "git-branch") and v.val.len > 0) {
+ self.default_repo_branch = v.val;
+ } else if (std.mem.eql(u8, v.key, "editor") and v.val.len > 0) {
+ self.editor_cmd = v.val;
+ } else {
+ self.warn("unknown porteur configuration `{s}`", .{v.key});
+ }
+ }
+
+ return self;
+}
+
+/// Return a subpath of the configuration directory.
+pub fn etc_path(self: Self, subpath: anytype) fs.Path {
+ var etc: fs.Path = .join(.{ self.etc, "porteur" });
+ etc.append(subpath);
+ return etc;
+}
+
+/// Return a subpath of a ports tree's root directory.
+pub fn ports_tree_path(self: Self, tree_name: []const u8, subpath: anytype) fs.Path {
+ var p: fs.Path = .join(.{ self.prefix, "porteur/ports", tree_name });
+ p.append(subpath);
+ return p;
+}
+
+/// Return the directory that contains the port's distfiles.
+pub fn ports_dist_dir(self: Self, tree_name: []const u8, port_name: []const u8) fs.Path {
+ var dest: fs.Path = undefined;
+ dest.len = 0;
+
+ var src = self.distfiles_dir;
+ loop: while (src.len > 0) {
+ inline for ([_]struct { key: []const u8, val: []const u8 }{
+ .{ .key = "{{TREENAME}}", .val = tree_name },
+ .{ .key = "{{PORTNAME}}", .val = port_name },
+ }) |v| {
+ if (std.mem.indexOf(u8, src, v.key)) |idx| {
+ @memcpy(dest.buf[dest.len .. dest.len + idx], src[0..idx]);
+ dest.len += idx;
+ @memcpy(dest.buf[dest.len .. dest.len + v.val.len], v.val);
+ dest.len += v.val.len;
+ src = src[idx + v.key.len ..];
+ continue :loop;
+ }
+ }
+
+ @memcpy(dest.buf[dest.len .. dest.len + src.len], src);
+ dest.len += src.len;
+ break :loop;
+ }
+
+ dest.buf[dest.len] = 0;
+ return dest;
+}
+
+/// Return a subpath of the directory that contains the sources of all the ports of a
+/// ports tree.
+pub fn ports_src_path(self: Self, tree_name: []const u8, subpath: anytype) fs.Path {
+ var p: fs.Path = .join(.{ self.prefix, "porteur/src", tree_name });
+ p.append(subpath);
+ return p;
+}
+
+/// Return the subpath of a temporary working directory.
+pub fn work_path(self: Self, subpath: anytype) fs.Path {
+ var p: fs.Path = .join(.{ self.prefix, "porteur/.wrk" });
+ p.append(subpath);
+ return p;
+}
+
+pub fn alloc(self: Self, size: usize) []u8 {
+ return self.allocator.alloc(u8, size) catch cli.fatal("out of memory", .{});
+}
+
+pub fn dealloc(self: Self, bytes: []const u8) void {
+ self.allocator.free(bytes);
+}
+
+pub fn err(self: Self, comptime fmt: []const u8, args: anytype) void {
+ _ = self;
+ cli.stderr.write_line("Error: " ++ fmt, args);
+ cli.stderr.flush();
+}
+
+pub fn warn(self: Self, comptime fmt: []const u8, args: anytype) void {
+ _ = self;
+ cli.stderr.write_line("Warning: " ++ fmt, args);
+ cli.stderr.flush();
+}
+
+pub fn info(self: Self, comptime fmt: []const u8, args: anytype) void {
+ _ = self;
+ cli.stdout.write_line(fmt, args);
+ cli.stdout.flush();
+}
+
+fn read_config(allocator: std.mem.Allocator, conf_file: fs.Path) conf.Config {
+ if (conf.Config.from_file(allocator, conf_file.name())) |res| {
+ return switch (res) {
+ .conf => |c| c,
+ .err => |e| empty: {
+ cli.stderr.write_line("Error: {s} (line {d})", .{ e.msg, e.line });
+ cli.stderr.flush();
+ break :empty .init(allocator);
+ },
+ };
+ } else |e| {
+ cli.stderr.write_line("Error: cannot read config ({s})", .{@errorName(e)});
+ cli.stderr.flush();
+ return .init(allocator);
+ }
+}
+
+test "replace distdir variables" {
+ var env: Self = undefined;
+ var dir: fs.Path = undefined;
+
+ env.distfiles_dir = "/porteur/distfiles";
+ dir = env.ports_dist_dir("not-relevant", "not-relevant");
+ try std.testing.expectEqualStrings("/porteur/distfiles", dir.name());
+
+ env.distfiles_dir = "/porteur/distfiles/{{TREENAME}}";
+ dir = env.ports_dist_dir("default", "not-relevant");
+ try std.testing.expectEqualStrings("/porteur/distfiles/default", dir.name());
+
+ env.distfiles_dir = "/porteur/distfiles/{{TREENAME}}/{{PORTNAME}}";
+ dir = env.ports_dist_dir("default", "myport");
+ try std.testing.expectEqualStrings("/porteur/distfiles/default/myport", dir.name());
+}