diff --git a/build.zig b/build.zig index a600bb8..070544a 100644 --- a/build.zig +++ b/build.zig @@ -1,6 +1,13 @@ const std = @import("std"); -pub fn build(b: *std.Build) void { +/// version if version isn't provided during build +const def_version = "0.0.0+testing"; + +pub fn build(b: *std.Build) !void { + const opt = b.addOptions(); + const ver = b.option([]const u8, "version", "sematic version") orelse def_version; + const sem_ver = try std.SemanticVersion.parse(ver); + opt.addOption(std.SemanticVersion, "version", sem_ver); const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); const lib_mod = b.createModule(.{ @@ -12,19 +19,25 @@ pub fn build(b: *std.Build) void { .linkage = .static, .name = "zig_squashfs", .root_module = lib_mod, + .version = sem_ver, }); + const exe_mod = b.createModule(.{ - .root_source_file = b.path("src/main.zig"), + .root_source_file = b.path("src/zig_unsquashfs.zig"), .target = target, .optimize = optimize, }); + exe_mod.addOptions("config", opt); const exe = b.addExecutable(.{ .linkage = .static, .name = "zig-unsquashfs", .root_module = exe_mod, + .version = sem_ver, }); + b.installArtifact(lib); b.installArtifact(exe); + const lib_unit_tests = b.addTest(.{ .root_module = lib_mod, }); diff --git a/src/main.zig b/src/main.zig deleted file mode 100644 index d030582..0000000 --- a/src/main.zig +++ /dev/null @@ -1,20 +0,0 @@ -const std = @import("std"); - -const Reader = @import("reader.zig"); - -const stdout = std.io.getStdOut(); - -pub fn main() !void { - var alloc: std.heap.GeneralPurposeAllocator(.{}) = .init; - var args = try std.process.argsWithAllocator(alloc.allocator()); - defer args.deinit(); - while (args.next()) |arg| { - if (std.mem.eql(u8, arg, "--help")) { - help(); - return; - } - } - //TODO -} - -fn help() void {} diff --git a/src/zig_unsquashfs.zig b/src/zig_unsquashfs.zig new file mode 100644 index 0000000..966ab91 --- /dev/null +++ b/src/zig_unsquashfs.zig @@ -0,0 +1,150 @@ +const std = @import("std"); +const config = @import("config"); + +const Reader = @import("reader.zig").Reader; + +const stdout = std.io.getStdOut(); + +var extr_files: std.ArrayList([]const u8) = undefined; +var offset: u64 = 0; +var verbose: bool = false; +var unbreak: bool = false; +var deref: bool = false; +var processors: u16 = 0; +var list: ListTypes = .None; + +var filename: []const u8 = ""; +var extr_location: []const u8 = ""; + +const ListTypes = enum { + None, + List, + ListAttr, + ListNumeric, +}; + +fn help() !void { + const help_msg = + \\Basic Usage: zig-unsquashfs [Options] SQUASHFS_FILE EXTRACT_LOCATION + \\ + \\General options: + \\ -e Path to a file or directory inside the archive to extract instead of the whole archive. + \\ Can be given multiple times. + \\ -o Skip before reading from the archive. + \\ -v Verbose output. + \\ --help Prints this help message. + \\ -h Same as --help + \\ + \\Extraction options: + \\ --unbreak-symlinks Attempt extract symlink targets along with symlinks. Will not place files outside of the extraction location. + \\ -us Same as --unbreak-symlinks + \\ --deref-symlinks Replace symlink files with their target. + \\ -ds Same as --deref-symlinks + \\ -p <#> Use at most # of processors. Defaults to logical core count. + \\ + \\Listing Options: + \\ -l List files instead of extracting. When used, you do not need to specify an extraction location. + \\ -ll Like -l, but with file attributes. + \\ -lln Like -ll, but with numeric uids and gids. + \\ + \\Other: + \\ --version Print version number. + \\ + ; + _ = try stdout.writeAll(help_msg); +} + +pub fn main() !void { + var alloc: std.heap.GeneralPurposeAllocator(.{}) = .init; + extr_files = .init(alloc.allocator()); + defer extr_files.deinit(); + var args = std.process.argsWithAllocator(alloc.allocator()) catch { + _ = try stdout.writeAll("Unable allocate memory"); + return; + }; + defer args.deinit(); + while (args.next()) |arg| { + if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) { + try help(); + return; + } else if (std.mem.eql(u8, arg, "-v")) { + verbose = true; + } else if (std.mem.eql(u8, arg, "--unbreak-symlinks") or std.mem.eql(u8, arg, "-us")) { + unbreak = true; + } else if (std.mem.eql(u8, arg, "--deref-symlinks") or std.mem.eql(u8, arg, "-ds")) { + deref = true; + } else if (std.mem.eql(u8, arg, "-l")) { + list = .List; + } else if (std.mem.eql(u8, arg, "-ll")) { + list = .ListAttr; + } else if (std.mem.eql(u8, arg, "-lln")) { + list = .ListNumeric; + } else if (std.mem.eql(u8, arg, "-e")) { + const next = args.next(); + if (next == null) { + _ = try stdout.writeAll("path required after -e\n"); + return; + } + try extr_files.append(next.?); + } else if (std.mem.eql(u8, arg, "-o")) { + const next = args.next(); + if (next == null) { + _ = try stdout.writeAll("offset required after -o\n"); + return; + } + offset = try std.fmt.parseInt(u64, next.?, 10); + } else if (std.mem.eql(u8, arg, "-p")) { + const next = args.next(); + if (next == null) { + _ = try stdout.writeAll("number required after -p\n"); + return; + } + processors = try std.fmt.parseInt(u16, next.?, 10); + } else if (std.mem.eql(u8, arg, "--version")) { + try config.version.format("", .{}, stdout.writer()); + _ = try stdout.write("\n"); + return; + } else if (filename.len == 0) { + filename = arg; + } else if (extr_location.len == 0) { + extr_location = arg; + } else { + _ = try stdout.writeAll("invalid or too many arguments\n"); + return; + } + } + if (filename.len == 0) { + _ = try stdout.writeAll("no archive given\n"); + return; + } + if (list == .None and extr_location.len == 0) { + _ = try stdout.writeAll("no extract location given\n"); + return; + } + var rdr: Reader = .init( + alloc.allocator(), + filename, + offset, + ) catch |err| { + try std.fmt.format(stdout.writer(), "Error opening {s} as squashfs: {any}", "\n", .{ filename, err }); + }; + if (list == .None) { + if (extr_files.items.len == 0) { + rdr.root.extract(&rdr, extr_location) catch |err| { + try std.fmt.format(stdout.writer(), "Error extracting archive: {any}", "\n", .{err}); + }; + } else { + for (extr_files.items) |path| { + var fil = rdr.root.open(&rdr, path) catch |err| { + try std.fmt.format(stdout.writer(), "Error extracting {s}: {any}", "\n", .{ path, err }); + }; + defer fil.deinit(alloc.allocator()); + fil.extract(&rdr, extr_location) catch |err| { + try std.fmt.format(stdout.writer(), "Error extracting {s}: {any}", "\n", .{ path, err }); + }; + } + } + return; + } + //TODO: listing +}