#!/usr/bin/bash
# bajar-paquetes: baja paquetes del servidor de descargas, comprobando su md5,
# y si se pide los instala. La contraparte de subir-paquetes. Reemplaza a
# download.sh e installsumma.sh, que bajaban sin comprobar el certificado y
# averiguando la IP del servidor con 8.8.8.8 (07/10/2026, ver BIN.md).
#
#   bajar-paquetes [opciones] PROYECTO[/PAQUETE]...
#   bajar-paquetes [opciones] ARCHIVO.summa...
#
# Primero baja PROYECTO.summa (queda en el directorio) y despues, con lo que
# dice ese .summa (PACKAGES y VERSION), los binarios. Con un .summa que ya se
# tiene, baja directamente los binarios que el dice.
#
#   PROYECTO      todos sus paquetes (los dice PROYECTO.summa, arriba en el
#                 servidor): tabular2 baja libtabular2-0, libtabular2-dev y
#                 tabular; summapack baja summapack, libmem-util0 y -dev
#   PROYECTO/PAQUETE   uno solo: tabular2/libtabular2-0
#   --para P      que bajar (obligatorio; bajar-paquetes no averigua el
#                 sistema, pedido del autor 07/10/2026):
#                   deb     los .deb (Ubuntu 22.04 y 24.04, Debian 12 y 13)
#                   fedora  los .rpm compilados en Fedora (la actual)
#                   suse    los .rpm compilados en openSUSE Tumbleweed
#                   arch    los .tgz compilados en Arch
#                   rpm, tgz   los .rpm (Fedora, openSUSE) y .tgz (Arch y
#                           las demas); desde el 08/10/2026 todo se compila
#                           en Ubuntu 22.04 con las bibliotecas propias
#                           (comunes): un solo deb, rpm y tgz para todas
#                   fuentes las fuentes (tar.gz); con --instalar construye
#                           paquetes locales de los proyectos Simusol y los
#                           instala con el gestor (.deb/APT o .rpm/DNF/Zypper); no hace
#                           make install directo ni usa prefijos privados
#                   tar.gz, summa   las fuentes sin compilar, el .summa
#                 Los compilados en una distribucion salen de siru/summapack/distros;
#                 si no hay, se usan los generales. Sin --para, en una
#                 terminal, muestra lo que hay en el servidor y pregunta.
#   --formato F   lo mismo que --para (de antes)
#   --inestable   la ultima subida, o la estable si no hay nada mas nuevo;
#                 en modo fuentes usa el manifiesto de fuentes inestable
#                 de cada proyecto. Por omision, se usa el catalogo estable
#   --version V   una version dada (6.0-38)
#   --solo-dependencias  con --para fuentes --instalar: solo lo que hace
#                 falta para compilar el proyecto, no el proyecto (lo usa el
#                 preparar de las fuentes de cada proyecto; etapa 3)
#   --prefijo P   no se admite con --para fuentes --instalar: un paquete
#                 nativo se instala en /usr; usar make install manual para
#                 un prefijo privado
#   --dir D       donde dejarlos (por omision ~/.cache/summapack)
#   --instalar    instalarlos: apt, dnf/zypper, o instalar-tgz para los .tgz
#   --sin-dependencias  solo lo pedido. Por omision, tambien las
#                 dependencias que son de nuestros proyectos (libslex0,
#                 libmem-util0...) y no estan instaladas, o estan en una
#                 version menor que la pedida; las del sistema (libc6...) las
#                 resuelve apt o dnf desde sus repositorios
#   --prueba      mostrar que bajaria e instalaria, sin hacerlo; en modo
#                 fuentes recorre tambien las dependencias de cada .summa
#
# En el servidor (ver subir-paquetes): PROYECTO.summa arriba (dice los
# paquetes y la version estable), los estables en PROYECTO/, y en
# PROYECTO/otros/ los .md5, lo demas y PAQ_UNSTABLE... mientras haya algo
# inestable. Cada archivo se baja con su .md5 y queda con su nombre de verdad
# (calcula_6.0-38_amd64.deb). Si ya esta en el directorio con el md5
# correcto, no se vuelve a bajar.
#
# El servidor: BAJAR_URL en el entorno o en ~/.config/simusol/subir; por
# omision https://simusol.org/downloads. Admite file:// para probar.
#
# Es un solo archivo, sin mas que bash, curl y md5sum: esta en la raiz del
# servidor de descargas, e installsumma lo baja de alli para instalar
# summapack en una maquina que no lo tiene.
set -euo pipefail

# ~/.config/simusol/subir solo completa lo que no venga en el entorno: el
# entorno manda (07/10/2026: al reves, una prueba con SUBIR_SERVIDOR=local
# termino corriendo contra el servidor real)
for _v in BAJAR_URL BAJAR_DIR; do eval "_entorno_$_v=\${$_v-}"; done
[ -f "$HOME/.config/simusol/subir" ] && . "$HOME/.config/simusol/subir"
for _v in BAJAR_URL BAJAR_DIR; do eval "[ -z \"\$_entorno_$_v\" ] || $_v=\$_entorno_$_v"; done
URL=${BAJAR_URL:-https://simusol.org/downloads}
DIR=${BAJAR_DIR:-$HOME/.cache/summapack}

para=""; formato=""; fuentes=0; solo_deps=0; PREFIX=/usr/local; prefijo_explicito=0; etiqueta=estable; instalar=0; prueba=0; con_dependencias=1; nombres=()
while [ $# -gt 0 ]; do
    case "$1" in
        --para|--formato) para=$2; shift ;;
        --prefijo)   PREFIX=$2; prefijo_explicito=1; shift ;;
        --solo-dependencias) solo_deps=1 ;;
        --inestable) etiqueta=UNSTABLE ;;
        --version)   etiqueta=$2; shift ;;
        --dir)       DIR=$2; shift ;;
        --instalar)  instalar=1 ;;
        --prueba)    prueba=1 ;;
        --sin-dependencias) con_dependencias=0 ;;
        -h|--help)   sed -n '2,67s/^# \{0,1\}//p' "$0"; exit 0 ;;
        -*)          echo "bajar-paquetes: no conozco $1 (--help)" >&2; exit 2 ;;
        *)           nombres+=("$1") ;;
    esac
    shift
done
set -- "${nombres[@]+"${nombres[@]}"}"
[ $# -gt 0 ] || { echo "uso: bajar-paquetes [opciones] PROYECTO[/PAQUETE]... (--help)" >&2; exit 2; }

# que bajar: lo dice --para; de ahi el formato, la marca de la distribucion
# donde se compilo, y el nombre de esa distribucion en el diccionario
marca=''; dist_dic=''
case "$para" in
    deb)          formato=deb; dist_dic=DEBIAN ;;
    rpm|tgz|tar.gz|summa) formato=$para ;;
    fuentes)      formato=tar.gz; fuentes=1 ;;
    # fedora, suse, arch: el nombre de la distribucion en el archivo (pedido
    # del autor, 08/10/2026); fcNN y tw eran las marcas de antes
    fedora|fc[0-9]*) formato=rpm; marca=fedora; dist_dic=FEDORA ;;
    suse|tw)      formato=rpm; marca=suse;  dist_dic=SUSE ;;
    arch)         formato=tgz; marca=arch;  dist_dic=ARCH ;;
    '')           ;;   # se elige mas abajo, mirando lo que hay

    *)            echo "bajar-paquetes: --para desconocido: $para (--help)" >&2; exit 2 ;;
