#!/bin/sh -e
# ==================================================================================
# portsreinstall main script
# Copyright (C) 2010-2012 Mamoru Sakaue, MwGhennndo, All Rights Reserved.
# This software is distributed under the 2-Clause BSD License.
# ==================================================================================

# ================================================
# ================ PREPARATION ===================
# ================================================

APPNAME=`basename $0`
MYVERSION=1.1.0

PREFIX=${PREFIX:-/usr/local}
CONFFILE=${PREFIX}/etc/${APPNAME}.conf
DBDIR=/var/tmp/${APPNAME}.db
PKGTOOLSCONF=${PREFIX}/etc/pkgtools.conf

# ============= Option check =============
help_mode=0
option_err=0
target_dependent_ports=
target_required_ports=
taboo_ports=
load_pkgtoolsconf=undef
show_version=no
avoid_vulner=no
skip_unchanged=no
keep_distfiles=no
renew_options=no
while :
do
	if [ "$1" = "-h" ]
	then
		help_mode=1
		shift
	elif [ "$1" = "-H" ]
	then
		help_mode=2
		shift
	elif [ "$1" = "-t" ]
	then
		target_dependent_ports=$target_dependent_ports,$2
		shift 2
	elif [ "$1" = "-T" ]
	then
		target_required_ports=$target_required_ports,$2
		shift 2
	elif [ "$1" = "-x" ]
	then
		taboo_ports=$taboo_ports,$2
		shift 2
	elif [ "$1" = "-p" ]
	then
		load_pkgtoolsconf=default
		shift
	elif [ "$1" = "-P" ]
	then
		load_pkgtoolsconf=override
		shift
	elif [ "$1" = "-Q" ]
	then
		load_pkgtoolsconf=no
		shift
	elif [ "$1" = "-V" ]
	then
		show_version=yes
		shift
	elif [ "$1" = "-s" ]
	then
		avoid_vulner=yes
		shift
	elif [ "$1" = "-q" ]
	then
		skip_unchanged=yes
		shift
	elif [ "$1" = "-d" ]
	then
		keep_distfiles=yes
		shift
	elif [ "$1" = "-N" ]
	then
		renew_options=yes
		shift
	else
		break
	fi
done

# if [ $# -gt 0 ]
# then
# 	option_err=1
# fi

credit ()
{
	echo "${APPNAME} version ${MYVERSION}"
	echo " -- Ports upgrading utility for massive forced reinstallation"
	echo " -- And for those who are pursuing the perfect packages environment"
	echo "Copyright (C) 2010, 2011 Mamoru Sakaue, MwGhennndo, All Rights Reserved."
	echo "Email: <sakaue.mamoru@samurai.mwghennn.net>"
	echo "Homepage: <http://www.mwghennndo.com/softwares/portsreinstall/>"
}

# Usage
if [ $help_mode -eq 1 -o $option_err -eq 1 ]
then
	echo "USAGE: ${APPNAME} [OPTIONS] [command]"
	echo
	echo "[OPTIONS]"
	echo " -h : Show this short help"
	echo " -H : Show long help"
	echo " -V : Show the current version"
	echo " -t glob1[,glob2,...] : Reinstall only target ports and their dependents"
	echo " -T glob1[,glob2,...] : Reinstall only target ports and their requirements"
	echo " -x glob1[,glob2,...] : Set the port glob(s) to be taboo"
	echo " -p : Import settings from pkgtools.conf(5) as the primary if exists (default)"
	echo " -P : Import settings from pkgtools.conf(5) as the secondary if exists"
	echo " -Q : Ignore pkgtools.conf(5) even if it exists"
	echo " -s : Build of vulnerable ports are avoided by triggering errors"
	echo " -q : Ports whose all requirements and themselves are not new are skipped."
	echo " -d : Do not clean up obsolete or unused distfiles."
	echo " -N : Renew option settings (only for redo command)"
	echo
	echo "[ARGUMENTS]"
	echo " command: do (default) | prepare | redo | clean | ok add globs..."
	echo "          | ok del globs... | taboo add globs... | taboo del globs..."
	echo "          | save [dir] | load path"
	echo "          | show todo | show done | show resolved | show failure | show taboo"
	echo "          | show deleted"
	echo
	echo "[DESCRIPTIONS]"
	echo " This utility is an alternative to portupgrade(1) and portmaster(8), and"
	echo "designed to be suitable for reinstallation of all packages after major version"
	echo "upgrade of the system or very long absence of ports upgrade."
	echo " Usually, you can execute with no option nor argument. If portupgrade(1) is"
	echo "installed, compatibility with it is taken into account by importing"
	echo "pkgtools.conf(5) and rebuilding the package database."
	exit $option_err
elif [ $help_mode -eq 2 ]
then
	credit
	echo
	echo "USAGE: ${APPNAME} [OPTIONS] [command]"
	echo
	echo "[OPTIONS]"
	echo " -h : Show short help"
	echo " -H : Show this long help"
	echo " -V : Show the current version"
	echo " -t glob1[,glob2,...] : Reinstall only target ports and their dependents"
	echo "        Target ports which are not installed yet are newly installed."
	echo "        This option is recognized in the first 'do' or 'redo' runs, and"
	echo "        transferred to the restarted runs."
	echo "        Dependency relations are inspected for all installed packages as well."
	echo "        Combination with '-T' option is available."
	echo " -T glob1[,glob2,...] : Reinstall only target ports and their requirements"
	echo "        Target ports which are not installed yet are newly installed."
	echo "        This option is recognized in the first 'do' or 'redo' runs, and"
	echo "        transferred to the restarted runs."
	echo "        Dependency relations are inspected for all installed packages as well."
	echo "        Combination with '-t' option is available."
	echo " -x glob1[,glob2,...] : Set the port glob(s) to be taboo"
	echo "        This option registers a port to be ignored as taboo."
	echo "        Mainly for ports that show terrible behaviors resulting in system crash."
	echo "        This option is recognized in the first 'do' or 'redo' runs, and"
	echo "        transferred to the restarted runs."
	echo "        Alternatively you can do the same thing by 'taboo' command."
	echo "        If you want to register permanently, set to the configuration file."
	echo " -p : Import settings from pkgtools.conf(5) as the primary"
	echo "        For duplicated configurations, values in pkgtools.conf are applied"
	echo "        first and then those in ${APPNAME}.conf are."
	echo "        This option is ignored when you restart a terminated process."
	echo "        (Default; ignored if ports-mgmt/portupgrade* is not installed)"
	echo " -P : Import settings from pkgtools.conf(5) as the secondary"
	echo "        For duplicated configurations, values in ${APPNAME}.conf are applied"
	echo "        first and then those in pkgtools.conf are."
	echo "        This option is ignored when you restart a terminated process."
	echo "        (Ignored if ports-mgmt/portupgrade* is not installed)"
	echo " -Q : Ignore pkgtools.conf(5) even if it exists."
	echo "        This option is ignored when you restart a terminated process."
	echo " -s : Build of vulnerable ports are avoided by triggering errors"
	echo "        Note that already installed vulnerable packages are untouched."
	echo "        If you desire to uninstall them, do it manually."
	echo "        This specification will be reasonable for the practical situations."
	echo "        This option is fixed at the first 'do' run, and transferred to the"
	echo "        following runs."
	echo "        If you want to continue the reinstallation process by resetting"
	echo "        this option, execute by 'redo' command with '-N' option."
	echo " -q : Ports whose all requirements and themselves are not new are skipped."
	echo "        This option is safe and useful when the all of the major version of"
	echo "        the system, configuration options of each ports, pkgtools.conf(5),"
	echo "        ${APPNAME}.conf are unchanged."
	echo "        This option is fixed at the first 'do' run, and transferred to the"
	echo "        following runs."
	echo "        If you want to continue the reinstallation process by resetting"
	echo "        this option, execute by 'redo' command with '-N' option."
	echo " -d : Do not clean up obsolete or unused distfiles."
	echo "        This option is fixed at the first 'do' run, and transferred to the"
	echo "        following runs."
	echo "        If you want to continue the reinstallation process by resetting"
	echo "        this option, execute by 'redo' command with '-N' option."
	echo " -N : Renew option settings."
	echo "        This option is effective only with 'redo' command."
	echo "        Option settings for '-s', '-q' and '-d' are reset according to"
	echo "        the simultaneously given ones."
	echo
	echo "[ARGUMENTS]"
	echo " command: (For optional operations or confirmation)"
	echo "      {one of the following commands}"
	echo "      do : full execution (default)"
	echo "      prepare : stop before the actual operations to the ports/packages"
	echo "      redo : execute again for failed ports and their dependents"
	echo "      clean : clean up the temporal database"
	echo "      ok add glob1 [[,]glob2 ...] : register manually resolved ports"
	echo "      ok del glob1 [[,]glob2 ...] : deregister manually resolved ports"
	echo "      taboo add glob1 [[,]glob2 ...]: register taboo ports"
	echo "      taboo del glob1 [[,]glob2 ...]: deregister taboo ports"
	echo "      save [dir] : save the current temporal database as a .tar.gz archive"
	echo "      load path : load a temporal database archive"
	echo "      show [arg] : show the list of ports to be reinstalled"
	echo "              todo : ports to be reinstalled (default)"
	echo "              done : already reinstalled ports"
	echo "              resolved : manually reinstalled ports"
	echo "              failure : failed ports"
	echo "              taboo : taboo ports"
	echo "              deleted : obsolete ports to be or have been deleted"
	echo
	echo "[CONFIGURATION FILE]"
	echo "          ${CONFFILE}"
	echo
	echo "[NOTATIONS]"
	echo " The 'glob' is either of pkgname_glob or portorigin_glob explained in the man"
	echo "page of portsdb(1) and ports_glob(1): for example, zip-3.0, zip-*, and archivers"
	echo "/zip. Here the wild card symbol '*' must be quoted or escaped. In evaluation of"
	echo "globs, ports_glob(1) is used if it is installed for better compatibility,"
	echo "otherwise an alternative internal function is used."
	echo
	echo "[DESCRIPTIONS]"
	echo " This utility realizes smart reinstallation of a large number of ports by"
	echo "allowing you to run when when the machine is free and terminate when busy."
	echo " Concretely, you can stop the process by CTRL+C anytime and restart quickly."
	echo " This functionality allows you, for example, to start this utility before lunch,"
	echo "terminate after lunch, restart before dinner, terminate after dinner, restart"
	echo "before going to bed, terminate after breakfast, restart before lunch, ..., and"
	echo "finally complete."
	echo
	echo " The policy of this utility is to complete the job by the most automatic and"
	echo "finally successful way even if it will take a long time in total."
	echo " All missing build- and run-time dependencies are newly installed if any."
	echo " All reinstallation processes are tried to be proceeded forcibly by ignoring"
	echo "errors or vulnerabilities as possible."
	echo " This utility pursues the consistency of the dependency relations in the latest"
	echo "version of the ports tree by on-the-fly entire reconstruction of the relations"
	echo "while portupgrade(1) and portmaster(8) believe the existing relations of the"
	echo "installed packages to reduce the total work time as much as possible."
	echo
	echo " In the simplest case, all a user has to do toward complete reinstallation is"
	echo "execute this utility without any arguments (corresponding to 'do' command). If"
	echo "the run completes with notations on failures for any ports, after resolving the"
	echo "errors manually, register the resolved ports by executing this utility with"
	echo "'ok add' command and then execute with 'redo' command. This task is continued"
	echo "until successful completion."
	echo
	echo " The algorithms of this utility are customized for massive reinstallation to"
	echo "be invoked after major upgrade of the system where rebuild of all third-party"
	echo "applications are encouraged before cleaning up obsolete system libraries."
	echo " Nevertheless, the all functionalities of this utility is applicable to any"
	echo "situations where complete reinstallation of the all or parts of ports is"
	echo "preferred, e.g., when you have been lazy in upgrade of ports for too long time."
	echo " For the usual purposes of upgrading packages installed by ports, you are"
	echo "recommended to use ports-mgmt/portupgrade or ports-mgmt/portmaster instead."
	echo
	echo " The scheme of this utility is divided into the temporal database construction"
	echo "phase and the reinstallation phase."
	echo " Execution by 'portsreinstall prepare' procedes to the end the first phase, and"
	echo "that without any argument procedes to the end of the second phase."
	echo " Each of these two major phases is divided into minor phases."
	echo " When the previously terminated process is restarted, completed minor phases are"
	echo "skipped.">&2
	echo
	echo " The massive minor phases belong to 'collecting dependencies of installed/"
	echo "missing packages', 'ordering dependencies' for the first major phase and"
	echo "'reinstallation' 'package database update' for the second."
	echo " Most of them are divided into more minor phases except for 'package database"
	echo "update'."
	echo
	echo " When option(s) '-t' or '-T' is/are given in the first run of 'do' or 'redo'"
	echo "commands, only the targets and their required or dependent ports are"
	echo "reinstalled."
	echo " This specification is effective until the completion of the run."
	echo " Run by 'redo' command without '-t' or '-T' options inherits the previous"
	echo "settings. Meanwhile, run by 'redo' command with '-t' or '-T' options resets the"
	echo "previous settings, and inspects the dependencies again if new ports are to be"
	echo "installed."
	echo
	echo " The user is encouraged to run this utility under script(1) so as to record all"
	echo "logs in order to resolve problems that you may (rather 'will', practically)"
	echo "encounter."
	echo " The solutions depend on the individual cases."
	echo " If the problem will be resolved by reconfiguration of the port option, execute"
	echo "'make config' at the corresponding port directory, and then restart this"
	echo "utility."
	echo " If the problem will be resolved by manual fetch of tarballs, do it and then"
	echo "restart this utility."
	echo " If the problem will be resolved by deleting a concerned package, do it by"
	echo "pkg_delete(1) with '-f' option or execute 'make deinstall' at the corresponding"
	echo "port directory, execute '${APPNAME} ok add \$glob' where '\$glob' is the ports"
	echo "glob of the concerned port, and then restart this utility."
	echo " If the problem will be resolved by manual reinstallation using pkg_add(1) or so"
	echo "on, do it and execute '${APPNAME} ok add \$glob' where '\$glob' is the ports"
	echo "glob of the concerned port, and then restart this utility by 'redo' command."
	echo
	echo " If you are familiar to the mechanism of Ports Collections system, in order to"
	echo "minimize package conflictions, it may be a good idea to delete packages which "
	echo "you don't think necessary before starting to use this utility."
	echo "Don't be afraid to delete necessary dependencies because all required ports are"
	echo "automatically installed."
	echo
	echo " If you run into confusion, it may be a good idea to clean up the temporal"
	echo "database by executing '${APPNAME} clean' and start again from the first."
	exit
