regpkg revision 1.10
1#! /bin/sh
2#
3# $NetBSD: regpkg,v 1.10 2006/01/08 10:10:03 apb Exp $
4#
5# Copyright (c) 2003 Alistair G. Crooks.  All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions
9# are met:
10# 1. Redistributions of source code must retain the above copyright
11#    notice, this list of conditions and the following disclaimer.
12# 2. Redistributions in binary form must reproduce the above copyright
13#    notice, this list of conditions and the following disclaimer in the
14#    documentation and/or other materials provided with the distribution.
15# 3. All advertising materials mentioning features or use of this software
16#    must display the following acknowledgement:
17#	This product includes software developed by Alistair G. Crooks.
18#	for the NetBSD project.
19# 4. The name of the author may not be used to endorse or promote
20#    products derived from this software without specific prior written
21#    permission.
22#
23# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
24# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
27# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34#
35
36# Usage: regpkg [options] set pkgname
37#
38# Registers a syspkg in the database directory,
39# and optionally creates a binary package.
40#
41# Options:
42#   -q		Quiet.
43#   -v		Verbose.
44#   -f		Force.
45#   -m		Ignore errors from missing files.
46#   -u		Update.
47#   -c		Use cached information from ${BUILD_INFO_CACHE}.
48#   -d destdir	Sets DESTDIR.
49#   -t binpkgdir Create a binary package (in *.tgz format) in the
50#		specified directory.  Without this option, a binary
51#		package is not created.
52#   -M metalog	Use the specified metalog file to override file
53#		or directory attributes when creating a binary package.
54#   -N etcdir	Use the specified directory for passwd and group files.
55#
56# When -f is set:  If the desired syspkg already exists, it is overwritten.
57# When -u is set:  If the desired syspkg already exists, it might be
58#		overwritten or left alone, depending on whether it's older
59#		or newer than the files that belong to the syspkg.
60# When neither -u nor -f are set:  It's an error for the desired syspkg
61#		to already exist.
62
63prog="${0##*/}"
64toppid=$$
65rundir="$(dirname "$0")" # ${0%/*} isn't good enough when there's no "/"
66. "${rundir}/sets.subr"
67
68bomb()
69{
70	#echo "${prog}: bomb: start, toppid=${toppid} \$\$=$$"
71	kill ${toppid}		# in case we were invoked from a subshell
72	#echo "${prog}: bomb: killed ${toppid}"
73	exit 1
74}
75
76# A literal newline
77nl='
78'
79# A literal tab
80tab='	'
81
82# Prefixes for error messages, warnings, and important informational
83# messages.
84ERROR="${prog}: ERROR: "
85WARNING="${prog}: WARNING: "
86NOTE="${prog}: NOTE: "
87ERRWARN="${ERROR}"	# may be changed by "-f" (force) command line flag
88ERRWARNNOTE="${ERROR}"	# may be changed by "-u" (update) command line flag
89
90#
91# All temporary files will go in ${SCRATCH}, which will be deleted on
92# exit.
93#
94SCRATCH="$( ${MKTEMP} -d "/var/tmp/${0##*/}.XXXXXX" )"
95if [ $? -ne 0 -o \! -d "${SCRATCH}" ]; then
96	echo >&2 "${prog}: Could not create scratch directory."
97	bomb
98fi
99
100#
101# cleanup() always deletes the SCRATCH directory, and might also
102# delete other files or directories.
103#
104es=0
105cleanup_must_delete_binpkgfile=false
106cleanup_must_delete_dbsubdir=false
107cleanup ()
108{
109	trap - 0
110	#echo "${prog}: cleanup start"
111	if ${cleanup_must_delete_binpkgfile:-false} && [ -e "${binpkgfile}" ]
112	then
113		echo >&2 "${prog}: deleting partially-created ${binpkgfile}"
114		rm -f "${binpkgfile}"
115	fi
116	if ${cleanup_must_delete_dbsubdir:-false} \
117	   && [ -e "${SYSPKG_DB_SUBDIR}" ]
118	then
119		echo >&2 "${prog}: deleting partially-created ${SYSPKG_DB_SUBDIR}"
120		rm -rf "${SYSPKG_DB_SUBDIR}"
121	fi
122	rm -rf "${SCRATCH}"
123	#echo "${prog}: cleanup done, exit ${es}"
124	exit ${es}
125}
126trap 'es=128; cleanup' 1 2 3 13 15	# HUP INT QUIT PIPE TERM
127trap 'es=$?; cleanup' 0 		# EXIT
128
129#
130# Parse command line args.
131#
132verbose=false
133verbosity=0
134quiet=false
135force=false
136update=false
137allowmissing=false
138DESTDIR="${DESTDIR}"
139binpkgdir=""
140metalog=""
141etcdir=""
142SYSPKG_DB_TOPDIR=""
143pkgset=""
144pkg=""
145parse_args ()
146{
147	while [ $# -gt 2 ]; do
148		case "$1" in
149		-q)	quiet=true ; verbose=false ;;
150		-v)	verbose=true ; quiet=false
151			verbosity=$(( ${verbosity} + 1 ))
152			;;
153		-f)	force=true ;;
154		-u)	update=true ;;
155		-m)	allowmissing=true ;;
156		-c)	# The -c option is ignored.  The BUILD_INFO_CACHE
157			# environment variable is used instead.
158			;;
159		-d)	DESTDIR="$2" ; shift ;;
160		-d*)	DESTDIR="${1#-?}" ;;
161		-t)	binpkgdir="$2" ; shift ;;
162		-t*)	binpkgdir="${1#-?}" ;;
163		-M)	metalog="$2" ; shift ;;
164		-M*)	metalog="${1#-?}" ;;
165		-N)	etcdir="$2" ; shift ;;
166		-N*)	etcdir="${1#-?}" ;;
167		*)	break ;;
168		esac
169		shift
170	done
171	if ${force}; then
172		ERRWARN="${WARNING}"
173	else
174		ERRWARN="${ERROR}"
175	fi
176	if ${update}; then
177		ERRWARNNOTE="${NOTE}"
178	else
179		ERRWARNNOTE="${ERRWARN}"
180	fi
181	DESTDIR="${DESTDIR%/}" # delete trailing "/" if any
182	if [ \! -n "${etcdir}" ]; then
183		etcdir="${DESTDIR}/etc"
184	fi
185	if [ -n "${binpkgdir}" -a \! -d "${binpkgdir}" ]; then
186		echo >&2 "${ERROR}binary pkg directory ${binpkgdir} does not exist"
187		bomb
188	fi
189	#
190	# SYSPKG_DB_TOPDIR is the top level directory for registering
191	# syspkgs.  It defaults to ${DESTDIR}/var/db/syspkg, but can be
192	# overridden by environment variables SYSPKG_DBDIR or PKG_DBDIR.
193	#
194	# Note that this corresponds to the default value of PKG_DBDIR
195	# set in .../distrib/syspkg/mk/bsd.syspkg.mk.
196	# 
197	SYSPKG_DB_TOPDIR="${SYSPKG_DBDIR:-${PKG_DBDIR:-${DESTDIR}/var/db/syspkg}}"
198
199	if [ $# -ne 2 ]; then
200		echo "Usage: regpkg [options] set pkgname"
201		bomb
202	fi
203
204	pkgset="$1"
205	pkg="$2"
206}
207
208#
209# make_PLIST() creates a skeleton PLIST from the pkgset description.
210#
211# The result is stored in the file ${PLIST}.
212#
213PLIST="${SCRATCH}/PLIST"
214make_PLIST ()
215{
216	if ${verbose} ; then
217		echo "Making PLIST for \"${pkg}\" package (part of ${pkgset} set)"
218	fi
219	prefix="${DESTDIR:-/}"
220	realprefix=/
221	${HOST_SH} "${rundir}/makeplist" -p "${prefix}" -I "${realprefix}" \
222		"${pkgset}" "${pkg}" \
223		>"${PLIST}" 2>"${SCRATCH}/makeplist-errors"
224	if ${EGREP} -v '^DEBUG:' "${SCRATCH}/makeplist-errors" ; then
225		# "find" invoked from makeplist sometimes reports
226		# errors about missing files or directories, and
227		# makeplist ignores the errors.  Catch them here.
228		echo >&2 "${ERROR}makeplist reported errors for ${pkg}:"
229		cat >&2 "${SCRATCH}/makeplist-errors"
230		echo >&2 "${ERROR}see above for errors from makeplist"
231		if ${allowmissing}; then
232			echo >&2 "${prog}: ${NOTE}: ignoring above errors, due to '-m' option."
233		else
234			${force} || bomb
235		fi
236	fi
237}
238
239#
240# init_allfiles() converts the PLIST (which contains relative filenames)
241# into a list of absolute filenames.  Directories are excluded from the
242# result.
243# 
244# The result is stored in the variable ${allfiles}.
245#
246allfiles=''
247init_allfiles ()
248{
249	[ -f "${PLIST}" ] || make_PLIST
250	allfiles="$( ${AWK} '
251		BEGIN { destdir = "'"${DESTDIR%/}"'" }
252		/^@cwd/ { prefix = $2; next }
253		/^@dirrm/ { next }
254		{ printf("%s%s%s\n", destdir, prefix, $0) }' "${PLIST}" )"
255}
256
257#
258# init_newestfile() finds the newest file (most recent mtime).
259#
260# The result is stored in the variable ${newestfile}.
261#
262newestfile=''
263init_newestfile ()
264{
265	[ -s "${allfiles}" ] || init_allfiles
266	# We assume no shell special characters in ${allfiles},
267	# and spaces only between file names, not inside file names.
268	# This should be safe, because it has no no user-specified parts.
269	newestfile="$( ${LS} -1dt ${allfiles} | ${SED} '1q' )"
270}
271
272#
273# Various ways of getting parts of the syspkg version number:
274#
275# get_osvers() - get the OS version number from osrelease.sh or $(uname -r),
276#		return it in ${osvers}, and set ${method}.
277# get_tinyvers() - get the tiny version number from the "versions" file,
278#		and return it in ${tinyvers}.  Does not set ${method}.
279# get_newest_rcsid_date() - get the newest RCS date,
280#		and return it in ${newest}.  Does not set ${method}.
281# get_newest_mtime_date() - get the newest file modification date,
282#		and return it in ${newest}.  Does not set ${method}.
283# get_newest_date() - get date from rcsid or mtime, return it in ${newest},
284#		and set ${method}.
285#
286get_osvers ()
287{
288	if [ -f ../../sys/conf/osrelease.sh ]; then
289		osvers="$(${HOST_SH} ../../sys/conf/osrelease.sh)"
290		method=osreleases
291	else
292		osvers="$(${UNAME} -r)"
293		method=uname
294	fi
295	#echo "${osvers}"
296}
297get_tinyvers ()
298{
299	tinyvers="$( ${AWK} '$1 ~ '/"${pkg}"/' { print $2 }' \
300			"${rundir}/versions" )"
301	case "${tinyvers}" in
302	"")	tinyvers=0
303		;;
304	esac
305	#echo "${tinyvers}"
306}
307get_newest_rcsid_date()
308{
309	[ -s "${allfiles}" ] || init_allfiles
310
311	# Old RCS identifiers might have 2-digit years, so we match both
312	# YY/MM/DD and YYYY/MM/DD.  We also try to deal with the Y10K
313	# problem by allowing >4 digit years.
314	newest=0
315	case "${allfiles}" in
316	"")	;;
317	*)	newest="$( ${IDENT} ${allfiles} 2>/dev/null | ${AWK} '
318			BEGIN { last = 0 }
319			$2 == "crt0.c,v" { next }
320			NF == 8 && \
321			$4 ~ /^[0-9][0-9]\/[0-9][0-9]\/[0-9][0-9]$/ \
322				{ t = "19" $4; gsub("/", "", t);
323				  if (t > last) last = t; }
324			NF == 8 && \
325			$4 ~ /^[0-9][0-9][0-9][0-9][0-9]*\/[0-9][0-9]\/[0-9][0-9]$/ \
326				{ t = $4; gsub("/", "", t);
327				  if (t > last) last = t; }
328			END { print last }' )"
329		method=ident
330		;;
331	esac
332	#echo "${newest}"
333}
334get_newest_mtime_date ()
335{
336	[ -s "${newestfile}" ] || init_newestfile
337
338	# We could simplify the awk program to take advantage of the
339	# fact thet it should have exactly one line of input.
340	newest="$( ${ENV_CMD} TZ=UTC LOCALE=C ${LS} -lT "${newestfile}" \
341		| ${AWK} '
342		BEGIN { newest = 0 }
343		{
344			t = $9 "";
345			if ($6 == "Jan") t = t "01";
346			if ($6 == "Feb") t = t "02";
347			if ($6 == "Mar") t = t "03";
348			if ($6 == "Apr") t = t "04";
349			if ($6 == "May") t = t "05";
350			if ($6 == "Jun") t = t "06";
351			if ($6 == "Jul") t = t "07";
352			if ($6 == "Aug") t = t "08";
353			if ($6 == "Sep") t = t "09";
354			if ($6 == "Oct") t = t "10";
355			if ($6 == "Nov") t = t "11";
356			if ($6 == "Dec") t = t "12";
357			if ($7 < 10) t = t "0";
358			t = t $7;
359			#these next two lines add the 24h clock onto the date
360			#gsub(":", "", $8);
361			#t = sprintf("%s.%4.4s", t, $8);
362			if (t > newest) newest = t;
363		}
364		END { print newest }' )"
365	#echo "${newest}"
366}
367get_newest_date ()
368{
369	get_newest_rcsid_date
370	case "${newest}" in
371	""|0)	get_newest_mtime_date
372		method=ls
373		;;
374	*)	method=rcsid
375		;;
376	esac
377	#echo "${newest}"
378}
379
380#
381# choose_version_number() chooses the syspkg version number,
382# by concatenating several components (OS version, syspkg "tiny"
383# version and date).  We end up with something like
384# osvers="3.99.15", tinyvers="0", newest="20060104",
385# and t="3.99.15.0.20060104".
386#
387# The result is stored in the variables ${t} and ${method}.
388#
389method=''
390t=''
391choose_version_number ()
392{
393	get_osvers ; m1="${method}"
394	get_tinyvers # does not set ${method}
395	get_newest_date ; m2="${method}"
396	t="${osvers}.${tinyvers}.${newest}"
397	method="${m1}.${m2}"
398
399	# print version number that we're using
400	if ${verbose}; then
401		echo "${pkg} - ${t} version using ${method} method"
402	fi
403}
404
405#
406# print_dir_exec_lines outputs an "@exec install" line for each
407# directory in ${PLIST}
408#
409print_dir_exec_lines ()
410{
411	local dir uname gname mode
412	local dot_slash_dir
413	local no_dot_dir
414	local word line
415	${AWK} '/^@dirrm/ { print $2 }' <"${PLIST}" | \
416	${SORT} | \
417	while read dir ; do
418		# Sanitise the name. ${dir} could be an absolute or
419		# relative name, with or without a leading "./".
420		# ${dot_slash_dir} always has a leading "./" (except when
421		# it's exactly equal to "."). ${no_dot_dir} never has a
422		# leading "." or "/" (except when it's exactly equal to
423		# ".").
424		case "${dir}" in
425		.|./|/)	dot_slash_dir=.  ;;
426		./*)	dot_slash_dir="${dir}" ;;
427		/*)	dot_slash_dir=".${dir}" ;;
428		*)	dot_slash_dir="./${dir}" ;;
429		esac
430		no_dot_dir="${dot_slash_dir#./}"
431		# Get the directory's owner, group, and mode
432		# from the live file system, or let it be overridden
433		# by the metalog.
434		eval "$( ${STAT} -f 'uname=%Su gname=%Sg mode=%#OLp' \
435				"${DESTDIR}/${dot_slash_dir}" )"
436		if [ -n "${metalog}" ]; then
437			line="$( echo "${dot_slash_dir}" | \
438				${AWK} -f "${rundir}/join.awk" \
439					/dev/stdin "${metalog}" )"
440			for word in ${line} ; do
441				case "${word}" in
442				uname=*|gname=*|mode=*)	eval "${word}" ;;
443				esac
444			done
445		fi
446		# XXX: Work around yet another pkg_add bug: @cwd lines
447		# do not actually cause the working directory to change,
448		# so file names in @exec lines need to be qualified by
449		# %D, which (in our case, since we know there's an
450		# "@cwd /" line) will be the dir name passed to
451		# "pkg_add -p PREFIX".
452		case "${no_dot_dir}" in
453		.) d="%D" ;;
454		*) d="%D/${no_dot_dir}" ;;
455		esac
456		cat <<EOF
457@exec install -d -o ${uname} -g ${gname} -m ${mode} ${d}
458EOF
459	done
460}
461
462#
463# register_syspkg() registers the syspkg in ${SYSPKG_DB_TOPDIR}.
464# This involves creating the subdirectory ${SYSPKG_DB_SUBDIR}
465# and populating it with several files.
466#
467register_syspkg ()
468{
469	cleanup_must_delete_dbsubdir=true
470	[ -n "${SYSPKG_DB_SUBDIR}" ] || bomb
471	mkdir -p "${SYSPKG_DB_SUBDIR}"
472
473	#
474	# Guess what versions of other packages to depend on.
475	#
476	# If we are using the OS version as part of the pkg
477	# version, then depend on any version ">=${osvers}".  For
478	# example, etc-sys-etc-1.6ZI.0.20040206 might depend on
479	# base-sys-root>=1.6ZI.
480	#
481	# Failing that, depend on any version "-[0-9]*".
482	#
483	# XXX: We could extend the format of the "deps" file to carry
484	# this sort of information, so we wouldn't have to guess.
485	#
486	case "${t}" in
487	${osvers}.*)	depversion=">=${osvers}" ;;
488	*)		depversion="-[0-9]*" ;;
489	esac
490
491	#
492	# Add the dependencies.
493	#
494	# We always add a "@pkgdep" line for each prerequisite package.
495	#
496	# If the prerequisite pkg is already registered (as it should be
497	# if our caller is doing things in the right order), then we put
498	# its exact version number in a "@blddep" line.
499	#
500	${AWK} '$1 ~ '/"${pkg}"/' { print $2 }' "${rundir}/deps" | ${SORT} | \
501	while read depname ; do
502		# ${pkgdepglob} is a shell glob pattern that should match
503		# any version of a pkg.  ${pkgdep} uses the special syntax
504		# for pkg dependencies, and is not usable as a shell
505		# glob pattern.
506		pkgdepglob="${depname}-[0-9]*"
507		pkgdep="${depname}${depversion}"
508		echo "@pkgdep ${pkgdep}"
509		blddep="$( cd "${SYSPKG_DB_TOPDIR}" && echo ${pkgdepglob} \
510			|| bomb )"
511		case "${blddep}" in
512		*\*)	# pkgdepglob did not match anything
513			echo >&2 "${WARNING}${pkg} depends on '${pkgdep}' but there is no matching syspkg in ${SYSPKG_DB_TOPDIR}"
514			;;
515		*\ *)	# pkgdepglob matched more than once.
516			echo >&2 "${ERRWARN}${pkg} depends on '${pkgdep}' but there are multiple matching syspkgs in ${SYSPKG_DB_TOPDIR}"
517			${force} || bomb
518			# If ${force} is set, then assume that the last
519			# match is the most recent.
520			# XXX: This might be wrong, because of
521			# differences between lexical sorting and
522			# numeric sorting.
523			lastmatch="${blddep##* }"
524			echo "@blddep ${lastmatch}"
525			;;
526		*)	# exactly one match.
527			# XXX: We ignore the possibility that the
528			# version we found via ${pkgdepglob} might not
529			# satisfy ${pkgdep}.  We could conceivably use
530			# "pkg_admin pmatch" to check, but that's not a
531			# host tool so we can't assume that it will be
532			# available.
533			echo "@blddep ${blddep}"
534			;;
535		esac
536	done >>"${PLIST}"
537
538	# create the comment (should be one line)
539	comment="$( ${AWK} '$1 ~ '/"${pkg}"/' \
540			{ print substr($0, length($1) + 2) }' \
541			"${rundir}/comments" )"
542	case "${comment}" in
543	"")	echo >&2 "${WARNING}no comment for \"${pkg}\" (using placeholder)"
544		comment="System package for ${pkg}"
545		;;
546	*"${nl}"*)
547		echo >&2 "${ERRWARN}multi-line comment for \"${pkg}\""
548		${force} || bomb
549		;;
550	esac
551	echo "${comment}" > "${SYSPKG_DB_SUBDIR}/+COMMENT"
552
553	# create the description (could be multiple lines)
554	descr="$( ${AWK} '$1 ~ '/"${pkg}"/' {
555			print substr($0, length($1) + 2) }' \
556			"${rundir}/descrs" )"
557	case "${descr}" in
558	"")	echo >&2 "${WARNING}no description for \"${pkg}\" (re-using comment)" 2>&1
559		descr="${comment}"
560		;;
561	esac
562	echo "${descr}" > "${SYSPKG_DB_SUBDIR}/+DESC"
563	${PRINTF} "\nHomepage:\nhttp://www.NetBSD.org/\n" >> "${SYSPKG_DB_SUBDIR}/+DESC"
564
565	# create the build information
566	if [ x"${BUILD_INFO_CACHE}" = x ]; then
567		{
568		# These variables describe the build
569		# environment, not the target.
570		echo "OPSYS=$(${UNAME} -s)"
571		echo "OS_VERSION=$(${UNAME} -r)"
572		${MAKE} -f- all <<EOF
573.include <bsd.own.mk>
574all:
575	@echo OBJECT_FMT=${OBJECT_FMT}
576	@echo MACHINE_ARCH=${MACHINE_ARCH}
577	@echo MACHINE_GNU_ARCH=${MACHINE_GNU_ARCH}
578EOF
579		# XXX: what's the point of reporting _PKGTOOLS_VER
580		# when we roll everything by hand without using
581		# the pkg tools?
582		echo "_PKGTOOLS_VER=$(${PKG_CREATE} -V)"
583		} > "${SYSPKG_DB_SUBDIR}/+BUILD_INFO"
584	else
585		cp "${BUILD_INFO_CACHE}" "${SYSPKG_DB_SUBDIR}/+BUILD_INFO"
586	fi
587
588	# test for attributes
589	args=""
590	attrs="$( ${AWK} '$1 ~ '/"${pkg}"/' { \
591			print substr($0, length($1) + 2) }' \
592		"${rundir}/attrs" )"
593	for a in "${attrs}"; do
594		case "${attrs}" in
595		"")	;;
596		preserve)
597			echo "${pkg}-${t}" >"${SYSPKG_DB_SUBDIR}/+PRESERVE"
598			args="${args} -n ${SYSPKG_DB_SUBDIR}/+PRESERVE"
599			;;
600		esac
601	done
602
603	#
604	# Create ${SYSPKGSIR}/+CONTENTS from ${PLIST}, by adding an
605	# "@name" line and a lot of "@comment MD5:" lines.
606	#
607	{
608		rcsid='$NetBSD: regpkg,v 1.10 2006/01/08 10:10:03 apb Exp $'
609		utcdate="$( ${ENV_CMD} TZ=UTC LOCALE=C \
610			${DATE} '+%Y-%m-%d %H:%M' )"
611		user="${USER:-root}"
612		host="$( ${HOSTNAME} )"
613		echo "@name ${pkg}-${t}"
614		echo "@comment Packaged at ${utcdate} UTC by ${user}@${host}"
615		echo "@comment Packaged using ${prog} ${rcsid}"
616		# XXX: "option extract-in-place" might help to get
617		#	pkg_add to create directories.
618		# XXX: no, it doesn't work.  Yet another pkg_add bug.
619		## echo "@option extract-in-place"
620		# Move the @pkgdep and @blddep lines up, so that
621		# they are easy to see when people do "less
622		# ${DESTDIR}/var/db/syspkg/*/+CONTENTS".
623		${EGREP} '^(@pkgdep|@blddep)' "${PLIST}" || true
624		# Now do the remainder of the file.
625		while read line ; do
626			case "${line}" in
627			@pkgdep*|@blddep*)
628				# already handled by grep above
629				;;
630			@cwd*)
631				# There should be exactly one @cwd line.
632				# Just after it, add an "@exec mkdir"
633				# line for every directory.  This is to
634				# work around a pkg-add bug (see
635				# <http://mail-index.NetBSD.org/tech-pkg/2003/12/11/0018.html>)
636				echo "${line}"
637				print_dir_exec_lines
638				;;
639			@*)	
640				# just pass through all other @foo lines
641				echo "${line}"
642				;;
643			*)	
644				# This should be a file name.  Pass it
645				# through, and append "@comment MD5:".
646				# XXX why not SHA256 ?
647				echo "${line}"
648				file="${DESTDIR}${line}"
649				if [ -f "${file}" -a -r "${file}" ];
650				then
651					md5sum="$( ${CKSUM} -n -m "${file}" \
652						   | ${AWK} '{print $1}'
653						)"
654					echo "@comment MD5:${md5sum}"
655				fi
656				;;
657			esac
658		done <"${PLIST}"
659	} >"${SYSPKG_DB_SUBDIR}/+CONTENTS"
660
661	#
662	#  Update ${SYSPKG_DB_TOPDIR}/pkgdb.byfile.db.
663	#
664	{
665		dbfile="${SYSPKG_DB_TOPDIR}/pkgdb.byfile.db"
666		dbtype="btree"
667		db_opts=''
668		# XXX: cross builds should really add "-E B" or "-E L"
669		# (big-endian or little-endian) to db_opts, but we don't
670		# do that yet.
671		if ${update} || ${force} ; then
672			# overwriting an existing entry is not an error
673			db_opts="${db_opts} -R"
674		fi
675		if [ ${verbosity} -lt 2 ]; then
676			# don't print all the keys added to the database
677			db_opts="${db_opts} -q"
678		fi
679
680		# Transform ${PLIST} into a form to be used as keys in
681		# ${dbfile}.  The results look like absolute paths,
682		# but they are really relative to ${DESTDIR}.
683		#
684		# "@dirrm ."		-> "/"
685		# "@dirrm foo/bar"	-> "/foo/bar"
686		# "@dirrm ./foo/bar"	-> "/foo/bar"
687		# "foo/bar/baz"		-> "/foo/bar/baz"
688		# "./foo/bar/baz"	-> "/foo/bar/baz"
689		#
690		dblist="${SCRATCH}/dblist"
691		${AWK} '/^@dirrm \.\//	{gsub("^.", "", $2); print $2; next}
692			/^@dirrm \.$/	{print "/"; next}
693			/^@dirrm/	{print "/" $2; next}
694			/^@/		{next}
695			/^\.\//		{gsub("^.", "", $0); print $0; next}
696			/./		{print "/" $0; next}' \
697			<"${PLIST}" >"${dblist}"
698		# Add all the path names to the database.
699		${AWK} '{print $1 "\t" "'"${pkg}-${t}"'"}' <"${dblist}" \
700		| ${DB} -w ${db_opts} -F "${tab}" -f - "${dbtype}" "${dbfile}"
701	}
702
703	if ${verbose} ; then
704		echo "Registered ${pkg}-${t} in ${SYSPKG_DB_TOPDIR}"
705	elif ! ${quiet} ; then
706		echo "Registered ${pkg}-${t}"
707	fi
708
709	cleanup_must_delete_dbsubdir=false
710}
711
712#
713# create_syspkg_tgz() creates the *.tgz file for the package.
714#
715# The output file is ${binpkgdir}/${pkg}-${t}.tgz.
716#
717create_syspkg_tgz ()
718{
719	#
720	# pkg_create does not understand metalog files, so we have to
721	# use pax directly.
722	#
723	# We create two specfiles: specfile_overhead describes the
724	# special files that are part of the package system's metadata
725	# (+CONTENTS, +COMMENT, +DESCR, and more); and specfile_payload
726	# describes the files and directories that we actually want as
727	# part of the package's payload.
728	#
729	# We then use the specfiles to create a compressed tarball that
730	# contains both the overhead files and the payload files.
731	#
732	# There's no trivial way to get a single pax run to do
733	# everything we want, so we run pax twice, with a different
734	# working directory and a different specfile each time.
735	#
736	# We could conceivably make clever use of pax's "-s" option to
737	# get what we want from a single pax run with a single (more
738	# complicated) specfile, but the extra trouble doesn't seem
739	# warranted.
740	#
741	cleanup_must_delete_binpkgfile=true
742	specfile_overhead="${SCRATCH}/spec_overhead"
743	specfile_payload="${SCRATCH}/spec_payload"
744	tarball_uncompressed="${SCRATCH}/tarball_uncompressed"
745
746	# Create a specfile for all the overhead files (+CONTENTS and
747	# friends).
748	{
749		plusnames_first="${SCRATCH}/plusnames_first"
750		plusnames_rest="${SCRATCH}/plusnames_rest"
751
752		# Ensure that the first few files are in the same order
753		# that "pkg_create" would have used, just in case anything
754		# depends on that.  Other files in alphabetical order.
755		SHOULD_BE_FIRST="+CONTENTS +COMMENT +DESC"
756		(
757			cd "${SYSPKG_DB_SUBDIR}" || bomb
758			for file in ${SHOULD_BE_FIRST}; do
759				[ -e "./${file}" ] && echo "${file}"
760			done >"${plusnames_first}"
761			${LS} -1 | ${FGREP} -v -f "${plusnames_first}" \
762				>"${plusnames_rest}" \
763				|| true
764		)
765
766		# Convert the file list to specfile format, and override the
767		# uid/gid/mode.
768		{
769			echo ". optional type=dir"
770			${AWK} '{print "./" $0 " type=file uid=0 gid=0 mode=0444"
771				}' "${plusnames_first}" "${plusnames_rest}"
772		} >"${specfile_overhead}"
773	}
774
775	# Create a specfile for the payload of the package.
776	{
777		spec1="${SCRATCH}/spec1"
778		spec2="${SCRATCH}/spec2"
779
780		# Transform ${PLIST} into simple specfile format:
781		#
782		# "@dirrm ."		-> "."
783		# "@dirrm foo/bar"	-> "./foo/bar"
784		# "@dirrm ./foo/bar"	-> "./foo/bar"
785		# "foo/bar/baz"		-> "./foo/bar/baz"
786		# "./foo/bar/baz"	-> "./foo/bar/baz"
787		#
788		# Ignores @cwd lines.  This should be safe, given how
789		# makeplist works.
790		${AWK} '/^@dirrm \.\//	{print $2; next}
791			/^@dirrm \.$/	{print "."; next}
792			/^@dirrm/	{print "./" $2; next}
793			/^@/		{next}
794			/^\.\//		{print $0; next}
795			/./		{print "./" $0; next}' \
796			<"${PLIST}" >"${spec1}"
797
798		# If metalog was specified, attributes from metalog override
799		# attributes in the file system.  We also fake up an
800		# entry for the ./etc/mtree/set.${pkgset} file.
801		{
802			if [ -n "${metalog}" ]; then
803				${AWK} -f "${rundir}/join.awk" \
804					"${spec1}" "${metalog}"
805				${AWK} -f "${rundir}/join.awk" \
806					"${spec1}" /dev/stdin <<EOF
807./etc/mtree/set.${pkgset} type=file mode=0444 uname=root gname=wheel
808EOF
809			else
810				cat "${spec1}"
811			fi
812		} >"${spec2}"
813
814		#
815		# If a file or directory to was mentioned explicitly
816		# in ${PLIST} but not mentioned in ${metalog}, then the
817		# file or directory will not be mentioned in ${spec2}.
818		# This is an error, and means that the metalog was
819		# not built correctly.
820		#
821		if [ -n "${metalog}" ]; then
822			names1="${SCRATCH}/names1"
823			names2="${SCRATCH}/names2"
824			${AWK} '{print $1}' <"${spec1}" | ${SORT} >"${names1}"
825			${AWK} '{print $1}' <"${spec2}" | ${SORT} >"${names2}"
826			if ${FGREP} -v -f "${names2}" "${spec1}" >/dev/null
827			then
828				cat >&2 <<EOM
829${ERRWARN}The metalog file (${metalog}) does not
830	contain entries for the following files or directories
831	which should be part of the ${pkg} syspkg:
832EOM
833				${FGREP} -v -f "${names2}" "${spec1}" >&2
834				${force} || bomb
835			fi
836			if ${FGREP} -v -f "${names1}" "${spec2}" >/dev/null
837			then
838				cat >&2 <<EOM
839${ERRWARN}The following lines are in the metalog file
840	(${metalog}), and the corresponding files or directories
841	should be in the ${pkg} syspkg, but something is wrong:
842EOM
843				${FGREP} -v -f "${names1}" "${spec2}" >&2
844				bomb
845			fi
846		fi
847
848		# Add lines (tagged "optional") for any implicit directories.
849		#
850		# For example, if we have a file ./foo/bar/baz, then we add
851		# "./foo/bar optional type=dir", "./foo optional type=dir",
852		# and ". optional type=dir", unless those directories were
853		# already mentioned explicitly.
854		#
855		${AWK} -f "${rundir}/getdirs.awk" "${spec2}" \
856		| ${SORT} -u >"${specfile_payload}"
857	}
858
859	# Use two pax invocations followed by gzip to create
860	# the tgz file.
861	#
862	# Remove any leading "./" from path names, because that
863	# could confuse tools that work with binary packages.
864	(
865		cd "${SYSPKG_DB_SUBDIR}" && \
866		${PAX} -O -w -d -N"${etcdir}" -M '-s,^\./,,' \
867			-f "${tarball_uncompressed}" \
868			<"${specfile_overhead}" \
869		|| bomb
870	)
871	(
872		cd "${DESTDIR:-/}" && \
873		${PAX} -O -w -d -N"${etcdir}" -M '-s,^\./,,' \
874			-a -f "${tarball_uncompressed}" \
875			<"${specfile_payload}" \
876		|| bomb
877	)
878	${GZIP_CMD} -9 <"${tarball_uncompressed}" >"${binpkgfile}" || bomb
879
880	# (Extra space is to make message line up with "Registered" message.)
881	if ${verbose} ; then
882		echo "  Packaged ${binpkgfile}"
883	elif ! ${quiet} ; then
884		echo "  Packaged ${binpkgfile##*/}"
885	fi
886
887	cleanup_must_delete_binpkgfile=false
888
889}
890
891#
892# do_register_syspkg() registers the syspkg if appropriate.
893#
894# If SYSPKG_DB_SUBDIR already exists, that might be an error, depending
895# on ${force} and ${update} flags.
896#
897do_register_syspkg ()
898{
899	# Check that necessary variables are defined
900	[ -n "${SYSPKG_DB_TOPDIR}" ] || bomb
901	[ -n "${SYSPKG_DB_SUBDIR}" ] || bomb
902
903	# Create SYSPKG_DB_TOPDIR if necessary
904	[ -d "${SYSPKG_DB_TOPDIR}" ] || mkdir -p "${SYSPKG_DB_TOPDIR}" || bomb
905
906	# A function to delete and re-register a syspkg
907	delete_and_reregister ()
908	{
909		echo >&2 "${ERRWARNNOTE}deleting and re-registering ${pkg}-${t}"
910		cleanup_must_delete_dbsubdir=true
911		rm -rf "${SYSPKG_DB_SUBDIR}"
912		register_syspkg
913	}
914
915	# Check whether another version of ${pkg} is already registered.
916	pattern="${pkg}-[0-9]*"
917	matches="$( cd "${SYSPKG_DB_TOPDIR}" && echo ${pattern} || bomb )"
918	case "${matches}" in
919	*\*)		;;	# wildcard did not match anything
920	"${pkg}-${t}")	;;	# exact match
921	*)	echo >&2 "${ERRWARN}another version of ${pkg} is already registered in ${SYSPKG_DB_TOPDIR} (while creating ${pkg}-${t})"
922		${force} || bomb
923		;;
924	esac
925
926	# Check whether the desired version of ${pkg} is already registered,
927	# and create it if appropriate.
928	if [ -d "${SYSPKG_DB_SUBDIR}" ]; then
929		echo >&2 "${ERRWARNNOTE}${pkg}-${t} is already registered"
930		${verbose} && echo >&2 "	in ${SYSPKG_DB_TOPDIR}"
931		if ${force}; then
932			delete_and_reregister
933		elif ${update}; then
934			#
935			# If all files in SYSPKG_DB_SUBDIR are newer
936			# than all files in the pkg, then do nothing.
937			# Else delete and re-register the pkg.
938			#
939			[ -n "${newestfile}" ] || init_newestfile
940			if [ -n "${newestfile}" ]; then
941				case "$( ${FIND} "${SYSPKG_DB_SUBDIR}" -type f \
942					! -newer "${newestfile}" -print )" \
943				in
944				"")	;;
945				*)	delete_and_reregister ;;
946				esac
947
948			else
949				# No files in the pkg?  (This could happen
950				# if a pkg contains only directories.)
951				# Do nothing.
952			fi
953		else
954			bomb
955		fi
956	else
957		register_syspkg
958	fi
959}
960
961#
962# do_create_syspkg_tgz() creates the the binary pkg (*.tgz) if
963# appropriate.
964#
965# If binpkgfile already exists, that might be an error, depending on
966# ${force} and ${update} flags.
967#
968do_create_syspkg_tgz ()
969{
970	[ -n "${binpkgfile}" ] || bomb
971
972	delete_and_recreate ()
973	{
974		echo >&2 "${ERRWARNNOTE}deleting and re-creating ${pkg}-${t}.tgz"
975		rm -f "${binpkgfile}"
976		create_syspkg_tgz
977	}
978
979	# Check whether another version of ${pkg} already exists.
980	pattern="${pkg}-[0-9]*"
981	matches="$( cd "${binpkgdir}" && echo ${pattern} || bomb )"
982	case "${matches}" in
983	*\*)	;;	# wildcard did not match anything
984	"${pkg}-${t}.tgz") ;;	# exact match
985	*)	echo >&2 "${ERRWARN}another version of ${pkg} already exists in ${binpkgdir} (while creating ${pkg}-${t}.tgz)"
986		${force} || bomb
987		;;
988	esac
989
990	# Check whether the desired version of ${pkg} already exists,
991	# and create it if appropriate.
992	if [ -e "${binpkgfile}" ]; then
993		echo >&2 "${ERRWARNNOTE}${pkg}-${t}.tgz already exists"
994		${verbose} && echo >&2 "	in ${binpkgdir}"
995		if ${force}; then
996			delete_and_recreate
997		elif ${update}; then
998			#
999			# If all files in SYSPKG_DB_SUBDIR are older
1000			# than ${binpkgfile}, then do nothing.
1001			# Else delete and re-create the tgz.
1002			#
1003			case "$( ${FIND} "${SYSPKG_DB_SUBDIR}" -type f \
1004				-newer "${binpkgfile}" -print )" \
1005			in
1006			"")	;;
1007			*)	delete_and_recreate ;;
1008			esac
1009		else
1010			bomb
1011		fi
1012	else
1013		create_syspkg_tgz
1014	fi
1015}
1016
1017####################
1018# begin main program
1019
1020parse_args ${1+"$@"}
1021make_PLIST
1022choose_version_number
1023SYSPKG_DB_SUBDIR="${SYSPKG_DB_TOPDIR}/${pkg}-${t}"
1024do_register_syspkg
1025if [ -n "${binpkgdir}" ]; then
1026	binpkgfile="${binpkgdir}/${pkg}-${t}.tgz"
1027	do_create_syspkg_tgz
1028fi
1029
1030exit 0
1031