unzip.zig 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. const builtin = @import("builtin");
  2. const std = @import("std");
  3. fn oom(e: error{OutOfMemory}) noreturn {
  4. @panic(@errorName(e));
  5. }
  6. fn fatal(comptime fmt: []const u8, args: anytype) noreturn {
  7. std.log.err(fmt, args);
  8. std.process.exit(0xff);
  9. }
  10. fn usage() noreturn {
  11. std.io.getStdErr().writer().print("Usage: unzip [-d DIR] ZIP_FILE\n", .{}) catch |e| @panic(@errorName(e));
  12. std.process.exit(1);
  13. }
  14. var windows_args_arena = if (builtin.os.tag == .windows)
  15. std.heap.ArenaAllocator.init(std.heap.page_allocator)
  16. else
  17. struct {}{};
  18. pub fn cmdlineArgs() [][*:0]u8 {
  19. if (builtin.os.tag == .windows) {
  20. const slices = std.process.argsAlloc(windows_args_arena.allocator()) catch |err| switch (err) {
  21. error.OutOfMemory => oom(error.OutOfMemory),
  22. //error.InvalidCmdLine => @panic("InvalidCmdLine"),
  23. error.Overflow => @panic("Overflow while parsing command line"),
  24. };
  25. const args = windows_args_arena.allocator().alloc([*:0]u8, slices.len - 1) catch |e| oom(e);
  26. for (slices[1..], 0..) |slice, i| {
  27. args[i] = slice.ptr;
  28. }
  29. return args;
  30. }
  31. return std.os.argv.ptr[1..std.os.argv.len];
  32. }
  33. pub fn main() !void {
  34. var cmdline_opt: struct {
  35. dir_arg: ?[]u8 = null,
  36. } = .{};
  37. const cmd_args = blk: {
  38. const cmd_args = cmdlineArgs();
  39. var arg_index: usize = 0;
  40. var non_option_len: usize = 0;
  41. while (arg_index < cmd_args.len) : (arg_index += 1) {
  42. const arg = std.mem.span(cmd_args[arg_index]);
  43. if (!std.mem.startsWith(u8, arg, "-")) {
  44. cmd_args[non_option_len] = arg;
  45. non_option_len += 1;
  46. } else if (std.mem.eql(u8, arg, "-d")) {
  47. arg_index += 1;
  48. if (arg_index == cmd_args.len)
  49. fatal("option '{s}' requires an argument", .{arg});
  50. cmdline_opt.dir_arg = std.mem.span(cmd_args[arg_index]);
  51. } else {
  52. fatal("unknown cmdline option '{s}'", .{arg});
  53. }
  54. }
  55. break :blk cmd_args[0..non_option_len];
  56. };
  57. if (cmd_args.len != 1) usage();
  58. const zip_file_arg = std.mem.span(cmd_args[0]);
  59. var out_dir = blk: {
  60. if (cmdline_opt.dir_arg) |dir| {
  61. break :blk std.fs.cwd().openDir(dir, .{}) catch |err| switch (err) {
  62. error.FileNotFound => {
  63. try std.fs.cwd().makePath(dir);
  64. break :blk try std.fs.cwd().openDir(dir, .{});
  65. },
  66. else => fatal("failed to open output directory '{s}' with {s}", .{ dir, @errorName(err) }),
  67. };
  68. }
  69. break :blk std.fs.cwd();
  70. };
  71. defer if (cmdline_opt.dir_arg) |_| out_dir.close();
  72. const zip_file = std.fs.cwd().openFile(zip_file_arg, .{}) catch |err|
  73. fatal("open '{s}' failed: {s}", .{ zip_file_arg, @errorName(err) });
  74. defer zip_file.close();
  75. try std.zip.extract(out_dir, zip_file.seekableStream(), .{
  76. .allow_backslashes = true,
  77. });
  78. }