fi

if [ $show_version = yes ]
then
	echo "${APPNAME} version ${MYVERSION}"
	exit
fi

command=$1
[ -n "$command" ] && shift

# ====================================================
# ================== FUNCTIONS =======================
# ====================================================

PORTSDIR=${PORTSDIR:-/usr/ports}

PORTS_MOVED_DB=${PORTSDIR}/MOVED
PORTS_INDEX_DB=${PORTSDIR}/INDEX-`uname -r | cut -d . -f 1`

str_escape_regexp_filter ()
{
	sed 's/\\/\\\\/g;s/|/\\|/g;s/\./\\./g;s|/|\\/|g;s/\[/\\[/g;s/\]/\\]/g;s/[(]/\\(/g;s/[)]/\\)/g;s/\+/\\+/g;s/\?/\\?/g;s/\*/\\*/g'
}

convert_glob_to_regexp_pattern ()
{
	sed 's/\+/\\+/g;s/\./\\./g;s|/|\\/|g;s/\*/.*/g;s/\?/g./;s/\[\!/[^/g;s/^/^/;s/$/$/'
}

convert_portsglob_to_regexp_pattern ()
{
	local glob_pattern regexp_pattern
	glob_pattern=$1
	if expr "$glob_pattern" : '^:' > /dev/null 2>&1
	then
		regexp_pattern=`echo $glob_pattern | sed 's/^://'`
	else
		regexp_pattern=`echo $glob_pattern | convert_glob_to_regexp_pattern`
	fi
	echo $regexp_pattern
}

if which -s ports_glob
then
	:
else
	ports_glob ()
	{
		local glob index
		glob=`convert_portsglob_to_regexp_pattern "$1"`
		[ -f "${DBDIR}/ports_glob:pkg.lst" ] || cut -d \| -f 1 "${PORTS_INDEX_DB}" > ${DBDIR}/ports_glob:pkg.lst
		[ -f "${DBDIR}/ports_glob:origin.lst" ] || cut -d \| -f 2 "${PORTS_INDEX_DB}" | sed -E "s/^`str_escape_regexp "${PORTSDIR}"`\///" > ${DBDIR}/ports_glob:origin.lst
		if expr "$glob" : '[^\/][^\/]*\/[^\/][^\/]*$' 2> /dev/null > /dev/null
		then
			grep -E "$glob" "${DBDIR}/ports_glob:origin.lst" > ${TMPDIR}/ports_glob:matched
			pkg_info -aoq | grep -E "$glob" >> ${TMPDIR}/ports_glob:matched
		else
			grep -n -E "$glob" "${DBDIR}/ports_glob:pkg.lst" | cut -d : -f 1 | while read index
			do
				sed -n ${index}p "${DBDIR}/ports_glob:origin.lst"
			done > ${TMPDIR}/ports_glob:matched
			pkg_info -oqx "$glob" >> ${TMPDIR}/ports_glob:matched
		fi
		sort -u "${TMPDIR}"/ports_glob:matched
	}
fi

str_escape_regexp ()
{
	echo $* | str_escape_regexp_filter
}

rm_a_line ()
{
	local item dstpath pattern
	item=$1
	dstpath=$2
	pattern=`str_escape_regexp $item`
	grep -v -E "^$pattern$" "$dstpath" 2> /dev/null > ${TMPDIR}/rm_a_line || :
	mv "${TMPDIR}/rm_a_line" "$dstpath"
}

add_a_line_if_new ()
{
	local item dstpath pattern
	item=$1
	dstpath=$2
	pattern=`str_escape_regexp $item`
	grep -m 1 -E "^$pattern$" "$dstpath" 2> /dev/null > /dev/null || echo $item >> $dstpath
}

record_success ()
{
	local origin clean recurse
	origin=$1
	str_escape_regexp_filter < ${DBDIR}/failed.list | sed "s|^|^|; s|$|$|" > ${TMPDIR}/record_success.grep_failed.list.tmp
	if [ `grep -m 1 -E -f "${TMPDIR}/record_success.grep_failed.list.tmp" "${DBDIR}/requires/$origin/requires" | wc -l` -eq 0 ]
	then
		add_a_line_if_new "$origin" "${DBDIR}/success.list"
		rm_a_line "$origin" "${DBDIR}/success_but_dependencies_failed.list"
		[ -e "${DBDIR}/requires/$origin/necessary_update" ] && mv "${DBDIR}/requires/$origin/necessary_update" "${DBDIR}/requires/$origin/necessary_update_completed"
	else
		rm_a_line "$origin" "${DBDIR}/success.list"
		add_a_line_if_new "$origin" "${DBDIR}/success_but_dependencies_failed.list"
	fi
	rm_a_line "$origin" "${DBDIR}/failed.list"
	rm -f "${DBDIR}/requires/$origin/note_failtre"
}

record_failure ()
{
	local origin clean
	origin=$1
	clean=$2
	add_a_line_if_new "$origin" "${DBDIR}/failed.list"
	rm_a_line "$origin" "${DBDIR}/success.list"
	rm_a_line "$origin" "${DBDIR}/success_but_dependencies_failed.list"
	if [ -z "$clean" -o "@$clean" = @clean ]
	then
		echo "*** Trying to clean the failed build... (Ignore failures)"
		env ${MAKE_ENVS} make clean ${MAKE_ARGS} || :
	fi
	echo "*** Skipping this port and proceeding to the next one forcibly..."
	echo
}

add_a_line_to_files_if_new ()
{
	local item
	item=$1
	while read filepath
	do
		add_a_line_if_new "$item" "$filepath"
	done
}

add_lines_if_new ()
{
	local filepath origin advance
	filepath=$1
	advance=$2
	while read origin
	do
		grep -m 1 -E "^`str_escape_regexp $origin`$" "$filepath" 2> /dev/null > /dev/null || echo $origin
	done > ${TMPDIR}/add_lines_if_new
	if [ "@$advance" = @advance ]
	then
		mv "$filepath" ${TMPDIR}/add_lines_if_new.bak
		cat "${TMPDIR}/add_lines_if_new" "${TMPDIR}/add_lines_if_new.bak" > $filepath
	else
		cat "${TMPDIR}/add_lines_if_new" >> $filepath
	fi
}

register_globs ()
{
	local globlist listpath mode dirpath origin
	globlist=$1
	listpath=$2
	mode=$3
	dirpath=`dirname "$listpath"`
	for glob in `echo $globlist | sed 's/,/ /g'`
	do
		if expr "@$glob" : '^@[^/][^/]*/[^/][^/]*$' > /dev/null 2> /dev/null
		then
			[ -d "${PORTSDIR}/$glob" ] && { echo $glob; continue; }
		fi
		origin=`ports_glob "$glob"`
		[ -n "$origin" ] || \
		{
			echo "ERROR: Invalid ports/package glob [$glob]" >&2
			exit 1;
		}
		echo $origin
	done | while read origin
	do
		[ -d "$dirpath" ] || mkdir -p "$dirpath"
		if [ "@$mode" = @remove ]
		then
			rm_a_line "$origin" "$listpath"
		elif grep -m 1 -E "^`str_escape_regexp $origin`$" "$listpath" > /dev/null 2> /dev/null
		then
			:
		else
			echo $origin >> "$listpath"
		fi
	done
}

expand_glob_pattern_to_origins ()
{
	local glob_pattern include_nonexistent regexp_pattern
	glob_pattern=$1
	include_nonexistent=$2
	{
		ports_glob "$glob_pattern" 2> /dev/null || :
		if expr "$glob_pattern" : '^[a-z][a-z]*\/[a-zA-Z0-9_+-][a-zA-Z0-9_+-]*$' > /dev/null 2>&1
		then
			if [ "x$include_nonexistent" = xyes ]
			then
				echo $glob_pattern
			else
				pkg_info -qO "$glob_pattern" | xargs pkg_info -qo
			fi
		elif expr "$glob_pattern" : '^[a-zA-Z0-9.,_+-][a-zA-Z0-9.,_+-]*$' > /dev/null 2>&1
		then
			pkg_info -qo "$glob_pattern" 2> /dev/null || :
		else
			regexp_pattern=`convert_portsglob_to_regexp_pattern "$glob_pattern"`
			if expr "$glob_pattern" : '.*\/' > /dev/null 2>&1
			then
				pkg_info -aqo | grep -E "$regexp_pattern" 2> /dev/null || :
			else
				pkg_info -qoX "$regexp_pattern" 2> /dev/null || :
			fi
		fi
	} | grep -v -e '^$' | sort -u
}

build_conflist_target_list ()
{
	local section
	section=$1
	if [ ! -e "${DBDIR}/COMPLETE_REFLECTCONF_${section}" ]
	then
		echo "-- ${section}_*"
		set | grep -e "^_CONF_${section}_" | cut -d = -f 1 | while read var
		do
			eval glob_pattern=\${$var}
			expand_glob_pattern_to_origins "$glob_pattern"
		done > ${DBDIR}/${section}_PORTS.conflist
		touch "${DBDIR}/COMPLETE_REFLECTCONF_${section}"
	fi
}

