#!/bin/sh
##
## Create a SysV(?) package from the installed directory
## Copyright (c) 2007 SATOH Fumiyasu @ OSS Technology Co., Japan
##
## Date: 2007-07-18, since 2007-03-05
## License: GNU General Public License version 3
##

set -u
set -e

## Options
## ======================================================================

if type nawk >/dev/null 2>&1; then
    awk_cmd="nawk"
elif type awk >/dev/null 2>&1; then
    awk_cmd="awk"
else
    echo "$0: error: awk not found" 1>&2
    exit 1
fi

tmp_dir="/var/tmp/ssbuild-pkgmk.$$"

os_name="`uname -s`"
os_ver="`uname -r`"

pkg_rev=""
pkg_category="application"
pkg_arch="`uname -m`"
pkg_extraname=""
pkg_vendor=""
pkg_basedir="/"
pkg_stamp="`uname -a` `date '+%Y-%m-%d %T'`"
pkg_classes="none"
pkg_preinstall_src=""
pkg_postinstall_src=""
pkg_preremove_src=""
pkg_postremove_src=""

attr_hash=""

def_file_mode=""
def_dir_mode=""
def_user="root"
def_group="sys"

system_dir_list="
/etc
/etc/init.d
/etc/rc?.d
/opt
/usr
/usr/bin
/usr/bin/sparcv9
/usr/bin/amd64
/usr/sbin
/usr/sbin/sparcv9
/usr/sbin/amd64
/usr/lib
/usr/lib/sparcv9
/usr/lib/amd64
/usr/lib/security
/usr/lib/security/sparcv9
/usr/lib/security/amd64
/usr/include
/usr/share
/usr/share/man
/usr/share/man/man[1-9]*
/usr/share/man/??
/usr/share/man/??/man[1-9]*
/usr/share/man/??_??.*
/usr/share/man/??_??.*/man[1-9]*
/usr/perl5
/usr/perl5/5.*
/usr/perl5/5.*/*
/usr/perl5/5.*/man/man*
/usr/perl5/site_perl
/usr/perl5/site_perl/5.*
/usr/perl5/site_perl/5.*/*
/usr/perl5/site_perl/5.*/*/auto
/usr/perl5/vendor_perl
/usr/perl5/vendor_perl/5.*
/usr/perl5/vendor_perl/5.*/*
/usr/perl5/vendor_perl/5.*/*/auto
/var
/var/lib
/var/run
/var/log
/var/spool
/var/tmp
"

case "$os_name" in
SunOS)
    os_name="Solaris"
    os_ver="`expr "$os_ver" : '[^.]*\.\(.*\)'`"
    os_nickname="sol$os_ver"
    pkg_arch="`uname -p`"
    ;;
*)
    os_nickname="`echo "$os_name" |tr A-Z a-z`$os_ver"
    ;;
esac

cmd_usage="Usage: $0 [OPTIONS] ROOT NAME VERSION DIR[/PACKAGE]

Arguments [default]:
 ROOT
    Target directory
 NAME
    Package name
 VERSION
    Package version
 DIR
    Destination directory or package filename
 PACKAGE
    Package filename [NAME-VERSION[-REVISION].$os_nickname-$pkg_arch.pkg]

Options [default]:
 -r, --revision REVISION
    Package revision [${pkg_revision:-None}]
 -a, --arch ARCHITECTURE
    Package archtecture [$pkg_arch]
 --extraname EXTRANAME
    Package extraname to append to package filename ('opt', 'local' and so on)
    [${pkg_extra:-None}]
 --vendor VENDOR
    Package vendor name [${pkg_vendor:-None}]
 -s, --summary SUMMARY
    Package summary [Same as the package name]
 -d, --desc, --description DESCRIPTION
    Package description [None]
 -c, --category CATEGORY
    Package category ('system' or 'application') [$pkg_category]
 --default-file-mode MODE
    Default mode for files in the package [${def_file_mode:-Passthrough}]
 --default-dir-mode MODE
    Default mode for directories in the package [${def_dir_mode:-Passthrough}]
 -u, --default-user USER
    Default user for owner of files in the package [$def_user]
 -g, --default-group GROUP
    Default group for owner group of files in the package [$def_group]
 --attr, --attribute PATH:[MODE]:[USER]:[GROUP]
    Set mode, user and group for the path
 --pre-install SCRIPT
 --post-install SCRIPT
 --pre-remove SCRIPT
 --post-remove SCRIPT
   Pre/post install/remove script
