#!/bin/sh

# Copyright (c) 2013-2014, 2016-2022, 2024-2026 LAAS/CNRS
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.

# Example:
# % cd /opt/robotpkg/var/lib/
# % ./pkgrepo2deb -d /tmp/debian -r robotpkg/packages/Ubuntu-12.10-x86_64/All -a
# Configure a web server on /tmp/debian.
# On a client, add to /etc/apt/sources.list:
# deb http://<host>/debian quantal robotpkg

prepend=robotpkg-
prepend_category=robotpkg/

version=1.16
: ${DEB_PACKAGER:=robotpkg@laas.fr}

# defaults
: ${AWK:=/usr/bin/awk}
: ${SED:=/bin/sed}
: ${GZIP_CMD:=/usr/bin/gzip}
: ${ROBOTPKG_ADD:=/opt/openrobots/sbin/robotpkg_add}
: ${ROBOTPKG_ADMIN:=/opt/openrobots/sbin/robotpkg_admin}
: ${DPKG_QUERY:=/usr/bin/dpkg-query}
: ${DPKG_DEB:=/usr/bin/dpkg-deb}
: ${DPKG_ARCHITECTURE:=/usr/bin/dpkg-architecture}
: ${UPDATE_ALTERNATIVES:=/usr/bin/update-alternatives}
: ${APT_FTPARCHIVE:=/usr/bin/apt-ftparchive}
: ${GPG:=/usr/bin/gpg --homedir=/opt/openrobots/etc/gnupg}

# todo
# -------------------------
#
# update/check all options combinations (help message and behaviour consistency)
# system dependencies (to be done manually on the target system)
# test pre/post-install
# build metapackage according to default set of option (if present)
# back to real dependencies ( >= ... )
#
# --------------------------
# known bugs
#
# (fillme)
#
# --------------------------

# to be regularly updated:
guess_debdistrib_codename()
{
	tz "in ${pkgdir}/${pkgfile}" OPSYS OS_VERSION && return 1

	codename=""
	case $OPSYS in
	Ubuntu)
		case $OS_VERSION in
		10.04)	codename=lucid;;
		12.04)	codename=precise;;
		12.10)	codename=quantal;;
		13.04)	codename=raring;;
		13.10)	codename=saucy;;
		14.04)	codename=trusty;;
		14.10)	codename=utopic;;
		15.04)	codename=vivid;;
		15.10)	codename=wily;;
		16.04)	codename=xenial;;
		16.10)	codename=yakkety;;
		17.04)	codename=zesty;;
		17.10)	codename=artful;;
		18.04)	codename=bionic;;
		18.10)	codename=cosmic;;
                19.04)	codename=disco;;
                19.10)	codename=eoan;;
                20.04)	codename=focal;;
                20.10)	codename=groovy;;
                21.04)	codename=hirsute;;
                21.10)	codename=impish;;
                22.04)	codename=jammy;;
                22.10)	codename=kinetic;;
                23.04)	codename=lunar;;
                23.10)	codename=mantic;;
                24.04)	codename=noble;;
                24.10)	codename=oracular;;
                25.04)	codename=plucky;;
                25.10)	codename=questing;;
                26.04)	codename=resolute;;
                26.10)	codename=stonking;;

                16.04.pal)	codename=erbium;;
                18.04.pal)	codename=ferrum;;
                esac;;
	Debian)
		case $OS_VERSION in
		7)	codename=wheezy;;
		8)	codename=jessie;;
                9)	codename=stretch;;
                10)	codename=buster;;
                11)	codename=bullseye;;
                12)	codename=bookworm;;
                13)	codename=trixie;;
                14)	codename=forky;;
                15)	codename=duke;;
		esac;;
	esac

	if [ -z "$codename" ]; then
		echo "guess_debdistrib_codename(): could not guess OS codename" 1>&2
		dis OPSYS OS_VERSION 1>&2
		return 1
	fi
}

