#!/bin/bash
# instalar-tgz: instala los .tgz que arma hacer-deb llevando registro de lo que
# puso, para poder sacarlo despues. Es lo que el articulo de 2011 llamaba
# Uinstall, para las maquinas sin dpkg ni rpm (07/10/2026, ver BIN.md).
#
#   instalar-tgz [--raiz R] [--prueba] [--forzar] ARCHIVO.tgz...
#   instalar-tgz [--raiz R] [--prueba] --quitar NOMBRE...
#   instalar-tgz [--raiz R] --lista
#   instalar-tgz [--raiz R] --archivos NOMBRE
#
#   --raiz R   instalar en R en lugar de / (una imagen, una jaula, o para
#              probar sin ser root)
#   --forzar   pisar archivos que son de un paquete de dpkg o rpm
#   --prueba   mostrar que haria, sin hacerlo
#
# El .tgz se llama NOMBRE_VERSION_ARQ.tgz. El registro queda en
# R/var/lib/summapack/tgz/: NOMBRE.lista (los archivos), NOMBRE.version y,
# si piso archivos que ya estaban, una copia en NOMBRE.respaldo/, que se
# devuelve al quitarlo. Instalar una version nueva saca los archivos de la
# anterior que la nueva ya no trae. Si el archivo es de un paquete de dpkg o
# rpm no se pisa, salvo --forzar. Antes de instalar en /, avisa (findbinlib)
# si hay programas del mismo nombre en /usr/local que lo taparian.
set -euo pipefail

raiz=/; prueba=0; forzar=0; modo=instalar; args=()
while [ $# -gt 0 ]; do
    case "$1" in
        --raiz)     raiz=$2; shift ;;
        --prueba)   prueba=1 ;;
        --forzar)   forzar=1 ;;
        --quitar)   modo=quitar ;;
        --lista)    modo=lista ;;
        --archivos) modo=archivos ;;
        -h|--help)  sed -n '2,22s/^# \{0,1\}//p' "$0"; exit 0 ;;
        -*)         echo "instalar-tgz: no conozco $1 (--help)" >&2; exit 2 ;;
        *)          args+=("$1") ;;
    esac
    shift
done
raiz=${raiz%/}
REG="$raiz/var/lib/summapack/tgz"

hacer() { if [ $prueba = 1 ]; then echo "  [prueba] $*"; else "$@"; fi; }

# de que paquete de dpkg o rpm es una ruta (vacio si de ninguno); solo en /
duenio() {
    [ -z "$raiz" ] || return 0
    local r
    if command -v dpkg-query >/dev/null && r=$(dpkg-query -S "$1" 2>/dev/null); then echo "dpkg:${r%%:*}"; return; fi
    if command -v rpm >/dev/null && r=$(rpm -qf "$1" 2>/dev/null); then echo "rpm:$r"; return; fi
}

sacar() {   # sacar NOMBRE ARCHIVO_LISTA: borra lo de la lista y los directorios que queden vacios
    local n=$1 lista=$2 f
    while IFS= read -r f; do
        [ -n "$f" ] || continue
        if [ -L "$raiz/$f" ] || [ -f "$raiz/$f" ]; then hacer rm -f "$raiz/$f"; fi
        if [ -e "$REG/$n.respaldo/$f" ] || [ -L "$REG/$n.respaldo/$f" ]; then
            hacer mkdir -p "$(dirname "$raiz/$f")"
            hacer mv "$REG/$n.respaldo/$f" "$raiz/$f"
            echo "  devuelto el anterior: /$f"
        fi
    done < "$lista"
    # directorios: del mas hondo al menos, solo si quedaron vacios
    sed 's#/[^/]*$##' "$lista" | sort -u | awk '{ d=$0; while (d != "" && d != ".") { print d; sub(/\/[^\/]*$/, "", d); if (index(d, "/") == 0) { print d; break } } }' \
        | sort -ru | while IFS= read -r d; do
            [ -d "$raiz/$d" ] && [ -z "$(ls -A "$raiz/$d")" ] && hacer rmdir "$raiz/$d" || true
        done
}