build_conflist_target_val_pair ()
{
	local section tag_target tag_val
	section=$1
	tag_target=$2
	tag_val=$3
	if [ ! -e "${DBDIR}/COMPLETE_REFLECTCONF_${section}" ]
	then
		echo "-- ${section}_*"
		set | grep -e "^_CONF_${section}_${tag_target}_" | cut -d = -f 1 | while read var
		do
			eval glob_pattern=\${$var}
			eval val=\$\{`echo $var | sed "s/^_CONF_${section}_${tag_target}_/_CONF_${section}_${tag_val}_/"`\}
			expand_glob_pattern_to_origins "$glob_pattern" | while read origin
			do
				path=${DBDIR}/requires_conflist/$origin
				[ -d "$path" ] || mkdir -p "$path"
				echo $val > $path/${section}.conflist
			done
		done
		touch "${DBDIR}/COMPLETE_REFLECTCONF_${section}"
	fi
}

add_to_obsolete_if_not_yet ()
{
	local origin pkgtag
	origin=$1
	add_a_line_if_new "$origin" "${DBDIR}/moved_or_lost.list"
	if [ ! -e "${DBDIR}/obsolete/$origin/pkgtag" ]
	then
		[ -d "${DBDIR}/obsolete/$origin" ] || mkdir -p "${DBDIR}/obsolete/$origin"
		pkgtag=`pkg_info -qO "$origin"`
		[ -n "$pkgtag" ] || pkgtag='[not installed]'
		echo $pkgtag > ${DBDIR}/obsolete/$origin/pkgtag
	fi
}

convert_origin_if_moved ()
{
	local origin_src recursedb_in recursedb iline_db date_moved why_moved
	# input/output origin
	origin_src=$1
	recursedb_in=$2
	recursedb=${recursedb_in:-${PORTS_MOVED_DB}}
	[ -d "${PORTSDIR}/$origin" ] && return
	add_to_obsolete_if_not_yet "$origin"
	grep -n -m 1 -E "^`str_escape_regexp $origin`\|" "$recursedb" 2> /dev/null > ${TMPDIR}/moved.info || :
	if [ `cat "${TMPDIR}/moved.info" | wc -l` -eq 0 ]
	then
		if [ -n "$recursedb_in" ]
		then
			echo "${DEPTH_INDEX}  ===> Disappeared port (MOVED broken?)"
		else
			echo "${DEPTH_INDEX}  ===> Nonexistent port (your original?)"
		fi
		[ -n "$origin_src" ] && add_to_obsolete_if_not_yet "$origin_src"
		return 1
	else
		iline_db=`cut -d : -f 1 "${TMPDIR}/moved.info"`
		sed 1,${iline_db}d "${PORTS_MOVED_DB}" > ${TMPDIR}/MOVED.DB
		origin=`sed -E 's/^[0-9]+://' "${TMPDIR}/moved.info" | cut -d '|' -f 2 || :`
		date_moved=`cut -d '|' -f 3 "${TMPDIR}/moved.info" || :`
		why_moved=`cut -d '|' -f 4 "${TMPDIR}/moved.info" || :`
		if [ -n "$origin" ]
		then
			echo "${DEPTH_INDEX}  ===> Moved to $origin at $date_moved because \"$why_moved\""
			convert_origin_if_moved "$origin_src" "${TMPDIR}/MOVED.DB" || return 1
		else
			echo "${DEPTH_INDEX}  ===> Deleted at $date_moved because \"$why_moved\""
			[ -n "$origin_src" ] || return 1
			origin=$origin_src
			echo "${DEPTH_INDEX}  ===> Going back to the original port $origin_src"
			convert_origin_if_moved || return 1
		fi
	fi
}

inspect_dependencies ()
{
	local origin origin_orig pkg origin_id origin_src port_exists origin_dependency DEPTH_INDEX_orig nlines iline
	origin=$1
	origin_orig=$origin
	DEPTH_INDEX_orig=${DEPTH_INDEX}
	DEPTH_INDEX="${DEPTH_INDEX}--"
	echo "${DEPTH_INDEX} $origin"
	origin_id=`echo $origin | tr / :`
	origin_src=
	if [ `echo $origin | grep -m 1 -E -f "${DBDIR}/REPLACE.grep_from_pattern.conflist" | wc -l` -eq 1 ]
	then
		origin_src=$origin
		origin=`echo $origin_src | sed -E -f "${DBDIR}/REPLACE.replace_pattern.conflist"`
		add_to_obsolete_if_not_yet "$origin_src"
		if [ -n "$origin" ]
		then
			add_a_line_if_new "$origin" "${DBDIR}/replaced_target.inspected.list"
			echo "${DEPTH_INDEX}  ===> Replaced with $origin by user configuration"
		else
			echo "${DEPTH_INDEX}  ===> Deleted by user configuration"
		fi
	fi
	if [ -n "$origin" ]
	then
		if convert_origin_if_moved "$origin_src" ''
		then
			port_exists=yes
		else
			port_exists=no
		fi
		if [ $port_exists = yes ]
		then
			rm_a_line "$origin" "${DBDIR}/moved_or_lost.list"
			cd "${PORTSDIR}/$origin"
			target_dir=${DBDIR}/requires/$origin
			[ -d "$target_dir/status" ] || mkdir -p "$target_dir/status"
			if [ -d "${DBDIR}/requires_conflist/$origin" ]
			then
				cp -R "${DBDIR}/requires_conflist/$origin/"* "$target_dir/" > /dev/null 2> /dev/null || :
			fi
			MAKE_ARGS="FORCE_PKG_REGISTER=yes DISABLE_VULNERABILITIES=yes `cat "$target_dir/MARG.conflist" 2> /dev/null || :`"
			MAKE_ENVS=`cat "$target_dir/MENV.conflist" 2> /dev/null || :`
			env ${MAKE_ENVS} make config-conditional ${MAKE_ARGS}
			pkg=`pkg_info -qO "$origin"`
			[ -n "$pkg" ] && echo $pkg > $target_dir/installed_version
			pkg_new=`(cd "${PORTSDIR}/$origin" && env ${MAKE_ENVS} make package-name ${MAKE_ARGS})`
			pkgtag=$pkg
			[ -n "$pkgtag" ] || pkgtag=$pkg_new
			if [ -z "$pkgtag" ]
			then
				pkgtag='?'
			
			elif [ "z$pkg" != "z$pkg_new" ]
			then
				echo $pkg_new > $target_dir/new_version
				if [ -n "$pkg" ]
				then
					pkgtag="$pkg => $pkg_new"
				else
					pkgtag="[new] $pkg_new"
				fi
			fi
			echo $pkgtag > $target_dir/pkgtag
			env ${MAKE_ENVS} make all-depends-list ${MAKE_ARGS} | sed "s|^${PORTSDIR}/||" > "$target_dir/requires" || :
			grep -E -f "${DBDIR}/REPLACE.grep_from_pattern.conflist" "$target_dir/requires" | grep -v -E "^`str_escape_regexp $origin`$" > ${TMPDIR}/replaced_target.tmp || :
			mv "$target_dir/requires" "$target_dir/requires.orig"
			sed -E -f "${DBDIR}/REPLACE.replace_pattern.conflist" "$target_dir/requires.orig" | grep -v '^$' > $target_dir/requires || :
			add_lines_if_new "${DBDIR}/replaced_target.list" < ${TMPDIR}/replaced_target.tmp
			grep -v -E -f "${DBDIR}/installed_ports.grep_pattern" "$target_dir/requires" > ${TMPDIR}/missing.$origin_id || :
			nlines=`cat "${TMPDIR}/missing.$origin_id" | wc -l`
			iline=1
			while [ $iline -le $nlines ]
			do
				origin_dependency=`sed -n ${iline}p "${TMPDIR}/missing.$origin_id"`
				iline=$(($iline+1))
				grep -m 1 -E "^`str_escape_regexp $origin_dependency`$" "${DBDIR}/target.inspected.list" > /dev/null 2> /dev/null && continue
				inspect_dependencies "$origin_dependency"
			done
			rm "${TMPDIR}/missing.$origin_id"
			env ${MAKE_ENVS} make fetch-urlall-list ${MAKE_ARGS} | sed -E 's|.*/([^/]+)$|\1|' | sort | uniq >> ${DBDIR}/distfiles.list
		fi
	fi
	[ "$origin_orig" = "$origin" ] || echo "s|^`str_escape_regexp $origin_orig`$|$origin|" >> ${DBDIR}/REPLACE.complete_replace_pattern.tmp
	add_a_line_if_new "$origin" "${DBDIR}/target.inspected.list"
	rm_a_line "$origin" "${DBDIR}/target_ports.remain"
	if [ -n "$origin_src" ]
	then
		add_a_line_if_new "$origin_src" "${DBDIR}/target.inspected.list"
		rm_a_line "$origin_src" "${DBDIR}/target_ports.remain"
	fi
	echo "${DEPTH_INDEX}  ===> ok"
	DEPTH_INDEX=${DEPTH_INDEX_orig}
}

cmt_fail_reinst ()
{
	local origin
	origin=$1
	echo "*** Giving up for this port $origin and proceeding to the next one forcibly..."
	echo
}

timestamp ()
{
	env LANG= date
}

warn_update_ports ()
{
	[ ${_STATUS_DB_OBSOLETE} = no ] && return
	echo "WARN: The Ports tree was updated after construction of the temporal database for ${APPNAME}." >&2
	echo "      You should consider executing " >&2
	echo "               ${APPNAME} clean" >&2
	echo "     to reset the temporal database unless you have special purposes." >&2
}

linear_list ()
{
	echo $* | sed -E 's/^ +//; s/ +$//; s/ +/, /; s/, ([^ ]+)$/ and \1/'
}

chk_privilege ()
{
	[ `id -u` -eq 0 ] || { echo "ERROR: Only the superuser can execute this command." >&2; exit 1; }
}

combine_lists ()
{
	local src_conf src_opt dst
	src_conf=$1
	src_opt=$2
	dst=$3
	cat "${DBDIR}/$src_conf" "${DBDIR}/$src_opt" 2> /dev/null | sort | uniq > ${DBDIR}/$dst || :
}

show_list_failure ()
{
	while read origin
	do
		note=`cat "${DBDIR}/requires/$origin/note_failtre"`
		if grep -m 1 -E "^`str_escape_regexp $origin`$" "${DBDIR}/manually_done.list" > /dev/null 2>&1
		then
			resolved=', resolved'
		else
			resolved=
		fi
		if [ -e "${DBDIR}/requires/$origin/pkgtag" ]
		then
			pkgtag=' ('`cat "${DBDIR}/requires/$origin/pkgtag"`')'
		else
			pkgtag=
		fi
		echo "$origin$pkgtag (error while [$note]$resolved)"
	done < ${DBDIR}/failed.list
}

# ==================================================
# ==================== MAIN ========================
# ==================================================

# Title
credit
echo
echo " Don't hesitate to terminate by CTRL+C anytime you feel the system is heavy to"
echo "use because you can restart the operation from the terminated point quickly."
echo
echo "The current time is `timestamp`"
echo

# Creation of temporary work directories
terminate_process ()
{
}
{ until TMPDIR=`mktemp -dq /tmp/"${APPNAME}".XXXXXX 2> /dev/null` ; do : ; done ; }
trap 'errno=$?; warn_update_ports; terminate_process; rm -r'`[ \`uname -s\` = FreeBSD ] && echo -n P`' "${TMPDIR}" 2> /dev/null; [ $errno -gt 0 -a $errno -ne 130 ] && echo "(Error exit by $errno)" >&2; exit $errno' 0 1 2 3 9 15 17 18

