const libarchive = @import("libarchive"); const std = @import("std"); const xdg = @import("xdg"); const zup = @import("root"); const Allocator = std.mem.Allocator; const ArchiveRead = libarchive.Read; const ArenaAllocator = std.heap.ArenaAllocator; const Config = zup.Config; const EasyHttp = zup.EasyHttp; const Installation = zup.Installation; const Installations = zup.Installations; pub const params = \\-f, --force Install over existing installations \\ ; pub const description = "Installs a Zig version. Run `zup list -i` to see installed s."; pub const min_args = 1; pub const max_args = 1; pub fn main(comptime Result: type, config: Config, res: Result) !void { const allocator = config.allocator; var available = try Installation.getAvailableList(config); defer Installation.deinitMap(allocator, &available); return perform(allocator, res.positionals[0].?, res.args.force != 0, available); } pub fn perform(allocator: Allocator, name: []const u8, force: bool, available: Installations) !void { if (!force and try Installation.isInstalled(allocator, name)) { std.log.err("{s} already installed, not overwriting without --force!", .{name}); return error.AlreadyInstalled; } const installation = available.get(name) orelse { std.log.err("Installation by name {s} not available!", .{name}); return error.InstallationNotFound; }; if (installation.url == null) { std.log.err("No tarball URL for {s} found!", .{name}); } const installation_dir = blk: { const data_home = try xdg.getDataHome(allocator, "zup"); defer allocator.free(data_home); break :blk try std.fs.path.join(allocator, &.{ data_home, name }); }; defer allocator.free(installation_dir); std.log.info("Installing {s}, version {f}", .{ name, installation.version }); std.log.info("Downloading from {s}...", .{installation.url_str.?}); const data = try EasyHttp.get(allocator, installation.url.?); defer allocator.free(data); std.log.info("Extracting...", .{}); var archive = try ArchiveRead.init(); defer archive.deinit(); try archive.supportFilter(.all); try archive.supportFormat(.all); try archive.openMemory(data); while (try archive.nextHeader()) |entry_c| { var entry = entry_c; const source = entry.pathname(); const dest = try preparePathname(allocator, installation_dir, source); defer allocator.free(dest); entry.setPathname(dest); try archive.extract(entry, 0); } std.log.info("Installed to {s}", .{installation_dir}); if (!try Installation.isActive(allocator, name)) { std.log.info("If you want to use this installation, run `zup switch {s}`", .{name}); } } fn preparePathname(allocator: Allocator, installation_dir: []const u8, source: []const u8) ![:0]u8 { const stripped = if (std.mem.indexOfScalar(u8, source, '/')) |idx| source[idx + 1 ..] else ""; return std.fs.path.joinZ(allocator, &.{ installation_dir, stripped }); }