// TODO: Change this to proper TOML in the future :) const es = @import("root"); const std = @import("std"); const Allocator = std.mem.Allocator; const BufMap = std.BufMap; const Config = @This(); const config_path = "arkta/es/es.ini"; hard_tabs: bool = es.conf.default_hard_tabs, line_limit: usize = es.conf.default_line_limit, page_overlap: usize = es.conf.default_page_overlap, tab_stop: usize = es.conf.default_tab_stop, pub fn readConfig(allocator: Allocator) !Config { var config = Config{}; var env_map = try std.process.getEnvMap(allocator); defer env_map.deinit(); if (env_map.get("XDG_CONFIG_DIRS")) |base_dirs| { var it = std.mem.split(u8, base_dirs, ":"); while (it.next()) |base_dir| { try readConfigInBaseDir(allocator, &config, base_dir); } } else { try readConfigInBaseDir(allocator, &config, "/etc/xdg"); } if (env_map.get("XDG_CONFIG_HOME")) |base_dir| { try readConfigInBaseDir(allocator, &config, base_dir); } else if (env_map.get("HOME")) |home| { const home_config = try std.fs.path.join(allocator, &.{ home, ".config" }); defer allocator.free(home_config); try readConfigInBaseDir(allocator, &config, home_config); } else { std.log.err("No $HOME environment variable available!", .{}); } return config; } fn parseBool(str: []const u8) !bool { if (std.mem.eql(u8, str, "true")) { return true; } else if (std.mem.eql(u8, str, "false")) { return false; } else { std.log.err("How to interpret '{s}' as a bool?", .{str}); return error.MalformedConfig; } } fn readConfigInBaseDir(allocator: Allocator, config: *Config, base_dir: []const u8) !void { const filename = try std.fs.path.join(allocator, &.{ base_dir, config_path }); defer allocator.free(filename); const file = std.fs.openFileAbsolute(filename, .{ .mode = .read_only }) catch |err| switch (err) { error.FileNotFound => return, else => return err, }; defer file.close(); const reader = file.reader(); while (try reader.readUntilDelimiterOrEofAlloc(allocator, '\n', 4096)) |line_buf| { defer allocator.free(line_buf); var line = std.mem.trim(u8, line_buf, &std.ascii.whitespace); if (line.len == 0 or line[0] == '#') { continue; } const split = if (std.mem.indexOfScalar(u8, line, '=')) |idx| idx else { return error.MalformedConfig; }; const key = std.mem.trimRight(u8, line[0..split], &std.ascii.whitespace); const value = std.mem.trimLeft(u8, line[(split + 1)..], &std.ascii.whitespace); if (std.mem.eql(u8, key, "hard-tabs")) { config.hard_tabs = try parseBool(value); } else if (std.mem.eql(u8, key, "line-limit")) { config.line_limit = try std.fmt.parseUnsigned(usize, value, 0); } else if (std.mem.eql(u8, key, "page-overlap")) { config.page_overlap = try std.fmt.parseUnsigned(usize, value, 0); } else if (std.mem.eql(u8, key, "tab-stop")) { config.tab_stop = try std.fmt.parseUnsigned(usize, value, 0); } // TODO: else ?? } }