#!/bin/awk -f
# $Id: packages,v 1.63 2006/03/21 22:18:22 lioka Exp $

#------------------------------------------------------------------------------
# packages/
# packages/groups
# packages/groups/<group>
# packages/groups/<group>/<package>
#                         :version      :ro: serial-version-release
#                         :size         :ro: ditto
#                         :summary      :ro: ditto
#                         :description  :ro: ditto
# ...                     :tbd          ...
# packages/pending
# packages/pending/install
# packages/pending/install/<package>    -> ../../groups/*/<package>
# packages/pending/remove
# packages/pending/remove/<package>     -> ../../groups/*/<package>
# packages/pending/upgrade
# packages/pending/upgrade/<package>    -> ../../groups/*/<package>
# packages/pending/keep
# packages/pending/keep/<package>       -> ../../groups/*/<package>
# packages/search
# packages/search/<key>
# packages/search/<key>/<package>       -> ../../groups/*/<package>
# packages/state
# packages/state/<package>
#                         :state        :r : available/be-removed/be-installed/installed
#                                       :w : install/remove
# packages/control
#                         :install      :ro: packages # to install
#                         :remove       :ro: -//-     # to remove
#                         :keep         :ro: -//-     # to keep
#                         :dsize        :ro: total    amount to download
#                         :isize        :ro: total    size to be used
#                         :control      :wo: -//-     abort/commit/continue/update/quit
# ...                     :tbd          ...
# packages/sources        :source<n>    :ro: sources.list entries
# packages/cdrom          :ident        :ro: currently inserted cdrom ident, if any
#------------------------------------------------------------------------------

# {{{ usage

func usage(x)
{
	V["usage/ver"] = "$Revision: 1.63 $"
	match(V["usage/ver"], / [0-9.]+ /)
	print "alterator backend to package installation/removal, v"	\
		substr(V["usage/ver"], RSTART, RLENGTH) "\n\
\n\
Usage: packages [options]\n\
\n\
Valid options are:\n\
  -h    display help screen\n\
  -l    get list of available objects\n\
  -t    check for existance of object\n\
  -r    read object information\n\
  -w    modify object state\n\
"
	exit(x)
}

# }}}
# {{{ name_of_script
func name_of_script(t,a)
{
	getline t <"/proc/self/cmdline"
	split(t,a,"\0")
	for (each in a)
	{
	    if (a[each]=="-f") return a[each+1]
	    if (substr(a[each],1,2)=="-f") return substr(a[each],3)
	}
}

# }}}
# {{{ errors

func onerror()
{
	V["onerror/cmd"] = "apt-pipe errors"

    while ((V["onerror/cmd"] | getline) > 0)
		print > "/dev/stderr"

	if ((close(V["onerror/cmd"])) != 0)
		exit(1)
}

# }}}
# {{{ getoptions

func getoptions()
{
	if (!DEBUG) {
		if(ARGC < 2 || ARGC > 3) usage(1)
	} else {
		if (ARGC > 3) DEBUG_ARG = ARGV[3]
	}

	if (ARGV[1] == "-h") usage(0)
	if (ARGC >= 3 && match(ARGV[1], /^-[tlrwx]$/)) {
		ARGC = 1
		return substr(ARGV[1], ++RSTART, --RLENGTH) "," ARGV[2]
	} else usage(1)
}

# }}}
# {{{ state/<package>

func names()
{
	V["names/cmd"] = "apt-pipe ls"
	while ((V["names/cmd"] | getline) > 0)
		print $1 " r"

	return close(V["names/cmd"])
}

