From 0ea7d8b7ee9fa8b8b554c6cefc14c191ec83186d Mon Sep 17 00:00:00 2001 From: Andre Henriques Date: Sun, 21 May 2023 21:45:18 +0100 Subject: [PATCH] batman --- .gitignore | 2 + build.zig | 70 ++++++++++++++++++++++++++++ flake.lock | 78 +++++++++++++++++++++++++++++++ flake.nix | 9 ++++ src/main.zig | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 287 insertions(+) create mode 100644 .gitignore create mode 100644 build.zig create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 src/main.zig diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..26e5f7b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +zig-out/ +zig-cache diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..1825edc --- /dev/null +++ b/build.zig @@ -0,0 +1,70 @@ +const std = @import("std"); + +// Although this function looks imperative, note that its job is to +// declaratively construct a build graph that will be executed by an external +// runner. +pub fn build(b: *std.Build) void { + // Standard target options allows the person running `zig build` to choose + // what target to build for. Here we do not override the defaults, which + // means any target is allowed, and the default is native. Other options + // for restricting supported target set are available. + const target = b.standardTargetOptions(.{}); + + // Standard optimization options allow the person running `zig build` to select + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not + // set a preferred release mode, allowing the user to decide how to optimize. + const optimize = b.standardOptimizeOption(.{}); + + const exe = b.addExecutable(.{ + .name = "ebook-reader", + // In this case the main source file is merely a path, however, in more + // complicated build scripts, this could be a generated file. + .root_source_file = .{ .path = "src/main.zig" }, + .target = target, + .optimize = optimize, + }); + + // This declares intent for the executable to be installed into the + // standard location when the user invokes the "install" step (the default + // step when running `zig build`). + b.installArtifact(exe); + + // This *creates* a Run step in the build graph, to be executed when another + // step is evaluated that depends on it. The next line below will establish + // such a dependency. + const run_cmd = b.addRunArtifact(exe); + + // By making the run step depend on the install step, it will be run from the + // installation directory rather than directly from within the cache directory. + // This is not necessary, however, if the application depends on other installed + // files, this ensures they will be present and in the expected location. + run_cmd.step.dependOn(b.getInstallStep()); + + // This allows the user to pass arguments to the application in the build + // command itself, like this: `zig build run -- arg1 arg2 etc` + if (b.args) |args| { + run_cmd.addArgs(args); + } + + // This creates a build step. It will be visible in the `zig build --help` menu, + // and can be selected like this: `zig build run` + // This will evaluate the `run` step rather than the default, which is "install". + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); + + // Creates a step for unit testing. This only builds the test executable + // but does not run it. + const unit_tests = b.addTest(.{ + .root_source_file = .{ .path = "src/main.zig" }, + .target = target, + .optimize = optimize, + }); + + const run_unit_tests = b.addRunArtifact(unit_tests); + + // Similar to creating the run step earlier, this exposes a `test` step to + // the `zig build --help` menu, providing a way for the user to request + // running the unit tests. + const test_step = b.step("test", "Run unit tests"); + test_step.dependOn(&run_unit_tests.step); +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..dde7ff5 --- /dev/null +++ b/flake.lock @@ -0,0 +1,78 @@ +{ + "nodes": { + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1659877975, + "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1661151577, + "narHash": "sha256-++S0TuJtuz9IpqP8rKktWyHZKpgdyrzDFUXVY07MTRI=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "54060e816971276da05970a983487a25810c38a7", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "zig": "zig" + } + }, + "zig": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1684670828, + "narHash": "sha256-e3HSmT9ufQ0WZYh6hOPx87dg/4jjLoJLl6zAEzev07E=", + "owner": "mitchellh", + "repo": "zig-overlay", + "rev": "22a39bd5ce8f8b0fdec3049ca5bfb54c748380bd", + "type": "github" + }, + "original": { + "owner": "mitchellh", + "repo": "zig-overlay", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..1727211 --- /dev/null +++ b/flake.nix @@ -0,0 +1,9 @@ +{ + description = "A very basic flake"; + + inputs.zig.url = "github:mitchellh/zig-overlay"; + + outputs = { self, zig, ... }: { + packages.x86_64-linux.zig = zig.packages.x86_64-linux.master; + }; +} diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 0000000..f36a7ff --- /dev/null +++ b/src/main.zig @@ -0,0 +1,128 @@ +const std = @import("std"); +const stdout = std.io.getStdOut().writer(); +const stderr = std.io.getStdErr().writer(); + +fn print(comptime str: []const u8, params: anytype) void { + stdout.print(str ++ "\n", params) catch {}; +} + +fn pErr(comptime str: []const u8, params: anytype) void { + stderr.print(str ++ "\n", params) catch {}; +} + +fn exit(comptime str: []const u8, params: anytype, exitCode: u8) void { + pErr(str, params); + std.os.exit(exitCode); +} + +fn usage() void { + pErr("reader ", .{}); + exit("", .{}, 1); +} + +const LOCAL_FILE_HEADER_SIGNATURE = 0x04034b50; +const ZipFileHeader = struct { + version: u16, + general: u16, + compression_method: u16, + last_mod_time: u16, + last_mod_date: u16, + crc_32: u32, + compressed_size: u32, + uncompressed_size: u32, + file_name_length: u16, + extra_field_length: u16, + + file_name: []u8, + extra_field: []u8, + + allocator: std.mem.Allocator, + + const Self = @This(); + + fn init(allocator: std.mem.Allocator, reader: std.fs.File.Reader) !Self { + if (try reader.readInt(u32, .Big) == LOCAL_FILE_HEADER_SIGNATURE) { + return error.InvalidError; + } + + var self = Self{ + .allocator = allocator, + .version = try reader.readInt(u16, .Big), + .general = try reader.readInt(u16, .Big), + .compression_method = try reader.readInt(u16, .Big), + .last_mod_time = try reader.readInt(u16, .Big), + .last_mod_date = try reader.readInt(u16, .Big), + .crc_32 = try reader.readInt(u32, .Big), + .compressed_size = try reader.readInt(u32, .Big), + .uncompressed_size = try reader.readInt(u32, .Big), + .file_name_length = try reader.readInt(u16, .Big), + .extra_field_length = try reader.readInt(u16, .Big), + .file_name = undefined, + .extra_field = undefined, + }; + + self.file_name = try allocator.alloc(u8, self.file_name_length); + self.extra_field = try allocator.alloc(u8, self.extra_field_length); + + _ = try reader.read(self.file_name); + _ = try reader.read(self.extra_field); + + return self; + } + + fn deinit(self: *Self) void { + self.allocator.free(self.file_name); + self.allocator.free(self.extra_field); + } +}; + +pub fn main() !void { + var args = std.process.args(); + + var filePath: ?[]const u8 = null; + + // Skip the current path + _ = args.next(); + while (args.next()) |arg| { + if (filePath == null) { + filePath = arg; + } else { + pErr("Invalid argument: {s}", .{arg}); + usage(); + } + } + + if (filePath == null) { + pErr("File path not provided. Please provide a path", .{}); + usage(); + } + + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer _ = gpa.deinit(); + + var allocator = gpa.allocator(); + + var book_path = try std.fs.realpathAlloc(allocator, filePath.?); + defer allocator.free(book_path); + + var file = std.fs.openFileAbsolute(book_path, .{}) catch |err| { + exit("Please provide a file path! Error: {?}", .{err}, 1); + return err; + }; + defer file.close(); + + var stat = try file.stat(); + + if (stat.kind != .File) { + exit("Please provide a valid file", .{}, 1); + } + + var reader = file.reader(); + + var first_file = try ZipFileHeader.init(allocator, reader); + defer first_file.deinit(); + + print("G: {}", .{first_file.file_name_length}); + print("T: {s}", .{first_file.file_name}); + print("O: {s}", .{first_file.extra_field}); +}