"

## Command-line options
## ----------------------------------------------------------------------

getopts_want_arg()
{
    if [ "$2" -eq 0 ]; then
	echo "$0: error: option requires an argument: $1" 1>&2
	exit ${3:-1}
    fi
}

while test "$#" -gt 0; do
    opt="$1"; shift
    case "$opt" in
    -r|--rev|--revision|-rel|--release)
	getopts_want_arg "$opt" "$#"
	pkg_rev="$1"; shift
	;;
    -a|--arch|--architecture)
	getopts_want_arg "$opt" "$#"
	pkg_arch="$1"; shift
	;;
    --extraname)
	getopts_want_arg "$opt" "$#"
	pkg_extraname="$1"; shift
	;;
    --vendor)
	getopts_want_arg "$opt" "$#"
	pkg_vendor="$1"; shift
	;;
    -s|--summary)
	getopts_want_arg "$opt" "$#"
	pkg_summary="$1"; shift
	;;
    -d|--desc|--description)
	getopts_want_arg "$opt" "$#"
	pkg_desc="$1"; shift
	;;
    -c|--category)
	getopts_want_arg "$opt" "$#"
	pkg_category="$1"; shift
	;;
    --default-file-mode)
	getopts_want_arg "$opt" "$#"
	def_file_mode="$1"; shift
	;;
    --default-dir-mode|--default-directory-mode)
	getopts_want_arg "$opt" "$#"
	def_dir_mode="$1"; shift
	;;
    -u|--default-user)
	getopts_want_arg "$opt" "$#"
	def_user="$1"; shift
	;;
    -a|--attr|--attribute)
	getopts_want_arg "$opt" "$#"
	file_attr="$1"; shift
	attr_hash="${attr_hash:+$attr_hash }`echo "$file_attr" |sed 's#//*#/#g;s#^/*\([^:]*\):\([^:]*\):\([^:]*\):\([^:]*\)$#m_hash["\1"]="\2";u_hash["\1"]="\3";g_hash["\1"]="\4";#'`"
	;;
    --pre-install)
	getopts_want_arg "$opt" "$#"
	pkg_preinstall_src="$1"; shift
	;;
    --post-install)
	getopts_want_arg "$opt" "$#"
	pkg_postinstall_src="$1"; shift
	;;
    --pre-remove)
	getopts_want_arg "$opt" "$#"
	pkg_preremove_src="$1"; shift
	;;
    --post-remove)
	getopts_want_arg "$opt" "$#"
	pkg_postremove_src="$1"; shift
	;;
    --)
	break
	;;
    -*)
	echo "$0: error: illegal option: $opt" 1>&2
	exit 1
	;;
    *)
	set -- "$opt" "$@"
	break
	;;
    esac
done

