unzip.zig 3.0 KB

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