esac

# (sin case dentro de $(...): el bash 5.1 de Ubuntu 22.04 no lo entiende)
if ! arq_deb=$(dpkg --print-architecture 2>/dev/null); then
    case "$(uname -m)" in x86_64) arq_deb=amd64 ;; aarch64) arq_deb=arm64 ;; *) arq_deb=$(uname -m) ;; esac
fi
arq_rpm=$(uname -m)

# los nombres de un paquete con una version (o UNSTABLE): primero el
# compilado en esta distribucion, si hay, y despues el general (Ubuntu)
nombres() {
    local n=$1 e=$2
    case "$formato" in
        deb)    echo "${n}_${e}_${arq_deb}.deb ${n}_${e}_all.deb" ;;
        tgz)    [ -z "$marca" ] || printf '%s ' "${n}_${e}+${marca}_${arq_deb}.tgz" "${n}_${e}+${marca}_all.tgz"
                echo "${n}_${e}_${arq_deb}.tgz ${n}_${e}_all.tgz" ;;
        rpm)    [ -z "$marca" ] || printf '%s ' "${n}-${e}.${marca}.${arq_rpm}.rpm" "${n}-${e}.${marca}.noarch.rpm"
                echo "${n}-${e}.${arq_rpm}.rpm ${n}-${e}.noarch.rpm" ;;
        tar.gz) echo "${n}_${e}.tar.gz" ;;
        summa)  echo "${n}_${e}.summa" ;;
    esac
}

traer() {   # traer ARCHIVO_REMOTO DESTINO: falla en silencio si no esta
    curl -fsSL --retry 2 -o "$2" "$URL/$1" 2>/dev/null
}

mkdir -p "$DIR"

# sin --para: lo que hay en el servidor para el primer proyecto pedido, y
# que elija (pedido del autor, 07/10/2026: "ofrecer lo que hay y elegir")
variantes() {   # variantes PROYECTO: las variantes de sus paquetes estables
    local p=$1 lista
    if [ "${URL#file://}" != "$URL" ]; then
        lista=$(ls -1 "${URL#file://}/$p" 2>/dev/null) || true
    else
        lista=$(curl -fsSL "$URL/$p/" 2>/dev/null | grep -o 'href="[^"/?]*"' | sed 's/^href="//; s/"$//') || true
    fi
    echo "$lista" | while read -r f; do
        case "$f" in
            *.deb)                           echo deb ;;
            *.fedora.*.rpm)                  echo fedora ;;
            *.suse.*.rpm)                    echo suse ;;
            *.rpm)                           echo rpm ;;
            *+arch_*.tgz)                    echo arch ;;
            *.tgz)                           echo tgz ;;
            *.tar.gz)                        echo fuentes ;;
        esac
    done | sort -u | while read -r o; do
        # orden del menu: fuentes primero (sin awk: openSUSE minimo no lo trae)
        case "$o" in fuentes) n=0 ;; deb) n=1 ;; fedora) n=2 ;; suse) n=3 ;; arch) n=4 ;; rpm) n=5 ;; tgz) n=6 ;; *) n=7 ;; esac
        echo "$n $o"
    done | sort -n | cut -d" " -f2
}
explicar() {
    case "$1" in
        deb) echo ".deb: Ubuntu 22.04 y 24.04, Debian 12 y 13" ;;
        fedora) echo ".rpm compilados en Fedora (la actual)" ;;
        suse) echo ".rpm compilados en openSUSE Tumbleweed" ;;
        arch) echo ".tgz compilados en Arch" ;;
        rpm) echo ".rpm: Fedora, openSUSE (compilados en Ubuntu 22.04: andan con glibc 2.35 o posterior)" ;;
        tgz) echo ".tgz: Arch y las demas (compilados en Ubuntu 22.04)" ;;
        fuentes) echo "las fuentes: se compilan aca, con lo nuestro que falte (lo preferido)" ;;
    esac
}
if [ -z "$para" ]; then
    primero=${1%%/*}; primero=$(basename "${primero%.summa}"); primero=${primero%%_*}
    opciones=$(variantes "$primero")
    if [ ! -t 0 ] || [ -z "$opciones" ]; then
        echo "bajar-paquetes: falta --para (deb, fedora, suse, arch, rpm, tgz, tar.gz, summa; --help)" >&2
        [ -z "$opciones" ] || echo "  para $primero hay: $(echo $opciones)" >&2
        exit 2
    fi
    echo "Para $primero hay:"
    i=0; for o in $opciones; do i=$((i + 1)); printf '  %d) %-7s %s\n' "$i" "$o" "$(explicar "$o")"; done
    printf 'Cual? (numero): '
    read -r n
    para=$(echo $opciones | tr ' ' '\n' | sed -n "${n}p")
    [ -n "$para" ] || { echo "bajar-paquetes: no elegi nada" >&2; exit 2; }
    case "$para" in
        deb)    formato=deb; dist_dic=DEBIAN ;;
        rpm|tgz|tar.gz) formato=$para ;;
        fuentes) formato=tar.gz; fuentes=1 ;;
        fedora) formato=rpm; marca=fedora; dist_dic=FEDORA ;;
        suse)   formato=rpm; marca=suse;  dist_dic=SUSE ;;
        arch)   formato=tgz; marca=arch;  dist_dic=ARCH ;;
    esac
fi

# el PROYECTO.summa del servidor, una vez por proyecto: dice los paquetes
# (PACKAGES) y la version estable (VERSION, la agrega subir-paquetes --estable)
declare -A summas=()
summa_de() {   # deja en SUMMA el .summa del proyecto; no se llama en un $(...)
    local p=$1
    if [ -z "${summas[$p]:-}" ]; then
        # primero el .summa: se baja y queda en DIR, y de el sale todo lo demas
        # Para una entrega de fuentes inestable, usar el manifiesto de esa
        # entrega. El catalogo estable sigue gobernando los binarios.
        local manifiesto="$p.summa"
        if [ "$fuentes" = 1 ] && [ "$etiqueta" = UNSTABLE ]; then
            if traer "$p/otros/${p}_UNSTABLE.summa" "$DIR/$p.summa.parcial"; then
                manifiesto="$p/otros/${p}_UNSTABLE.summa"
            fi
        fi
        if [ "$manifiesto" = "$p.summa" ] && ! traer "$manifiesto" "$DIR/$p.summa.parcial"; then
            rm -f "$DIR/$p.summa.parcial"
            echo "bajar-paquetes: no hay $p.summa en $URL (se pide el proyecto: tabular2, no libtabular2-0; para uno solo, PROYECTO/PAQUETE)" >&2
            return 1
        fi
        mv "$DIR/$p.summa.parcial" "$DIR/$p.summa"
        echo "summa: $DIR/$p.summa"
        summas[$p]=$DIR/$p.summa
    fi
    SUMMA=${summas[$p]}
}
campo() { sed -n -e "s/^$2=\"\\(.*\\)\"\$/\\1/p" -e "s/^$2='\\(.*\\)'\$/\\1/p" "$1" | tail -1; }

declare -A dependencia=()   # lo agregado como dependencia de otro
pedidos=()
for a in "$@"; do
    # un .summa que ya se tiene: los binarios que el dice
    if [ -f "$a" ] && [ "${a%.summa}" != "$a" ]; then
        p=$(basename "$a" .summa); p=${p%%_*}
        summas[$p]=$(readlink -f "$a")
        a=$p
    fi
    case "$a" in
        */*) pedidos+=("$a") ;;
        *)   case "$formato" in
                 tar.gz|summa) pedidos+=("$a/$a") ;;
                 *) summa_de "$a" || exit 1
                    for q in $(campo "$SUMMA" PACKAGES); do pedidos+=("$a/$q"); done ;;
             esac ;;
    esac
