diff --git a/README.md b/README.md index 42502e8..21eeac1 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ For a list of applets, run `./pbui` or `./pbui -h`. Currently supported applets - `du` - `uniq` - `shuf` + - `sha1` To run a given applet, use: `./pbui [APPLET] [arguments]`. diff --git a/build.zig b/build.zig index 664daec..651e019 100644 --- a/build.zig +++ b/build.zig @@ -32,6 +32,9 @@ pub fn build(b: *Builder) void { var rm_tests = b.addTest("src/rm.zig"); rm_tests.setBuildMode(mode); + var sha1_tests = b.addTest("src/sha1.zig"); + sha1_tests.setBuildMode(mode); + const test_step = b.step("test", "Run library tests"); test_step.dependOn(&main_tests.step); test_step.dependOn(&shuf_tests.step); @@ -39,4 +42,5 @@ pub fn build(b: *Builder) void { test_step.dependOn(&false_tests.step); test_step.dependOn(&mkdir_tests.step); test_step.dependOn(&rm_tests.step); + test_step.dependOn(&sha1_tests.step); } diff --git a/src/main.zig b/src/main.zig index 592d43c..b309789 100644 --- a/src/main.zig +++ b/src/main.zig @@ -15,6 +15,7 @@ const std = @import("std"); const du = @import("du.zig"); const uniq = @import("uniq.zig"); const shuf = @import("shuf.zig"); +const sha1 = @import("sha1.zig"); const stdout = &std.io.getStdOut().outStream(); const warn = std.debug.warn; const testing = std.testing; @@ -40,6 +41,7 @@ pub fn usage(args: [][]u8) anyerror!u8 { \\du \\uniq \\shuf + \\sha1 \\ , .{}); @@ -75,6 +77,7 @@ pub fn main() anyerror!u8 { _ = try func_map.put("uniq", uniq.main); //_ = try func_map.put("ls", ls.main); _ = try func_map.put("shuf", shuf.main); + _ = try func_map.put("sha1", sha1.main); // check basename of exe var buffer: [100]u8 = undefined; diff --git a/src/sha1.zig b/src/sha1.zig new file mode 100644 index 0000000..32aa20e --- /dev/null +++ b/src/sha1.zig @@ -0,0 +1,105 @@ +const std = @import("std"); +const Sha1 = std.crypto.Sha1; +const opt = @import("opt.zig"); +const Allocator = std.mem.Allocator; +const stdout = &std.io.getStdOut().outStream(); +const warn = std.debug.warn; +const BUFSIZ = 4096; + +pub fn sha1(allocator: *Allocator, file: std.fs.File) ![40]u8 { + var h = Sha1.init(); + var hash: [20]u8 = undefined; + var real_out: [40]u8 = undefined; + + var file_buffer = std.ArrayList(u8).init(allocator); + defer file_buffer.deinit(); + + var read_buffer: [BUFSIZ]u8 = undefined; + + var size = try file.readAll(&read_buffer); + while (size > 0) : (size = try file.readAll(&read_buffer)) { + try file_buffer.insertSlice(file_buffer.items.len, read_buffer[0..size]); + } + h.reset(); + h.update(file_buffer.items[0..]); + h.final(hash[0..]); + var i: u8 = 0; + while (i < 20) : (i += 1) { + if (hash[i] <= 15) { + _ = try std.fmt.bufPrint(real_out[i * 2 ..], "0{x}", .{hash[i]}); + } else { + _ = try std.fmt.bufPrint(real_out[i * 2 ..], "{x}", .{hash[i]}); + } + } + + return real_out; +} + +const Sha1Flags = enum { + Help, + Version, +}; + +var flags = [_]opt.Flag(Sha1Flags){ + .{ + .name = Sha1Flags.Help, + .long = "help", + }, + .{ + .name = Sha1Flags.Version, + .long = "version", + }, +}; + +pub fn main(args: [][]u8) anyerror!u8 { + var it = opt.FlagIterator(Sha1Flags).init(flags[0..], args); + while (it.next_flag() catch { + return 1; + }) |flag| { + switch (flag.name) { + Sha1Flags.Help => { + warn("sha1 [FILE_NAME ..]\n", .{}); + return 1; + }, + Sha1Flags.Version => { + warn("(version info here)\n", .{}); + return 1; + }, + } + } + + var files = std.ArrayList([]u8).init(std.heap.page_allocator); + while (it.next_arg()) |file_name| { + try files.append(file_name); + } + + if (files.items.len > 0) { + for (files.items) |file_name| { + const file = std.fs.cwd().openFile(file_name[0..], std.fs.File.OpenFlags{ .read = true, .write = false }) catch |err| { + try stdout.print("Error: cannot open file {}\n", .{file_name}); + return 1; + }; + // run command + var result = sha1(std.heap.page_allocator, file) catch |err| { + try stdout.print("Error: {}\n", .{err}); + return 1; + }; + try stdout.print("{}\n", .{result}); + file.close(); + } + } else { + var result = sha1(std.heap.page_allocator, std.io.getStdIn()) catch |err| { + try stdout.print("Error: {}\n", .{err}); + return 1; + }; + try stdout.print("{}\n", .{result}); + } + return 0; +} + +test "hash on license" { + var file = try std.fs.cwd().openFile("LICENSE", std.fs.File.OpenFlags{ .read = true }); + var result = try sha1(std.heap.page_allocator, file); + + std.debug.assert(std.mem.eql(u8, result[0..], "8624bcdae55baeef00cd11d5dfcfa60f68710a02")); +}