untar function
Untar a tar file's contents into the given destinationDir.
If decoder
is provided, it's used to decode the file contents before
processing it (e.g. gzip.decoder
could be used to decode the file).
If decoder
is null
(the default), the tarFile is assumed to be gzipped
in case its name ends with either .tar.gz
or .tgz
, or to be a simple
tar archive otherwise.
Use NoEncoding to ensure the tar is always treated as a plain archive.
If a tar entry's name has any ..
or .
components in its path,
these components are removed.
File permissions are set only on MacOS and Linux. The lastModified
value
is set for all created files.
The destination directory is created if necessary and returned.
Implementation
Future<Directory> untar(String tarFile,
{required String destinationDir,
Converter<List<int>, List<int>>? decoder}) async {
logger.finer(() => 'Untar $tarFile');
var tarStream = File(tarFile).openRead();
if (decoder == null) {
if (tarFile.endsWith('.tar.gz') || tarFile.endsWith('.tgz')) {
tarStream = tarStream.transform(gzip.decoder);
}
} else {
tarStream = tarStream.transform(decoder);
}
final tarReader = TarReader(tarStream);
while (await tarReader.moveNext()) {
final entry = tarReader.current;
final name = entry.name
.split('/')
.where((e) => e != '..' && e != '.')
.where((e) => e.trim().isNotEmpty)
.join(Platform.pathSeparator);
if (entry.type == TypeFlag.dir) {
logger.finer(() => 'Extracting tar entry directory: $name');
await Directory(p.join(destinationDir, name)).create(recursive: true);
} else if (entry.type case TypeFlag.reg || TypeFlag.regA) {
final mode = (0xfff & entry.header.mode).toRadixString(8);
logger.finer(() => 'Extracting tar entry file: $name (mode=$mode)');
final output = File(p.join(destinationDir, name));
await output.parent.create(recursive: true);
final outStream = output.openWrite();
await entry.contents.pipe(outStream);
await output.setLastModified(entry.header.modified);
await output._setPermissions(mode);
} else {
logger.fine(
() => 'Ignoring tar entry with unrecognized typeFlag: ${entry.type}');
}
}
return Directory(destinationDir);
}