Skip to main content

UNTAR

Constant UNTAR 

Source
pub const UNTAR: &str = "# The project into a Windows machine: unpacks the tar.gz $archive into $dest (replacing an older\n# copy), leaving out the archive\'s first $strip folders. Plain .NET (GZipStream), so it runs on\n# every Windows Isoloom supports: PowerShell 2.0 and later, and no tar.exe before Windows Server\n# 2019. Symbolic links are left out (counted).\n& {\n  $ErrorActionPreference = \'Stop\'\n  $sep = [IO.Path]::DirectorySeparatorChar\n  $new = \"$dest.new\"\n  if (Test-Path $new) { Remove-Item $new -Recurse -Force }\n  [void][IO.Directory]::CreateDirectory($new)\n  $file = New-Object IO.FileStream($archive, [IO.FileMode]::Open, [IO.FileAccess]::Read)\n  $in = New-Object IO.Compression.GZipStream($file, [IO.Compression.CompressionMode]::Decompress)\n  $utf8 = New-Object Text.UTF8Encoding($false)\n  $header = New-Object byte[] 512\n  $chunk = New-Object byte[] 65536\n  function Fill([byte[]]$b, [int]$n) {\n    $got = 0\n    while ($got -lt $n) {\n      $r = $in.Read($b, $got, $n - $got)\n      if ($r -le 0) { throw \"$archive ends early\" }\n      $got += $r\n    }\n  }\n  function Field([int]$at, [int]$len) {\n    $end = [Array]::IndexOf($header, [byte]0, $at, $len)\n    if ($end -lt 0) { $end = $at + $len }\n    $utf8.GetString($header, $at, $end - $at)\n  }\n  # Copies $size bytes to $out (or skips them, with no $out), then the padding after them.\n  function Body([long]$size, $out) {\n    $left = $size\n    while ($left -gt 0) {\n      $n = [int][Math]::Min([long]$chunk.Length, $left)\n      Fill $chunk $n\n      if ($out) { $out.Write($chunk, 0, $n) }\n      $left -= $n\n    }\n    $pad = [int]((512 - $size % 512) % 512)\n    if ($pad) { Fill $chunk $pad }\n  }\n  $pax = @{}\n  $links = 0\n  $files = 0\n  while ($true) {\n    Fill $header 512\n    if ($header[148] -eq 0) { break } # the end: zero blocks have no checksum\n    $type = [char]$header[156]\n    $sizeText = (Field 124 12).Trim()\n    $size = [long]0\n    if ($sizeText) { $size = [Convert]::ToInt64($sizeText, 8) }\n    if ($pax[\'size\']) { $size = [long]$pax[\'size\'] }\n    if ($type -eq \'x\') {\n      # pax records (\"<length> key=value\\n\"): a long path, a large size, for the next entry.\n      $ms = New-Object IO.MemoryStream\n      Body $size $ms\n      $b = $ms.ToArray()\n      $i = 0\n      while ($i -lt $b.Length) {\n        $sp = [Array]::IndexOf($b, [byte]32, $i)\n        $len = [int]$utf8.GetString($b, $i, $sp - $i)\n        $eq = [Array]::IndexOf($b, [byte]61, $sp)\n        $pax[$utf8.GetString($b, $sp + 1, $eq - $sp - 1)] = $utf8.GetString($b, $eq + 1, $i + $len - $eq - 2)\n        $i += $len\n      }\n      continue\n    }\n    if ($type -eq \'L\' -or $type -eq \'K\') {\n      # GNU tar\'s long name (or link target) for the next entry.\n      $ms = New-Object IO.MemoryStream\n      Body $size $ms\n      if ($type -eq \'L\') { $pax[\'path\'] = $utf8.GetString($ms.ToArray()).TrimEnd([char]0) }\n      continue\n    }\n    $name = Field 0 100\n    $prefix = Field 345 155\n    if ($prefix) { $name = \"$prefix/$name\" }\n    if ($pax[\'path\']) { $name = $pax[\'path\'] }\n    $pax = @{}\n    $parts = @($name.Split(\'/\') | Where-Object { $_ -ne \'\' -and $_ -ne \'.\' })\n    if ($parts.Count -le $strip) { Body $size $null; continue }\n    $path = Join-Path $new ([string]::Join([string]$sep, [string[]]($parts[$strip..($parts.Count - 1)])))\n    if ($type -eq \'5\' -or $type -eq \'0\' -or $type -eq [char]0) {\n      # A name Windows can\'t take (`:`, `*`, too long...) is left out, with a warning.\n      $out = $null\n      try {\n        if ($type -eq \'5\') {\n          [void][IO.Directory]::CreateDirectory($path)\n        } else {\n          [void][IO.Directory]::CreateDirectory([IO.Path]::GetDirectoryName($path))\n          $out = [IO.File]::Create($path)\n        }\n      } catch {\n        Write-Warning \"Not copied: $name ($($_.Exception.Message))\"\n        Body $size $null\n        continue\n      }\n      if ($out) {\n        try { Body $size $out } finally { $out.Close() }\n        $files++\n      } else {\n        Body $size $null\n      }\n    } else {\n      if ($type -eq \'2\') { $links++ }\n      Body $size $null\n    }\n  }\n  $in.Close()\n  if (Test-Path $dest) { Remove-Item $dest -Recurse -Force }\n  Move-Item $new $dest\n  Remove-Item $archive -Force\n  $note = \'\'\n  if ($links) { $note = \" ($links symbolic links left out)\" }\n  \"$files files in $dest$note\"\n}\n";
Expand description

Unpacks the project’s tar.gz on Windows ($archive, $dest, $strip set before it): no tar.exe before Windows Server 2019.