done

# intentar MD5 ARCHIVO...: baja el .md5 y el archivo (del primer lugar donde
# este) y comprueba la suma; deja en hallado lo bajado
intentar() {
    local m=$1; shift
    local md5tmp suma real u
    if [ $prueba = 1 ]; then echo "  [prueba] probaria $URL/$m y $URL/$1"; return 1; fi
    md5tmp=$(mktemp)
    traer "$m" "$md5tmp" || { rm -f "$md5tmp"; return 1; }
    suma=$(cut -d' ' -f1 < "$md5tmp")
    real=$(sed -E 's/^[^ ]+ +//; s/^\*//' < "$md5tmp")
    [ -n "$real" ] || real=$(basename "$1")
    real=$(basename "$real")
    if [ -f "$DIR/$real" ] && [ "$(md5sum < "$DIR/$real" | cut -d' ' -f1)" = "$suma" ]; then
        echo "ya estaba: $DIR/$real"
    else
        rm -f "$DIR/$real.parcial"
        for u in "$@"; do traer "$u" "$DIR/$real.parcial" && break; done
        [ -s "$DIR/$real.parcial" ] || { rm -f "$md5tmp" "$DIR/$real.parcial"; echo "bajar-paquetes: esta $m pero no se pudo bajar el archivo" >&2; exit 1; }
        if [ "$(md5sum < "$DIR/$real.parcial" | cut -d' ' -f1)" != "$suma" ]; then
            rm -f "$DIR/$real.parcial" "$md5tmp"
            echo "bajar-paquetes: $real bajo con otro md5 que el del servidor: se descarta" >&2
            exit 1
        fi
        mv "$DIR/$real.parcial" "$DIR/$real"
        echo "bajado: $DIR/$real"
    fi
    cp "$md5tmp" "$DIR/$real.md5"
    rm -f "$md5tmp"
    hallado=$DIR/$real
}

# ---- dependencias: las de nuestros proyectos que no esten instaladas (o
# esten en una version menor que la pedida) se bajan tambien; las del sistema
# (libc6, python3...) las resuelve el instalador (apt, dnf) desde sus
# repositorios. Pedido del autor, 07/10/2026.
declare -A proyecto_de=()   # paquete -> proyecto, de los .summa del servidor
mapa_cargado=0
cargar_mapa() {
    [ $mapa_cargado = 0 ] || return 0
    mapa_cargado=1
    local lista f p q
    if [ "${URL#file://}" != "$URL" ]; then
        lista=$(cd "${URL#file://}" 2>/dev/null && ls -1 -- *.summa 2>/dev/null) || true
    else
        lista=$(curl -fsSL "$URL/" 2>/dev/null | grep -o 'href="[^"/?]*\.summa"' | sed 's/^href="//; s/"$//' | sort -u) || true
    fi
    for f in $lista; do
        p=${f%.summa}
        summa_de "$p" > /dev/null 2>&1 || continue
        for q in $(campo "$SUMMA" PACKAGES); do proyecto_de[$q]=$p; done
    done
}

# las dependencias de un paquete bajado, una por renglon (a | b, con version)
dependencias_de() {
    local f=$1 deb t c
    case "$f" in
        *.deb) { dpkg-deb -f "$f" Pre-Depends 2>/dev/null; echo; dpkg-deb -f "$f" Depends 2>/dev/null; } | tr ',' '\n' ;;
        *.rpm)
            # las bibliotecas van como libX.so.N()(64bit): se pasan al nombre
            # del paquete Debian (libX + N, libX-N si X termina en cifra, _
            # por -), que es como se llaman los nuestros en el .summa
            rpm -qpR "$f" 2>/dev/null | grep -v '^rpmlib(\|^/' | while IFS= read -r r; do
                case "$r" in
                    lib*.so.*)
                        b=${r%%.so.*}; n=${r#*.so.}; n=${n%%(*}; n=${n%%.*}; b=$(echo "$b" | tr _ -)
                        case "$b" in *[0-9]) echo "$b-$n" ;; *) echo "$b$n" ;; esac ;;
                    *) echo "$r" ;;
                esac
            done ;;
        *.tgz)
            # el .tgz trae su control en var/lib/summapack/tgz/NOMBRE.control
            # (desde 07/10/2026); si no, del .deb del mismo paquete (al lado
            # en el servidor), si hay con que abrirlo
            c=$(basename "$f"); c=${c%%_*}
            if tar -xzOf "$f" "./var/lib/summapack/tgz/$c.control" > /dev/null 2>&1; then
                tar -xzOf "$f" "./var/lib/summapack/tgz/$c.control" | sed -n 's/^Pre-Depends: //p; s/^Depends: //p; s/^Recommends: //p' | tr ',' '\n'
            else
                deb=$(basename "${f%.tgz}.deb" | sed -E 's/\+[^_]+_/_/'); t=$(mktemp -d)
                if traer "$P_DE_TGZ/$deb" "$t/$deb" || traer "$P_DE_TGZ/otros/$deb" "$t/$deb"; then
                    if command -v dpkg-deb >/dev/null; then
                        { dpkg-deb -f "$t/$deb" Depends 2>/dev/null; echo; dpkg-deb -f "$t/$deb" Recommends 2>/dev/null; } | tr ',' '\n'
                    elif command -v ar >/dev/null; then
                        for c in control.tar.zst control.tar.xz control.tar.gz; do
                            (cd "$t" && ar x "$deb" "$c" 2>/dev/null) && break
                        done
                        tar -xOf "$t"/control.tar.* ./control 2>/dev/null | sed -n 's/^Depends: //p; s/^Recommends: //p' | tr ',' '\n'
                    fi
                fi
                rm -rf "$t"
            fi ;;
    esac | sed 's/^ *//; s/ *$//' | grep -v '^$' || true
}

