#!/bin/bash
# Author: sfs <https://puppyrus.org>
# Author: crims0n <https://minios.dev>

# Check if argument is provided
[ ! "$1" ] && echo "Builds a common dpkg database from all bundles
    Usage:   $0 [bundles mount points location] [optional: changes location]
    Example: $0 /run/initramfs/memory/bundles /run/initramfs/memory/changes" && exit 1

# Ensure the script is run as root
if [ "$(id -u)" -ne 0 ]; then
    echo "This script must be run as root!"
    exit 1
fi

# Logs
mkdir -p "/var/log/minios"
exec 19>"/var/log/minios/minios-update-dpkg.trace.log"
BASH_XTRACEFD=19
set -x

# Variables
BUNDLES="$1"
CHANGES="$2"
BEXT="sb"
DPKG_STATUS_FILE="/var/lib/dpkg/status"

[ -d "$(dirname "${DPKG_STATUS_FILE}")" ] || exit

export HOME="/root"
export LC_ALL="C"

TEMP_STATOVERRIDE=$(mktemp "/run/statoverride.XXXXXX")
TEMP_STATUS=$(mktemp "/run/dpkg_status.XXXXXX")

# Remove temporary files upon exit
trap 'rm -f "${TEMP_STATOVERRIDE}"; rm -f "${TEMP_STATUS}"' EXIT

# Concatenate and sort statoverride
cat $(ls "${BUNDLES}"/*/var/lib/dpkg/statoverride "${CHANGES}/var/lib/dpkg/statoverride" 2>/dev/null) >"${TEMP_STATOVERRIDE}"
cat "${TEMP_STATOVERRIDE}" | sort -u >"/var/lib/dpkg/statoverride"
cat $(ls "${BUNDLES}"/*/var/lib/dpkg/status "${CHANGES}/var/lib/dpkg/status" 2>/dev/null) >"${TEMP_STATUS}"

# Parse dpkg status
perl -00 -ne '
    # Extract package name and version
    ($pkg) = m/^Package:\s*(\S+)/m or die "No package name found";
    ($ver) = m/^Version:\s*(\S+)/m or die "No version found";

    # Determine if new version should be stored
    if (!exists($db{$pkg}) || compare_versions($ver, $db{$pkg}) > 0) {
      $db{$pkg} = $ver;
      $data{$pkg} = $_;
    }

    # Print package data for each package
    END {
      print "$data{$_}\n" for keys %db;
    }

    # Define function for version comparison
    sub compare_versions {
      my ($va, $vb) = @_;
      my @va = $va =~ /(\d+|[^\d]+)/g;
      my @vb = $vb =~ /(\d+|[^\d]+)/g;
      while (@va && @vb) {
          my $a = shift @va;
          my $b = shift @vb;
          my $res = ($a =~ /\d/ && $b =~ /\d/) ? ($a <=> $b) : ($a cmp $b);
          return $res if $res;
      }
      return @va ? 1 : @vb ? -1 : 0;
    }
' "${TEMP_STATUS}" >"${DPKG_STATUS_FILE}"