if [ $# -ne 4 ]; then
    echo "$cmd_usage"
    exit 0
fi

pkg_root="$1"; shift
pkg_name="$1"; shift
pkg_ver="$1"; shift
pkg_file="$1"; shift

: ${pkg_summary:=$pkg_name}

case "$pkg_file" in
.|./*|../*)
    pkg_file="`pwd`/$pkg_file"
    ;;
esac
if [ -d "$pkg_file" ]; then
    pkg_file="$pkg_file/$pkg_name-$pkg_ver${pkg_rev:+-$pkg_rev}.$os_nickname-$pkg_arch${pkg_extraname:+-$pkg_extraname}.pkg"
fi

pkg_info="$pkg_root/pkginfo"
pkg_depend="$pkg_root/depend"
pkg_preinstall="$pkg_root/preinstall"
pkg_postinstall="$pkg_root/postinstall"
pkg_preremove="$pkg_root/preremove"
pkg_postremove="$pkg_root/postremove"
pkg_proto="$pkg_root/prototype"

## pkginfo
## ======================================================================

echo "Building pkginfo file..."

cat >"$pkg_info" <<EOT
PKG="$pkg_name"
NAME="$pkg_summary"
VERSION=$pkg_ver${pkg_rev:+,REV=$pkg_rev}
ARCH="$pkg_arch"
CATEGORY="$pkg_category"
PSTAMP="$pkg_stamp"
${pkg_basedir:+BASEDIR="$pkg_basedir"}
${pkg_desc:+DESC="$pkg_desc"}
${pkg_vendor:+VENDOR="$pkg_vendor"}
${pkg_classes:+CLASSES="$pkg_classes"}
EOT

## FIXME: depend
## ======================================================================

echo "Building depend file..."
touch "$pkg_depend"

## FIXME: space
## ======================================================================

## FIXME: request
## ======================================================================

## preinstall, postinstall, preremove, postremove
## ======================================================================

echo "Building install/remove script..."
if [ -n "$pkg_preinstall_src" ]; then
    cp "$pkg_preinstall_src" "$pkg_preinstall"
    chmod +x "$pkg_preinstall"
fi
if [ -n "$pkg_postinstall_src" ]; then
    cp "$pkg_postinstall_src" "$pkg_postinstall"
    chmod +x "$pkg_postinstall"
fi
if [ -n "$pkg_preremove_src" ]; then
    cp "$pkg_preremove_src" "$pkg_preremove"
    chmod +x "$pkg_preremove"
fi
if [ -n "$pkg_postremove_src" ]; then
    cp "$pkg_postremove_src" "$pkg_postremove"
    chmod +x "$pkg_postremove"
fi

## prototype
## ======================================================================

echo "Building prototype file..."

## Expand wildcards and translate to rlative path
system_dir_list="`echo $system_dir_list |sed 's#//*#/#g;s#^/##;s# /# #g'`"

echo "i pkginfo" >"$pkg_proto"
if [ -f "$pkg_preinstall" ]; then echo "i preinstall" >>"$pkg_proto"; fi
if [ -f "$pkg_postinstall" ]; then echo "i postinstall" >>"$pkg_proto"; fi
if [ -f "$pkg_preremove" ]; then echo "i preremove" >>"$pkg_proto"; fi
if [ -f "$pkg_postremove" ]; then echo "i postremove" >>"$pkg_proto"; fi

(cd "$pkg_root" && pkgproto .) \
    |"$awk_cmd" '
	BEGIN {
	    split("'"$system_dir_list"'", system_dir_list)
	    for (i in system_dir_list) {
		system_dir_hash[system_dir_list[i]] = 1
	    }
	    '"$attr_hash"'
	    def_file_mode = "'"$def_file_mode"'"
	    def_dir_mode = "'"$def_dir_mode"'"
	    def_user = "'"$def_user"'"
	    def_group = "'"$def_group"'"
	}
	$1 == "f" && $3 !~ /\// {
	    next
	}
	{
	    NF=6
	    if ($1 == "s") {
		print
		next
	    }

	    if (system_dir_hash[$3]) {
		$4="?"
		$5="?"
		$6="?"
	    }
	    else {
		if (length($4) == 4) {
		    $4 = substr($4, 2)
		}
		if ($1 == "f" && def_file_mode != "") {
		    $4 = def_file_mode
		}
		else if ($1 == "d" && def_dir_mode != "") {
		    $4 = def_dir_mode
		}
		$5 = def_user
		$6 = def_group
	    }

	    if (length(m_hash[$3])) {
		$4 = m_hash[$3]
	    }
	    if (length(u_hash[$3])) {
		$5 = u_hash[$3]
	    }
	    if (length(g_hash[$3])) {
		$6 = g_hash[$3]
	    }

	    print
	}
    ' \
    >>"$pkg_proto"

## pkgmk and pkgtrans
## ======================================================================

trap 'rm -rf "$tmp_dir"' 0 HUP INT QUIT TERM PIPE
mkdir -m 0700 "$tmp_dir"

echo "Building package instance..."
pkgmk -r "$pkg_root" -f "$pkg_proto" -d "$tmp_dir"

echo "Building package file from instance..."
pkgtrans -s "$tmp_dir" "$pkg_file" "$pkg_name"

## FIXME: Remove pkginfo and prototype file in $pkg_root?

exit 0

