-- This script will handle Allow-Duplicated packages when more
-- than one is available during an install operation, and will
-- also upgrade these packages in a dist-upgrade operation (if
-- they match a regex in RPM::Allow-Duplicated-Upgrade).
--
-- This script must be plugged in the following slots:
--
--   Scripts::AptGet::Install::SelectPackage
--   Scripts::AptGet::DistUpgrade
--   Scripts::Synaptic::DistUpgrade
--
-- Author: Gustavo Niemeyer <niemeyer@conectiva.com>

rex = require("rex_posix")

function realname(name)
    local s, e, name = string.find(name, "(.+)#")
    return name
end

function get_pkglist(name)
    local j = 1
    local packages = {}
    for i, p in ipairs(pkglist()) do
	if realname(pkgname(p)) == name then
	    packages[j] = p
	    j = j + 1
	end
    end
    return packages
end

-- select best choice of virtual provided packages
function select_goodpackage(packages)
    local goodpkg = packages[1]
    local goodpkgname = realname(pkgname(goodpkg))

    if goodpkgname then
        -- Check if every package has the same real name, and
        -- leave only the one with the greatest version, if
        -- that's the case.
        for i = 2, table.getn(packages) do
            local nextpkg = packages[i]
            local nextpkgname = realname(pkgname(nextpkg))
            if nextpkgname ~= goodpkgname then
		--markkeep(goodpkg)
                goodpkg = nil
                break
            end
            if not pkgvercand(goodpkg)
               or pkgvercand(nextpkg) and
                  verstrcmp(verstr(pkgvercand(goodpkg)),
                    verstr(pkgvercand(nextpkg))) == -1 then
			--markkeep(goodpkg)
			goodpkg = nextpkg
            end
        end
        if goodpkg and pkgvercand(goodpkg) then
	    selected = goodpkg
	    markinstall(goodpkg)
        end
    end
    if not selected then
        -- Strip #... from package names if we can't find a good solution.
        for i, name in ipairs(packagenames) do
            local name = realname(name)
            if name and name ~= virtualname then
                packagenames[i] = name
            end
        end
    end
end

if script_slot == "Scripts::AptGet::Install::PreResolve" then
    for i, pkg in ipairs(pkglist()) do
	s, e, name = string.find(pkgname(pkg), "(.+)#")
	if statinstall(pkg) and s then
	    -- unmark selected packages
	    markkeep(pkg)
	    local packages = {}
	    packages = get_pkglist(name)

	    select_goodpackage(packages)
	end
    end
end

if script_slot == "Scripts::AptGet::Install::SelectPackage" then
    local found = 0
    for i, pkg in ipairs(packages) do
	p = pkgfind(pkgname(pkg))
	if pkgvercur(p) then
	    found = 1
	end
    end
    if found == 0 then
	select_goodpackage(packages)
    end
end

if script_slot == "Scripts::AptGet::DistUpgrade" or
   script_slot == "Scripts::AptGet::Upgrade" or
   script_slot == "Scripts::Synaptic::DistUpgrade" or
   script_slot == "Scripts::Synaptic::Upgrade" then
    -- Automatically install newer versions of all packages which
    -- are registered in the Allow-Duplicated scheme and are matched
    -- by the regular expressions in RPM::Allow-Duplicated-Upgrade.

    -- Compile expressions with package names which should
    -- be considered for upgrade.
    local updatelist = confgetlist("RPM::Allow-Duplicated-Upgrade")
    for i, expr in ipairs(updatelist) do
        updatelist[i] = rex.new(expr)
    end

    if table.getn(updatelist) ~= 0 then

        -- Gather information about Allow-Duplicated packges.
        local canddups = {}
        local curdups = {}
        for i, pkg in pairs(pkglist()) do 
            local name = realname(pkgname(pkg))
            if name then
                if pkgvercur(pkg) then
                    if not curdups[name] then
                        curdups[name] = {}
                    end
                    table.insert(curdups[name],
                             verstr(pkgvercur(pkg)))
                elseif pkgvercand(pkg) then
                    if not canddups[name] then
                        canddups[name] = {}
                    end
                    table.insert(canddups[name],
                             verstr(pkgvercand(pkg)))
                end
            end
        end

        -- Compile expressions with package names which should be hold.
        local holdlist = confgetlist("RPM::Hold")
        for i, expr in ipairs(holdlist) do
            holdlist[i] = rex.new(expr)
        end

        -- Remove packages without any matches in updatelist, or with
        -- any matches in holdlist.
        for name, _ in pairs(curdups) do
            local found = false
            for i, expr in ipairs(updatelist) do
                if expr:match(name) then
                    found = true
                    break
                end
            end
            if found then
                for i, expr in ipairs(holdlist) do
                    if expr:match(name) then
                        found = false
                        break
                    end
                end
            end
            if not found then
                curdups[name] = nil
            end
        end

        -- Mark the newest packages for installation.
        for name, _ in pairs(curdups) do
            if canddups[name] then
                -- Check the best candidate version.
                local bestver = nil
                for i, ver in ipairs(canddups[name]) do
                    if not bestver or
                       verstrcmp(bestver, ver) == -1 then
                        bestver = ver
                    end
                end

                -- Now check if it's newer than all installed
                -- versions.
                for i, ver in ipairs(curdups[name]) do
                    if verstrcmp(ver, bestver) == 1 then
                        bestver = nil
                        break
                    end
                end

                -- Finally, mark it for installation.
                if bestver then
                    markinstall(name.."#"..bestver)
                end
            end
        end
    end
end

-- vim:ts=4:sw=4:et