help ()
{
	cat << EOF
syntax:
${1##*/} [-r rpkgrepo] [-d debrepo] [-a] [-l] [path/to/rpkg-package.tgz [ rpkg-package2.tgz [...]]]

This tool will make binary-only debian packages or debian
repository (packages and administrative files {Packages{,.gz},
Release{,.gpg}}) from the the whole robotpkg binary package
repository or from specified packages in the arguments list.

Warning:
debian dependencies and conflicts are strict and generated
or updated according to the very versions from the existing
packages inside the robotpkg repository only.
Dependencies will be resolved and forced on latest releases only.

options:
  -r|--rpkgrepo <d>	robotpkg repository directory (check for pkg_summary.gz)
  -d|--debrepo <d>	debian repository directory to create/update packages
			and administrative files
  -a|--all		debianize all robotpkg packages from robotpkg directory
  -l|--later		do not produce debian administrative files
  -V|--version		show version
  --longhelp		(examples, signing info)

EOF
}

helpdebug ()
{
	help "$1"
	cat << EOF
  +l|--require-license
  -v|--verbose
     --test-debnamize

  -- Examples --

  ${1##*/} -r robotpkg-repo -d debian-repo -a
    debianize (build or update) the entire repository

  ${1##*/} -r robotpkg-repo -d debian-repo package1.tgz [robotpkg-repo/]package2.tgz
    update debian repository with some new packages

  ${1##*/} -r robotpkg-repo -d debian-repo -l -a
  ${1##*/} -r robotpkg-repo -d debian-repo -l package1.tgz robotpkg-repo/package2.tgz
    process rpkg-packages but do not update debian administrative files

  ${1##*/} -d debian-repo
    update debian administrative files only
    (Packages{,gz}, Release{,gpg})

  ${1##*/} -r robotpkg-repo package1.tgz robotpkg-repo/package2.tgz
    build single debian packages in a /tmp/temporary directory

  -- Key Signing --

  # http://wiki.debian.org/Keysigning

  # should be password-protected (ymmv in case of automated process):
  ${GPG} --gen-key

  # locate your key in the list (probably the last one)
  # the key is 1A2B3C4D in the example below:
  ${GPG} --fingerprint
    pub   1111A/1A2B3C4D 2013-05-28
          Key fingerprint = 9EB0 E328 405D 5081 10BC  1FC9 DBE0 BCFB 3AD7 3E99
          uid                  Me <me@example.com>

  then
    # register your public key on public servers ..
    ${GPG} --keyserver subkeys.pgp.net --send-key 1A2B3C4D

    # .. and tell your users how to get it:
    ${GPG} --keyserver subkeys.pgp.net --recv-keys 1A2B3C4D && ${GPG} --armor --export 1A2B3C4D | sudo apt-key add -
  or
    # export your public key into a file ..
    ${GPG} --output me@example.com.gpg.key -armor --export 1A2B3C4D

    # .. and make it available to users telling them to:
    "wget http://your-server-url/path/to/me@example.com.gpg.key -O - | sudo apt-key add -"

  -- tools --

  apt-rdepends -d <pkgname> | dot -T png -o /tmp/out.png && display /tmp/out.png

EOF
}

# test for executables in args
# return 0 if all of them are in PATH
tx()
{
	ret=0
	for exec in "$@"; do
		if ! type "$exec" >/dev/null 2>&1; then
			echo "cannot find '${exec}' in PATH" 1>&2
			ret=1
		fi
	done
	return $ret
}

# test "zero" empty variables content
# (return true = 0 if empty, or false = 1)
# first argument is error text complement
tz()
{
	what="$1"
	shift
	zero=false
	for v in "$@"; do
		if [ -z "`eval echo \\$$v`" ]; then
			echo "\$$v is undefined" 1>&2
			zero=true
		fi
	done

	$zero && echo "^^^^ some required variables are not defined ${what}" 1>&2
	$zero
}

# display variables name and content
dis ()
{
	for v in "$@"; do
		echo "$v=`eval echo \\$$v`"
	done
}

updaterepo()
{
	tz "(internal error)" codename DEBARCH debrepo && return 1

        codenamedir=dists/${codename}
	repodir=$codenamedir/robotpkg/binary-${DEBARCH}
        {
	    mkdir -p ${debrepo}/${repodir} || return 1
            test -n "${DEBPKG}" && test -f "${DEBPKG}" && {
                mv -f "${DEBPKG}" "${debrepo}/${repodir}/." || return 1
                echo "updated ${debrepo}/${repodir}/${DEBFULLNAME}"
            }
        }

	if ${later} || test -z "${debrepo}"; then return 0; fi
        if ${doitall}; then return 0; fi

	(
            set -e
            flock -w 300 9
            cd $debrepo

	    # incremental update of Packages
	    test -s ${repodir}/Packages || \
                ${APT_FTPARCHIVE} packages ${repodir} >${repodir}/Packages
	    rm -f ${repodir}/Packages~
	    mv ${repodir}/Packages ${repodir}/Packages~
	    {
	        ${AWK} -F: -vskip="${DEBFULLNAME}" '			\
	          NF {							\
	            if ($1 == "Filename") { filename = $2 };		\
	            if (e) { e = e "\n" $0; } else { e = $0; }		\
	          }							\
	          !NF {							\
		    sub(".*/", "", filename);				\
	            if (filename != skip) { print e; print; }		\
	            e = "";						\
	          }' ${repodir}/Packages~
                if test -f ${repodir}/${DEBFULLNAME}; then
	          ${APT_FTPARCHIVE} packages ${repodir}/${DEBFULLNAME}
                fi
	    } >${repodir}/Packages
	    ${GZIP_CMD} -c9 < ${repodir}/Packages > ${repodir}/Packages.gz
	    rm -f ${repodir}/Packages~

            # incremental update of Contents
	    test -s ${codenamedir}/Contents-${DEBARCH} ||
		touch ${codenamedir}/Contents-${DEBARCH}
	    rm -f ${codenamedir}/Contents-${DEBARCH}~
	    mv ${codenamedir}/Contents-${DEBARCH} \
                ${codenamedir}/Contents-${DEBARCH}~
            {
                ${AWK} -vskip="robotpkg/${DEBBASE}" '			\
		  {							\
		    split($2,pkgs,/,/); $2="";				\
		    for(p in pkgs) if (pkgs[p] != skip) {		\
		      if ($2) $2=$2 "," pkgs[p]; else $2 = pkgs[p]	\
		    }							\
		    if ($2) print $1 " " $2				\
		  }' ${codenamedir}/Contents-${DEBARCH}~
                if test -f ${repodir}/${DEBFULLNAME}; then
                  ${APT_FTPARCHIVE} contents ${repodir}/${DEBFULLNAME} |
                    ${AWK} '{print $1 " robotpkg/'"${DEBBASE}"'"}'
                fi
            } | ${AWK} '						\
		  {							\
		    split($2,pkgs,/,/);					\
		    for(p in pkgs) if (file[$1]) {			\
		      file[$1] = file[$1] "," pkgs[p]			\
		    } else {						\
		      file[$1] = pkgs[p]				\
		    }							\
		  }							\
		  END { for(f in file) print f " " file[f] }		\
		' >${codenamedir}/Contents-${DEBARCH}
	    ${GZIP_CMD} -c9 <${codenamedir}/Contents-${DEBARCH} \
                >${codenamedir}/Contents-${DEBARCH}.gz
	    rm -f ${codenamedir}/Contents-${DEBARCH}~

            # update of Release
	    ${APT_FTPARCHIVE} \
                -o "APT::FTPArchive::Release::Origin=robotpkg"		\
                -o "APT::FTPArchive::Release::Suite=$codename"		\
                -o "APT::FTPArchive::Release::Codename=$codename"	\
                -o "APT::FTPArchive::Release::Components=robotpkg"	\
                release $codenamedir > $codenamedir/Release
            # try to sign it twice ... gpg2 sometimes can't connect to its
            # infamous agent and there is no way to disable this ...
            # suppress output to avoid warning about acquiring lock.
            rm -f $codenamedir/Release.gpg
            ${GPG} 2>/dev/null >&2 --output $codenamedir/Release.gpg \
                   -ba $codenamedir/Release || {
                rm -f $codenamedir/Release.gpg
                ${GPG} --output $codenamedir/Release.gpg \
                       -ba $codenamedir/Release ||
                    echo >&2 "Error signing release"
            }
	) 9>${debrepo}/${codenamedir}/pkgrepo2deb.lock || {
            echo >&2 "Error updating repository"
            return 1
        }
        rm -f ${debrepo}/${codenamedir}/pkgrepo2deb.lock
}


buildrepo()
{
	set +o noglob
	(
	    cd ${debrepo} || exit 1
	    for i in dists/*; do
                test -d $i || continue;

		$verbose && echo "scanning repository directory ${debrepo}/$i"
		# generate Packages and Contents-<arch> globally
                for j in $i/robotpkg/binary-*; do
                    ${APT_FTPARCHIVE} packages $j >$j/Packages
	            ${GZIP_CMD} -c9 <$j/Packages > $j/Packages.gz

                    arch=${j##*-}
                    ${APT_FTPARCHIVE} contents $j | ${AWK} '{	\
			      split($2,pkgs,/,/); $2="";		\
			      for(p in pkgs) {				\
			        if ($2) $2=$2 ",robotpkg/" pkgs[p];	\
			        else $2 = "robotpkg/" pkgs[p]		\
			      }						\
			      print $1 " " $2				\
			    }' >$i/Contents-$arch
	            ${GZIP_CMD} -c9 <$i/Contents-$arch >$i/Contents-$arch.gz
                done

		${APT_FTPARCHIVE} \
                    -o "APT::FTPArchive::Release::Origin=robotpkg"	\
                    -o "APT::FTPArchive::Release::Suite=${i#dists/}"	\
                    -o "APT::FTPArchive::Release::Codename=${i#dists/}"	\
                    -o "APT::FTPArchive::Release::Components=robotpkg"	\
                    release $i > $i/Release
		rm -f $i/Release.gpg
		${GPG} --output $i/Release.gpg -ba $i/Release
	    done
	)
	set -o noglob
}

debnamize()
{
    # default prefix
    test $# -gt 0 || set -- $prepend

	# todo: try to simplify this blob
	# dependencies and conflicts:
		# 1 remove trailing .tgz
		# 1bis prepend name
		# 2 change abc-123-xyz-456 to abc-123-xyz=456
		# 2bis change abc-123-xyz-456~opt1-opt2 to abc-123-xyz+opt1-opt2=456
		# 2ter mv ~opts after base name (-> +opts)
		# 3 transform operator to debian way [ a>=1<2 --> a (>= 1) (< 2) ]
		# 4 duplicate name if multiple comparison statements [ --> a (>= 1), a (< 2) ] (useless here)
		# 5,6 transform comparison operators
		# 7 removes empty version dependency
		# 8 rework last comma
	${SED} -e "s#\.tgz\$##g"					\
	    -e "s#^#$1#1"					\
	    -e "s#\(.*\)-\([0-9][^~-]*\)\$#\1=\2#1"			\
	    -e "s#\(.*\)-\([0-9][^-]*\)~\(.*\)\$#\1+\3=\2#1"		\
	    -e "s#\([^<>=]*\)\([<>=]*\)\([^~]*\)~\([^~<>=]*\)\([^~]*\).*#\1+\4\2\3\5#g"\
	    -e "s#\([^<>=]*\)\([<>=]*\)\([^<>= ,]*\)#\1 (\2 \3)#g"	\
	    -e "s#\([^<>=]*\)\(([^)]*)\) \(([^)]*)\)#\1\2\, \1\3#g"	\
	    -e "s,(< ,(<< ,g"						\
	    -e "s,(> ,(>> ,g"						\
	    -e "s,( ),,g"						\
	    -e "s/[, ]*$/,/g"						\
	| tr A-Z_ a-z-					# no caps, no _
}

debnamize_test()
{
	s()
	{
		echo "-------------"
		echo "$@"
		echo "$@" | debnamize
	}

	s "ding-dong123"
	s "ding-dong123-1.2.3r1"
	s "ding-dong123-1.2.3r1~opt1-truc+opt2"
	s "ding-dong123>=1.2.3r1"
	s "ding-dong123>=1.2.3r1<=4.5.6r2"
	s "ding-dong123>=1.2.3~opt1-truc+opt2"
	s "ding-dong123>=1.2.3r1~opt1-truc+opt2"
	s "ding-dong123>=1.2.3r1~opt1-truc+opt2<=4.5.6"
	s "ding-dong123>=1.2.3r1~opt1-truc+opt2<=4.5.6r1"
	s "ding-dong123>=1.2.3r1~opt1-truc+opt2<=4.5.6r1~opt1-truc+opt2"
	s "ding-dong123>=1.2.3r1<=4.5.6r1~opt1-truc+opt2"
	s "ding-dong123>=1.2.3<=4.5.6r1~opt1-truc+opt2"
}

# evaluate rpkg variables from .tgz/+BUILD_INFO or from a pkg_summary block
# ${1} is the file
# ${2} (optional) is a variable name prefix
eval_rpkg_vars()
{
	eval $(${SED} -e "s,[\"\`],,g" -e "s,^\([^=]*=\)\(.*\)$,${2}\1\"\2\",g" < ${1})
}


# extract rpkg file
# return 1 on error
extract_rpkg()
{
	tz "(internal error)" pkgroot pkgdir pkgfile pkgpath && return 1

	set -e
	rm -rf ${pkgroot}
	mkdir -p ${pkgroot}/DEBIAN
        ( cd ${pkgroot}/DEBIAN && ${ROBOTPKG_ADD} -P .. -x "${pkgpath}" )
        {
	    test -r "$pkgroot/DEBIAN/+BUILD_INFO"
	    test -r "$pkgroot/DEBIAN/+CONTENTS"
        } || {
            echo "Cannot read robotpkg's packed file '${pkgpath}/+BUILD_INFO'" 1>&2
	    return 1
	}

	return 0
}

# convert robotpkg dependency constraint into a list of matching package names
# $@ is a dependency list
# result on stdout
rpkgdep_lsall()
{
	dep="$@"
	dirs="$rpkgrepo;$PKG_PATH"

	sIFS="$IFS"
	IFS=';'
	set -- $dirs
	IFS="$sIFS"

	for dir in "$@"; do
            dir=${dir#file://}
            test -d "$dir" || continue
            ${ROBOTPKG_ADMIN} -b -d "$dir" lsall $dep
	done | ${AWK} '!d[$0]++' | debnamize
}

# setup debian variables
# return 1 on error
debvars()
{
	if tz "in ${pkgdir}/${pkgfile}" MACHINE_ARCH PKGBASE PKGVERSION; then
		rm -rf "${pkgroot}"
		return 1
	fi

	PKGVERSIONNUMBER=${PKGVERSION%~*}			# remove ~options

	PKGOPTIONS=${PKGVERSION#*~}				# take only options ~xxx
	PKGOPTIONS=${PKGOPTIONS#${PKGVERSION}}			# empty if equals to original
	PKGOPTIONS=+${PKGOPTIONS}				# prepend +
	PKGOPTIONS=${PKGOPTIONS%+}				# remove + if empty options

	DEBARCH=`$DPKG_ARCHITECTURE \
                   -t "$MACHINE_ARCH"-linux-gnu -q DEB_TARGET_ARCH`
	DEBBASE="`echo "${prepend}${PKGBASE}${PKGOPTIONS}" | tr A-Z_ a-z-`"	# no caps, no _
	DEBVERSION="`echo "${PKGVERSIONNUMBER}" | tr A-Z_ a-z-`"		# debian forbids _ in version
	DEBFULLNAME="${DEBBASE}_${DEBVERSION}_${DEBARCH}.deb"

	# provides main package (without options)
	PROVIDES="`echo "${prepend}${PKGBASE}" | tr A-Z_ a-z-`"	# no caps, no _

	guess_debdistrib_codename || return 1

	return 0
}

# return 0 (ok) if deb need rebuild
# return 1 (bad) if rpkg is older than deb
# $1=what to say (now: "skipping ")
deb_need_rebuild()
{
	tz "(internal error)" pkgpath codename DEBARCH DEBFULLNAME && return 1

	debpath=${debrepo}/dists/${codename}/robotpkg/binary-${DEBARCH}/${DEBFULLNAME}

	if [ ! -z "${debrepo}" -a ${pkgpath} -ot ${debpath} ]; then
		echo "${1}${pkgpath} older than ${debpath} ..."
		return 1
	fi

	return 0
}

# make debian package
# (caller should forget all new variables)
make_deb()
{
	debvars || return 1;

	if ! deb_need_rebuild "skipping "; then
		rm -rf "${pkgroot}"
		return 0
	fi

	if tz "in ${pkgpath}" CATEGORIES PREFIX MAINTAINER; then
		rm -rf "${pkgroot}"
		return 1
	fi

	${allowemptylicense} && [ -z "$LICENSE" ] && LICENSE="not-specified"

	${verbose} && dis PKGBASE PKGVERSION PKGREV PKGRAWVERSION DEBBASE DEBVERSION

	test -z "${rpkgrepo}" && rpkgrepo=`dirname ${pkgpath}`

	# dependencies
	rawdeps=`${SED} -ne "/^@pkgdep/s,,,gp" <${pkgroot}/DEBIAN/+CONTENTS`
	$verbose && echo "original dependencies: '$rawdeps'"
        deps=
	for d in ${rawdeps}; do
	    dep=`rpkgdep_lsall $d | ${SED} "s,$, |,g"`
            if test -z "$dep"; then
                echo "Cannot resolve dependency '$d'" 1>&2
                return 1
            fi

	    deps="${dep%|}${deps}" # remove trailing |
	done
	deps=`echo ${deps} | ${SED} "s#, |# |#g"` # suppress newlines, replace ", |" by " |"
	deps=${deps%,} # remove last comma
	$verbose && echo "exact dependencies: '$deps'"

	# system dependencies
        while read d; do
            case "$d" in
                *:*:*) dep="${d#*:}"; dep="${dep#*:}";;
                *) dep=`query_sysdep_pkg "$d"`;;
            esac
            $verbose && echo "system dependency: '$d' resolved to $dep"
            if test -n "$dep"; then
                case " $deps," in
                    *" ${dep%,},"*) ;;
                    *) deps="${deps:+$deps, }${dep%,}";;
                esac
            fi
        done <<-EOF
		`${SED} -ne '/^NEEDED=/s///p' <${pkgroot}/DEBIAN/+BUILD_INFO`
	EOF
	$verbose && echo "full dependencies: '$deps'"

	# conflicts
	rawcfls=`${SED} -ne "/^@pkgcfl/s,,,gp" <${pkgroot}/DEBIAN/+CONTENTS`
	$verbose && echo "original conflicts: '$rawcfls'"
	for d in ${rawcfls}; do
		newcfls=`rpkgdep_lsall $d`
		cfls="${newcfls}${cfls}"
	done
	cfls=`echo ${cfls}` # suppress newlines
	cfls=${cfls%,} # remove last comma
	$verbose && echo "exact conflicts: '$cfls'"

	# optional control lines
	OPTIONAL=""

	[ -z "$HOMEPAGE" ]	|| OPTIONAL="${OPTIONAL}Homepage: ${HOMEPAGE}${newline}"
	[ -z "$deps" ]		|| OPTIONAL="${OPTIONAL}Depends: ${deps}${newline}"
				   OPTIONAL="${OPTIONAL}Conflicts: ${cfls:+${cfls}, }${PROVIDES}${newline}"
				   OPTIONAL="${OPTIONAL}Provides: ${PROVIDES}${newline}Replaces: ${PROVIDES}${newline}"

	# keep first category, add prepend
	CATEGORY=${prepend_category}${CATEGORIES%% *}

	cat << EOF > ${pkgroot}/DEBIAN/control
Section: ${CATEGORY}
Priority: extra
Maintainer: $MAINTAINER
Source: ${pkgpath}
Package: $DEBBASE
Version: $DEBVERSION
Architecture: $DEBARCH
${OPTIONAL}Description: `${SED} -ne 1p <${pkgroot}/DEBIAN/+COMMENT`
 License: $LICENSE
 Debian-Packager: ${DEB_PACKAGER}
`${SED} -e "s,^, ,g" -e 's,^[ \t]*$, .,g' < ${pkgroot}/DEBIAN/+DESC`

EOF

	if ${verbose}; then
		echo "------------ vvv debian control file vvv ------------"
		cat ${pkgroot}/DEBIAN/control
		echo "------------ ^^^ debian control file ^^^ ------------"
	fi

	# add data to debian's doc directory
	mkdir -p ${pkgroot}${PREFIX}/share/doc/debian/${PKGBASE}
	mkdir -p ${pkgroot}/usr/share/doc
	ln -s ../../..${PREFIX}/share/doc/debian/${PKGBASE} ${pkgroot}/usr/share/doc/${DEBBASE}
	set +o noglob
	cp -R ${pkgroot}/DEBIAN/* ${pkgroot}${PREFIX}/share/doc/debian/${PKGBASE}
	set -o noglob


	# postinst and prerm
        metadir="$PKG_PREFIX/share/doc/debian/$PKGBASE"
        postinst=
        prerm=

        if test -x ${pkgroot}/DEBIAN/+INSTALL; then
            postinst=$postinst'$PKG_METADATA_DIR/+INSTALL'
            postinst=$postinst' "$PKGBASE-$PKGVERSION" POST-INSTALL;'
        fi

        if test -x ${pkgroot}/DEBIAN/+DEINSTALL; then
            prerm=$prerm'$PKG_METADATA_DIR/+DEINSTALL'
            prerm=$prerm' "${PKGBASE}-${PKGVERSION}" DEINSTALL;'
        fi

        if test -f ${pkgroot}/DEBIAN/+DISPLAY; then
            ruler='========================================================='
            postinst=$postinst'echo Please note the following:;'
            postinst=$postinst"echo $ruler;"
            postinst=$postinst'while read l; do echo $l; done'
            postinst=$postinst' <$PKG_METADATA_DIR/+DISPLAY;'
            postinst=$postinst"echo $ruler;"
        fi

        if test -n "$postinst"; then
            cat << EOF > ${pkgroot}/DEBIAN/postinst
#!/bin/sh
set -e 1
export PKGBASE='$PKGBASE'
export PKGVERSION='$PKGVERSION'
export PKG_PREFIX='$PREFIX'
export PKG_METADATA_DIR='$PREFIX/share/doc/debian/$PKGBASE'
export PKG_REFCOUNT_DBDIR='$PREFIX/var/db/robotpkg.refcount/'
$postinst
EOF
            chmod 0755 ${pkgroot}/DEBIAN/postinst
        fi

        if test -n "$prerm"; then
            cat << EOF > ${pkgroot}/DEBIAN/prerm
#!/bin/sh
set -e 1
export PKGBASE='${PKGBASE}'
export PKGVERSION='${PKGVERSION}'
export PKG_PREFIX='${PREFIX}'
export PKG_METADATA_DIR='$PREFIX/share/doc/debian/$PKGBASE'
export PKG_REFCOUNT_DBDIR='$PREFIX/var/db/robotpkg.refcount/'
$prerm
EOF
            chmod 0755 ${pkgroot}/DEBIAN/prerm
        fi

	# build deb package

	mkdir -p "${TEMPNAME}"
	DEBPKG="${TEMPNAME}/${DEBFULLNAME}"

	if ! ${DPKG_DEB} --root-owner-group -b "${pkgroot}" "${DEBPKG}"; then
		( echo ""
		  echo "something went wrong with '${pkgpath}', command was:"
		  echo "\t${DPKG_DEB} -b \"${pkgroot}\" \"${DEBPKG}\""
		  echo ""
		) 1>&2

		return 1
	fi

	$verbose && echo "\"${DEBPKG}\" is generated from \"${pkgpath}\""
	rm -rf "${pkgroot}"

	if [ -z "${debrepo}" ]; then
		echo "${DEBFULLNAME} left in ${DEBPKG}" 1>&2
	else
		if updaterepo "${debrepo}"; then
			rm -f "${DEBPKG}"
		else
			echo "${DEBFULLNAME} left in ${DEBPKG}" 1>&2
			return 1
		fi
	fi
}

# remove debian package
rm_deb()
{
    set +o noglob
    for deb in ${debrepo}/dists/*/robotpkg/binary-*/$prepend$1_*.deb; do
      test -f $deb || continue;

      # check missing source (rm_deb is called only for missing sources)
      src=`${DPKG_DEB} -f "$deb" source` || continue;
      test -f "$src" && continue

      echo "deleting $deb"
      rm -f $deb;

      # build required vars to update repo files
      codename=`dirname $deb`
      codename=`dirname $codename`
      codename=`dirname $codename`
      codename=`basename $codename`
      DEBARCH=`dirname $deb`
      DEBARCH=${DEBARCH#*/binary-}
      DEBFULLNAME=`basename $deb`
      DEBBASE=${DEBFULLNAME%_*_*.deb}

      updaterepo "${debrepo}"
    done
    set -o noglob
}

# called from pkgsummary_loop
# ${summ} is a file having one rpkg set of variables
# return 1 (=bad) on error
pkgsummarysub_extract_and_debianize()
{
( # inside subshell, we want to forget variables

	# evaluate env from pkg_summary.gz
	eval_rpkg_vars ${summ}

	tz "in pkg_summary.gz" PKGNAME && { echo "see below:" 1>&2; cat ${summ} 1>&2; echo "----" 1>&2; return 1; }

	PKGABI=${PKGNAME%~*}
	PKGOPTIONS=${PKGNAME#${PKGABI}}
	PKGBASE=${PKGABI%-*}
	PKGVERSION=${PKGABI#$PKGBASE-}${PKGOPTIONS}

	pkgdir="${rpkgrepo}"
	pkgfile="${FILE_NAME:-${PKGNAME}.tgz}"
	pkgroot="${TEMPNAME}.${pkgfile}"
	pkgpath=${pkgdir}/${pkgfile}

	debvars || return 1

	# this test (need_rebuild) is also made in make_deb()
	# but here we have enough info to decide
	# (make_deb() is also called from elsewhere)
	if ! deb_need_rebuild "skipping "; then
		rm -rf "${pkgroot}"
		return 0
	fi

	extract_rpkg || return 1;

	# get missing variables from extracted r-package
	eval_rpkg_vars ${pkgroot}/DEBIAN/+BUILD_INFO dup
	PREFIX=${dupPREFIX}
	MAINTAINER=${dupMAINTAINER}
	# here we can also check dupVARs against VARs

	make_deb || return 1;

	return 0

) # end of subshell
}

# loop on every entries in pkg_summary.gz
# $@ = subcommand and arguments
# return 1 on error
pkgsummary_loop()
{
	ret=0
	todo=false
	summ=${TEMPNAME}.summary
	rm -f ${summ}
	${GZIP_CMD} -cd ${rpkgrepo}/pkg_summary.gz | \
	while read line; do
		if [ -z "${line}" ]; then
			$todo && { "$@" || ret=1; }
			rm -f "${summ}"
			touch "${summ}"
			todo=false
		else
			echo "$line" >> ${summ}
			todo=:
		fi
	done
	$todo && { "$@" || ret=1; }
	rm -f "${summ}"

	return ${ret}
}

# Remove /usr in /usr/{bin/,sbin/,lib}*
optusr() {
    if test -z "${1##/usr/bin/*}"; then
        echo "/bin/${1##/usr/bin/}"
    elif test -z "${1##/usr/sbin/*}"; then
        echo "/sbin/${1##/usr/sbin/}"
    elif test -z "${1##/usr/lib*}"; then
        echo "/lib${1##/usr/lib}"
    else
        echo "$1"
    fi
}

# find package name corresponding to an installed file
query_sysdep_pkg()
{
    # split version/file
    IFS=: read -r v d <<-EOF
	$@
	EOF

    # skip special /notfound file
    case "$d" in ""|"/notfound") return;; esac
    # skip directories, that can produce a stupid list of matches, like
    # for instance  dpk-query -S /usr ...
    if test -d "$d"; then return; fi

    # extract version from requirement
    v=${v%%~*}; p=${v%%[><=]*}; v=${v#$p}

    {
        # simple case: dpkg-query
        ${DPKG_QUERY} 2>/dev/null -S "$d" || {
            # not found: try alternatives
            if test -L "$d"; then
                alts=`
                  ${UPDATE_ALTERNATIVES} 2>/dev/null --query "${d##*/}" |
                  ${AWK} -F: '/Alternative:/ {print $2}'`
                if test -n "$alts"; then
                    eval ${DPKG_QUERY} 2>/dev/null -S $alts ||
                      echo >&2 "sysdep package for '$d' not found"
                else
                    # not found: try to follow link or optional /usr
                    d=`readlink -f $d`
                    ${DPKG_QUERY} 2>/dev/null -S "$d" ||
                      ${DPKG_QUERY} 2>/dev/null -S `optusr $d` ||
                        echo >&2 "sysdep package for '$d' not found"
                fi
            else
                # not found: try to follow link or optional /usr
                d=`readlink -f $d`
                ${DPKG_QUERY} 2>/dev/null -S "$d" ||
                  ${DPKG_QUERY} 2>/dev/null -S `optusr $d` ||
                    echo >&2 "sysdep package for '$d' not found"
            fi
        }
    } | while read pkg; do
        # to get the installed package epoch, if any
        ${DPKG_QUERY} 2>/dev/null -Wf '${Package}:${Version}\n' "${pkg%%:*}"
    done |
        # stick the epoch in front of each requirement. this is a hack (#188).
        ${AWK} -v v="$v" -F: \
               '{if ($3) { gsub(/[<>=]+/, "&" $2 ":", v) }; print $1 v}' |
        debnamize "" |
        ${SED} -e ':a' -e N -e '$!ba' -e 's/,\n/ | /g'
}

###################################################### MAIN starts here

[ -z "$1" ] && { help "$0"; exit 1; }

set -o noglob
repo=""
rpkgs=
keepdeb=false
verbose=false
allowemptylicense=:
later=false
doitall=false

while [ ! -z "$1" ]; do
	case "$1" in

		-\?|-h|--help)
			help "$0"
			exit 0;;

		--longhelp)
			helpdebug "$0"
			exit 0;;

		-d|--debrepo)
			debrepo="$2"
			shift;;

		-r|--rpkgrepo)
			rpkgrepo="$2"
			shift;;

		-a|--all)
			doitall=:;;

		-l|--later)
			later=:;;

		-V|--version)
			echo "${version}"
			exit 0;;

		-v|--verbose)
			verbose=:;;

		+l|--require-license)
			allowemptylicense=false;;

		--test-debnamize)
			debnamize_test;;

		-*)
			echo "bad option '$1'" 1>&2
			help "$0"
			exit 1;;

		*)
			case " $rpkgs " in
				" $1 ") ;;
				*) rpkgs="$rpkgs $1";;
			esac;;
	esac
	shift
