Warning: this is an htmlized version!
The original is across this link,
and the conversion rules are here.
-- -*- coding: raw-text-unix -*-
-- Edrx, 2004sep08
-- Current version: 2004dec22
-- (find-angg "LUA/debs2.lua")
--
-- debs.lua: find the .debs corresponding to installed packages.
--
-- To create a new Debian system from an existing one (say, in another
-- partition) we usually need a repository of .debs; and we usually
-- want the new system to have a subset of the packages that are
-- installed in the current one, with exactly the same versions, and,
-- for dozens of reasons, we want to need to access the net as little
-- as possible. The best solution is then to create a local repository
-- with the right selection of .debs, but how do we do that?
--
-- This library provides some functions to help in that task. It is
-- easy to obtain the names of the packages currently installed, each
-- with its version string:
--
--   # (find-man "1 grep-status")
--   # (find-sh "grep-status -s Package,Version -F Status ' installed'")
--   grep-status --show-field=Package,Version --field=Status ' installed'
--
-- Usually _all_ the original .debs corresponding to those packages
-- are available somewhere in the system:
--
--   * the ones that were downloaded by apt are at
--     (find-fline "/var/cache/apt/archives/")
--
--   * the ones coming from CD installs are inside the CD images;
--     we can either mount the real CD or its .iso image
--
--   * the ones coming from local repositories, are, huh, well, just
--     files inside these local repositories
--
--   * the ones that have been installed with "dpkg -i" from
--     nonstandard .debs, like things that we have compiled locally or
--     that we have downloaded from very nonstandard places, are in
--     directories that we know; we are always very organized when we
--     put our root hats on :)
--
-- So we just need to list these .debs, typically with
--
--   find dir1 dir2 ... dirn -name '*.deb'
--
-- and then cross the output with the output of "grep-status" to
-- obtain the first listed .deb corresponding to each installed
-- package, plus a listing of the installed packages for which no
-- corresponding .deb was found.
--
-- After that we need a few other tricks to convert the output to a
-- series of "cp"s or "ln"s. Check the examples at the end of the
-- file. (But they're not well-documented, sorry).


strfind = string.find
splitdebversion = function (str)
    local _, __, epoch, colon, version
    _, __, epoch, colon, version = strfind(str, "^(.*)(%%3a)(.*)$")
    if _ then return epoch, colon, version end
    _, __, epoch, colon, version = strfind(str, "^(.*)(:)(.*)$")
    if _ then return epoch, colon, version end
    return nil, nil, str
  end
splitdeb = function (str)
    local _, __, dir, name, epoch, colon, version, arch
    _, __, dir, rest = strfind(str, "^(.*/)([^/]*)$")
    if _ then str = rest end
    _, __, name, v, arch = strfind(str, "^([^_]*)_([^_]*)_([^_.]*).deb$")
    if not _ then return end
    epoch, colon, version = splitdebversion(v)
    return {dir=dir, name=name, epoch=epoch, colon=colon,
            version=version, arch=arch}
  end
readinstalleddebs = function (fname)
    for str in io.lines(fname) do
      local _, __, field, contents = strfind(str, "^(.*): (.*)$")
      if field == "Package" then name = contents end
      if field == "Version" then
        epoch, colon, version = splitdebversion(contents)
        installed[name] = {name=name, epoch=epoch, colon=colon,
                           version=version}
      end
    end
  end

readdeblist = function (fname)
    for pathname in io.lines(fname) do
      local struct = splitdeb(pathname)
      if struct and
         installed[struct.name] and
         not installed[struct.name].pathname and
         installed[struct.name].version == struct.version then
        installed[struct.name].pathname = pathname
      end
    end
  end

readall = function (fname_installeddebs, ...)
    installed = {}
    readinstalleddebs(fname_installeddebs)
    for i=1,table.getn(arg) do readdeblist(arg[i]) end
  end
printall = function ()
    for name,struct in installed do
      print(struct.pathname or "# no deb found for " .. name)
    end
  end

if arg and string.gsub(arg[0], ".*/", "") == "debs.lua" then
  readall(unpack(arg))
  printall()
end



--[[
# Examples and tests:
#*
lua50 -e '
  dofile(os.getenv("HOME").."/LUA/debs.lua")
  PP(splitdeb("libstartup-notification0_0.7-1_i386.deb"))
  PP(splitdeb("libstdc++5-3.3-dev_1%3a3.3.4-6sarge1.2_i386.deb"))
  PP(splitdeb("/foo/libstdc++5_1:3.3.4-6sarge1.2_i386.deb"))
'
#*
mkdir       /tmp/cdd/
sudo umount /tmp/cdd/
sudo mount -o loop /big/hdb2/CDs/debian-br-cdd/sarge-i386-1.raw /tmp/cdd
find /tmp/cdd/ -name '*.deb'     | sort | tee /tmp/cdddebs
ls /var/cache/apt/archives/*.deb | tee /tmp/vcdebs
ls /big/hdb2/CDs/cdd-extra/*.deb | tee /tmp/cddextradebs
grep-status --show-field=Package,Version --field=Status ' installed' \
  | tee /tmp/installed-debs
#*
lua50 -e '
  dofile("'$HOME/LUA/debs.lua'")
  installed = {}
  readinstalleddebs("/tmp/installed-debs")
  readdeblist("/tmp/vcdebs")
  readdeblist("/tmp/cddextradebs")
  readdeblist("/tmp/cdddebs")
  for name,struct in installed do
    print("# " .. name, "->", struct.pathname or "")
  end
' | sort | tee /tmp/o | l -S
#*
lua50 -e '
  dofile("'$HOME/LUA/debs.lua'")
  readall("/tmp/installed-debs",
          "/tmp/vcdebs", "/tmp/cddextradebs", "/tmp/cdddebs")
  for name,struct in installed do
    print(struct.pathname or "# no deb found for " .. name)
  end
' | sort | tee /tmp/o | l -S
#*
lua50 ~/LUA/debs.lua \
  /tmp/installed-debs \
  /tmp/vcdebs /tmp/cddextradebs /tmp/cdddebs
#*
--]]