func setstate()
{
	V["state/cmd"] = "apt-pipe " $2 " -y " gensub(/^.+\//, "", "", Path)
	return closecmd(V["state/cmd"])
}

func state()
{
	V["state/rs"] = RS
	V["state/state"] = "unavailable"
	V["state/realname"] = V["state/name"]

	RS = ""
	V["state/cmd"] = "apt-pipe ls " V["state/realname"]
	if ((V["state/cmd"] | getline) > 0 && V["state/realname"] == $1)
		V["state/state"] = "available"

	if (close(V["state/cmd"]) != 0)
		return(1)

	if (V["state/state"] == "unavailable") {
		V["state/cmd"] = "apt-pipe ls " V["state/realname"] "*"
		if ((V["state/cmd"] | getline) > 0 && $1 ~ ("^" V["state/realname"] "#")) {
			V["state/realname"] = $1
			V["state/state"] = "available"
		}
		if (closecmd(V["state/cmd"]) != 0)
			return(1)
	}

	V["state/cmd"] = "apt-pipe ls -i " V["state/realname"]
	if ((V["state/cmd"] | getline) > 0 && V["state/realname"] == $1)
		V["state/state"] = "installed"

	if (close(V["state/cmd"]) != 0)
		return(1)

	V["state/cmd"] = "apt-pipe ls -u " V["state/realname"]
	if ((V["state/cmd"] | getline) > 0 && V["state/realname"] == $1)
		V["state/state"] = "upgradable"

	if (close(V["state/cmd"]) != 0)
		return(1)

	RS = "\n[[:alnum:]]"
	V["state/cmd"] = "apt-pipe status"
	while ((V["state/cmd"] | getline) > 0) {
		if (match($0, /he.+(REMOVED|installed|upgraded)[[:print:]]*[^[:print:]]+(.+)/, _)) {
			(_[2] " ") ~ ("[[:space:]]" V["state/realname"] "[[:space:]]") && \
				V["state/state"] = "be-" tolower(_[1])
		}
	}

	delete _
	RS = V["state/rs"]
	return close(V["state/cmd"])
}

func getstate()
{
	V["state/name"] = gensub(/^.+\//, "", "", Path)
	if (state())
		return 1
	
	print "state:" V["state/state"]
}

# }}}
# {{{ pending

func pending_not_upgraded(name)
{
	for (e in Pending)
		if (e ~ /^upgrade/ && Pending[e] == name)
			return 0
	return 1
}

func pending()
{
	if (DEBUG)
		V["pending/cmd"] = "cat " DEBUG_ARG
	else
		V["pending/cmd"] = "apt-pipe status"

	getpending(V["pending/cmd"])

	while((V["pending/cmd"] | getline) > 0);

	if (close(V["pending/cmd"]))
		return 1

	V["pending/rs"] = RS
	V["pending/cmd"] = "apt-pipe ls -u"

	RS = ""
	if ((V["pending/cmd"] | getline) > 0)
		for (i=1; i <= NF; i++)
			if (pending_not_upgraded($i))
				Pending["keep", j++] = $i


	RS = V["pending/rs"]

	return (close(V["pending/cmd"]))
}

func pending_groups()
{
	if (pending())
		return(1)

	for (e in Pending)
		++group[gensub(/,.+$/, "", "", e)]

	n = asorti(group, sgroup)

	for (i=1; i<=n; i++)
		print sgroup[i] " d"
}

func pending_isgroup()
{
	V["pending/group"] = gensub(/^.+\//, "", "", Path)

	if (pending())
		return(1)

	for (e in Pending)
		if (gensub(/,.+$/, "", "", e) == V["pending/group"]) {
			print V["pending/group"] " d"
			break
		}
}

func pending_group()
{
	V["pending/group"] = gensub(/^.+\//, "", "", Path)

	if (pending())
		return(1)

	for (e in Pending)
		if (gensub(/,.+$/, "", "", e) == V["pending/group"])
			print Pending[e] " r"
}

func pending_ispackage()
{
	V["pending/name"] = gensub(/^.+\//, "", "", Path)
	
	if (pending())
		return(1)

	for (e in Pending)
		if (Pending[e] == V["pending/name"])
			print Pending[e] " r"
}

func pending_package()
{
	return package()
}

# }}}
# {{{ packages/groups

func groups()
{
	V["groups/fs"] = FS
	V["groups/cmd"] = "apt-pipe ls -g"

	FS = "\t"
	while ((V["groups/cmd"] | getline) > 0)
		++group[$1]

	n = asorti(group, sgroup)
	for (i=1; i<=n; i++)
		print gensub(/\//, "|", "g", sgroup[i]) " d"

	# ADMFS
	# gensub(/\//, "|", "g", gensub(/ /, "\\\\ ", "g", sgroup[i])) " d"

	delete group
	delete sgroup
	FS = V["groups/fs"]

	return close(V["groups/cmd"])
}

func isgroup()
{
	V["isgroup/fs"] = FS
	V["isgroup/cmd"] = "apt-pipe ls -g"
	V["isgroup/mygroup"] = gensub(/^.+\//, "", "", Path)

	gsub(/\|/, "/", V["isgroup/mygroup"])

	FS = "\t"
	while ((V["isgroup/cmd"] | getline) > 0)
		if ($1 == V["isgroup/mygroup"])
			V["isgroup/exists"] = 1

	if (V["isgroup/exists"])
		print "groups/" gensub(/\//, "|", "g", V["isgroup/mygroup"]) " d"
	# ADMFS
	# gensub(/\//, "|", "g", gensub(/ /, "\\\\ ", "g", $2)) " d"

	FS = V["groups/fs"]

	return close(V["isgroup/cmd"])
}

func groups_group()
{
	V["groups/group/rs"] = RS
	V["groups/group/cmd"] = "apt-pipe ls -G " \
		gensub(/ /, "\\\\ ", "g", gensub(/\|/, "/", "g", gensub(/^.+\//, "", "", Path)))

	RS = ""
	V["groups/group/cmd"] | getline
	RS = V["groups/group/rs"]

	for (i = 1; i < NF; i += 2)
		print $i ":" $(i+1) " r"

	return close(V["groups/group/cmd"])
}

# }}}
# {{{ packages/groups/.../name

func ispackage()
{
	V["ispackage/rs"] = RS
	V["ispackage/name"] = gensub(/^.+\//, "", "", Path)
	V["ispackage/cmd"] = "apt-pipe ls " V["ispackage/name"]

	RS = ""
	V["ispackage/cmd"] | getline
	RS = V["ispackage/rs"]

	if($1 == V["ispackage/name"])
		print $1 " r"

	return close(V["ispackage/cmd"])
}

func package()
{
	V["package/rs"] = RS
	V["package/name"] = gensub(/^.+\//, "", "", Path)
	V["package/cmd"] = "apt-pipe show " V["package/name"]
	V["package/re"] = "^.+"												\
		"\\<Installed Size:[[:space:]]+([[:digit:]]+).+"				\
		"\\<Version:[[:space:]]+([^[:space:]]+).+"						\
		"\\<Description:[[:space:]]+([[:print:]]+)"						\
		"(.+)$"

	RS = ""
	# FIXME no more than one, most recent, package
	V["package/cmd"] | getline V["package/info"]

	split(gensub(V["package/re"], "\\1\0\\2\0\\3\0\\4", "", V["package/info"]), _, "\0")

	print											\
		"version:" _[2]								\
		"\nsize:" _[1]								\
		"\nsummary:" _[3]							\
		"\ndescription:" gensub(/[[:space:]]+/, " ", "g", _[4])

	# maybe-suck in rest
	while ((V["package/cmd"] | getline) > 0);

	delete _
	RS = V["packages/rs"]
	return close(V["package/cmd"])
}

# }}}
# {{{ control

func getcontrol()
{
	V["control/rs"] = RS

	V["control/cmd"] = "apt-pipe ls"
	while ((V["control/cmd"] | getline) > 0)
		V["control/available"] += NF
	close(V["control/cmd"])
	
	V["control/cmd"] = "apt-pipe ls -i"
	while ((V["control/cmd"] | getline) > 0)
		V["control/installed"] += NF
	close(V["control/cmd"])

	print													\
		"available:" V["control/available"] "\n"			\
		"installed:" V["control/installed"]

	if (DEBUG)
		V["control/cmd"] = "cat " DEBUG_ARG
	else
		V["control/cmd"] = "apt-pipe status"
	
 	V["control/re0"] = "^"							\
 		"([[:digit:]]+)[[:space:]][^[:digit:]]+"	\
 		"([[:digit:]]+)[[:space:]][^[:digit:]]+"	\
 		"([[:digit:]]+)[[:space:]][^[:digit:]]+"	\
 		"([[:digit:]]+)[[:space:]].+$"
	V["control/re1"] = "^Will[[:space:]]need[[:space:]]"	\
		"([[:digit:]]+[^[:space:]]+).+$"
	V["control/re2"] = "^After[[:space:]]unpacking[[:space:]]"	\
		"([^[:space:]]+)[[:space:]](of|disk).+$"

	while ((V["control/cmd"] | getline) > 0) {
		if ($0 ~ V["control/re0"]) {
			print gensub(V["control/re0"],		\
						 "upgrade:\\1\n"		\
						 "install:\\2\n"		\
						 "remove:\\3\n"			\
						 "keep:\\4", "", $0)
		}
		if ($0 ~ V["control/re1"]) {
			print gensub(V["control/re1"],		\
						 "dsize:\\1", "", $0)
		}
		if ($0 ~ V["control/re2"]) {
			print gensub(V["control/re2"],		\
						 "isize:\\1\n"			\
						 "foobaz:\\2", "", $0)
			break
		}
	}
	return closecmd(V["control/cmd"])
}

func control_quit()
{
	V["control/cmd"] = "apt-pipe quit"
	return closecmd(V["control/cmd"])
}

func control_exec()
{
	return system(name_of_script() " -x " $2 "|sh &> /dev/null &")
}

func control_abort()
{
	dbus("progress", "abort")
}

func control_continue()
{
	dbus("progress", "end")
}

func control_update()
{
	if (DEBUG)
		V["control/cmd"] = "cat " DEBUG_ARG
	else
		V["control/cmd"] = "apt-pipe update -q |tee /var/cache/alterator/update.log"

	dbus("progress", "begin")

	if (listsources()) {
		dbus("progress", "abort")
		return 1
	}

	V["sources/cmd"] = "LC_ALL=C cat"
	for (i in Sources)
		V["sources/cmd"] = V["sources/cmd"] " " Sources[i]

	while((V["sources/cmd"] | getline) > 0)
		if (/^rpm / && ! /^rpm[[:blank:]]+cdrom:/)
			V["update/count"] += 2 * (NF - 3)

	close(V["sources/cmd"])

	if (V["update/count"] == 0) {
		dbus("progress", "end")
		return 0
	}

	dbus("progress", "update", V["update/count"])

	while ((V["control/cmd"] | getline) > 0) {
		if (/^(Get|Hit)/) {
			dbus("step", gensub(/^(Get:[0-9]+|Hit) /, "", ""), 1)
			continue
		}

		if (/^Building Dependency Tree/) {
			dbus("progress", "end")
			continue
		}

		if (/^Err/) {
			errstr = gensub(/^Err /, "", "")
			V["control/cmd"] | getline
			if (! /^[[:blank:]]+Please use apt-cdrom/)
				dbus("error", "update", errstr)
			continue
		}
	}

	return (errstr != "")
}

func control_commit()
{
	if (DEBUG)
		V["control/cmd"] = "cat " DEBUG_ARG
	else
		V["control/cmd"] = "apt-pipe commit -qy|tee /var/cache/alterator/packages.log"

	V["control/install"] = 0

	dbus("progress", "begin")

	if (getpending(V["control/cmd"]))
		return progress_error("assert", "pending")

	for (e in Pending)
		e ~ /install|upgrade/ && V["control/install"]++

	if ((V["control/cmd"] | getline) && /^Need to get/) {
		if (match($4, /^([0-9]+)([^\/]+)(\/|$)/, _) && _[1] != "0") {
			V["control/download"] = 1
			V["control/units"] = _[2]
			dbus("progress", "download", _[1] * 2)
			delete _
		}
	} else
		return progress_error("assert", "get")

	if (! ((V["control/cmd"] | getline) > 0 && /^After unpacking/))
		return progress_error("assert", "unpacking")

	if (V["control/download"] && progress_download())
		return progress_error("assert", "download")

	V["control/cmd"] | getline

	if (/^There are broken packages/ ||
		/^You can try to fix/)
		return progress_error("state", "unmet")

	else if (! /^Committing changes/)
		return progress_error("assert", "commit")

	V["control/cmd"] | getline
	if (! /^Preparing/)
		return progress_error("assert", "preparing")

	if (V["control/install"] && progress_install())
		return progress_error("assert", "install")

	dbus("progress", "end")
	
	# suck in the rest, if any
	while ((V["control/cmd"] | getline) > 0);

	return close(V["control/cmd"])
}

func progress_download()
{
	while ((V["control/cmd"] | getline) > 0) {
		if (/^Fetched/)
			return 0
		if (/^There are broken/)
			return 0
		if ($1 ~ /Get:[0-9]+/) {
			match($(NF), /^\[([0-9]+)([0-9\.]*)(.+)\]$/, _) &&
				dbus("step", $(NF-2), _[3] == V["control/units"] ? _[1] : "1")
		} else if ($1 ~ /Err/) {
			V["control/cmd"] | getline
		} else if ($1 ~ /Failed/) {
			dbus("error", "download", $4)
		}
	}

	return 1
}

func progress_install()
{	
	dbus("progress", "install", V["control/install"])

	for (e in Pending)
		if (e ~ /^remove/)
			delete Pending[e]

	while ((V["control/cmd"] | getline) > 0) {
		if (/^Done\.$/)
			return 0
		if (/^Reading Package Lists/) {
			dbus("error", "state", "unfinished")
			return 0
		}

		n = findpending(gensub(/^([^[:blank:]#]+).+$/, "\\1", ""))
		if (n != "") {
			sub(/^[^[:blank:]#]+[[:blank:]#]*/, "")
			dbus("step", n, 1)
			if ($0 != "")
				dbus("error", "install", $0)
		}
	}

	return 1
}

func progress_error(errorcode, errorinfo)
{
	while ((V["control/cmd"] | getline) > 0);
	dbus("error", errorcode, errorinfo)
	dbus("progress", "end")
	return close(V["control/cmd"])
}

# }}}
# {{{ sources

func listvendors()
{
	if (aptconfig()) return 1

	V["vendors/list"] = V["aptconfig/root"] V["aptconfig/etc"] V["aptconfig/vendorlist"]
	V["vendors/parts"] = V["aptconfig/root"] V["aptconfig/etc"] V["aptconfig/vendorparts"]
	V["listvendors/cmd"] = "LC_ALL=C ls -1 " V["vendors/parts"] "/*.list 2> /dev/null"

	i = 0
	Vendors[i] = V["vendors/list"]
	while ((V["listvendors/cmd"] | getline) > 0)
		Vendors[++i] = $0

#	for (i in Vendors) print "#" Vendors[i]
	
	return close(V["listvendors/cmd"])
}

func getvendors()
{	
	V["vendors/cmd"] = "cat"
	for (i in Vendors)
		V["vendors/cmd"] = V["vendors/cmd"] " " Vendors[i]

	V["vendors/rs"] = RS

	RS = "simple-key"
	while ((V["vendors/cmd"] | getline) > 0) {
		if ($1 == "") continue
		if (/Name/ && ! /Group/)
			print gensub(/"/, "", "g", $1) ":" gensub(/.+Name[[:blank:]]+"([^"]+)".+$/, "\\1", "", $0)
	}

	RS = V["vendors/rs"]
	return close(V["vendors/cmd"])
}

func listsources()
{
	if (aptconfig()) return 1

	V["sources/list"] = V["aptconfig/root"] V["aptconfig/etc"] V["aptconfig/sourceslist"]
	V["sources/parts"] = V["aptconfig/root"] V["aptconfig/etc"] V["aptconfig/sourcesparts"]
	V["listsources/cmd"] = "LC_ALL=C ls -1 " V["sources/parts"] "/*.list 2> /dev/null"

	i = 0
	Sources[i] = V["sources/list"]
	while ((V["listsources/cmd"] | getline) > 0)
		Sources[++i] = $0

#	for (i in Sources) print "#" Sources[i]
	
	close(V["listsources/cmd"])
	return 0
}

func getsources()
{
	V["sources/cmd"] = "cat"
	for (i in Sources)
		V["sources/cmd"] = V["sources/cmd"] " " Sources[i]

	V["sources/mode"] = gensub(/^.+\//, "", "", Path)
	V["sources/re"] = "[[:blank:]]*(rpm|rpm-src)[[:blank:]]+(\\[[^]]+\\])*[[:blank:]]*(.+[[:alnum:]])[[:blank:]]*$"
	if (V["sources/mode"] == "active")
		V["sources/re"] = "^" V["sources/re"]
	else
		V["sources/re"] = "^#+" V["sources/re"]

	while ((V["sources/cmd"] | getline) > 0) {
		if ($0 ~ V["sources/re"]) {
			split(gensub(V["sources/re"], "\\1\0\\2\0\\3", ""), _, "\0")
			print gensub(/:/, "#", "g", _[3]) ":" _[1] "|" gensub(/[][]/, "", "g", _[2])
			delete _
		}
	}

	return (close(V["sources/cmd"]))
}

func changesources()
{
	# $2: (rpm|rpm-src)|key|uri
	split($2, _, "|")
	V["sources/mode"] = gensub(/^.+\//, "", "", Path)
	if (V["sources/mode"] == "active") {
		V["sources/line"] = _[1] " "
		if (_[2] != "")
			V["sources/line"] = V["sources/line"] "[" _[2] "] "
		V["sources/line"] = V["sources/line"] gensub(/#/, ":", "g", _[3])
	} else {
		V["sources/line"] = "^[[:blank:]]*" _[1] "[[:blank:]]+"
		if (_[2] != "")
			V["sources/line"] = V["sources/line"] "\\[" _[2] "\\][[:blank:]]+"
		_[3] = gensub(/([][()])/, "\\\\\\1", "g", _[3])
		V["sources/line"] = V["sources/line"] gensub(/#/, ":", "g", _[3])
	}
	delete _
	V["continue"] = 1
}

func addsource()
{
	n = 0
	V["sources/local"] = V["sources/parts"] "/local.list"
	while ((getline < V["sources/local"]) > 0)
		if ($0 !~ /\# Modified by alterator-packages/)
			_[n++] = $0
	
	print "# Modified by alterator-packages " strftime("%T %F") > V["sources/local"]
	for (i=0; i < n; i++) print _[i] >> V["sources/local"]
	print V["sources/line"] >> V["sources/local"]
}

func rmsource()
{
	# print "removing '" V["sources/line"] "'" >> "/dev/stderr"
	for (i in Sources) {
		n = 0
		V["sources/match"] = 0
		# print "checking " Sources[i] "..." >> "/dev/stderr"
		while ((getline < Sources[i]) > 0) {
			if ($0 ~ V["sources/line"]) {
				V["sources/match"] = 1
			} else if ($0 !~ /\# Modified by alterator-packages/) {
				_[n++] = $0
			}
		}
		if (V["sources/match"]) {
			# print "matched in " Sources[i] >> "/dev/stderr"
			print "# Modified by alterator-packages " strftime("%T %F") > Sources[i]
			for (j=0; j<n; j++) print _[j] >> Sources[i]
		}
		delete _
	}
}

# }}}
# {{{ cdrom

func addcdrom()
{
	if (DEBUG)
		V["addcdrom/cmd"] = "cat " DEBUG_ARG
	else
		V["addcdrom/cmd"] = "LC_ALL=C apt-cdrom -m add 2>&1"

	while ((V["addcdrom/cmd"] | getline) > 0) {
		if (/^rpm(-src)*[[:blank:]]+cdrom:/)
			# print gensub(/^(rpm)(-src)*.+cdrom:(.+)$/, "cdrom#\\3:\\1\\2|", "", $0)
			print "type:" gensub(/^(rpm)(-src)*(.+)$/, "\\1\\2", "", $0)
		if (/E: /)
			print "error:" gensub(/^E: /, "", "", $0)
	}

	close(V["addcdrom/cmd"])
	return 0
}

func getcdrom()
{
	V["getcdrom/cmd"] = "LC_ALL=C apt-cdrom -m ident 2>&1"

	while((V["getcdrom/cmd"] | getline) > 0) {
		# Using CD-ROM mount point /media/cdrom/
		# Mounting CD-ROM
		# Identifying.. [3d711f892b8061b68e7cb8ab466289c2-2]
		# Stored Label: 'ALT Linux 2.9.9.7 Installer (caprifoil)'
		if (/^Identifying/)
			print "identity:" gensub(/^.+\[([^]]+).+$/, "\\1", "")
		if (/^Stored Label/)
			print "label:" gensub(/^.+'([^']*)'.*$/, "\\1", "")
		if (/^E: /)
			print "error:" gensub(/^E: /, "", "", $0)
	}

	close(V["getcdrom/cmd"])
	return 0
}

func getcdinfo()
{
	if (aptconfig()) return 1

	V["cdinfo/path"] =  V["aptconfig/mountpoint"] "/.disk/info"
	V["cdinfo/lscmd"] = "LC_ALL=C ls " V["aptconfig/mountpoint"] "/*/base/release 2>/dev/null"
	V["cdinfo/catcmd"] = "LC_ALL=C cat " V["aptconfig/mountpoint"] "/*/base/release 2>/dev/null"

	getline V["cdinfo/label"] < V["cdinfo/path"]
	
	V["cdinfo/lscmd"] | getline
	V["cdinfo/origin"] = gensub("^" V["aptconfig/mountpoint"] "/+([^/]+)/base/release", "\\1", "", $0)
	close (V["cdinfo/lscmd"])
	
	while ((V["cdinfo/catcmd"] | getline) > 0)
		if (/^Components:/)
			V["cdinfo/components"] = gensub(/^Components: /, "", "", $0)
	close(V["cdinfo/catcmd"])
		  
	if (V["cdinfo/label"] != "" &&
		V["cdinfo/origin"] != "" &&
		V["cdinfo/components"] != "")
		print "uri:cdrom#[" V["cdinfo/label"] "]/ " V["cdinfo/origin"] " " V["cdinfo/components"]
	else
		print "uri:"

	return 0
}

func getmountpoint()
{
	if (aptconfig()) return 1
	print "mountpoint:" V["aptconfig/mountpoint"]
	return 0
}

func setmountpoint()
{
	RS = V["RS"]
	FS = V["FS"]

	V["setmountpoint/re"] = "^[[:blank:]]*Acquire::CDROM::mount[[:blank:]]+"
	V["setmountpoint/line"] = "Acquire::CDROM::mount \"" $2 "\"; // Added by alterator-packages"
	V["setmountpoint/config"] = V["aptconfig/root"] V["aptconfig/etc"] V["aptconfig/main"]

	n = 0
	matched = 0
	while ((getline < V["setmountpoint/config"]) > 0)
		if ($0 ~ V["setmountpoint/re"])
			matched = 1
		else
			_[n++] = $0

	if (matched) {
		print _[0] > V["setmountpoint/config"]
		for (i=1; i<n; i++) print _[i] >> V["setmountpoint/config"]
	}
	
	print V["setmountpoint/line"] >> V["setmountpoint/config"]
	delete _

	return 0
}

# }}}
# {{{ search

func search()
{
	V["search/cmd"] = "apt-pipe search " gensub(/^.+\//, "", "", Path)

	while ((V["search/cmd"] | getline) > 0)
		Found[$1] = 1

	if (close(V["search/cmd"]) != 0)
		return(1)

	V["search/fs"] = FS
	V["search/cmd"] = "apt-pipe ls -g"
	FS = "\t"
	while ((V["search/cmd"] | getline) > 0)
		if (Found[$2]) ++FoundGroup[$1]
	if (close(V["search/cmd"]) != 0)
		return(1)

	for (i in FoundGroup) {
		V["search/cmd"] = "apt-pipe ls -G " i
		while ((V["search/cmd"] | getline) > 0)
			if (Found[$1]) print $1 ":" $2 " r"
		close(V["search/cmd"])
	}

	delete Found
	delete FoundGroup
	FS = V["search/fs"]

	return(0)
}

func search_package()
{
	return package()
}

# }}}
# {{{ misc

func aptconfig()
{
	V["aptconfig/cmd"] = "apt-config dump"

	while ((V["aptconfig/cmd"] | getline) > 0) {
		if (/^Dir /)
			V["aptconfig/root"] = gensub(/^.+"([^"]+)".+$/, "\\1", "")
		if (/^Dir::Etc /)
			V["aptconfig/etc"] = gensub(/^.+"([^"]+)".+$/, "\\1", "")
		if (/^Dir::Etc::main/)
			V["aptconfig/main"] = gensub(/^.+"([^"]+)".+$/, "\\1", "")
		if (/^Dir::Etc::parts/)
			V["aptconfig/parts"] = gensub(/^.+"([^"]+)".+$/, "\\1", "")
		if (/^Dir::Etc::sourcelist /)
			V["aptconfig/sourceslist"] = gensub(/^.+"([^"]+)".+$/, "\\1", "")
		if (/^Dir::Etc::sourceparts /)
			V["aptconfig/sourcesparts"] = gensub(/^.+"([^"]+)".+$/, "\\1", "")
		if (/^Dir::Etc::vendorlist /)
			V["aptconfig/vendorlist"] = gensub(/^.+"([^"]+)".+$/, "\\1", "")
		if (/^Dir::Etc::vendorparts /)
			V["aptconfig/vendorparts"] = gensub(/^.+"([^"]+)".+$/, "\\1", "")
		if (/^Acquire::CDROM::mount/)
			V["aptconfig/mountpoint"] = gensub(/^.+"([^"]+)".+$/, "\\1", "")
	}

	return close(V["aptconfig/cmd"])
}

func getpending(cmd)
{
	V["getpending/rs"] = RS
	V["getpending/fs"] = FS

 	RS = "[[:digit:]]+[[:blank:]]upgraded,[[:blank:]][^[:cntrl:]]+upgraded\\.[[:cntrl:]]"
	FS = "(WARNING: )*The[[:print:]]+be[[:space:]]"

	i = (cmd | getline)
	
	RS = V["getpending/rs"]
	FS = V["getpending/fs"]

	if (i < 0)
		return 1

	delete Pending

	for (i=2; i<=NF; i++) {
		sub(/^upgraded[[:space:]]+/, "upgrade ", $i)
		sub(/^REMOVED:[[:space:]]+/, "remove ", $i)
		sub(/^installed:[[:space:]]+/, "install ", $i)
		sub(/^removed.+are doing!/, "essential ", $i)
		gsub(/\([^)]+\)?/, "", $i)
		split($i, _)
		m = _[1]
		delete _[1]
		j = 0
		for (e in _)
			Pending[m,j++] = _[e]
	}

	delete _

#	for (e in Pending)
#		print e ":" Pending[e]
}

func findpending(name)
{
	for (e in Pending)
		if (name == Pending[e]) {
			delete Pending[e]
			return name
		}
	for (e in Pending)
		if (index(Pending[e], name) == 1) {
			name = Pending[e]
			delete Pending[e]
			return name
		}

	return ""
}

func closecmd(cmd)
{
	while((cmd | getline) > 0);
	return close(cmd)
}

func dbus(address, command, arg) {
	if (! DEBUG)
		printf "alterator-mailbox-send '\"%s\" \"%s\" \"%s\"'\n", address, command, arg
	else
		printf "%-8s %-32s %-4s\n", address, command, arg

	fflush()
}

# }}}
# {{{ BEGIN

BEGIN											\
{
	V["rc"] = 0
	V["continue"] = 0
	SUBSEP = ","

	switch (Path = getoptions()) {
		case /^l,\/$/ : {
			print "groups d\nsearch d\nstate d\npending d\nsources d\ncdrom d\ncontrol r"
			break
		}

		case /^t,(groups|search|state|pending|sources|cdrom)$/ : {
			print gensub(/^.+,/, "", "", Path) " d"
			break
		}
		case /^t,control$/ : {
			print gensub(/^.+,/, "", "", Path) " r"
			break
		}
#-- groups
		case /^l,groups$/ : {
			V["rc"] = groups()
			break
		}
		case /^l,groups\/[^\/]+$/ : {
			V["rc"] = groups_group()
			break
		}
		case /^t,groups\/[^\/]+$/ : {
			V["rc"] = isgroup()
			break
		}
		case /^r,groups\/[^\/]+\/[^\/]+$/ : {
			V["rc"] = package()
			break
		}
#-- search
		case /^l,search$/ : {
			break
		}
		case /^l,search\/[^\/]+$/ : {
			V["rc"] = search()
			break
		}
		case /^t,search\/[^\/]+$/ : {
			print gensub(/^t,/, "", "", Path) " d"
			break
		}
		case /^r,search\/[^\/]+\/[^\/]+$/ : {
			V["rc"] = search_package()
			break
		}
#-- state
		case /^l,state$/ : {
			V["rc"] = names()
			break
		}
		case /^r,state\/[^\/]+$/ : {
			V["rc"] = getstate()
			break
		}
		case /^w,state\/[^\/]+$/ : {
			V["continue"] = 1
			break
		}
		case /^t,(state|groups\/[^\/]+)\/[^\/]+$/ : {
			V["rc"] = ispackage()
			break
		}
#-- pending
		case /^l,pending$/ : {
			V["rc"] = pending_groups()
			break
		}
		case /^t,pending\/(install|remove|upgrade|keep|essential)$/ : {
			V["rc"] = pending_isgroup()
			break
		}
		case /^l,pending\/(install|remove|upgrade|keep|essential)$/ : {
			V["rc"] = pending_group()
			break
		}
		case /^t,pending\/(install|remove|upgrade|keep|essential)\/[^\/]+$/ : {
			V["rc"] = pending_ispackage()
			break
		}
		case /^r,pending\/(install|remove|upgrade|keep|essential)\/[^\/]+$/ : {
			V["rc"] = pending_package()
			break
		}
#-- control
		case /^r,control$/ : {
			V["rc"] = getcontrol()
			break
		}
		case /^w,control$/ : {
			V["continue"] = 1
			break
		}
		case /^x,abort$/ : {
			V["rc"] = control_abort()
			break
		}
		case /^x,continue$/ : {
			V["rc"] = control_continue()
			break
		}
		case /^x,commit$/ : {
			V["rc"] = control_commit()
			break
		}
		case /^x,update$/ : {
			V["rc"] = control_update()
			break
		}
#-- sources
		case /^l,sources$/ : {
			print "active r\npassive r\nvendors r"
			break
		}
		case /^t,sources\/(active|passive|vendors)$/ : {
			print gensub(/^.+\//, "", "", Path) " r"
			break
		}
		case /^r,sources\/vendors$/ : {
			listvendors() || getvendors()
			break
		}
		case /^r,sources\/(active|passive)$/ : {
			listsources() || getsources()
			break
		}
		case /^w,sources\/(active|passive)$/ : {
			V["continue"] = ! listsources()
			break
		}
#-- cdrom
		case /^l,cdrom$/ : {
			print "add r\nident r\ninfo r\nmountpoint r"
			break
		}
		case /^t,cdrom\/(add|ident|info|mountpoint)$/ : {
			print gensub(/^.+\//, "", "", Path) " r"
			break
		}
		case /^r,cdrom\/add$/ : {
			V["rc"] = addcdrom()
			break
		}
		case /^r,cdrom\/ident$/ : {
			V["rc"] = getcdrom()
			break
		}
		case /^r,cdrom\/info$/ : {
			V["rc"] = getcdinfo()
			break
		}
		case /^r,cdrom\/mountpoint$/ : {
			V["rc"] = getmountpoint()
			break
		}
		case /^w,cdrom\/mountpoint$/ :{
			V["continue"] = !aptconfig()
			break
		}
#-- rest
		default : {
			print Path >> "/tmp/packages.err"
			exit(1)
		}
	}

	V["rc"] && onerror()

	if (V["continue"] == 0) exit(0)

	V["RS"] = RS
	V["FS"] = FS
	RS = ""
	FS = ":|\n"
	V["continue"] = 0
}

# }}}
# {{{ BODY

/^state:(install|remove)/ {
	setstate() && onerror()
	exit(0)
}

/^control:(abort|continue|commit|update)/ {
	control_exec() && onerror()
	exit(0)
}

/^control:quit/ {
	control_quit() && onerror()
	exit(0)
}

/^source:/ {
	changesources() && onerror()
}

/^mountpoint:/ {
	setmountpoint() && onerror()
}

# }}}
# {{{ END

END {

	if (V["continue"] == 0) exit(0)
	RS = V["RS"]
	FS = V["FS"]
	
	switch (Path) {
		case /^w,sources\/active$/ : {
			V["rc"] = addsource()
			break
		}
		case /^w,sources\/passive$/ : {
			V["rc"] = rmsource()
			break
		}
		default : {
			print Path >> "/tmp/packages.err"
			exit(1)
		}
	}
}

# }}}

#------------------------------------------------------------------------------
# Local variables:
# mode: awk
# mode: folding
# End:
