1 /** 2 * ae.sys.archive 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 */ 13 14 module ae.sys.archive; 15 16 import std.array; 17 import std.datetime; 18 import std.exception; 19 import std.file; 20 import std.path; 21 import std.process; 22 import std.string; 23 24 import ae.sys.file; 25 import ae.sys.install.sevenzip; 26 import ae.utils.path : haveExecutable; 27 28 /// Unzips a .zip file to the target directory. 29 void unzip(string zip, string target) 30 { 31 import std.zip; 32 auto archive = new ZipArchive(zip.read); 33 foreach (name, entry; archive.directory) 34 { 35 auto path = buildPath(target, name).replace("\\", "/"); 36 ensurePathExists(path); 37 if (name.endsWith(`/`)) 38 { 39 if (!path.exists) 40 path.mkdirRecurse(); 41 } 42 else 43 std.file.write(path, archive.expand(entry)); 44 45 auto attr = entry.fileAttributes; 46 if (attr) 47 path.setAttributes(attr); 48 49 auto time = entry.time().DosFileTimeToSysTime(UTC()); 50 path.setTimes(time, time); 51 } 52 } 53 54 /// Unpacks a file with 7-Zip to the specified directory, 55 /// installing it locally if necessary. 56 void un7z(string archive, string target) 57 { 58 sevenZip.require(); 59 target.mkdirRecurse(); 60 auto pid = spawnProcess([sevenZip.exe, "x", "-o" ~ target, archive]); 61 enforce(pid.wait() == 0, "Extraction failed"); 62 } 63 64 /// Unpacks an archive to the specified directory. 65 /// Uses std.zip for .zip files, and invokes tar (if available) 66 /// or 7-Zip (installing it locally if necessary) for other file types. 67 /// Always unpacks compressed tar archives in one go. 68 void unpack(string archive, string target) 69 { 70 bool untar(string longExtension, string shortExtension, string tarSwitch, string unpacker) 71 { 72 if ((archive.toLower().endsWith(longExtension) || archive.toLower().endsWith(shortExtension)) && haveExecutable(unpacker)) 73 { 74 target.mkdirRecurse(); 75 auto pid = spawnProcess(["tar", "xf", archive, tarSwitch, "--directory", target]); 76 enforce(pid.wait() == 0, "Extraction failed"); 77 return true; 78 } 79 return false; 80 } 81 82 if (archive.toLower().endsWith(".zip")) 83 archive.unzip(target); 84 else 85 if (haveExecutable("tar") && ( 86 untar(".tar.gz", ".tgz", "--gzip", "gzip") || 87 untar(".tar.bz2", ".tbz", "--bzip2", "bzip2") || 88 untar(".tar.lzma", ".tlz", "--lzma", "lzma") || 89 untar(".tar.xz", ".txz", "--xz", "xz"))) 90 {} 91 else 92 { 93 auto tar = archive.stripExtension; 94 if (tar.extension.toLower == ".tar") 95 { 96 un7z(archive, archive.dirName); 97 enforce(tar.exists, "Expected to unpack " ~ archive ~ " to " ~ tar); 98 scope(exit) tar.remove(); 99 un7z(tar, target); 100 } 101 else 102 un7z(archive, target); 103 } 104 }