# la version instalada de un paquete (vacio si no esta)
instalada() {
    local q=$1 v
    if command -v dpkg-query >/dev/null && v=$(dpkg-query -W -f='${Status} ${Version}' "$q" 2>/dev/null) \
       && [ "${v#install ok installed }" != "$v" ]; then echo "${v#install ok installed }"; return; fi
    if command -v rpm >/dev/null && v=$(rpm -q --qf '%{VERSION}-%{RELEASE}' "$q" 2>/dev/null); then echo "$v"; return; fi
    [ -f "/var/lib/summapack/tgz/$q.version" ] && cat "/var/lib/summapack/tgz/$q.version"
    return 0
}
# cumple VERSION OP PEDIDA
cumple() {
    local v=$1 op=$2 w=$3
    [ -n "$op" ] || return 0
    if command -v dpkg >/dev/null; then dpkg --compare-versions "$v" "$op" "$w"; return; fi
    case "$op" in
        '>='|'>>'|'>') [ "$(printf '%s\n%s\n' "$w" "$v" | sort -V | head -1)" = "$w" ] ;;
        '<='|'<<'|'<') [ "$(printf '%s\n%s\n' "$w" "$v" | sort -V | head -1)" = "$v" ] ;;
        *)             [ "$v" = "$w" ] ;;
    esac
}

# --para fuentes --instalar: cada proyecto se baja cuando se compila (mas
# abajo); el .summa de unas fuentes no trae VERSION
pedidos_fuentes=()
if [ $fuentes = 1 ] && [ $instalar = 1 ]; then pedidos_fuentes=("${pedidos[@]}"); pedidos=(); fi

declare -A en_cola=()
declare -A del_sistema=()   # dependencias del sistema (nombres de Debian)
bajados=()
k=0
for pq in "${pedidos[@]}"; do en_cola[${pq#*/}]=1; done
while [ $k -lt ${#pedidos[@]} ]; do
    pq=${pedidos[$k]}; k=$((k + 1))
    p=${pq%%/*}; nombre=${pq#*/}
    # No bajar ni reinstalar un paquete que ya satisface la version pedida.
    # Para --inestable se consulta su manifiesto (pequeno); las dependencias
    # agregadas a la cola siguen usando la version estable.
    if [ "$instalar" = 1 ] && [ "$fuentes" = 0 ]; then
        version_pedida=''
        if [ "$etiqueta" = estable ] || [ -n "${dependencia[$pq]:-}" ]; then
            summa_de "$p" || exit 1
            version_pedida=$(campo "$SUMMA" VERSION)
        elif [ "$etiqueta" = UNSTABLE ]; then
            if traer "$p/otros/${p}_UNSTABLE.summa" "$DIR/$p.UNSTABLE.summa.parcial"; then
                version_pedida=$(campo "$DIR/$p.UNSTABLE.summa.parcial" VERSION)
            fi
            rm -f "$DIR/$p.UNSTABLE.summa.parcial"
            if [ -z "$version_pedida" ]; then
                summa_de "$p" || exit 1
                version_pedida=$(campo "$SUMMA" VERSION)
            fi
        else
            version_pedida=$etiqueta
        fi
        version_instalada=$(instalada "$nombre")
        if [ -n "$version_pedida" ] && [ -n "$version_instalada" ]; then
            if { [ "$etiqueta" != estable ] && [ "$etiqueta" != UNSTABLE ] && [ -z "${dependencia[$pq]:-}" ] && [ "$version_instalada" = "$version_pedida" ]; } ||
               { { [ "$etiqueta" = estable ] || [ "$etiqueta" = UNSTABLE ] || [ -n "${dependencia[$pq]:-}" ]; } && cumple "$version_instalada" '>=' "$version_pedida"; }; then
                echo "  $nombre $version_instalada: ya instalado (se pide $version_pedida); se omite"
                continue
            fi
        fi
    fi
    hallado=''
    # lo inestable: el nombre fijo, mientras haya algo inestable (las
    # dependencias que se agregan van siempre en su version estable)
    if [ "$etiqueta" = UNSTABLE ] && [ -z "${dependencia[$pq]:-}" ]; then
        for c in $(nombres "$nombre" UNSTABLE); do
            intentar "$p/otros/$c.md5" "$p/otros/$c" && break
        done
    fi
    # lo estable (o una version dada): el nombre exacto
    if [ -z "$hallado" ]; then
        if [ "$etiqueta" = estable ] || [ "$etiqueta" = UNSTABLE ] || [ -n "${dependencia[$pq]:-}" ]; then
            summa_de "$p" || exit 1; v=$(campo "$SUMMA" VERSION)
            [ -n "$v" ] || { echo "bajar-paquetes: $p.summa no dice VERSION: nada estable todavia (--inestable)" >&2; exit 1; }
        else
            v=$etiqueta
        fi
        for c in $(nombres "$nombre" "$v"); do
            intentar "$p/otros/$c.md5" "$p/$c" "$p/otros/$c" && break
        done
    fi
    if [ $prueba = 1 ]; then
        bajados+=("$DIR/$(nombres "$nombre" "${v:-$etiqueta}" | cut -d' ' -f1)")
        continue
    fi
    [ -n "$hallado" ] || { echo "bajar-paquetes: no hay $p/$nombre ($formato, $etiqueta) en $URL" >&2; exit 1; }
    bajados+=("$hallado")
    # sus dependencias
    [ $con_dependencias = 1 ] || continue
    case "$formato" in deb|rpm|tgz) ;; *) continue ;; esac
    P_DE_TGZ=$p
    while IFS= read -r d; do
        # a | b: alcanza con una
        resuelta=0; candidata=''
        IFS='|' read -ra alts <<< "$d"
        for alt in "${alts[@]}"; do
            alt=$(echo "$alt" | sed 's/^ *//; s/ *$//')
            q=${alt%% *}; q=${q%%:*}
            op=$(echo "$alt" | sed -nE 's/^[^ ]+ *\(? *(>=|<=|>>|<<|=|>|<) *([^ )]+).*$/\1/p')
            w=$(echo "$alt" | sed -nE 's/^[^ ]+ *\(? *(>=|<=|>>|<<|=|>|<) *([^ )]+).*$/\2/p')
            if [ -n "${en_cola[$q]:-}" ]; then resuelta=1; break; fi
            iv=$(instalada "$q")
            if [ -n "$iv" ] && cumple "$iv" "$op" "$w"; then resuelta=1; break; fi
            cargar_mapa
            if [ -z "$candidata" ] && [ -n "${proyecto_de[$q]:-}" ]; then candidata=$q; fi
        done
        [ $resuelta = 0 ] || continue
        if [ -z "$candidata" ]; then
            # es del sistema: con deb y rpm la resuelve apt o dnf; con tgz se
            # instala despues con el diccionariodepaquetes
            q=${alts[0]}; q=$(echo "$q" | sed 's/^ *//; s/ .*//; s/:.*//')
            del_sistema[$q]=1
            continue
        fi
        dp=${proyecto_de[$candidata]}
        echo "  dependencia de $nombre: $dp/$candidata${iv:+ (instalada $iv, se pide $op $w)}"
        en_cola[$candidata]=1
        dependencia[$dp/$candidata]=1
        pedidos+=("$dp/$candidata")
    done < <(dependencias_de "$hallado")
done

[ $instalar = 1 ] || exit 0
if [ "$fuentes" = 0 ] && [ ${#bajados[@]} -eq 0 ]; then
    echo 'Todos los paquetes pedidos ya estan instalados; nada que hacer.'
    exit 0
fi

# Una instalacion administrada por la distribucion usa /usr. Un prefijo
# privado solo tiene sentido con make install manual, no con apt/dnf.
if [ "$fuentes" = 1 ]; then
    if [ "$prefijo_explicito" = 1 ]; then
        echo "bajar-paquetes: --prefijo no corresponde a paquetes nativos; usar make install para un prefijo privado" >&2
        exit 2
    fi
    [ -r /etc/os-release ] || { echo 'bajar-paquetes: falta /etc/os-release' >&2; exit 2; }
    . /etc/os-release
    case "$ID" in
        debian|ubuntu) fuentes_backend=deb ;;
        fedora) fuentes_backend=fedora ;;
        opensuse*|sles) fuentes_backend=suse ;;
        *) echo "bajar-paquetes: no hay backend de paquetes locales desde fuentes para $ID; no se hara make install directo" >&2; exit 2 ;;
    esac
    PREFIX=/usr