# Check of conflicting option
[ -n "$target_dependent_ports" -a -d "${DBDIR}/COMPLETE_PARSE_OPTION_TARGET_PORTS" ] && echo "WARN: -t option is specified but ignored because we are restarting the previous run." >&2
[ -n "$target_required_ports" -a -d "${DBDIR}/COMPLETE_PARSE_OPTION_TARGET_PORTS" ] && echo "WARN: -T option is specified but ignored because we are restarting the previous run." >&2
[ "$load_pkgtoolsconf" != undef -a -d "${DBDIR}/COMPLETE_IMPORT_PKGTOOLS_CONF" ] && echo "WARN: -p, -P or -Q option is specified but ignored by following the previous settings." >&2

# Check whether the temporal database is newer than the ports tree
if [ "${PORTS_INDEX_DB}" -nt "${DBDIR}" ]
then
	_STATUS_DB_OBSOLETE=yes
else
	_STATUS_DB_OBSOLETE=no
fi

# Creation of temporal database directory
if [ -d "${DBDIR}" ]
then
	DBVERSION=`cat "${DBDIR}"/MYVERSION 2> /dev/null || :`
	# Reserved for compatibility check
else
	mkdir -p "${DBDIR}"
	echo ${MYVERSION} > ${DBDIR}/MYVERSION
fi

# Taboo list given by options
register_globs "$taboo_ports" "${DBDIR}/taboo.list"

# ------- Commands -------

# Special modes
case ${command:-do} in
clean)
	chk_privilege
	echo "Starting to clean up the temporal database..."
	rm -rf "${DBDIR}"
	echo "Done"
	exit
	;;
ok)
	chk_privilege
	warn_update_ports
	opr=$1
	shift
	case $opr in
	add)
		register_globs "$*" "${DBDIR}/manually_done.list"
		echo "`linear_list $*` is/are registered to the list of manually resolved ports"
		;;
	del)
		register_globs "$*" "${DBDIR}/manually_done.list" remove
		echo "`linear_list $*` is/are deregistered from the list of manually resolved ports"
		;;
	esac
	echo "Now the following ports have been manually resolved:"
	[ -r "${DBDIR}/manually_done.list" ] && cat  "${DBDIR}/manually_done.list"
	exit
	;;
taboo)
	chk_privilege
	warn_update_ports
	opr=$1
	shift
	case $opr in
	add)
		register_globs "$*" "${DBDIR}/taboo.list"
		echo "`linear_list $*` is/are registered to the list of ports to be ignored."
		;;
	del)
		register_globs "$*" "${DBDIR}/taboo.list" remove
		echo "`linear_list $*` is/are deregistered from the list of ports to be ignored."
		;;
	esac
	combine_lists TABOO_PORTS.conflist taboo.list taboo.all.list
	echo "Now the following ports are registered to be ignored:"
	cat "${DBDIR}/taboo.all.list"
	exit
	;;
save)
echo $*
	chk_privilege
	[ -d "${DBDIR}" ] || { echo "ERROR: Database has not built yet." >&2; exit 1; }
	savedir=$1
	[ -n "$savedir" ] || { echo "ERROR: Directory to save the temporal database archive is not specified." >&2; exit 1; }
	[ -d "$savedir" ] || { echo "ERROR: Directory [$savedir] is not found." >&2; exit 1; }
	srcdir=`dirname "${DBDIR}"`
	srcnode=`basename "${DBDIR}"`
	savefile=`realpath "$savedir"`/${APPNAME}_`date +%S%m%d_%H%M%S`.tar.gz
	echo "Starting to save the temporal database as [$savefile]..."
	( cd "$srcdir" && tar czf "$savefile" "$srcnode" )
	echo "Done"
	exit
	;;
load)
	chk_privilege
	loadfile=$1
	[ -n "$loadfile" ] || { echo "ERROR: Database archive is not specified." >&2; exit 1; }
	[ -f "$loadfile" ] || { echo "ERROR: Database archive is not found." >&2; exit 1; }
	loadfile=`realpath "$loadfile"`
	echo "Starting to load the temporal database..."
	rm -rf "${DBDIR}"
	srcdir=`dirname "${DBDIR}"`
	srcnode=`basename "${DBDIR}"`
	[ -d "$srcdir" ] || mkdir -p "$srcdir"
	( cd "$srcdir" && tar xzf "$loadfile" )
	echo "Done"
	exit
	;;
show)
	[ -d "${DBDIR}" ] || { echo "ERROR: Database has not built yet." >&2; exit 1; }
	warn_update_ports
	_filter_skip_unchanged=
	pkgnamedb=requires
	case ${1:-todo} in
	todo)
		echo "The following ports are to be reinstalled or newly installed:"
		list=reinst_todo.list
		[ -e "${DBDIR}/reinst_todo.list" ] || list=reinst_order.list
		_filter_skip_unchanged=necessary_update
		;;
	done)
		echo "The following ports have been successfully reinstalled or newly installed:"
		list=success.list
		_filter_skip_unchanged=necessary_update_completed
		;;
	redo)
		echo "The following ports themselves have been successfully reinstalled or newly installed,"
		echo "but are to be reinstalled again because their dependencies were failed:"
		list=success_but_dependencies_failed.list
		_filter_skip_unchanged=necessary_update
		;;
	resolved)
		echo "The following ports had problems which have been manually resolved:"
		list=manually_done.list
		;;
	failure)
		echo "The following ports experienced failures and kept to be old:"
		show_list_failure
		exit
		;;
	taboo)
		echo "The following ports are registered as taboo:"
		list=taboo.all.list
		;;
	deleted)
		echo "The following ports are to be or have been deleted:"
		list=moved_or_lost.actual.list
		pkgnamedb=obsolete
		;;
	*)
		echo "ERROR: Invalid show argument [$1]" >&2
		exit 1
		;;
	esac
	[ -r "${DBDIR}/$list" ] || exit 0
	[ -e "${DBDIR}"/saved_options.sh ] && . "${DBDIR}"/saved_options.sh
	if [ -n "${_filter_skip_unchanged}" -a $skip_unchanged = yes ]
	then
		while read origin
		do
			[ -e "${DBDIR}/requires/$origin/${_filter_skip_unchanged}" ] || continue
			if [ -e "${DBDIR}/$pkgnamedb/$origin/pkgtag" ]
			then
				echo $origin '('`cat "${DBDIR}/$pkgnamedb/$origin/pkgtag"`')'
			else
				echo $origin
			fi
		done < ${DBDIR}/$list
	else
		while read origin
		do
			if [ -e "${DBDIR}/$pkgnamedb/$origin/pkgtag" ]
			then
				echo $origin '('`cat "${DBDIR}/$pkgnamedb/$origin/pkgtag"`')'
			else
				echo $origin
			fi
		done < ${DBDIR}/$list
	fi
	exit
	;;
redo)
	chk_privilege
	warn_update_ports
	touch "${DBDIR}/MODE_REDO"
	rm -f "${DBDIR}/COMPLETE_CLEANUP_REINST_STATUS"
	[ $renew_options = yes ] && rm -f "${DBDIR}/COMPLETE_SAVE_OPTIONS"
	if [ -n "$target_required_ports$target_dependent_ports" ]
	then
		rm -f "${DBDIR}/COMPLETE_PARSE_OPTION_TARGET_PORTS"
		touch "${DBDIR}/REQUIRE_CHK_NEW_TARGET"
	fi
	echo "[REDO mode]"
	;;
prepare)
	chk_privilege
	warn_update_ports
	;;
do)
	chk_privilege
	;;
*)
	echo "ERROR: Invalid command [$command]" >&2
	exit 1
	;;
esac

# ------- Load from pkgtools.conf(5) -------

if [ ! -e "${DBDIR}/COMPLETE_SAVE_OPTIONS" ]
then
	[ -e "${DBDIR}"/saved_options.sh ] && echo "(Previous option settings for '-s', '-q' and '-d' are reset.)"
	cat > ${DBDIR}/saved_options.sh << eof
avoid_vulner=$avoid_vulner
skip_unchanged=$skip_unchanged
keep_distfiles=$keep_distfiles
eof
	touch "${DBDIR}/COMPLETE_SAVE_OPTIONS"
else
	t_avoid_vulner=$avoid_vulner
	t_skip_unchanged=$skip_unchanged
	t_keep_distfiles=$keep_distfiles
	. "${DBDIR}"/saved_options.sh
	[ $t_avoid_vulner = no -o $t_avoid_vulner = $avoid_vulner ] || echo "WARN: -s option is specified but ignored by transferring the settings from the previous run." >&2
	[ $t_skip_unchanged = no -o $t_skip_unchanged = $skip_unchanged ] || echo "WARN: -q option is specified but ignored by transferring the settings from the previous run." >&2
	[ $t_keep_distfiles = no -o $t_keep_distfiles = $keep_distfiles ] || echo "WARN: -d option is specified but ignored by transferring the settings from the previous run." >&2
fi

if [ ! -e "${DBDIR}/COMPLETE_IMPORT_PKGTOOLS_CONF" ]
then
	PORTUPGRADE=`which portupgrade | head -n 1 || :`
	if [ $load_pkgtoolsconf = undef ]
	then
		if [ -n "${PORTUPGRADE}" -a -e /usr/local/etc/pkgtools.conf ]
		then
			load_pkgtoolsconf=default
		else
			load_pkgtoolsconf=no
		fi
	elif [ $load_pkgtoolsconf != undef -a `which portupgrade | wc -l` -eq 0 ]
	then
		echo "WARN: pkgtools.conf is ignored because portupgrade is not installed" >&2
		load_pkgtoolsconf=no
	fi
	
	if [ $load_pkgtoolsconf != no ]
	then
		echo "-- Starting to parse pkgtools.conf at `timestamp` (by using installed portupgrade)"
		portupgrade_pkg=`pkg_info -qO ports-mgmt/portupgrade`
		[ -n "$portupgrade_pkg" ] || portupgrade_pkg=`pkg_info -qO ports-mgmt/portupgrade-devel`
		[ `expr "$portupgrade_pkg" : '^portupgrade-devel-'` -ne 0 ] && echo "WARN: Combination with portupgrade-devel-* has not tested."
		istart=`grep -m 1 -n -e '^def init_global$' "${PORTUPGRADE}" | cut -d : -f 1` || :
		[ -n "$istart" ] || { echo "ERROR: The current installed version of portupgrade is unsupported." >&2; }
		sed 1,$(($istart-1))d "${PORTUPGRADE}" > ${TMPDIR}/portupgrade.0
		iend=`grep -m 1 -n -e '^end$' "${TMPDIR}"/portupgrade.0 | cut -d : -f 1`
		sed -n 1,${iend}p "${TMPDIR}"/portupgrade.0 > ${TMPDIR}/portupgrade.init_global
		ruby > ${DBDIR}/pkgtools.conf.converted << eof
MYNAME = 'portupgrade'
require 'pkgtools'
`cat "${TMPDIR}"/portupgrade.init_global`
init_global
init_pkgtools_global
load_config
alt_moved = config_value(:ALT_MOVED)
hold_pkgs = config_value(:HOLD_PKGS)
ignore_moved = config_value(:IGNORE_MOVED)
alt_pkgdep = config_value(:ALT_PKGDEP)
make_args = config_value(:MAKE_ARGS)
make_env = config_value(:MAKE_ENV)
beforebuild = config_value(:BEFOREBUILD)
beforedeinstall = config_value(:BEFOREDEINSTALL)
afterinstall = config_value(:AFTERINSTALL)

printf("ENV_PORTSDIR=%s\n", ENV['PORTSDIR'])

i = 0
alt_moved.each do |val|
        printf("ALT_MOVED_pkgtoolsconf_%d_='%s'\n", i, val)
        i = i + 1
end

i = 0
hold_pkgs.each do |val|
        printf("HOLD_pkgtoolsconf_%d_='%s'\n", i, val)
        i = i + 1
end
ignore_moved.each do |val|
        printf("HOLD_pkgtoolsconf_%d_='%s'\n", i, val)
        i = i + 1
end

i = 0
alt_pkgdep.each do |pat, alt|
        printf("REPLACE_FROM_pkgtoolsconf_%d_='%s'\n", i, pat)
        printf("REPLACE_TO_pkgtoolsconf_%d_='%s'\n", i, alt)
        i = i + 1
end

i = 0
make_args.each do |target, definition|
        printf("MARG_TARGET_pkgtoolsconf_%d_='%s'\n", i, target)
        printf("MARG_DEF_pkgtoolsconf_%d_='%s'\n", i, definition)
        i = i + 1
end

i = 0
make_env.each do |target, definition|
        printf("MENV_TARGET_pkgtoolsconf_%d_='%s'\n", i, target)
        printf("MENV_DEF_pkgtoolsconf_%d_='%s'\n", i, definition)
        i = i + 1
end

i = 0
beforebuild.each do |target, command|
        printf("BEFOREBUILD_TARGET_pkgtoolsconf_%d_='%s'\n", i, target)
        printf("BEFOREBUILD_COMMAND_%d_='%s'\n", i, command)
        i = i + 1
end

i = 0
beforedeinstall.each do |target, command|
        printf("BEFOREDEINSTALL_TARGET_pkgtoolsconf_%d_='%s'\n", i, target)
        printf("BEFOREDEINSTALL_COMMAND_%d_='%s'\n", i, command)
        i = i + 1
end

i = 0
afterinstall.each do |target, command|
        printf("AFTERINSTALL_TARGET_pkgtoolsconf_%d_='%s'\n", i, target)
        printf("AFTERINSTALL_COMMAND_%d_='%s'\n", i, command)
        i = i + 1
end
eof
		if [ $load_pkgtoolsconf = default ]
		then
			cat "${DBDIR}"/pkgtools.conf.converted "${CONFFILE}" > ${DBDIR}/setup.conf 
		elif [ $load_pkgtoolsconf = override ]
		then
			cat "${CONFFILE}" "${DBDIR}"/pkgtools.conf.converted > ${DBDIR}/setup.conf
		fi
		echo "===> ok"
		echo
	else
		ln -s "${CONFFILE}" "${DBDIR}"/setup.conf
	fi
	touch "${DBDIR}/COMPLETE_IMPORT_PKGTOOLS_CONF"
fi

# ------- Configurations -------

if [ ! -e "${DBDIR}/COMPLETE_PARSE_OPTION_TARGET_PORTS" ]
then
	rm -f "${DBDIR}/target_dependent_ports.specified" "${DBDIR}/target_required_ports.specified"
	register_globs "$target_dependent_ports" "${DBDIR}/target_dependent_ports.specified"
	register_globs "$target_required_ports" "${DBDIR}/target_required_ports.specified"
	touch "${DBDIR}/COMPLETE_PARSE_OPTION_TARGET_PORTS"
fi

# Parse configuration file
if [ ! -e "${DBDIR}/COMPLETE_PARSE_CONF" ]
then
	echo "Start to parse configuration file [${APPNAME}.conf] at `timestamp`"
	(
		set -e
		_CONFVARS='ENV ALT_MOVED HOLD TABOO REPLACE_FROM REPLACE_TO MARG_TARGET MARG_DEF MENV_TARGET MENV_DEF BEFOREBUILD BEFOREDEINSTALL AFTERINSTALL'
		for item in ${_CONFVARS}
		do
			set | grep -e "^${item}_" | cut -d = -f 1 | sed 's/^/unset /'
		done > ${TMPDIR}/unsetvars.sh
		. "${TMPDIR}"/unsetvars.sh
		. "${DBDIR}"/setup.conf
		for item in ${_CONFVARS}
		do
			set | grep -e "^${item}_" | sed 's/^/_CONF_/'
		done > ${DBDIR}/confvars.sh
	) || { echo "ERROR: Invalid syntax in [${CONFFILE}]" >&2; exit 1; }
	touch "${DBDIR}/COMPLETE_PARSE_CONF"
fi
. "${DBDIR}/confvars.sh"

if [ ! -e "${DBDIR}/COMPLETE_REFLECTCONF_1" ]
then
	echo "Starting to reflect settings of replacement defined in the configuration file at `timestamp`"
	echo PORTSDIR=${_CONF_ENV_PORTSDIR:-${PORTSDIR}} > "${DBDIR}/setenv.sh"
	echo DISTDIR=${_CONF_ENV_DISTDIR:-${DISTDIR:-${PORTSDIR}/distfiles}} >> "${DBDIR}/setenv.sh"
	
	if [ ! -e "${DBDIR}/COMPLETE_REFLECTCONF_ALT_MOVED" ]
	then
		echo "-- ALT_MOVED_*"
		cp -p "${PORTS_MOVED_DB}" "${TMPDIR}/MOVED.conflist"
		set | grep -e '^_CONF_ALT_MOVED_' | cut -d = -f 1 | while read var
		do
			eval movedsb_path=\${$var}
			cat "$movedsb_path" >> ${DBDIR}/MOVED.conflist
		done
		touch "${DBDIR}/COMPLETE_REFLECTCONF_ALT_MOVED"
	fi
	[ "${DBDIR}/MOVED.conflist" -nt "${PORTS_MOVED_DB}" ] && PORTS_MOVED_DB=${DBDIR}/MOVED.conflist
	
	build_conflist_target_list HOLD
	build_conflist_target_list TABOO
	combine_lists TABOO_PORTS.conflist taboo.list taboo.all.list
	
	if [ ! -e "${DBDIR}/COMPLETE_REFLECTCONF_REPLACE" ]
	then
		echo "-- REPLACE_*"
		cp /dev/null "${DBDIR}/REPLACE.grep_from_pattern.conflist"
		cp /dev/null "${DBDIR}/REPLACE.replace_pattern.conflist"
		set | grep -e '^_CONF_REPLACE_FROM_' | cut -d = -f 1 | while read var
		do
			eval glob_pattern=\${$var}
			eval to=\${`echo $var | sed 's/^_CONF_REPLACE_FROM_/_CONF_REPLACE_TO_/'`}
			expand_glob_pattern_to_origins "$glob_pattern" yes > ${TMPDIR}/origins.2.tmp || :
			if [ `cat "${TMPDIR}/origins.2.tmp" | wc -l` -eq 0 ]
			then
				echo "WARN: Original package to be replaced [$glob_pattern] is obsolete." >&2
				echo "      If still required, use a pattern for port origins instead." >&2
				continue
			fi
			str_escape_regexp_filter < ${TMPDIR}/origins.2.tmp | sed "s|^|^|; s|$|$|" >> ${DBDIR}/REPLACE.grep_from_pattern.conflist

			if [ -z "$to" -o "$to" = delete ]
			then
				to=
			else
				[ -d "${PORTSDIR}/$to" ] || echo "WARN: replacement port [$to] is obsolete" >&2
			fi
			str_escape_regexp_filter < ${TMPDIR}/origins.2.tmp | sed "s|^|s:^|; s|$|$:$to:|" >> ${DBDIR}/REPLACE.replace_pattern.conflist
		done
		touch "${DBDIR}/COMPLETE_REFLECTCONF_REPLACE"
	fi
	touch "${DBDIR}/COMPLETE_REFLECTCONF_1"
fi
. "${DBDIR}/setenv.sh"

if [ ! -e "${DBDIR}/COMPLETE_REFLECTCONF_2" ]
then
	echo "Starting to reflect settings for each port defined in the configuration file at `timestamp`"
	build_conflist_target_val_pair MARG TARGET DEF
	build_conflist_target_val_pair MENV TARGET DEF
	build_conflist_target_val_pair BEFOREBUILD TARGET COMMAND
	build_conflist_target_val_pair BEFOREDEINSTALL TARGET COMMAND
	build_conflist_target_val_pair AFTERINSTALL TARGET COMMAND
	touch "${DBDIR}/COMPLETE_REFLECTCONF_2"
	echo
fi

# ------- Database construction -------

terminate_process_common ()
{
	local msg_where
	[ -n "${_MSG_CURRENT_STAGE}" ] && msg_where=" during ${_MSG_CURRENT_STAGE}"
	echo
	if [ $errno -eq 130 ]
	then
		echo "INFO: Terminated at `timestamp`$msg_where"
		echo
		echo " You can restart this process from the terminated point by"
	else
		echo "INFO: Aborted at `timestamp`$msg_where"
		echo
		echo " You may restart this process from the aborted point by"
	fi
	echo "executing without options or arguments as:"
	echo "  ${APPNAME}"
}

terminate_process ()
{
	terminate_process_common
	echo " Instead, if you only want to construct the temporal database so as to stop"
	echo "before the actual reinstallation, execute as:"
	echo "  ${APPNAME} prepare"
	terminate_process ()
	{
	}
}

# Target ports
if [ ! -e "${DBDIR}/COMPLETE_INSTALLED_PORTS" ]
then
	echo "Starting to collect installed packages at `timestamp`"
	pkg_info -aoq 2> /dev/null > ${DBDIR}/installed_ports
	str_escape_regexp_filter < "${DBDIR}/installed_ports" | sed 's/^/^/; s/$/$/' > ${DBDIR}/installed_ports.grep_pattern
	touch "${DBDIR}/COMPLETE_INSTALLED_PORTS"
	echo
fi

if [ ! -e "${DBDIR}/COMPLETE_TARGETS" ]
then
	echo "Starting to check newly installing ports at `timestamp`"
	cp "${DBDIR}/installed_ports" "${DBDIR}/target_ports"
	cat "${DBDIR}/target_dependent_ports.specified" "${DBDIR}/target_required_ports.specified" 2> /dev/null | sort -u | add_lines_if_new "${DBDIR}"/target_ports
	touch "${DBDIR}/COMPLETE_TARGETS"
	echo
elif [ -e "${DBDIR}/REQUIRE_CHK_NEW_TARGET" ]
then
	echo "Starting to check newly installing ports at `timestamp`"
	cp "${DBDIR}/target_ports" "${DBDIR}/target_ports.new"
	sort -u "${DBDIR}/target_dependent_ports.specified" "${DBDIR}/target_required_ports.specified" | add_lines_if_new "${DBDIR}"/target_ports.new
	if [ `cat "${DBDIR}/target_ports" | wc -l` -ne `cat "${DBDIR}/target_ports.new" | wc -l` ]
	then
		echo "WARN: The temporal database will be refreshed so as to reinstall the all failed ports and their dependents."
		rm -f "${DBDIR}/COMPLETE_COLLECED_ALL_DEPENDENCIES" \
			"${DBDIR}/COMPLETE_DISTFILES_LIST" \
			"${DBDIR}/COMPLETE_CONVERT_REQUIRES_LIST" \
			"${DBDIR}/COMPLETE_INSPECT_DEPENDENTS" \
			"${DBDIR}/COMPLETE_COPY_DEPENDENCY_TMPFILES" \
			"${DBDIR}/COMPLETE_ORDERED_ALL_DEPENDENCIES" \
			"${DBDIR}/COMPLETE_CHECKED_UNSATISFIED_DEPENDENCIES" \
			"${DBDIR}/COMPLETE_REFLECTCONF_2"
	fi
	echo
fi
rm -f "${DBDIR}/REQUIRE_CHK_NEW_TARGET"

# Inspection of all dependencies
if [ ! -e "${DBDIR}/COMPLETE_COLLECED_ALL_DEPENDENCIES" ]
then
	echo "Starting to inspect dependencies of installed packages at `timestamp`"
	[ -d "${DBDIR}/requires" ] || mkdir -p "${DBDIR}/requires"
	touch "${DBDIR}/moved_or_lost.list"
	touch "${DBDIR}/target.inspected.list"
	if [ -f "${DBDIR}/target_ports.remain" ]
	then
		echo "INFO: Restarting from the previously terminated point"
	else
		cp -p "${DBDIR}/target_ports" "${DBDIR}/target_ports.remain"
	fi
	cp "${DBDIR}/target_ports.remain" "${TMPDIR}/target_ports"
	DEPTH_INDEX='--'
	nlines=`cat "${TMPDIR}/target_ports" | wc -l`
	iline=1
	while [ $iline -le $nlines ]
	do
		origin=`sed -n ${iline}p "${TMPDIR}/target_ports"`
		iline=$(($iline+1))
		inspect_dependencies "$origin"
		continue
	done
	touch "${DBDIR}/REPLACE.complete_replace_pattern.tmp"
	sort "${DBDIR}/REPLACE.complete_replace_pattern.tmp" | uniq > ${DBDIR}/REPLACE.complete_replace_pattern
	touch "${DBDIR}/COMPLETE_COLLECED_ALL_DEPENDENCIES"
	echo