case $modo in
lista)
    [ -d "$REG" ] || exit 0
    for v in "$REG"/*.version; do [ -f "$v" ] && echo "$(basename "$v" .version) $(cat "$v")"; done
    ;;
archivos)
    [ ${#args[@]} = 1 ] || { echo "uso: instalar-tgz --archivos NOMBRE" >&2; exit 2; }
    [ -f "$REG/${args[0]}.lista" ] || { echo "instalar-tgz: ${args[0]} no esta instalado" >&2; exit 1; }
    sed 's#^#/#' "$REG/${args[0]}.lista"
    ;;
quitar)
    [ ${#args[@]} -gt 0 ] || { echo "uso: instalar-tgz --quitar NOMBRE..." >&2; exit 2; }
    for n in "${args[@]}"; do
        [ -f "$REG/$n.lista" ] || { echo "instalar-tgz: $n no esta instalado" >&2; exit 1; }
        echo "quitando $n $(cat "$REG/$n.version")"
        sacar "$n" "$REG/$n.lista"
        hacer rm -rf "$REG/$n.lista" "$REG/$n.version" "$REG/$n.respaldo"
    done
    ;;
instalar)
    [ ${#args[@]} -gt 0 ] || { echo "uso: instalar-tgz ARCHIVO.tgz... (--help)" >&2; exit 2; }
    [ $prueba = 1 ] || [ -w "${raiz:-/}" ] || { echo "instalar-tgz: no puedo escribir en ${raiz:-/} (sudo, o --raiz)" >&2; exit 1; }
    for tgz in "${args[@]}"; do
        b=$(basename "$tgz" .tgz)
        n=${b%%_*}; resto=${b#*_}; v=${resto%_*}
        [ "$n" != "$b" ] || { echo "instalar-tgz: $tgz no se llama NOMBRE_VERSION_ARQ.tgz" >&2; exit 1; }
        nueva=$(mktemp)
        # los archivos y enlaces (no los directorios), sin el ./ del principio
        tar -tvzf "$tgz" | awk '$1 !~ /^d/' | sed -E 's/^([^ ]+ +){5}//; s/ -> .*$//; s#^\./##' > "$nueva"
        # que no pise lo de dpkg o rpm
        choques=0
        while IFS= read -r f; do
            d=$(duenio "/$f")
            if [ -n "$d" ]; then
                echo "  /$f es de $d" >&2; choques=1
            fi
        done < "$nueva"
        if [ $choques = 1 ] && [ $forzar = 0 ]; then
            rm -f "$nueva"
            echo "instalar-tgz: $n pisaria archivos de otros paquetes: no se instala (--forzar para hacerlo igual)" >&2
            exit 1
        fi
        if [ -z "$raiz" ] && command -v findbinlib >/dev/null; then
            tapan=$(findbinlib "$(grep '^usr/bin/' "$nueva" | sed 's#^usr/bin/##' | tr '\n' ' ')" "" "" "" "$n" 2>/dev/null || true)
            [ -z "$tapan" ] || echo "  aviso: estos lo taparian en el PATH: $tapan"
        fi
        echo "instalando $n $v"
        hacer mkdir -p "$REG"
        # lo que ya estaba y no es de una version anterior de este: respaldo
        while IFS= read -r f; do
            if { [ -e "$raiz/$f" ] || [ -L "$raiz/$f" ]; } && ! grep -qxF -- "$f" "$REG/$n.lista" 2>/dev/null; then
                hacer mkdir -p "$(dirname "$REG/$n.respaldo/$f")"
                hacer cp -a "$raiz/$f" "$REG/$n.respaldo/$f"
            fi
        done < "$nueva"
        hacer tar -xzf "$tgz" -C "${raiz:-/}" --no-overwrite-dir
        # lo de la version anterior que la nueva no trae
        if [ -f "$REG/$n.lista" ]; then
            viejos=$(mktemp)
            grep -vxF -f "$nueva" "$REG/$n.lista" > "$viejos" || true
            [ -s "$viejos" ] && { echo "  saco lo que la version $(cat "$REG/$n.version") traia y esta no:"; sacar "$n" "$viejos"; }
            rm -f "$viejos"
        fi
        if [ $prueba = 1 ]; then
            echo "  [prueba] registro: $REG/$n.lista ($(wc -l < "$nueva") archivos)"
        else
            cp "$nueva" "$REG/$n.lista"; echo "$v" > "$REG/$n.version"
        fi
        rm -f "$nueva"
    done
    if [ -z "$raiz" ] && [ $prueba = 0 ] && [ "$(id -u)" = 0 ]; then ldconfig 2>/dev/null || true; fi
    ;;
esac