fi

# ---- --para fuentes --instalar: construir paquetes de los proyectos
# Simusol desde sus fuentes e instalarlos mediante el gestor de la distro.
# Nunca se hace make install directo en el sistema destinatario.
declare -A hecho_fuentes=() externas=()
multiarch=$(dpkg-architecture -qDEB_HOST_MULTIARCH 2>/dev/null || true)
export PKG_CONFIG_PATH="${multiarch:+$PREFIX/lib/$multiarch/pkgconfig:}$PREFIX/lib/pkgconfig:$PREFIX/share/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}"
export PATH="$PREFIX/bin:$PATH"
compilando=()
orden() {
    if [ $prueba = 1 ]; then echo "  [prueba] $*"; else echo "+ $*"; "$@"; fi
}
sudo_si_hace_falta() { if [ "$(id -u)" = 0 ]; then orden "$@"; else orden sudo "$@"; fi; }
# Las dependencias ajenas a Simusol pertenecen al gestor de la distribucion.
# Construir desde fuentes se refiere al proyecto pedido y a los otros
# proyectos Simusol, no a reconstruir Perl, Gnuplot o las herramientas base.
declare -A sistema_fuentes_hecho=()
repos_fuentes_actualizados=0
instalar_sistema_fuentes() {
    local q=$1 dist dic nombre linea version_estado estado candidata
    [ -z "${sistema_fuentes_hecho[$q]:-}" ] || return 0
    sistema_fuentes_hecho[$q]=1
    if command -v apt-get >/dev/null; then dist=DEBIAN
    elif command -v dnf >/dev/null; then dist=FEDORA
    elif command -v zypper >/dev/null; then dist=SUSE
    elif command -v pacman >/dev/null; then dist=ARCH
    elif command -v apk >/dev/null; then dist=ALPINE
    else echo "bajar-paquetes: no hay gestor para instalar $q" >&2; exit 1
    fi
    dic=$DIR/diccionariodepaquetes
    [ -f "$dic" ] || traer diccionariodepaquetes "$dic" || true
    nombre=''
    if [ -f "$dic" ]; then
        while IFS=: read -r paquete distro traducido resto; do
            if [ "$paquete" = "$q" ] && [ "$distro" = "$dist" ]; then nombre=$traducido; break; fi
        done < "$dic"
    fi
    nombre=${nombre:-$q}
    [ "$nombre" = - ] && return 0
    if [ "$repos_fuentes_actualizados" = 0 ]; then
        case "$dist" in
            DEBIAN)
                if [ "$prueba" = 1 ]; then echo '  [prueba] apt-get update';
                elif ! sudo_si_hace_falta apt-get update; then
                    echo 'bajar-paquetes: aviso: apt-get update fallo; se continuara con los indices disponibles. APT seguira comprobando firmas al instalar.' >&2
                fi ;;
            FEDORA)
                if [ "$prueba" = 1 ]; then echo '  [prueba] dnf makecache';
                else sudo_si_hace_falta dnf makecache -y; fi ;;
            SUSE)
                if [ "$prueba" = 1 ]; then echo '  [prueba] zypper refresh';
                else sudo_si_hace_falta zypper --non-interactive refresh; fi ;;
            ALPINE)
                if [ "$prueba" = 1 ]; then echo '  [prueba] apk update';
                else sudo_si_hace_falta apk update; fi ;;
            # pacman -Sy aislado deja Arch en estado de actualizacion parcial;
            # no hacer un -Syu global por una dependencia de este proyecto.
        esac
        repos_fuentes_actualizados=1
    fi
    version_estado=''
    case "$dist" in
        DEBIAN)
            estado=$(dpkg-query -W -f='${Status}' "$nombre" 2>/dev/null) || estado=''
            if [ "$estado" = 'install ok installed' ]; then
                version_estado=$(dpkg-query -W -f='${Version}' "$nombre" 2>/dev/null) || version_estado=''
            fi ;;
        FEDORA|SUSE) version_estado=$(rpm -q --qf '%{VERSION}-%{RELEASE}' "$nombre" 2>/dev/null) || version_estado='' ;;
        ARCH) version_estado=$(pacman -Q "$nombre" 2>/dev/null) || version_estado='' ;;
        ALPINE) version_estado=$(apk info -v "$nombre" 2>/dev/null) || version_estado='' ;;
    esac
    if [ "$dist" = DEBIAN ]; then
        candidata=$(LC_ALL=C apt-cache policy "$nombre" | sed -n 's/^  Candidate: //p' | head -1)
        if [ -n "$version_estado" ] && [ -n "$candidata" ] && [ "$candidata" != '(none)' ] &&
           dpkg --compare-versions "$candidata" gt "$version_estado"; then
            echo "  dependencia del sistema $nombre: instalada $version_estado; disponible $candidata; se actualiza"
            if [ "$prueba" = 1 ]; then echo "  [prueba] apt-get install --only-upgrade $nombre"
            else sudo_si_hace_falta apt-get install --yes --only-upgrade "$nombre"; fi
            return 0
        fi
    fi
    if [ -n "$version_estado" ]; then
        echo "  dependencia del sistema $nombre: instalada $version_estado${candidata:+; disponible $candidata}"
        case "$dist" in
            FEDORA)
                if [ "$prueba" = 1 ]; then echo "  [prueba] dnf upgrade $nombre"
                else sudo_si_hace_falta dnf upgrade -y "$nombre"; fi ;;
            SUSE)
                if [ "$prueba" = 1 ]; then echo "  [prueba] zypper update $nombre"
                else sudo_si_hace_falta zypper --non-interactive update "$nombre"; fi ;;
            ALPINE)
                if [ "$prueba" = 1 ]; then echo "  [prueba] apk upgrade $nombre"
                else sudo_si_hace_falta apk upgrade "$nombre"; fi ;;
            *) echo '   ya esta en la version disponible; se omite' ;;
        esac
        return 0
    fi
    echo "  dependencia del sistema $nombre: no instalada"
    if [ "$prueba" = 1 ]; then echo "  [prueba] instalar $nombre"; return 0; fi
    case "$dist" in
        DEBIAN) sudo_si_hace_falta apt-get install --yes "$nombre" ;;
        FEDORA) sudo_si_hace_falta dnf install -y "$nombre" ;;
        SUSE) sudo_si_hace_falta zypper --non-interactive install "$nombre" ;;
        ARCH) sudo_si_hace_falta pacman -S --needed --noconfirm "$nombre" ;;
        ALPINE) sudo_si_hace_falta apk add "$nombre" ;;
    esac
}
# instalar_en_prefijo DIR ORDEN LOG: corre ORDEN en DIR (con sudo si
# PREFIX no es escribible) y deja que el sistema encuentre las bibliotecas
instalar_en_prefijo() {
    local dir=$1 inst=$2 log=$3
    if [ -w "$PREFIX" ] || { mkdir -p "$PREFIX" 2>/dev/null && [ -w "$PREFIX" ]; }; then
        ( cd "$dir" && PREFIX=$PREFIX bash -c "$inst" ) >> "$log" 2>&1
    else
        ( cd "$dir" && sudo env PREFIX="$PREFIX" PATH="$PATH" bash -c "$inst" ) >> "$log" 2>&1
    fi || return 1
    # la carpeta de bibliotecas en ld.so.conf.d si no esta, y ldconfig despues
    # de cada instalacion (si no, las nuevas no aparecen)
    if [ -d "$PREFIX/lib" ] && [ -d /etc/ld.so.conf.d ]; then
        if ! grep -rqsx "$PREFIX/lib" /etc/ld.so.conf /etc/ld.so.conf.d/; then
            if [ "$(id -u)" = 0 ]; then echo "$PREFIX/lib" > /etc/ld.so.conf.d/summapack-fuentes.conf
            elif [ "${PREFIX#/usr}" != "$PREFIX" ] || [ "${PREFIX#/opt}" != "$PREFIX" ]; then
                echo "$PREFIX/lib" | sudo tee /etc/ld.so.conf.d/summapack-fuentes.conf > /dev/null
            fi
        fi
        if [ "$(id -u)" = 0 ]; then ldconfig
        elif [ ! -w "$PREFIX" ]; then sudo ldconfig || true; fi
    fi
}