fi

# Inspection of all required distfiles
if [ ! -e "${DBDIR}/COMPLETE_DISTFILES_LIST" -a $keep_distfiles = no ]
then
	echo "Starting to summarize distfiles list at `timestamp`"
	sort "${DBDIR}/distfiles.list" 2> /dev/null | uniq | str_escape_regexp_filter | sed 's|^|/|; s|$|$|' > ${DBDIR}/distfiles.grep.pattern || :
	touch "${DBDIR}/COMPLETE_DISTFILES_LIST"
	echo
fi

# Convert requires-lists to actual ones
if [ ! -e "${DBDIR}/COMPLETE_CONVERT_REQUIRES_LIST" ]
then
	echo "Starting conversion of requires-lists to actual ones at `timestamp`"
	if [ -f "${DBDIR}/convert_requires_lists.remain" ]
	then
		echo "INFO: Restarting from the previously terminated point"
	else
		find "${DBDIR}/requires" -depth 2 -type d > ${DBDIR}/convert_requires_lists.remain
	fi
	cp "${DBDIR}/convert_requires_lists.remain" "${TMPDIR}/convert_requires_lists"
	while read dbpath
	do
		portname=`basename "$dbpath"`
		catpath=`dirname "$dbpath"`
		catname=`basename "$catpath"`
		origin=$catname/$portname
		sed -E -f "${DBDIR}/REPLACE.complete_replace_pattern" "$dbpath/requires" | grep -v '^$' | sort | uniq > $dbpath/requires.new
		mv "$dbpath/requires.new" "$dbpath/requires"
		sed -i '' 1d "${DBDIR}/convert_requires_lists.remain"
	done < ${TMPDIR}/convert_requires_lists
	touch "${DBDIR}/COMPLETE_CONVERT_REQUIRES_LIST"
	echo
fi

# Inspection of dependents
if [ ! -e "${DBDIR}/COMPLETE_INSPECT_DEPENDENTS" ]
then
	echo "Starting inspection of dependents at `timestamp`"
	if [ -f "${DBDIR}/inspect_dependent.remain" ]
	then
		echo "INFO: Restarting from the previously terminated point"
	else
		find "${DBDIR}/requires" -depth 2 -type d > ${DBDIR}/inspect_dependent.remain
	fi
	cp "${DBDIR}/inspect_dependent.remain" "${TMPDIR}/inspect_dependent"
	while read dbpath
	do
		portname=`basename "$dbpath"`
		catpath=`dirname "$dbpath"`
		catname=`basename "$catpath"`
		origin=$catname/$portname
		sed "s|^|${DBDIR}/requires/|; s|$|/dependents|" "$dbpath/requires" | add_a_line_to_files_if_new "$origin"
		sed -i '' 1d "${DBDIR}/inspect_dependent.remain"
	done < ${TMPDIR}/inspect_dependent
	touch "${DBDIR}/COMPLETE_INSPECT_DEPENDENTS"
	echo
fi

# Inspection of necessary updates
if [ ! -e "${DBDIR}/COMPLETE_NECESSARY_UPDATES" ]
then
	echo "Starting inspection of necessary updates at `timestamp`"
	if [ -f "${DBDIR}/necessary_updates.remain" ]
	then
		echo "INFO: Restarting from the previously terminated point"
	else
		find "${DBDIR}/requires" -depth 2 -type d > ${DBDIR}/necessary_updates.remain
	fi
	cp "${DBDIR}/necessary_updates.remain" "${TMPDIR}/necessary_updates"
	while read dbpath
	do
		portname=`basename "$dbpath"`
		catpath=`dirname "$dbpath"`
		catname=`basename "$catpath"`
		origin=$catname/$portname
		if [ -e "${DBDIR}/requires/$origin/new_version" ]
		then
			touch "${DBDIR}/requires/$origin/necessary_update"
			if [ -e "${DBDIR}/requires/$origin/dependents" ]
			then
				while read origin_dependent
				do
					touch "${DBDIR}/requires/$origin_dependent/necessary_update"
				done < ${DBDIR}/requires/$origin/dependents
			fi
		fi
		sed -i '' 1d "${DBDIR}/necessary_updates.remain"
	done < ${TMPDIR}/necessary_updates
	touch "${DBDIR}/COMPLETE_NECESSARY_UPDATES"
	echo
fi

# Copying dependencies for preparation
if [ ! -e "${DBDIR}/COMPLETE_COPY_DEPENDENCY_TMPFILES" ]
then
	echo "Starting preparation for order of dependencies at `timestamp`"
	find "${DBDIR}/requires" -depth 3 -name requires -exec cp -p {} {}.remained \;
	touch "${DBDIR}/COMPLETE_COPY_DEPENDENCY_TMPFILES"
	echo
fi

# Clean up of reinstallation status for preparation
if [ -e "${DBDIR}/MODE_REDO" -a ! -e "${DBDIR}/COMPLETE_CLEANUP_REINST_STATUS" ]
then
	echo "Starting preparation for order of dependencies at `timestamp`"
	find "${DBDIR}/requires" -depth 3 -type d -name status -exec rm -rf {} \; -exec mkdir {} \;
	touch "${DBDIR}/COMPLETE_CLEANUP_REINST_STATUS"
	echo
fi

# Order the ports considering dependencies
if [ ! -e "${DBDIR}/COMPLETE_ORDERED_ALL_DEPENDENCIES" ]
then
	echo "Starting order of dependencies at `timestamp`"
	[ -f "${DBDIR}/reinst_order.list.tmp" ] && echo "INFO: Restarting from the previously terminated point"
	touch "${DBDIR}/reinst_order.list.tmp"
	cd "${DBDIR}/requires"
	cat > ${TMPDIR}/order_dependencies.awk << eof
