Subject: Small script for copying packages
To: None <tech-pkg@netbsd.org>
From: Robert Elz <kre@munnari.OZ.AU>
List: tech-pkg
Date: 06/20/2005 09:59:17
This is a multipart MIME message.

--==_Exmh_-21020503100
Content-Type: text/plain; charset=us-ascii

I have this script I use for copying binary packages from one
place to another, that I thought I should share in case it can
save someone else some effort sometime.

Usage is:
	pkg_cp pkg... target-directory

(no other options, though arguably, '-p' should be an option rather
that simply enforced by the script).

Purpose is to copy all the packages named to the directory that is the
last arg of the script (command).    Well, that's just "cp", what this
script also does, is also copy any dependencies (run time dependencies)
of the packages that it is asked to copy, so, if you want kde, you can
just do

	pkg_cp kde-3.1.4.tgz /some/where

and you end up with a sh*tload of stuff in the /some/where directory...

Dependencies (and packages) are copied only if they don't already exist
in the target directory.   Dependencies are copied only if no suitable
package to depend upon exists in the target directory (that is, if there
is something there which satisfies the dependency, that's enough, even if
it isn't the latest version of the dependency available - if something
different is wanted, copy the dependency package explicitly, or start with
an empty target directory).

All arg packages are copied before any dependencies (and then all dependencies
of arg packages are copied, before any of their dependencies, and so on).

If nothing adequate is already in the target directory when it is time
to copy dependencies, they are sought first in the directory from which
the package that required them was copied, and if not found there, from
any other directories named on the command line (as paths to packages to
copy) in the order they appear on the command line.   Dependencies are
added for packages in the order of the command line (and then in the order
an earlier dependency was added).   This matters in a case like

	pkg_cp source1/X source2/Y target

where source1/X required D>=1.2 and source1/D-1.2 exists, and source2/Y
required D>=1.3 (and source2/D-1.3 exists).   As written above, both
D-1.2 and D-1.3 will appear in target (assuming no D-* existed previously).

But, if the command had been

	pkg_cp source2/Y source1/X target

only D-1.3 would have been copied to target, that being sufficient to
satisfy the dependency of X, and copied before X's dependency is required.

Or that's how I think it works - this is so esoteric that I haven't actually
checked to make sure it behaves as intended (and if it doesn't, I'm not sure
that it is important enough to fix).

A warning is printed to stderr if a required dependency cannot be located
anywhere (that is, not already in the target directory, and in no source
directories named on the command line).   No env vars ae used to name
extra places to look (though adding that wouldn't be all that hard).

The basic technique for dependency finding and handling here was blatantly
stolen from pkgtools/cdpack (though pkg_cp avoids the need for temp files
or directories).

This script has no inbuilt assumptions about why the packages might be
being copied, so it does no checking for NO_PKG_ON_* type settings in the
package (and nor should it ever!)

That's it.    If someone improves this, please send it back (I actually use
this thing.)

kre

(script attached - no man page, or anything else (it needs no config or
installation really) exists).



--==_Exmh_-21020503100
Content-Type: text/plain ; name="pkg_cp"; charset=us-ascii
Content-Description: pkg_cp
Content-Disposition: attachment; filename="PKG_CP"

#! /bin/sh

dirscan() {
	local t

	t="$1"
	shift

	while [ $# -gt 1 ]
	do
		if [ "%${t}%" = "%${1}%" ]
		then
			echo "$2"
			return
		fi
		shift 2
	done
}

DIRLIST=
setdirlist() {
	local d;
	local r;

	d="$1"
	case "${d}" in
	'')	d=.;;
	/*)	return;;
	?*/)	d="${d%%/}";;
	esac

	r=`dirscan ${d} ${DIRLIST}`
	case "${r}" in
	'')	;;
	*)	return;;
	esac

	r=`cd "${d}" 2>/dev/null && /bin/pwd`
	case "$r" in
	'')	r="Error";;
	esac

	DIRLIST="${DIRLIST}${DIRLIST:+ }${d} ${r}"
}

pathname() {
	case "$1" in
	/*[!/]*/)	echo "${1%%/}";;
	/*/)		echo "/";;
	/*)		echo "$1";;
	'')		dirscan . ${DIRLIST};;
	*)		dirscan "$1" ${DIRLIST};;
	esac
}

case "$#" in
0|1)	echo "Usage: $0 pkg... target" >&2; exit 1;;
esac

case "$*" in
*'|||'*)	echo "Sorry, Cannot handle that arg list" >&2; exit 1;;
esac

set -- "$@" '|||'

while [ "%${2}%" != '%|||%' ]
do
	A="$1"
	shift
	set -- "$@" "${A}"
done

TARGET="$1"
shift
shift

test -d "${TARGET}" || {
	echo "Target '${TARGET}' is not a directory"
	exit 1
}

test -w "${TARGET}" || {
	echo "Target '${TARGET}' is not writeable"
	exit 1
}

JUNK="${TARGET}/_NONSENSE__;&3.$$"
trap 'rm -f "${JUNK}; exit 2' 0 1 2 3 13 15
touch "${JUNK}" 2>/dev/null || {
	echo "Target '${TARGET}' seems to be on a read-only filesystem"
	exit 1
}
rm -f "${JUNK}"
trap -  0 1 2 3 13 15

SRCDIRS=
for src
do
	pkg=`basename "${src}"`
	pkgdir="${src%${pkg}}"
	setdirlist "${pkgdir}"
	pkgdir=`pathname "${pkgdir}"`

	eval "case '${SRCDIRS}' in
		*' ${pkgdir} '*)			;;
		*)	SRCDIRS='${SRCDIRS} ${pkgdir} '	;;
		esac"
done

PKGS="$*"

while [ -n "${PKGS}" ]
do
	set -- ${PKGS}
	PKGS=

	deplist=
	for arg
	do
		test -f "${arg}" || {
			echo "${arg} does not exist" >&2
			continue
		}

		pkg=`basename "${arg}"`
		pkgdir=`pathname "${arg%${pkg}}"`
		pkgname="${pkg%.tgz}"

		test -f "${TARGET}/${pkg}" && continue
		cp -p "${arg}" "${TARGET}/${pkg}"

		deps=`tar -x -z -O -f "${arg}" --fast-read +CONTENTS |
				awk '/^@pkgdep/ {printf("%s ",$2)}'`
		test -z "${deps}" && continue

		flags="$-"
		set -f
		for dep in ${deps}
		do
			eval "case '${deplist}' in
				*' ${dep} '*)	;;
				*)		deplist='${deplist} ${dep} ';;
				esac"
		done

		case "$flags" in
		*f*)	;;
		*)	set +f;;
		esac

	done

	flags="$-"
	set -f

	for dep in ${deplist}
	do
		x=`pkg_admin lsbest "${TARGET}/${dep}"`
		test -n "$x" && continue;

		for dir in "${pkgdir}" ${SRCDIRS}
		do
			x=`pkg_admin lsbest "${dir}/${dep}"`
			test -n "$x" && break
		done
		test -n "$x" && {
			PKGS="${PKGS}${PKGS:+ }${x}"
			continue
		}
		echo "Warning: Cannot locate ${dep} for ${pkgname}" >&2
	done

	case "$flags" in
	*f*)	;;
	*)	set +f;;
	esac

done

--==_Exmh_-21020503100--