untar function

Future<Directory> untar(
  1. String tarFile, {
  2. required String destinationDir,
  3. Converter<List<int>, List<int>>? decoder,
})

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);
}