From 51def513e39a393f452ba9163278c3cef8ca7b29 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 11 Sep 2024 13:45:24 +0200 Subject: [PATCH] improve more error messages --- src/main.zig | 80 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 23 deletions(-) diff --git a/src/main.zig b/src/main.zig index 255f0fb..29691e6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,6 +1,7 @@ const std = @import("std"); const info = std.log.info; const debug = std.log.debug; +const stderr = std.io.getStdErr().writer(); const print = std.debug.print; const eql = std.mem.eql; const Allocator = std.mem.Allocator; @@ -21,7 +22,9 @@ pub fn main() !void { }, ArgumentError.UnknownParameter, // ArgumentError.MissingAuthenticatorParam, // - ArgumentError.MissingConfigLocation, + ArgumentError.MissingConfigLocation, // + ArgumentError.FailedToOpenConfigFile, // + ArgumentError.TooManyParsinErrors, // => { // do nothing, error message is already written (because the message contains the name of the unknown parameter) }, @@ -167,7 +170,7 @@ fn zeroPad(allocator: Allocator, digits: u4, x: anytype) ![]u8 { return result; } -const ArgumentError = error{ InvalidOtpAuthUrl, UnknownParameter, MissingConfigLocation, AuthenticatorNotFound, MissingAuthenticatorParam }; +const ArgumentError = error{ InvalidOtpAuthUrl, UnknownParameter, MissingConfigLocation, AuthenticatorNotFound, MissingAuthenticatorParam, FailedToOpenConfigFile, TooManyParsinErrors }; fn parseArgs(allocator: Allocator, args: []const []const u8) !Args { var result = Args{ @@ -274,34 +277,65 @@ fn configLocation(allocator: Allocator) ![]const u8 { /// Reads the config file into a list of OtpAuthUrl. /// The caller should use an arena allocator and free the memory eventually. fn read_config(allocator: Allocator, config_location: []const u8) !ArrayList(OtpAuthUrl) { - const file = try std.fs.cwd().openFile(config_location, .{}); - defer file.close(); + const file_result = std.fs.cwd().openFile(config_location, .{}); + if (file_result) |file| { + defer file.close(); - var buf_reader = std.io.bufferedReader(file.reader()); - var in_stream = buf_reader.reader(); + var buf_reader = std.io.bufferedReader(file.reader()); + var in_stream = buf_reader.reader(); - var authenticators = std.ArrayList(OtpAuthUrl).init(allocator); + var authenticators = std.ArrayList(OtpAuthUrl).init(allocator); - var line_no: usize = 1; - while (try in_stream.readUntilDelimiterOrEofAlloc(allocator, '\n', 1024 * 1024)) |line| { - if (line.len > 0 and std.mem.trim(u8, line, " \r").len > 0) { - const authenticatorResult = parseOtpAuthUrl(allocator, line); - if (authenticatorResult) |authenticator| { - try authenticators.append(authenticator); - } else |err| { - switch (err) { - error.InvalidOtpAuthUrl => { - try std.io.getStdErr().writer().print("invalid otpauth url in line {d} in {s}\n", .{ line_no, config_location }); - }, - else => { - return err; - }, + var number_of_errors: u32 = 0; + var line_no: usize = 1; + while (try in_stream.readUntilDelimiterOrEofAlloc(allocator, '\n', 1024 * 1024)) |line| { + if (line.len > 0 and std.mem.trim(u8, line, " \r").len > 0) { + const authenticatorResult = parseOtpAuthUrl(allocator, line); + if (authenticatorResult) |authenticator| { + try authenticators.append(authenticator); + } else |err| { + number_of_errors += 1; + switch (err) { + error.UnexpectedCharacter, error.InvalidFormat => { + try stderr.print("Unexpected character in line {d} in file {s}. Line will be ignored.\n", .{ line_no, config_location }); + if (number_of_errors >= 10) { + try stderr.print("too many parsing errors\n", .{}); + return ArgumentError.TooManyParsinErrors; + } + }, + error.InvalidOtpAuthUrl => { + try std.io.getStdErr().writer().print("invalid otpauth url in line {d} in {s}\n", .{ line_no, config_location }); + }, + else => { + return err; + }, + } } } + line_no += 1; } - line_no += 1; + return authenticators; + } else |err| { + //debug("file open error: {}\n", .{err}); + switch (err) { + error.AccessDenied, // + error.BadPathName, + error.InvalidWtf8, + => { + try stderr.print("cannot open config file: {}\n", .{err}); + }, + error.FileNotFound => { + try stderr.print("config file not found. Create a new config file in $HOME/.zig-totp or $XDG_CONFIG_HOME/zig-totp.\n", .{}); + }, + error.IsDir => { + try stderr.print("the configuration location must be a file, but is a directory.\n", .{}); + }, + else => { + return err; + }, + } + return ArgumentError.FailedToOpenConfigFile; } - return authenticators; } fn executeGetList(allocator: Allocator, config_location: []const u8) !std.ArrayList([]const u8) {