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.datetime; 17 import std.exception; 18 import std.file; 19 import std.path; 20 import std.process; 21 import std.string; 22 23 import ae.sys.file; 24 import ae.sys.install.sevenzip; 25 import ae.utils.path : haveExecutable; 26 27 /// Unzips a .zip file to the target directory. 28 void unzip(string zip, string target) 29 { 30 import std.zip; 31 auto archive = new ZipArchive(zip.read); 32 foreach (name, entry; archive.directory) 33 { 34 auto path = buildPath(target, name); 35 ensurePathExists(path); 36 if (name.endsWith(`/`)) 37 { 38 if (!path.exists) 39 path.mkdirRecurse(); 40 } 41 else 42 std.file.write(path, archive.expand(entry)); 43 44 auto attr = entry.fileAttributes; 45 if (attr) 46 path.setAttributes(attr); 47 48 auto time = entry.time().DosFileTimeToSysTime(UTC()); 49 path.setTimes(time, time); 50 } 51 } 52 53 /// Unpacks a file with 7-Zip to the specified directory, 54 /// installing it locally if necessary. 55 void un7z(string archive, string target) 56 { 57 sevenZip.require(); 58 target.mkdirRecurse(); 59 auto pid = spawnProcess([sevenZip.exe, "x", "-o" ~ target, archive]); 60 enforce(pid.wait() == 0, "Extraction failed"); 61 } 62 63 /// Unpacks an archive to the specified directory. 64 /// Uses std.zip for .zip files, and invokes tar (if available) 65 /// or 7-Zip (installing it locally if necessary) for other file types. 66 /// Always unpacks compressed tar archives in one go. 67 void unpack(string archive, string target) 68 { 69 bool untar(string longExtension, string shortExtension, string tarSwitch, string unpacker) 70 { 71 if ((archive.toLower().endsWith(longExtension) || archive.toLower().endsWith(shortExtension)) && haveExecutable(unpacker)) 72 { 73 target.mkdirRecurse(); 74 auto pid = spawnProcess(["tar", "xf", archive, tarSwitch, "--directory", target]); 75 enforce(pid.wait() == 0, "Extraction failed"); 76 return true; 77 } 78 return false; 79 } 80 81 if (archive.toLower().endsWith(".zip")) 82 archive.unzip(target); 83 else 84 if (haveExecutable("tar") && ( 85 untar(".tar.gz", ".tgz", "--gzip", "gzip") || 86 untar(".tar.bz2", ".tbz", "--bzip2", "bzip2") || 87 untar(".tar.lzma", ".tlz", "--lzma", "lzma") || 88 untar(".tar.xz", ".txz", "--xz", "xz"))) 89 {} 90 else 91 { 92 auto tar = archive.stripExtension; 93 if (tar.extension.toLower == ".tar") 94 { 95 un7z(archive, archive.dirName); 96 enforce(tar.exists, "Expected to unpack " ~ archive ~ " to " ~ tar); 97 scope(exit) tar.remove(); 98 un7z(tar, target); 99 } 100 else 101 un7z(archive, target); 102 } 103 }