# ---- las dependencias externas, desde sus fuentes originales (etapa 2):
# el catalogo fuentesexternas (al lado, el de summapack instalado, o el de
# la raiz del servidor)
CATALOGO=''
catalogo() {
    [ -z "$CATALOGO" ] || return 0
    local c
    for c in "$(dirname "$0")/fuentesexternas" "$(dirname "$0")/../share/summapack/data/fuentesexternas" \
             /usr/share/summapack/data/fuentesexternas /usr/local/share/summapack/data/fuentesexternas; do
        [ -f "$c" ] && { CATALOGO=$c; return 0; }
    done
    CATALOGO=$DIR/fuentesexternas
    traer fuentesexternas "$CATALOGO" || CATALOGO=/dev/null
}
cat_campo() {   # cat_campo NOMBRE CAMPO
    # sin awk (openSUSE minimo no lo trae): la seccion [NOMBRE], la primera
    # linea "CAMPO:"
    local l dentro=0
    while IFS= read -r l; do
        case "$l" in
            \[*) [ "$l" = "[$1]" ] && dentro=1 || dentro=0 ;;
            "$2:"*) [ $dentro = 1 ] && { l=${l#"$2:"}; echo "${l#"${l%%[! ]*}"}"; return 0; } ;;
        esac
    done < "$CATALOGO"
    return 0
}
declare -A hecho_externa=() sin_catalogo=() faltan_base=()
compilar_externa() {
    local q=$1 comp dep url sum f t dir cons inst conf
    [ -z "${hecho_externa[$q]:-}" ] || return 0
    hecho_externa[$q]=1
    catalogo
    if ! grep -qxF "[$q]" "$CATALOGO" 2>/dev/null; then sin_catalogo[$q]=1; return 0; fi
    comp=$(cat_campo "$q" comprobar)
    if [ -n "$comp" ] && PREFIX=$PREFIX bash -c "$comp" > /dev/null 2>&1; then
        echo "  $q: ya esta"; return 0
    fi
    if [ "$(cat_campo "$q" base)" = si ]; then faltan_base[$q]=1; return 0; fi
    for dep in $(cat_campo "$q" depende); do compilar_externa "$dep"; done
    url=$(cat_campo "$q" url); sum=$(cat_campo "$q" sha256)
    # el nombre en la copia del servidor (comunes/fuentes/): archivo: o el
    # de la direccion
    arch=$(cat_campo "$q" archivo); arch=${arch:-$(basename "$url")}
    f=$DIR/$arch
    echo "==> $q: $(cat_campo "$q" proyecto) $(cat_campo "$q" version), de sus fuentes ($url)"
    if [ $prueba = 1 ]; then echo "  [prueba] bajar, comprobar sha256, compilar e instalar en $PREFIX"; return 0; fi
    if [ ! -f "$f" ] || [ "$(sha256sum < "$f" | cut -d' ' -f1)" != "$sum" ]; then
        # de su lugar original; si no esta o vino distinta, de nuestra copia
        # (comunes/fuentes/ en el servidor; pedido del autor, 08/10/2026). En
        # los dos casos se comprueba la sha256 del catalogo.
        rm -f "$f.parcial"
        for u in "$url" "comunes/fuentes/$arch"; do
            if [ "$u" = "$url" ]; then curl -fsSL --retry 2 -o "$f.parcial" "$u" || true
            else echo "  (de la copia del servidor: $URL/$u)"; traer "$u" "$f.parcial" || true; fi
            [ -s "$f.parcial" ] && [ "$(sha256sum < "$f.parcial" | cut -d' ' -f1)" = "$sum" ] && break
            [ ! -s "$f.parcial" ] || echo "bajar-paquetes: $u bajo con otra sha256 que la del catalogo: se descarta" >&2
            rm -f "$f.parcial"
        done
        [ -s "$f.parcial" ] || { echo "bajar-paquetes: no se pudo bajar $q ($url ni la copia del servidor)" >&2; exit 1; }
        mv "$f.parcial" "$f"
    fi
    t=$(mktemp -d)
    # con las fechas del tar: con --touch, make cree viejos los archivos
    # generados (aclocal.m4, configure) y quiere rehacerlos con autotools
    tar -xf "$f" -C "$t"
    dir=$(ls -d "$t"/*/ | head -1)
    conf=$(cat_campo "$q" configure); cons=$(cat_campo "$q" construir); inst=$(cat_campo "$q" instalar)
    [ -n "$cons" ] || cons="./configure --prefix=\"\$PREFIX\" $conf && make"
    [ -n "$inst" ] || inst='make install'
    ( cd "$dir" && PREFIX=$PREFIX bash -c "$cons" ) > "$t/compilar.log" 2>&1 || {
        tail -30 "$t/compilar.log"; echo "bajar-paquetes: no compilo $q (todo en $t/compilar.log)" >&2; exit 1; }
    instalar_en_prefijo "$dir" "$inst" "$t/compilar.log" || {
        tail -30 "$t/compilar.log"; echo "bajar-paquetes: no instalo $q (todo en $t/compilar.log)" >&2; exit 1; }
    hash -r
    echo "   $q instalado en $PREFIX"
}

deps_de_summa() {
    campo "$1" BUILDDEPENDS | tr ',' '\n' | sed 's/|.*//; s/([^)]*)//g; s/\[[^]]*\]//g; s/<[^>]*>//g; s/^ *//; s/ *$//' \
        | grep -v '^$' | grep -v '^\$' | grep -v '^debhelper' || true
}
proyecto_fuentes_instalado() {
    # Informar todos los paquetes, incluso si uno falta, y omitir la
    # construccion solo cuando todos satisfacen la version publicada.
    local s=$1 v q instalada estado faltan=0
    v=$(campo "$s" VERSION)
    [ -n "$v" ] || return 1
    for q in $(campo "$s" PACKAGES); do
        if [ "$fuentes_backend" = deb ]; then
            estado=$(dpkg-query -W -f='${Status}' "$q" 2>/dev/null) || estado=''
            if [ "$estado" = 'install ok installed' ]; then
                instalada=$(dpkg-query -W -f='${Version}' "$q" 2>/dev/null) || instalada=''
            else
                instalada=''
            fi
        else
            instalada=$(rpm -q --qf '%{VERSION}-%{RELEASE}' "$q" 2>/dev/null) || instalada=''
        fi
        if [ -z "$instalada" ]; then
            echo "  $q: no instalado; version publicada $v"
            faltan=1
        elif { [ "$fuentes_backend" = deb ] && dpkg --compare-versions "$instalada" ge "$v"; } ||
             { [ "$fuentes_backend" != deb ] && { [ "$instalada" = "$v" ] ||
                 [ "$(printf '%s\n%s\n' "$v" "$instalada" | sort -V | tail -1)" = "$instalada" ]; }; }; then
            echo "  $q: instalada $instalada; version publicada $v; ya esta"
        else
            echo "  $q: instalada $instalada; version publicada $v; hay que actualizar"
            faltan=1
        fi
    done
    [ "$faltan" = 0 ]
}
herramientas_paquete_fuentes() {
    local faltan=0 orden
    for orden in dpkg-buildpackage dpkg-deb dh fakeroot; do
        command -v "$orden" >/dev/null || faltan=1
    done
    if [ "$fuentes_backend" != deb ]; then
        command -v rpmbuild >/dev/null || faltan=1
    fi
    [ "$faltan" = 1 ] || return 0
    case "$fuentes_backend" in
        deb) sudo_si_hace_falta apt-get install --yes dpkg-dev debhelper fakeroot ;;
        fedora) sudo_si_hace_falta dnf install -y dpkg-dev debhelper fakeroot rpm-build ;;
        suse) sudo_si_hace_falta zypper --non-interactive install dpkg debhelper fakeroot rpm-build ;;
    esac
}
instalar_fuentes() {   # instalar_fuentes PROYECTO [solo-dependencias]
    local p=$1 solo=${2:-} s q dp pcs v t dir cons inst conf
    [ -z "${hecho_fuentes[$p]:-}" ] || return 0
    hecho_fuentes[$p]=1
    summa_de "$p" > /dev/null || exit 1
    s=$SUMMA
    for q in $(deps_de_summa "$s"); do
        cargar_mapa
        dp=${proyecto_de[$q]:-}
        if [ -z "$dp" ]; then instalar_sistema_fuentes "$q"; continue; fi
        [ "$dp" != "$p" ] || continue
        summa_de "$dp" > /dev/null || exit 1
        echo "  $p necesita $q: se compila $dp desde sus fuentes"
        instalar_fuentes "$dp"
    done
    [ -z "$solo" ] || { echo "   lo que necesita $p, instalado"; return 0; }
    if proyecto_fuentes_instalado "$s"; then
        echo "  $p: todos sus paquetes ya estan; se omite la construccion"
        return 0
    fi
    v=$(campo "$s" VERSION)
    [ -n "$v" ] || { echo "bajar-paquetes: $p.summa no dice VERSION" >&2; exit 1; }
    if [ "$prueba" = 1 ]; then
        if [ "$fuentes_backend" = deb ]; then
            echo "  [prueba] bajar y comprobar ${p}_$v.tar.gz; construir .deb e instalar $p con apt"
        else
            echo "  [prueba] bajar y comprobar ${p}_$v.tar.gz; construir .rpm e instalar $p con dnf/zypper"
        fi
        return 0
    fi
    hallado=''
    intentar "$p/otros/${p}_$v.tar.gz.md5" "$p/${p}_$v.tar.gz" "$p/otros/${p}_$v.tar.gz" || true
    [ -n "$hallado" ] || { echo "bajar-paquetes: no hay las fuentes de $p ($v) en $URL" >&2; exit 1; }
    t=$(mktemp -d)
    tar --touch -xzf "$hallado" -C "$t"
    dir=$(ls -d "$t"/*/ | head -1)
    # Los tarballs pueden contener un debian/ historico. El .summa de la
    # entrega es la fuente de verdad; regenerar control y rules antes de
    # compilar. SummaPack trae el generador para su propio bootstrap.
    local generador=''
    for paquete in "$dir/share/summapack/data/summa.py" \
                   /usr/share/summapack/data/summa.py \
                   /usr/local/share/summapack/data/summa.py; do
        [ -f "$paquete" ] && { generador=$paquete; break; }
    done
    [ -n "$generador" ] || { echo "bajar-paquetes: falta summa.py para construir $p desde su .summa" >&2; exit 1; }
    python3 "$generador" debian "$dir" > "$t/generar.log" 2>&1 || {
        tail -30 "$t/generar.log"; echo "bajar-paquetes: no se genero debian/ de $p" >&2; exit 1; }
    # Fuera de Debian, dpkg-shlibdeps no conoce los paquetes de las
    # bibliotecas del sistema (por ejemplo, /lib64/libm.so.6 en Fedora).
    # El DEB es solo un intermediario: el RPM definitivo calcula sus propias
    # dependencias ELF mediante AutoReqProv. No alterar el recorrido DEB.
    if [ "$fuentes_backend" != deb ] && ! grep -q '^override_dh_shlibdeps:' "$dir/debian/rules"; then
        printf '\noverride_dh_shlibdeps:\n' >> "$dir/debian/rules"
    fi
    herramientas_paquete_fuentes
    echo "==> construyendo paquetes de $p $v (en $dir)"
    ( cd "$dir" && dpkg-buildpackage -us -uc -b -d ) > "$t/compilar.log" 2>&1 || {
        tail -30 "$t/compilar.log"; echo "bajar-paquetes: no se pudieron construir los paquetes de $p (todo en $t/compilar.log)" >&2; exit 1; }
    local paquetes=() paquete
    for paquete in "$t"/*.deb; do
        [ -f "$paquete" ] || continue
        case "$paquete" in *-dbgsym_*) continue ;; esac
        paquetes+=("$paquete")
    done
    [ ${#paquetes[@]} -gt 0 ] || { echo "bajar-paquetes: $p no produjo ningun .deb (ver $t/compilar.log)" >&2; exit 1; }
    if [ "$fuentes_backend" = deb ]; then
        # Puede haber un paquete ya instalado (por ejemplo siru) que exija
        # una version nueva de este mismo proyecto. Permitir que APT repare
        # esa dependencia con el .deb local, pero nunca que quite paquetes.
        sudo_si_hace_falta apt-get install --yes --no-remove --fix-broken "${paquetes[@]}"
        echo "   $p instalado desde paquetes .deb; se desinstala con apt"
    else
        local biblioteca='' arbol rpm paquetes_rpm=()
        for biblioteca in "$dir/share/summapack/data/hacer-pkg.inc" \
                          /usr/share/summapack/data/hacer-pkg.inc \
                          /usr/local/share/summapack/data/hacer-pkg.inc; do
            [ -f "$biblioteca" ] && break
        done
        [ -f "$biblioteca" ] || { echo "bajar-paquetes: falta hacer-pkg.inc para convertir los DEB de $p a RPM" >&2; exit 1; }
        . "$biblioteca"
        for paquete in "${paquetes[@]}"; do
            arbol=$(mktemp -d "$t/arbol.XXXXXX")
            dpkg-deb -x "$paquete" "$arbol"
            RPM_DIST="$fuentes_backend" RPM_AUTOREQ=1 rpm_de_deb "$paquete" "$arbol" "$t"
        done
        for rpm in "$t"/*.rpm; do [ -f "$rpm" ] && paquetes_rpm+=("$rpm"); done
        [ ${#paquetes_rpm[@]} -gt 0 ] || { echo "bajar-paquetes: $p no produjo RPM (ver $t)" >&2; exit 1; }
        case "$fuentes_backend" in
            fedora) sudo_si_hace_falta dnf install -y "${paquetes_rpm[@]}" ;;
            suse) sudo_si_hace_falta zypper --non-interactive install --allow-unsigned-rpm "${paquetes_rpm[@]}" ;;
        esac
        echo "   $p instalado desde paquetes .rpm; se desinstala con dnf/zypper"
    fi
}
if [ $fuentes = 1 ]; then
    for pq in "${pedidos_fuentes[@]}"; do instalar_fuentes "${pq%%/*}" $( [ $solo_deps = 1 ] && echo solo-dependencias ); done
    if [ ${#faltan_base[@]} -gt 0 ]; then
        echo "faltan herramientas de base (instalarlas con la distribucion): ${!faltan_base[*]}"
    fi
    if [ ${#sin_catalogo[@]} -gt 0 ]; then
        echo "dependencias externas que no estan en fuentesexternas (tienen que estar instaladas): ${!sin_catalogo[*]}"
    fi
    case "$PREFIX" in /usr|/usr/local) ;; *)
        echo "Para usarlo: export PATH=\"$PREFIX/bin:\$PATH\" LD_LIBRARY_PATH=\"$PREFIX/lib\" PKG_CONFIG_PATH=\"$PREFIX/lib/pkgconfig\"" ;;
    esac
    exit 0
fi

# las dependencias del sistema de los .tgz (los .deb y .rpm las resuelven
# apt y dnf): el nombre de cada una en esta distribucion sale del
# diccionariodepaquetes (al lado, el de summapack instalado, o el de la raiz
# del servidor), y se instalan con el administrador de paquetes que haya
instalar_del_sistema() {
    local dist dic q n nombres=() t
    local -a equivalentes
    dist=$dist_dic
    # sin --para de una distribucion (tgz, rpm generales): la columna del
    # diccionario del instalador que hay (no se averigua la distribucion)
    if [ -z "$dist" ]; then
        if command -v pacman >/dev/null; then dist=ARCH
        elif command -v dnf >/dev/null; then dist=FEDORA
        elif command -v zypper >/dev/null; then dist=SUSE
        elif command -v apt-get >/dev/null; then dist=DEBIAN
        fi
    fi
    # primero el del servidor (el mas nuevo: el que trae summapack instalado
    # puede ser de antes que el paquete que se instala); si no, uno local
    t=$(mktemp)
    if traer diccionariodepaquetes "$t"; then dic=$t
    else
        for dic in "$(dirname "$0")/diccionariodepaquetes" /usr/share/summapack/data/diccionariodepaquetes /usr/local/share/summapack/data/diccionariodepaquetes; do
            [ -f "$dic" ] && break
        done
    fi
    for q in "${!del_sistema[@]}"; do
        n=''
        [ -f "$dic" ] && n=$(grep -v '^#' "$dic" | while IFS=: read -r a b c _; do [ "$a" = "$q" ] && [ "$b" = "$dist" ] && { echo "$c"; break; }; done; true)
        [ -n "$n" ] || n=$q
        if [ "$n" != - ]; then
            read -r -a equivalentes <<< "$n"
            nombres+=("${equivalentes[@]}")
        fi
    done
    [ ${#nombres[@]} -gt 0 ] || return 0
    echo "dependencias del sistema ($dist): ${nombres[*]}"
    if command -v pacman >/dev/null; then sudo_si_hace_falta pacman -S --needed --noconfirm "${nombres[@]}"
    elif command -v dnf >/dev/null; then sudo_si_hace_falta dnf install -y "${nombres[@]}"
    elif command -v zypper >/dev/null; then sudo_si_hace_falta zypper --non-interactive install "${nombres[@]}"
    elif command -v apk >/dev/null; then sudo_si_hace_falta apk add "${nombres[@]}"
    elif command -v apt-get >/dev/null; then sudo_si_hace_falta apt-get install --yes "${nombres[@]}"
    else echo "bajar-paquetes: instalar a mano: ${nombres[*]}" >&2
    fi
}

case "$formato" in
    deb)
        rutas=(); for b in "${bajados[@]}"; do rutas+=("$(readlink -f "$b" 2>/dev/null || echo "$b")"); done
        sudo_si_hace_falta apt-get install --yes --no-remove --fix-broken "${rutas[@]}" ;;
    rpm)
        case "$marca" in
            suse)  sudo_si_hace_falta zypper --non-interactive install --allow-unsigned-rpm --recommends "${bajados[@]}" ;;
            fedora) sudo_si_hace_falta dnf install -y "${bajados[@]}" ;;
            *)     if command -v dnf >/dev/null; then sudo_si_hace_falta dnf install -y "${bajados[@]}"
                   elif command -v zypper >/dev/null; then sudo_si_hace_falta zypper --non-interactive install --allow-unsigned-rpm --recommends "${bajados[@]}"
                   else sudo_si_hace_falta rpm -Uvh "${bajados[@]}"; fi ;;
        esac ;;
    tgz)
        # instalar-tgz es de summapack: si no esta (instalando summapack con
        # installsumma), se desempaqueta primero el .tgz de summapack, que lo
        # trae, y con ese se instala todo (summapack tambien, asi queda
        # registrado)
        it=$(command -v instalar-tgz || true)
        if [ -z "$it" ]; then
            for b in "${bajados[@]}"; do
                case "$(basename "$b")" in summapack_*.tgz)
                    sudo_si_hace_falta tar --no-overwrite-dir -xzf "$b" -C /
                    it=/usr/bin/instalar-tgz ;;
                esac
            done
        fi
        [ -n "$it" ] || { echo "bajar-paquetes: hace falta instalar-tgz (de summapack): bajar-paquetes --instalar summapack" >&2; exit 1; }
        sudo_si_hace_falta "$it" "${bajados[@]}"
        [ ${#del_sistema[@]} = 0 ] || instalar_del_sistema ;;
    *)
        echo "bajar-paquetes: un $formato no se instala" >&2; exit 2 ;;
esac
