diff --git a/build.zig b/build.zig index 641555b..1a3f814 100644 --- a/build.zig +++ b/build.zig @@ -16,9 +16,12 @@ pub fn build(b: *std.Build) void { const optimize = b.standardOptimizeOption(.{}); const clap_dep = b.dependency("clap", .{ .target = target, .optimize = optimize }); + // const zbench_dep = b.dependency("zbench", .{ .target = target, .optimize = optimize }); const clap = clap_dep.module("clap"); - + // const zbench = zbench_dep.module("zbench"); + var opt = b.addOptions(); + opt.addOption([]const u8, "version", "0.0.1"); const exe = b.addExecutable(.{ .name = "zb64", // In this case the main source file is merely a path, however, in more @@ -27,8 +30,9 @@ pub fn build(b: *std.Build) void { .target = target, .optimize = optimize, }); - + exe.addModule("build_info", opt.createModule()); exe.addModule("clap", clap); + // exe.addModule("zbench", zbench); // This declares intent for the executable to be installed into the // standard location when the user invokes the "install" step (the default diff --git a/build.zig.zon b/build.zig.zon index 9f0f87c..ccb5847 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -5,6 +5,6 @@ .clap = .{ .url = "https://github.com/Hejsil/zig-clap/archive/refs/tags/0.7.0.tar.gz", .hash = "1220f48518ce22882e102255ed3bcdb7aeeb4891f50b2cdd3bd74b5b2e24d3149ba2" - }, - }, + } + } } diff --git a/src/main.zig b/src/main.zig index 90233b4..a517227 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,20 +1,18 @@ const std = @import("std"); const clap = @import("clap"); +const build_info = @import("build_info"); const base64 = std.base64; - const debug = std.debug; const io = std.io; -//const allocator = std.heap.GeneralPurposeAllocator(); - pub fn main() !void { const params = comptime clap.parseParamsComptime( \\-h, --help Display this help and exit. - \\-d, --decode Decode base64 to string. - \\-e, --encode Encode string to base64. - \\ + \\-d, --decode Decode base64 to string. + \\-e, --encode Encode string to base64. + \\-v, --version Display version. + \\ String to encode or decode. ); - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); @@ -32,52 +30,117 @@ pub fn main() !void { const stdout = std.io.getStdOut(); - if (res.args.help != 0) + if (res.args.help != 0) { + usage(); try clap.help(std.io.getStdErr().writer(), clap.Help, ¶ms, .{}); - if (res.args.encode) |e| { - debug.print("--encode = {s}\n", .{e}); - - const encoder = std.base64.standard.Encoder; - const encoded = try allocator.alloc(u8, encoder.calcSize(e.len)); - - defer allocator.free(encoded); - - try stdout.writer().print("Encoded string: {s}", .{encoder.encode(encoded, e)}); - } - - if (res.args.decode) |d| { - debug.print("--decode = {s}\n", .{d}); - - // const decoder = std.base64.standard.Decoder; - // var decodeSize = decoder.calcSizeForSlice(d) catch |err| { - // std.debug.print("unable decode string: {}\n", .{err}); - // return; - // }; - - // const decoded = allocator.alloc(u8, decodeSize) catch |err| { - // std.debug.print("unable allocate: {}\n", .{err}); - // return; - // }; - - // defer allocator.free(decoded); - - // try decoder.decode(decoded, d); - const decodedString = try base64_decode(allocator, d); - - defer allocator.free(decodedString); - - try stdout.writer().print("Decoded string: {s}", .{decodedString}); - return; } - for (res.positionals) |pos| - debug.print("{s}\n", .{pos}); + if (res.args.version != 0) { + version(); + + return; + } + + if (res.args.encode != 0) { + for (res.positionals) |pos| { + const encodedString = try base64_encode(allocator, pos); + + defer allocator.free(encodedString); + + try stdout.writer().print("{s}", .{encodedString}); + + return; + } else { + const stdin = io.getStdIn(); + var input = try read_from_stream(allocator, stdin); + defer input.deinit(); + + const encodedString = try base64_encode(allocator, input.items); + defer allocator.free(encodedString); + + try stdout.writer().print("{s}", .{encodedString}); + + return; + } + } + + if (res.args.decode != 0) { + for (res.positionals) |pos| { + const decodedString = base64_decode(allocator, pos) catch |err| { + std.debug.print("unable to decode string '{s}'\n", .{pos}); + + return err; + }; + + defer allocator.free(decodedString); + + try stdout.writer().print("{s}", .{decodedString}); + + return; + } else { + const stdin = io.getStdIn(); + var input = read_from_stream(allocator, stdin) catch |err| { + std.debug.print("unable to read from stream\n", .{}); + + return err; + }; + defer input.deinit(); + + const decodedString = base64_decode(allocator, input.items) catch |err| { + std.debug.print("unable to decode string '{s}'\n", .{std.mem.trim(u8, input.items, "\r\n")}); + + return err; + }; + + defer allocator.free(decodedString); + + std.debug.print("{s}", .{decodedString}); + + return; + } + } + + if (res.positionals.len <= 1) { + usage(); + try clap.help(std.io.getStdErr().writer(), clap.Help, ¶ms, .{}); + } } -fn base64_decode(allocator: std.mem.Allocator, string: []const u8) ![]u8 { + +fn usage() void { + std.debug.print("Usage: zb64 [-e] [-d] \n\n", .{}); +} + +fn version() void { + std.debug.print("zb64 - {s}\n", .{build_info.version}); +} + +fn read_from_stream(allocator: std.mem.Allocator, stream: std.fs.File) !std.ArrayList(u8) { + var input = std.ArrayList(u8).init(allocator); + + var fifo = std.fifo.LinearFifo(u8, .{ .Static = 1024 }).init(); + + try fifo.pump(stream.reader(), input.writer()); + defer fifo.deinit(); + + return input; +} + +fn base64_encode(allocator: std.mem.Allocator, string: []const u8) ![]const u8 { + const encoder = std.base64.standard.Encoder; + const encoded = try allocator.alloc(u8, encoder.calcSize(string.len)); + + defer allocator.free(encoded); + + const encodedString = try allocator.dupe(u8, encoder.encode(encoded, string)); + + return encodedString; +} + +fn base64_decode(allocator: std.mem.Allocator, b64string: []const u8) ![]u8 { const decoder = std.base64.standard.Decoder; - var decodeSize = decoder.calcSizeForSlice(string) catch |err| { + var decodeSize = decoder.calcSizeForSlice(b64string) catch |err| { return err; }; @@ -87,7 +150,7 @@ fn base64_decode(allocator: std.mem.Allocator, string: []const u8) ![]u8 { defer allocator.free(decoded); - decoder.decode(decoded, string) catch |err| { + decoder.decode(decoded, b64string) catch |err| { return err; }; @@ -96,10 +159,24 @@ fn base64_decode(allocator: std.mem.Allocator, string: []const u8) ![]u8 { return decodedString; } -test "base64_decode returns decoded string" { - var base64String = "aGVqIGxrc2pkbGthanNkIGxha3NqZGxramFzZA=="; +test "base64_decode() returns decoded string" { + const test_allocator = std.testing.allocator; + const base64String = "aGVqIGxrc2pkbGthanNkIGxha3NqZGxramFzZA=="; var expectedString = "hej lksjdlkajsd laksjdlkjasd"; - const decodedString = base64_decode(base64String); - try std.testing.expectEqual(expectedString, decodedString); + const decodedString = try base64_decode(test_allocator, base64String); + defer test_allocator.free(decodedString); + + try std.testing.expectEqualStrings(expectedString, decodedString); +} + +test "base64_encode() returns encoded string" { + const test_allocator = std.testing.allocator; + const expectedEncodedString = "aGVqIGxrc2pkbGthanNkIGxha3NqZGxramFzZA=="; + var string = "hej lksjdlkajsd laksjdlkjasd"; + + const encodedString = try base64_encode(test_allocator, string); + defer test_allocator.free(encodedString); + + try std.testing.expectEqualStrings(expectedEncodedString, encodedString); }