from __future__ import annotations

import os
import tarfile
from pathlib import Path
from shutil import rmtree


# This function is the same as Path.is_relative_to in 3.9, but we can't use that yet
def is_relative_to(child_path: str | os.PathLike[str], root_path: str | os.PathLike[str]) -> bool:
    return Path(root_path).resolve() in Path(child_path).resolve().parents


def untar(archive_path: str, output_path: str, overwrite: bool = True, strip: int = 0) -> None:
    if overwrite and os.path.exists(output_path):
        rmtree(output_path)

    with tarfile.open(archive_path, mode="r") as archive:
        for member in archive.getmembers():
            # Tarfiles allow file names starting with "/", or containing "../" etc
            # See https://github.com/advisories/GHSA-gw9q-c7gh-j9vm
            if not is_relative_to(Path(output_path, member.name), output_path):
                # Normalise the path to just the basename
                strip = -1

            # Change member paths to strip N levels, like untar --strip-components=N
            member.name = member.name.split("/", strip)[-1]

        archive.extractall(output_path)