BEGIN {
	it = 0;
	i = 0;
}
{
	if (NF == 0)
	{
		i = 0;
	}
	else
	{
		if (i == 0)
		{
			target = \$0;
			sub (/\/requires\.remained$/, "", target);
			sub (/^\.\//, "", target);
			srcikey[it] = target;
			srckeyi[target] = it;
			it++;
		}
		else
		{
			src[it-1,i-1] = \$0;
			srcimax[it-1] = srcsize[it-1] = i;
		}
		i++;
	}
}
END {
	ntargets = it;
	norder = 0;
	order_str = "";
	icycle = 0;
	lf_order_str = "";
	while (1)
	{
		is_operated = 0;
		for (it = 0; it < ntargets; it++)
		{
			if (!(it in srcikey)) continue;
			if (srcsize[it] > 0) continue;
			is_operated = 1;
			target = srcikey[it];
# 			print "[" icycle "]-- " target;
			delete srcikey[it];
			order[norder++] = target;
			order_str = order_str lf_order_str;
			order_str = sprintf ("%s%s", order_str, target);
			lf_order_str = "\n";
			for (jt = 0; jt < ntargets; jt++)
			{
				for (j = 0; j < srcimax[jt]; j++)
				{
					if ((jt,j) in src && src[jt,j] == target)
					{
						delete src[jt,j];
						srcsize[jt]--;
						break;
					}
				}
			}
		}
		if (is_operated == 0) break;
		icycle++;
	}
	reinst_order_list = sprintf ("%s%s", ENVIRON["DBDIR"], "/reinst_order.list.tmp");
	print order_str > reinst_order_list;
	unsatisfied = "";
	for (it = 0; it < ntargets; it++)
	{
		if (srcsize[it] == 0) continue;
		reqs = "";
		sp_reqs = "";
		for (i = 0; i < srcimax[it]; i++)
		{
			if ((it,i) in src)
			{
				reqs = reqs ", " src[it,i];
				sp_reqs = ", ";
			}
		}
		unsatisfied = sprintf ("%s%s [%d] (%s)\n", unsatisfied, srcikey[it], srcsize[it], reqs);
	}
	if (unsatisfied != "")
	{
		unsatisfied_list = sprintf ("%s%s", ENVIRON["DBDIR"], "/unsatisfied.list");
		print unsatisfied > unsatisfied_list;
		exit 1;
	}
}
eof
	find . -depth 3 -name requires.remained -exec echo {} \; -exec cat {} \; -exec echo \; | \
		env DBDIR=${DBDIR} awk -f "${TMPDIR}"/order_dependencies.awk || { \
			echo "ERROR: Unsatisfied dependencies are remained" >&2
			cat "${DBDIR}/unsatisfied.list"
			echo "*** Aborted by ${APPNAME}"
			echo "The ports tree seems broken. You might have caught an incomplete version."
			echo "You are encouraged to update the ports tree by portsnap(8)."
			echo "Then execute"
			echo " ${APPNAME} clean"
			echo "before restart."
			terminate_process ()
			{
			}
			exit 1
		}
	cp "${DBDIR}/reinst_order.list.tmp" "${DBDIR}/reinst_order.list"
	touch "${DBDIR}/COMPLETE_ORDERED_ALL_DEPENDENCIES"
	rm -f "${TMPDIR}/targets"
	echo
fi

# Deinstallation of obsolete packages
if [ ! -e "${DBDIR}/COMPLETE_LIST_DEINST_OBS_PKGS" ]
then
	echo "Starting to compose a list of deinstallation of obsolete packages at `timestamp`"
	while read origin
	do
		origin_ptn=`str_escape_regexp ${origin}`
		[ `grep -m 1 -E "^${origin_ptn}$" "${DBDIR}/HOLD_PORTS.conflist" 2> /dev/null | wc -l` -gt 0 ] || echo $origin
	done < ${DBDIR}/moved_or_lost.list > ${DBDIR}/moved_or_lost.actual.list.tmp
	mv "${DBDIR}"/moved_or_lost.actual.list.tmp "${DBDIR}"/moved_or_lost.actual.list
	touch "${DBDIR}/COMPLETE_LIST_DEINST_OBS_PKGS"
	echo
fi

# ------- Main operations -------

if [ "$command" = prepare ]
then
	echo "Done (skipped reinstallation) at `timestamp`"
	echo
	echo " You can restart this process from the aborted/terminated point by"
	echo "executing without options or arguments as:"
	echo "  ${APPNAME}"
	terminate_process ()
	{
	}
	exit
fi

terminate_process ()
{
	terminate_process_common
	terminate_process ()
	{
	}
}

# Deinstallation of obsolete packages
if [ ! -e "${DBDIR}/COMPLETE_DEINST_OBS_PKGS" ]
then
	_MSG_CURRENT_STAGE_general="deinstallation of obsolete packages"
	_MSG_CURRENT_STAGE=${_MSG_CURRENT_STAGE_general}
	echo "Starting deinstallation of obsolete packages at `timestamp`"
	[ -d "${DBDIR}/backup_obsolete" ] || mkdir -p "${DBDIR}/backup_obsolete"
	[ -d "${DBDIR}/status_deinst" ] || mkdir -p "${DBDIR}/status_deinst"
	if [ `cat "${DBDIR}/moved_or_lost.list" | wc -l` -gt 0 ]
	then
		savedir=`pwd`
		if [ -f "${DBDIR}/moved_or_lost.list.remained" ]
		then
			echo "INFO: Restarting from the previously terminated point"
		else
			cp -p "${DBDIR}/moved_or_lost.list" "${DBDIR}/moved_or_lost.list.remained"
		fi
		cp -p "${DBDIR}/moved_or_lost.list.remained" "${TMPDIR}/moved_or_lost.tmp"
		nlines=`cat "${TMPDIR}/moved_or_lost.tmp" | wc -l`
		iline=1
		cd "${DBDIR}/backup_obsolete"
		while [ $iline -le $nlines ]
		do
			origin=`sed -n ${iline}p "${TMPDIR}/moved_or_lost.tmp"`
			iline=$(($iline+1))
			currentpkg=`pkg_info -qO "$origin" 2> /dev/null`
			[ -n "$currentpkg" ] || continue
			origin_ptn=`str_escape_regexp ${origin}`
			if [ `grep -m 1 -E "^${origin_ptn}$" "${DBDIR}/HOLD_PORTS.conflist" 2> /dev/null | wc -l` -gt 0 ]
			then
				echo "-- (Skipping a hold package for port $origin as $currentpkg)"
				continue
			fi
			_MSG_CURRENT_STAGE=${_MSG_CURRENT_STAGE_general}
			echo "-- (Creating backup package for $origin as $currentpkg)"
			tag=`echo $origin | sed 's|/|.|'`
			if [ ! -e "${DBDIR}/status_deinst/$tag.backup" ]
			then
				pkg_create -b "$currentpkg" || { echo "*** Continuating forcibly by hoping success..."; continue; }
				touch "${DBDIR}/status_deinst/$tag.backup"
			fi
			pkg_delete -f "$currentpkg" || { echo "*** Continuating forcibly by hoping success..."; continue; }
			rm_a_line "$origin" "${DBDIR}/moved_or_lost.list.remained"
			_MSG_CURRENT_STAGE=${_MSG_CURRENT_STAGE_general}
			echo
		done
		cd "$savedir"
	fi
	touch "${DBDIR}/COMPLETE_DEINST_OBS_PKGS"
	_MSG_CURRENT_STAGE=
	echo
fi

# Reinstallation of remained ports
[ -e "${DBDIR}/MODE_REDO" ] && rm -f "${DBDIR}/COMPLETE_REINSTALLATION" "${DBDIR}/COMPLETE_CLEANUP_OBSLETE_DISTFILES" "${DBDIR}/COMPLETE_REBUILD_PKGDB"
if [ ! -e "${DBDIR}/COMPLETE_REINSTALLATION" ]
then
	_MSG_CURRENT_STAGE_general="reinstallation"
	_MSG_CURRENT_STAGE=${_MSG_CURRENT_STAGE_general}
	echo "Starting reinstallation at `timestamp`"
	savedir=`pwd`
	mkdir "${TMPDIR}/backup"
	[ -d "${DBDIR}/backup_failure" ] || mkdir -p "${DBDIR}/backup_failure"
	if [ ! -e "${DBDIR}/MODE_REDO" -a -f "${DBDIR}/reinst_todo.list" ]
	then
		echo "INFO: Restarting from the previously terminated point"
	else
		cp -p "${DBDIR}/reinst_order.list" "${DBDIR}/reinst_todo.list"
	fi
	rm -f "${DBDIR}/MODE_REDO"
	cp -p "${DBDIR}/reinst_todo.list" "${TMPDIR}/reinst_todo.tmp"
	touch "${DBDIR}/failed.list"
	touch "${DBDIR}/success_but_dependencies_failed.list"
	touch "${DBDIR}/success.list"
	nlines_tot=$((`cat "${DBDIR}/reinst_order.list" | wc -l`))
	nlines=`cat "${TMPDIR}/reinst_todo.tmp" | wc -l`
	icount=$(($nlines_tot-$nlines))
	iline=1
	while [ $iline -le $nlines ]
	do
		_MSG_CURRENT_STAGE=${_MSG_CURRENT_STAGE_general}
		origin=`sed -n ${iline}p "${TMPDIR}/reinst_todo.tmp"`
		iline=$(($iline+1))
		icount=$(($icount+1))
		MAKE_ARGS="FORCE_PKG_REGISTER=yes `[ $avoid_vulner = yes ] || echo DISABLE_VULNERABILITIES=yes` `cat "${DBDIR}/requires/$origin/MARG.conflist" 2> /dev/null || :`"
		MAKE_ENVS=`cat "${DBDIR}/requires/$origin/MENV.conflist" 2> /dev/null || :`
		counter="[$icount/$nlines_tot $(($icount*100/$nlines_tot))%]"
		if [ -e "${DBDIR}"/requires/$origin/new_version ]
		then
			newpkg=`cat "${DBDIR}"/requires/$origin/new_version`
		else
			newpkg=
		fi
		currentpkg=`pkg_info -qO "$origin" 2> /dev/null || :`
		pkgtag=$currentpkg
		[ -n "$pkgtag" ] || pkgtag=$newpkg
		if [ -z "$pkgtag" ]
		then
			pkgtag='?'
		
		elif [ -n "$newpkg" -a "x$currentpkg" != "x$newpkg" ]
		then
			if [ -n "$currentpkg" ]
			then
				pkgtag="$currentpkg => $newpkg"
			else
				pkgtag="[new] $newpkg"
			fi
		fi
		echo $pkgtag > ${DBDIR}/requires/$origin/pkgtag
		position_msg="$origin ($pkgtag)"
		if grep -m 1 -E "^`str_escape_regexp ${origin}`$" "${DBDIR}/success.list" 2> /dev/null > /dev/null
		then
			echo "========== $counter (Skipping an already reinstalled package for port $position_msg at `timestamp`) =========="
			echo
			rm_a_line "$origin" "${DBDIR}/reinst_todo.list"
			continue
		fi
		if [ $skip_unchanged = yes -a ! -e "${DBDIR}/requires/$origin/necessary_update" ]
		then
			echo "========== $counter (Skipping a port, $position_msg, because itself and the all of its requirements are already latest at `timestamp`) =========="
			echo
			rm_a_line "$origin" "${DBDIR}/reinst_todo.list"
			continue
		fi
		_is_all=y
		_is_target=
		_is_dependent=
		if [ `cat "${DBDIR}/target_required_ports.specified" 2> /dev/null | wc -l` -gt 0 ]
		then
			_is_all=
			if [ ! -f "${DBDIR}/requires/$origin/dependents.pattern" ]
			then
				str_escape_regexp_filter < "${DBDIR}/requires/$origin/dependents" | sed 's/^/^/;s/$/$/' > "${DBDIR}/requires/$origin/dependents.pattern" 2> /dev/null || :
			fi
			if grep -m 1 -E "^`str_escape_regexp $origin`$" "${DBDIR}/target_required_ports.specified" > /dev/null 2> /dev/null
			then
				_is_target=y
			elif grep -m 1 -E -f "${DBDIR}/requires/$origin/dependents.pattern" "${DBDIR}/target_required_ports.specified" > /dev/null 2> /dev/null
			then
				_is_required=y
			fi
		fi
		_is_required=
		if [ `cat "${DBDIR}/target_dependent_ports.specified" 2> /dev/null | wc -l` -gt 0 ]
		then
			_is_all=
			if [ ! -f "${DBDIR}/requires/$origin/requires.pattern" ]
			then
				str_escape_regexp_filter < "${DBDIR}/requires/$origin/requires" | sed 's/^/^/;s/$/$/' > "${DBDIR}/requires/$origin/requires.pattern" 2> /dev/null || :
			fi
			if [ -n "${_is_target}" ] || grep -m 1 -E "^`str_escape_regexp $origin`$" "${DBDIR}/target_dependent_ports.specified" > /dev/null 2> /dev/null
			then
				_is_target=y
			elif grep -m 1 -E -f "${DBDIR}/requires/$origin/requires.pattern" "${DBDIR}/target_dependent_ports.specified" > /dev/null 2> /dev/null
			then
				_is_dependent=y
			fi
		fi
		if [ -z "${_is_all}" ]
		then
			if [ -n "${_is_target}${_is_dependent}${_is_required}" ]
			then
				[ "${_is_target}" = y ] && echo "(Target port)"
				[ "${_is_dependent}" = y ] && echo "(Dependent of the target port(s))"
				[ "${_is_required}" = y ] && echo "(Required by the target port(s))"
			else
				echo "========== $counter (Skipping an irrelative package for port $position_msg at `timestamp`) =========="
				echo
				rm_a_line "$origin" "${DBDIR}/reinst_todo.list"
				continue
			fi
		fi
		if grep -m 1 -E "^`str_escape_regexp ${origin}`$" "${DBDIR}/HOLD_PORTS.conflist" 2> /dev/null > /dev/null
		then
			echo "========== $counter (Skipping a hold package for port $position_msg at `timestamp`) =========="
			echo
			rm_a_line "$origin" "${DBDIR}/reinst_todo.list"
			continue
		fi
		if grep -m 1 -E "^`str_escape_regexp $origin`$" "${DBDIR}/taboo.all.list" 2> /dev/null > /dev/null
		then
			echo "========== $counter (Ignored a taboo port $position_msg at `timestamp`) =========="
			echo
			rm_a_line "$origin" "${DBDIR}/reinst_todo.list"
			continue
		fi
		if grep -m 1 -E "^`str_escape_regexp $origin`$" "${DBDIR}/manually_done.list" 2> /dev/null > /dev/null
		then
			echo "========== $counter (Marking a manually-done port $position_msg as success at `timestamp`) =========="
			echo
			record_success $origin
			rm_a_line "$origin" "${DBDIR}/reinst_todo.list"
			continue
		fi
		if [ -e "${DBDIR}/requires/$origin/installed_version" ]
		then
			insttarget=reinstall
			instdesc='a reinstallation'
		else
			insttarget=install
			instdesc='an installation'
		fi
		_MSG_CURRENT_STAGE="$instdesc process for $position_msg $counter"
		echo "========== $counter Starting $instdesc process for $position_msg at `timestamp` =========="
		[ "${_is_target}" = y ] && echo '(Target port)'
		[ "${_is_dependent}" = y ] && echo '(Dependent of the target port(s))'
		[ "${_is_required}" = y ] && echo '(Required by the target port(s))'
		cd "${PORTSDIR}/$origin"
		if [ -e "${DBDIR}/requires/$origin/status/in_build" ]
		then
			echo "(Restarting the previously terminated build process...)"
		else
			touch "${DBDIR}/requires/$origin/status/in_build"
		fi
		if [ -e "${DBDIR}/requires/$origin/BEFOREBUILD.conflist" -a ! -e "${DBDIR}/requires/$origin/status/COMPLETE_BEFOREBUILD" ]
		then
			echo "-- BEFOREBUILD operations (start)"
			sh -e "${DBDIR}/requires/$origin/BEFOREBUILD.conflist" || \
			{
				echo "ERROR: while BEFOREBUILD operations for ${PORTSDIR}/$position_msg." >&2
				echo 'BEFOREBUILD operations' > ${DBDIR}/requires/$origin/note_failtre
				record_failure $origin noclean
				rm_a_line "$origin" "${DBDIR}/reinst_todo.list"
				cmt_fail_reinst "$origin"
				continue
			}
			echo "-- BEFOREBUILD operations (end)"
			touch "${DBDIR}/requires/$origin/status/COMPLETE_BEFOREBUILD"
		fi
		if [ ! -e "${DBDIR}/requires/$origin/status/COMPLETE_CLEAN_BEFORE_BUILD" ]
		then
			env ${MAKE_ENVS} make clean ${MAKE_ARGS} || \
			{
				echo "ERROR: Check the permission of directory ${PORTSDIR}/$position_msg." >&2
				echo 'clean before build' > ${DBDIR}/requires/$origin/note_failtre
				record_failure $origin noclean
				rm_a_line "$origin" "${DBDIR}/reinst_todo.list"
				cmt_fail_reinst "$origin"
				continue
			}
			echo
			touch "${DBDIR}/requires/$origin/status/COMPLETE_CLEAN_BEFORE_BUILD"
		fi
		if [ ! -e "${DBDIR}/requires/$origin/status/COMPLETE_FETCH" ]
		then
			if [ ! -e "${DBDIR}/requires/$origin/status/FAILED_FETCH" ]
			then
				if env ${MAKE_ENVS} make checksum ${MAKE_ARGS}
				then
					:
				else
					touch "${DBDIR}/requires/$origin/status/FAILED_FETCH"
				fi
			fi
			if [ -e "${DBDIR}/requires/$origin/status/FAILED_FETCH" ]
			then
				echo "WARN: Refetching distfiles for ${PORTSDIR}/$position_msg." >&2
# 				if [ ! -e "${DBDIR}/requires/$origin/status/COMPLETE.FAILED_FETCH.DISTCLEAN" ]
# 				then
# 					env ${MAKE_ENVS} make distclean NOCLEANDEPENDS=yes ${MAKE_ARGS} || \
# 					{
# 						echo "ERROR: Failed in distclean for ${PORTSDIR}/$position_msg." >&2
# 						echo 'distclean for refetch' > ${DBDIR}/requires/$origin/note_failtre
# 						record_failure $origin
# 						rm_a_line "$origin" "${DBDIR}/reinst_todo.list"
# 						cmt_fail_reinst "$origin"
# 						continue
# 					}
# 					touch "${DBDIR}/requires/$origin/status/COMPLETE.FAILED_FETCH.DISTCLEAN"
# 				fi
				if [ ! -e "${DBDIR}/requires/$origin/status/COMPLETE.FAILED_FETCH.REFETCH" ]
				then
					env ${MAKE_ENVS} make fetch ${MAKE_ARGS} FETCH_ARGS=-Ap || \
					{
						echo "ERROR: Failed in fetch for ${PORTSDIR}/$position_msg." >&2
						echo 'fetch' > ${DBDIR}/requires/$origin/note_failtre
						record_failure $origin
						rm_a_line "$origin" "${DBDIR}/reinst_todo.list"
						cmt_fail_reinst "$origin"
						continue
					}
					env ${MAKE_ENVS} make checksum ${MAKE_ARGS} || \
					{
						echo "ERROR: Failed in checksum for ${PORTSDIR}/$position_msg." >&2
						echo 'checksum' > ${DBDIR}/requires/$origin/note_failtre
						record_failure $origin
						rm_a_line "$origin" "${DBDIR}/reinst_todo.list"
						cmt_fail_reinst "$origin"
						continue
					}
					touch "${DBDIR}/requires/$origin/status/COMPLETE.FAILED_FETCH.REFETCH"
				fi
			fi
			touch "${DBDIR}/requires/$origin/status/COMPLETE_FETCH"
		fi
		if [ ! -e "${DBDIR}/requires/$origin/status/COMPLETE_BUILD" ]
		then
			if env ${MAKE_ENVS} make ${MAKE_ARGS}
			then
				:
			else
				echo 'build' > ${DBDIR}/requires/$origin/note_failtre
				record_failure $origin
				rm_a_line "$origin" "${DBDIR}/reinst_todo.list"
				cmt_fail_reinst "$origin"
				continue
			fi
			touch "${DBDIR}/requires/$origin/status/COMPLETE_BUILD"
		fi
		rm -f "${DBDIR}/requires/$origin/status/in_build"
		if [ -e "${DBDIR}/requires/$origin/status/in_install" ]
		then
			echo "(Restarting the previously terminated installation process...)"
		else
			touch "${DBDIR}/requires/$origin/status/in_install"
		fi
		if [ -n "$currentpkg" -a ! -e "${DBDIR}/requires/$origin/status/COMPLETE_PKG_BACKUP" ]
		then
			echo "-- (Creating temporal backup package for $position_msg)"
			cd "${DBDIR}/backup_failure"
			pkg_create -b "$currentpkg" || \
			{
				rm -f "${DBDIR}/backup_failure/$currentpkg.tbz"
				echo "WARN: Failed to create the backup package, but ignored by hoping success." >&2
			}
			cd "${PORTSDIR}/$origin"
			touch "${DBDIR}/requires/$origin/status/COMPLETE_PKG_BACKUP"
		fi
		if [ -n "$currentpkg" -a ! -e "${DBDIR}/requires/$origin/status/COMPLETE_BEFOREDEINSTALL" ]
		then
			if [ -e "${DBDIR}/requires/$origin/BEFOREDEINSTALL.conflist" ]
			then
				echo "-- BEFOREDEINSTALL operations (start)"
				sh -e "${DBDIR}/requires/$origin/BEFOREDEINSTALL.conflist" || \
				{
					echo "ERROR: while BEFOREDEINSTALL operations for ${PORTSDIR}/$position_msg." >&2
					echo 'BEFOREDEINSTALL operations' > ${DBDIR}/requires/$origin/note_failtre
					record_failure $origin noclean
					rm_a_line "$origin" "${DBDIR}/reinst_todo.list"
					cmt_fail_reinst "$origin"
					continue
				}
				echo "-- BEFOREDEINSTALL operations (end)"
			fi
			touch "${DBDIR}/requires/$origin/status/COMPLETE_BEFOREDEINSTALL"
		fi
		if [ -n "$currentpkg" -a ! -e "${DBDIR}/requires/$origin/status/COMPLETE_DEINSTALL" ]
		then
			pkg_delete -f $currentpkg || \
			{
				echo "WARN: Failed to deinstall $currentpkg by pkg_delete." >&2
			}
			echo "-- (Trying to deinstall by ports to make sure. This usually ends up with warnings.)"
			env ${MAKE_ENVS} make deinstall ${MAKE_ARGS} || \
			{
				echo "WARN: Failed to deinstall $currentpkg by make deinstall." >&2
			}
			touch "${DBDIR}/requires/$origin/status/COMPLETE_DEINSTALL"
		fi
		if [ ! -e "${DBDIR}/requires/$origin/status/COMPLETE_INSTALL" ]
		then
			if [ ! -e "${DBDIR}/requires/$origin/status/FAILED_INSTALL" ]
			then
				if env ${MAKE_ENVS} make $insttarget ${MAKE_ARGS}
				then
					touch "${DBDIR}/requires/$origin/status/COMPLETE_INSTALL"
				else
					touch "${DBDIR}/requires/$origin/status/FAILED_INSTALL"
				fi
			fi
			if [ -e "${DBDIR}/requires/$origin/status/FAILED_INSTALL" ]
			then
				if [ ! -e "${DBDIR}/requires/$origin/status/COMPETE.FAILED_INSTALL.RECOVER" ]
				then
					echo "*** Trying to deinstall the failed/terminated installation... (Ignore failures)"
					if [ -n "$currentpkg" ]
					then
						pkg_delete -f $currentpkg || :
					fi
					echo "*** Trying to deinstall by ports to make sure. (This usually ends up with warnings)"
					env ${MAKE_ENVS} make deinstall ${MAKE_ARGS} || :
					if [ -n "$currentpkg" ]
					then
						echo "*** Restoring the backup..."
						if [ -e "${DBDIR}/backup_failure/$currentpkg.tbz" ]
						then
							echo "WARN: No backup exists, gave up." >&2
						elif pkg_add -fF "${DBDIR}/backup_failure/$currentpkg.tbz"
						then
							:
						else
							echo "WARN: Failed to restore $currentpkg. Note that your system may experience troubles by this error." >&2
						fi
					fi
					touch "${DBDIR}/requires/$origin/status/COMPETE.FAILED_INSTALL.RECOVER"
				fi
				if [ -n "$currentpkg" -a ! -e "${DBDIR}/requires/$origin/status/COMPETE.FAILED_INSTALL.AFTERINSTALL" -a -e "${DBDIR}/requires/$origin/AFTERINSTALL.conflist" ]
				then
					echo "-- AFTERINSTALL operations (start)"
					sh -e "${DBDIR}/requires/$origin/AFTERINSTALL.conflist" || { echo "-- (This error is ignored)"; }
					echo "-- AFTERINSTALL operations (end)"
					touch "${DBDIR}/requires/$origin/status/COMPETE.FAILED_INSTALL.AFTERINSTALL"
				fi
				echo 'install' > ${DBDIR}/requires/$origin/note_failtre
				record_failure $origin noclean
				rm_a_line "$origin" "${DBDIR}/reinst_todo.list"
				cmt_fail_reinst "$origin"
				continue
			fi
		fi
		if [ -e "${DBDIR}/requires/$origin/AFTERINSTALL.conflist" -a ! -e "${DBDIR}/requires/$origin/status/in_install.AFTERINSTALL" ]
		then
			echo "-- AFTERINSTALL operations (start)"
			sh -e "${DBDIR}/requires/$origin/AFTERINSTALL.conflist" || \
			{
				echo "ERROR: while AFTERINSTALL operations for ${PORTSDIR}/$position_msg." >&2
				echo 'AFTERINSTALL operations' > ${DBDIR}/requires/$origin/note_failtre
				record_failure $origin noclean
				rm_a_line "$origin" "${DBDIR}/reinst_todo.list"
				cmt_fail_reinst "$origin"
				continue
			}
			echo "-- AFTERINSTALL operations (end)"
			touch "${DBDIR}/requires/$origin/status/in_install.AFTERINSTALL"
		fi
		if [ ! -e "${DBDIR}/requires/$origin/status/COMPLETE_CLEAN_AFTER_INSTALL" ]
		then
			env ${MAKE_ENVS} make clean ${MAKE_ARGS} || \
			{
				echo "WARN: Failed to clean $position_msg." >&2
			}
			touch "${DBDIR}/requires/$origin/status/COMPLETE_CLEAN_AFTER_INSTALL"
		fi
		record_success $origin
		rm_a_line "$origin" "${DBDIR}/reinst_todo.list"
		rm -f "${DBDIR}/backup_failure/$currentpkg.tbz"
		rm "${DBDIR}/requires/$origin/status/in_install"
		_MSG_CURRENT_STAGE=${_MSG_CURRENT_STAGE_general}
		echo
	done
	cd "$savedir"
	touch "${DBDIR}/COMPLETE_REINSTALLATION"
	_MSG_CURRENT_STAGE=
	echo
fi

# Clean up obsolete or unused distfiles
if [ ! -e "${DBDIR}/COMPLETE_CLEANUP_OBSLETE_DISTFILES" ]
then
	echo "Starting to clean up obsolete or unused distfiles at `timestamp`"
	find "${DISTDIR}" -type f | grep -v -E -f "${DBDIR}/distfiles.grep.pattern" | while read distfile
	do
		echo "  $distfile"
		rm -f "$distfile"
	done
	touch "${DBDIR}/COMPLETE_CLEANUP_OBSLETE_DISTFILES"
	echo
fi

# Rebuild of package database
if [ ! -e "${DBDIR}/COMPLETE_REBUILD_PKGDB" ]
then
	if which -s pkgdb
	then
		echo "Starting to rebuild package database for portupgrade at `timestamp`"
		pkgdb -fu
	fi
	touch "${DBDIR}/COMPLETE_REBUILD_PKGDB"
	echo
fi

# Notice of failures
touch "${DBDIR}/failed.list"
if [ `cat "${DBDIR}/failed.list" | wc -l` -gt 0 ]
then
	echo "*** (Re)installation of the following ports were unsuccessful."
	echo " Please recover them manually."
	echo " You are recommended to read ${PORTSDIR}/UPDATING to resolve the problems."
	echo " If you remember the last date to have upgraded the concerned ports,"
	echo "pkg_updating(1) will be useful."
	echo " You should also consider whether the failed ports are really required, viz.,"
	echo "not obsolete dependencies, by using pkg_info(1) with -R option."
	echo " After resolving the problems, execute"
	echo "        ${APPNAME} ok [resolved_port_globs]"
	echo "and restart by"
	echo "        ${APPNAME} redo"
	echo "****************"
	show_list_failure
	echo "*** Warned by ${APPNAME}"
	echo
fi

# End
terminate_process ()
{
}

echo "All done at `timestamp`"