done

tx ${ROBOTPKG_ADMIN} ${ROBOTPKG_ADD} ${GZIP_CMD} ${DPKG_DEB} ${APT_FTPARCHIVE} \
    || exit 1
test -z "${rpkgrepo}" || test -r "${rpkgrepo}/pkg_summary.gz" || { echo "cannot find pkg_summary.gz in directory '${rpkgrepo}' (-r option)" 1>&2; exit 1; }

TEMPNAME="${TMPDIR:-/tmp}/pkg2deb.$$"
exitval=0
newline='
'

if ${doitall}; then

	test -z "${rpkgs}" || { echo "cannot honour option --all with a non-empty argument list."; exit 1; }

	pkgsummary_loop pkgsummarysub_extract_and_debianize

        if ! ${later} && ! test -z "${debrepo}"; then
	    buildrepo
	    test -d ${TEMPNAME} && rmdir ${TEMPNAME}
        fi
else

	for rpkg in $rpkgs; do
		[ -z "${rpkg}" ] && continue

		pkgdir="`dirname "${rpkg}"`"
		pkgname="`basename "${rpkg}"`"
		pkgfile=${pkgname}
		pkgpath=${rpkg}
		pkgroot="${TEMPNAME}.${pkgfile}"

		if [ ! -r "${rpkg}" ]; then
			$verbose && echo "${rpkg} is not readable"
			rm_deb ${pkgname%-*}
			continue
		fi


		# inside subshell, we want to forget variables:
		( extract_rpkg && eval_rpkg_vars ${pkgroot}/DEBIAN/+BUILD_INFO && make_deb; )
		[ "$?" = 0 ] || exitval=1

	done # loop on arguments

fi

$verbose && echo "exit=$exitval"
exit $exitval
