Skip to content

Commit ecf6f5b

Browse files
committed
Upgrade to zig-current, passing the environ map around
1 parent 56710ac commit ecf6f5b

4 files changed

Lines changed: 108 additions & 102 deletions

File tree

src/config.zig

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -163,18 +163,41 @@ pub const Config = struct {
163163
}
164164
};
165165

166+
/// Get the application data directory for turbocrypt
167+
/// - macOS: ~/Library/Application Support/turbocrypt
168+
/// - Linux: $XDG_DATA_HOME/turbocrypt or ~/.local/share/turbocrypt
169+
/// - Windows: %LOCALAPPDATA%\turbocrypt
170+
fn getAppDataDir(allocator: std.mem.Allocator, appname: []const u8, environ_map: *const std.process.Environ.Map) ![]const u8 {
171+
const native_os = builtin.os.tag;
172+
if (native_os == .windows) {
173+
const local_app_data = environ_map.get("LOCALAPPDATA") orelse return error.EnvironmentVariableNotFound;
174+
return try std.fs.path.join(allocator, &[_][]const u8{ local_app_data, appname });
175+
} else if (native_os == .macos) {
176+
const home = environ_map.get("HOME") orelse return error.EnvironmentVariableNotFound;
177+
return try std.fs.path.join(allocator, &[_][]const u8{ home, "Library", "Application Support", appname });
178+
} else {
179+
// Linux/Unix: use XDG_DATA_HOME or default to ~/.local/share
180+
if (environ_map.get("XDG_DATA_HOME")) |xdg_data| {
181+
return try std.fs.path.join(allocator, &[_][]const u8{ xdg_data, appname });
182+
} else {
183+
const home = environ_map.get("HOME") orelse return error.EnvironmentVariableNotFound;
184+
return try std.fs.path.join(allocator, &[_][]const u8{ home, ".local", "share", appname });
185+
}
186+
}
187+
}
188+
166189
/// Get the full path to the config file
167-
pub fn getConfigFilePath(allocator: std.mem.Allocator) ![]const u8 {
168-
const app_data_dir = try std.fs.getAppDataDir(allocator, "turbocrypt");
190+
pub fn getConfigFilePath(allocator: std.mem.Allocator, environ_map: *const std.process.Environ.Map) ![]const u8 {
191+
const app_data_dir = try getAppDataDir(allocator, "turbocrypt", environ_map);
169192
defer allocator.free(app_data_dir);
170193

171194
return try std.fs.path.join(allocator, &[_][]const u8{ app_data_dir, config_filename });
172195
}
173196

174197
/// Load config from file
175198
/// Returns a default config if file doesn't exist
176-
pub fn load(allocator: std.mem.Allocator, io: std.Io) !Config {
177-
const config_path = try getConfigFilePath(allocator);
199+
pub fn load(allocator: std.mem.Allocator, io: std.Io, environ_map: *const std.process.Environ.Map) !Config {
200+
const config_path = try getConfigFilePath(allocator, environ_map);
178201
defer allocator.free(config_path);
179202

180203
const max_size = 1024 * 1024; // 1MB max config file
@@ -197,9 +220,9 @@ pub fn load(allocator: std.mem.Allocator, io: std.Io) !Config {
197220
}
198221

199222
/// Save config to file with secure permissions
200-
pub fn save(config: Config, allocator: std.mem.Allocator, io: std.Io) !void {
223+
pub fn save(config: Config, allocator: std.mem.Allocator, io: std.Io, environ_map: *const std.process.Environ.Map) !void {
201224
// Get app data directory
202-
const app_data_dir = try std.fs.getAppDataDir(allocator, "turbocrypt");
225+
const app_data_dir = try getAppDataDir(allocator, "turbocrypt", environ_map);
203226
defer allocator.free(app_data_dir);
204227

205228
// Ensure directory exists

src/keyloader.zig

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ pub const env_var_name = "TURBOCRYPT_KEY_FILE";
1313
///
1414
/// Returns an owned slice that the caller must free.
1515
/// Returns null if key should be loaded from config.
16-
pub fn resolveKeyPath(allocator: std.mem.Allocator, optional_cli_path: ?[]const u8) !?[]const u8 {
16+
pub fn resolveKeyPath(allocator: std.mem.Allocator, optional_cli_path: ?[]const u8, environ_map: *const std.process.Environ.Map) !?[]const u8 {
1717
// Priority 1: CLI argument
1818
if (optional_cli_path) |cli_path| {
1919
if (cli_path.len > 0) {
@@ -22,13 +22,10 @@ pub fn resolveKeyPath(allocator: std.mem.Allocator, optional_cli_path: ?[]const
2222
}
2323

2424
// Priority 2: Environment variable
25-
if (std.process.getEnvVarOwned(allocator, env_var_name)) |env_path| {
25+
if (environ_map.get(env_var_name)) |env_path| {
2626
if (env_path.len > 0) {
27-
return env_path; // Already owned
27+
return try allocator.dupe(u8, env_path);
2828
}
29-
allocator.free(env_path);
30-
} else |_| {
31-
// Environment variable not set or error reading it - continue to next priority
3229
}
3330

3431
// Priority 3: Config file - return null to signal key should be loaded from config
@@ -41,16 +38,16 @@ pub fn resolveKeyPath(allocator: std.mem.Allocator, optional_cli_path: ?[]const
4138
/// 3. Config file (use stored key)
4239
///
4340
/// Returns error.KeyNotFound if no key is configured.
44-
pub fn resolveKey(allocator: std.mem.Allocator, optional_cli_path: ?[]const u8, password_opt: ?[]const u8, io: std.Io) ![16]u8 {
45-
const key_path = try resolveKeyPath(allocator, optional_cli_path);
41+
pub fn resolveKey(allocator: std.mem.Allocator, optional_cli_path: ?[]const u8, password_opt: ?[]const u8, io: std.Io, environ_map: *const std.process.Environ.Map) ![16]u8 {
42+
const key_path = try resolveKeyPath(allocator, optional_cli_path, environ_map);
4643

4744
if (key_path) |path| {
4845
// Load from file
4946
defer allocator.free(path);
5047
return try keygen.readKeyFile(path, password_opt, io);
5148
} else {
5249
// Load from config
53-
var cfg = try config.load(allocator, io);
50+
var cfg = try config.load(allocator, io, environ_map);
5451
defer cfg.deinit(allocator);
5552

5653
if (cfg.key) |key_data| {
@@ -86,17 +83,17 @@ pub fn resolveKey(allocator: std.mem.Allocator, optional_cli_path: ?[]const u8,
8683
}
8784

8885
/// Get the full path to the config file
89-
pub fn getConfigFilePath(allocator: std.mem.Allocator) ![]const u8 {
90-
return try config.getConfigFilePath(allocator);
86+
pub fn getConfigFilePath(allocator: std.mem.Allocator, environ_map: *const std.process.Environ.Map) ![]const u8 {
87+
return try config.getConfigFilePath(allocator, environ_map);
9188
}
9289

9390
/// Set the default key in the config file
9491
/// key_data should be in the same format as key files:
9592
/// - 16 bytes: plain key
9693
/// - 21 bytes: password-protected (1 flag byte + 16 XOR'd bytes + 4 checksum bytes)
97-
pub fn setDefaultKey(allocator: std.mem.Allocator, key_data: []const u8, io: std.Io) !void {
94+
pub fn setDefaultKey(allocator: std.mem.Allocator, key_data: []const u8, io: std.Io, environ_map: *const std.process.Environ.Map) !void {
9895
// Load existing config
99-
var cfg = try config.load(allocator, io);
96+
var cfg = try config.load(allocator, io, environ_map);
10097
defer cfg.deinit(allocator);
10198

10299
// Free old key if exists
@@ -108,11 +105,11 @@ pub fn setDefaultKey(allocator: std.mem.Allocator, key_data: []const u8, io: std
108105
cfg.key = try allocator.dupe(u8, key_data);
109106

110107
// Save config
111-
try config.save(cfg, allocator, io);
108+
try config.save(cfg, allocator, io, environ_map);
112109
}
113110

114111
/// Get information about where the key would be loaded from (for user feedback)
115-
pub fn describeKeySource(allocator: std.mem.Allocator, optional_cli_path: ?[]const u8, io: std.Io) ![]const u8 {
112+
pub fn describeKeySource(allocator: std.mem.Allocator, optional_cli_path: ?[]const u8, io: std.Io, environ_map: *const std.process.Environ.Map) ![]const u8 {
116113
// Check CLI argument
117114
if (optional_cli_path) |cli_path| {
118115
if (cli_path.len > 0) {
@@ -121,18 +118,17 @@ pub fn describeKeySource(allocator: std.mem.Allocator, optional_cli_path: ?[]con
121118
}
122119

123120
// Check environment variable
124-
if (std.process.getEnvVarOwned(allocator, env_var_name)) |env_path| {
125-
defer allocator.free(env_path);
121+
if (environ_map.get(env_var_name)) |env_path| {
126122
if (env_path.len > 0) {
127123
return try std.fmt.allocPrint(allocator, "Environment variable {s}: {s}", .{ env_var_name, env_path });
128124
}
129-
} else |_| {}
125+
}
130126

131127
// Check config file
132-
const config_path = try config.getConfigFilePath(allocator);
128+
const config_path = try config.getConfigFilePath(allocator, environ_map);
133129
defer allocator.free(config_path);
134130

135-
var cfg = config.load(allocator, io) catch {
131+
var cfg = config.load(allocator, io, environ_map) catch {
136132
return try allocator.dupe(u8, "No key configured");
137133
};
138134
defer cfg.deinit(allocator);
@@ -151,16 +147,20 @@ pub fn describeKeySource(allocator: std.mem.Allocator, optional_cli_path: ?[]con
151147

152148
test "resolveKeyPath - CLI argument takes priority" {
153149
const allocator = std.testing.allocator;
150+
var environ_map = std.process.Environ.Map.init(allocator);
151+
defer environ_map.deinit();
154152

155-
const result = try resolveKeyPath(allocator, "/path/from/cli");
153+
const result = try resolveKeyPath(allocator, "/path/from/cli", &environ_map);
156154
defer if (result) |path| allocator.free(path);
157155

158156
try std.testing.expectEqualStrings("/path/from/cli", result.?);
159157
}
160158

161159
test "resolveKeyPath - returns null when no path configured" {
162160
const allocator = std.testing.allocator;
161+
var environ_map = std.process.Environ.Map.init(allocator);
162+
defer environ_map.deinit();
163163

164-
const result = try resolveKeyPath(allocator, null);
164+
const result = try resolveKeyPath(allocator, null, &environ_map);
165165
try std.testing.expect(result == null);
166166
}

0 commit comments

Comments
 (0)