#!/bin/bash
########################################################################
#                                                                      #
# filopack.sh v. 0.3.9, philodej@gmail.com                             #
# ----------------------------------------                             #
#                                                                      #
# (c) Copyright Filodej 2008.                                          #
# Permission to copy, use, modify, sell and distribute this software   #
# is granted provided this copyright notice appears in all copies.     #
# This software is provided "as is" without express or implied         #
# warranty, and with no claim as to its suitability for any purpose.   #
#                                                                      #
# DESCRIPTION                                                          #
# Very simple (and stupid) packaging system.                           #
# It is implemented specifically as a helper for packages available    #
# at http://filodej.blogspot.com site the packages are built for the   #
# purposes WMU-6500FS with JoKeR's firmware                            #
# (see http://mgb111.pradnik.net/) but could be used for any other     #
# system as well.                                                      #
# It serves for package creation, uploading, downloading, enumeration, #
# installation and uninstalation.                                      #
# It can be used on the box as well as on the chroot-ed "mirror"       #
# system.                                                              #
#                                                                      #
# For a short tutorial see:                                            # 
#   http://filodej.blogspot.com/2008/11/simple-packaging-sysetm.html   #
#                                                                      #
########################################################################

PACKAGE=
VALID_ACTIONS="help,version,config,init,print,packages,genlist,pack,upload,install,remove,download,delete,touch,summary"
CMD_LINE_PARSER=`getopt -o o:p:f:grR --long $VALID_ACTIONS -n 'filopack.sh' -- "$@"`
PREFIX="sys/"
OUTFMT="tbz2"
LISTFMT="text"
REUSE="n"
REMOTE="n"
CONFIG_DIR=".filopack"
EXTENSIONS="tar.bz2 tgz tar"
PACKAGE_LIST=".packages"
WEB_ROOT="http://filodej.ic.cz"
EMAIL="philodej@gmail.com"
VERSION=0.3.9

function usage
{
    cat << EOF
    
Stupid packaging system (version $VERSION)

Usage:
 
  filopack.sh [OPTIONS] [<action>] [<package-name>]

Basic usage:

  Basic actions: 
    (typically used when you just download and install/uninstall pre-built packages)

    --help     ... prints detailed help message
    --version  ... prints version of the script
    --config   ... perform system (re)configuration
    --packages ... lists all packages available for download
    --download ... downloads a selected package
    --print    ... just prints the files of selected package
    --install  ... unpacks (installs) the package  
    --delete   ... deletes the given package archive
    --remove   ... removes (uninstalls) the package
    --summary  ... generates a summary of installed packages
EOF
}

function help
{
    usage
    cat << EOF
    
  Advanced actions:
    (used when you build an application from source code and create a package)
    
    --init     ... creates timestamp (<package-name>.ts file)
    --touch    ... touches all files of given package
    --pack     ... creates a new package
                     based on:
	               previously created timestamp (<package-name>.ts file)
	 	     generates:
	               archive <package-name>.<fmt> along with file list
		       <config-dir>/<package-name>.lst.
    --upload   ... uploads (via scp) the package to the destination (box)
    --genlist  ... generates a list of all locally created packages
                     (it can be later used as a public package register)

  Advanced options:
    -o <fmt> ... archive format
                ( supported: 'tar', 'tgz', 'tbz2', 'lst')
		( 'lst' just generates .lst file and prints filenames)
    -r       ... performs install/remove actions remotely (locally by default)
    -p       ... prefix path ('sys/' by default)
    -f       ... specify custom file selection filter 
                 ('-newer <package-name>.ts' by default, '' performs no filtering)
    -R       ... reuse the previously generated filename list (.lst file)
                 (instead of using "timestamp" method)
    -g       ... generate HTML package list (instead of default plain text list)
EOF
}

function update_index
{
    echo "Retrieving package index... (Connecting to $WEB_ROOT)"
    wget -O $CONFIG_DIR/$PACKAGE_LIST "$WEB_ROOT/filopack/$PACKAGE_LIST"  &> /dev/null
}

function init 
{
    echo > $CONFIG_DIR/$PACKAGE.ts
    echo "Timestamp written to file $CONFIG_DIR/$PACKAGE.ts"
}

function packages
{
    update_index
    echo "Available packages:"
    cut -d":" -f1 $CONFIG_DIR/$PACKAGE_LIST
}

function list_prologue {
    if [ $1 == "html" ]
    then
	echo "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">"
	echo "<html><body>"
	echo "<head>"
	echo "<title>Filodej's package index</title>"
	echo "<link rev=\"made\" href=\"mailto:$EMAIL\">"
	echo "<meta name=\"description\" content=\"Filodej's simple packaging system index file\">"
	echo "<meta name=\"author\" content=\"filodej\">"
	echo "<meta name=\"keywords\" content=\"wmu6500fs, linux, packages\">"
	echo "<meta name=\"generator\" content=\"filopack.sh v. $VERSION\">"
	echo "</head>"
	echo "<body>"
    fi
}

function list_epilogue {
    if [ $1 == "html" ]
    then
	echo "</body></html>"
    fi
}

function list_row {
    case $1 in
        "html" ) echo "<a href="$CONFIG_DIR/$2.lst">[files]</a>";
		 echo "<a href=\"$2.$3\">[binary]</a>";
		 echo $2;
		 echo "<br/>";;
	"text" ) echo $2 $3;;
    esac						    
}

function generate_list
{
    list_prologue $1
    for name in $(ls .filopack/*.lst); do
        base=$(basename $name .lst)
        for ext in $EXTENSIONS; do
#           echo $base.$ext
            if [ -f $base.$ext ]
            then
                list_row $1 $base $ext
            fi
        done
    done
    list_epilogue $1
}
											    
function delete
{
    update_index
    line=$(cat $CONFIG_DIR/$PACKAGE_LIST | grep "$PACKAGE:")
    BASE=${line%:*}
    EXT=${line#*:}
    read -p "Are you sure to delete the $BASE.$EXT archive (y/n)?"
    [ "$REPLY" == "y" ] && rm $BASE.$EXT
}

function download 
{
    update_index
    line=$(cat $CONFIG_DIR/$PACKAGE_LIST | grep "$PACKAGE:")
    BASE=${line%:*}
    EXT=${line#*:}
    if [ -z "$BASE" ]
    then
	echo "Package $PACKAGE not available (try to use --packages switch)"
    else
        echo "Downloading package $PACKAGE from $WEB_ROOT ..."
    	wget -O $CONFIG_DIR/$PACKAGE.lst "$WEB_ROOT/filopack/$CONFIG_DIR/$PACKAGE.lst"
	wget -O $BASE.$EXT "$WEB_ROOT/filopack/$BASE.$EXT"
    fi
}

function summary 
{
    update_index
    echo
    echo "Filodej package summary:"
    echo "Package name                          downloaded [#installed/#total]"
    echo "--------------------------------------------------------------------"
    cat $CONFIG_DIR/$PACKAGE_LIST | while read line; 
    do 
	BASE=${line%:*}
	EXT=${line#*:}
	LST=$CONFIG_DIR/$BASE.lst
	if [ ! -f $LST ] 
	then 
	    wget -O $LST "$WEB_ROOT/filopack/.filopack/$BASE.lst" &> /dev/null 
	fi
	if [ -f $BASE.$EXT ]
	then
	    downloaded="yes"
	else
	    downloaded="no"
	fi
	installed=0
	all=0
	for file in $(cat $LST);
	do
	    let "++all"
	    if [ -f $file ]
	    then
		let "++installed"
	    fi
	done
	let division=$installed/$all
	printf "%-40s %3s           [%4d/%4d]\n" $BASE $downloaded $installed $all
    done
}

function print
{
    if [ $REUSE == "y" ] || ( [ -z "$FILTER" ] && [ ! -f "$CONFIG_DIR/$PACKAGE.ts" ] )
    then
	cat $CONFIG_DIR/$PACKAGE.lst
    else
	if [ -z "$FILTER" ]; then FILTER="-newer $CONFIG_DIR/$PACKAGE.ts"; fi
        find $PREFIX -not -type d $FILTER
    fi
}

function pack
{
    if [ $REUSE != "y" ]
    then
	if [ -z "$FILTER" ]; then FILTER="-newer $CONFIG_DIR/$PACKAGE.ts"; fi
        find $PREFIX -not -type d $FILTER > $CONFIG_DIR/$PACKAGE.lst
    fi
    if [ $OUTFMT != "lst" ]
    then
#	tar $PACK_CMD $PACKAGE.$EXT --files-from $CONFIG_DIR/$PACKAGE.lst 2>&1 | tee $CONFIG_DIR/$PACKAGE.log
        cat $CONFIG_DIR/$PACKAGE.lst | xargs tar $PACK_CMD $PACKAGE.$EXT  2>&1 | tee $CONFIG_DIR/$PACKAGE.log
#        cat $CONFIG_DIR/$PACKAGE.lst | xargs ls
 
    else
	cat $CONFIG_DIR/$PACKAGE.lst
    fi
}

function upload
{
    read -p "Sure to upload $PACKAGE to $DEST_USER@$DEST:$DEST_DIR (y/n)?"
    if [ "$REPLY" == "y" ]
    then
	scp $PACKAGE.* $DEST_USER@$DEST:$DEST_DIR 
	scp $CONFIG_DIR/$PACKAGE.lst $DEST_USER@$DEST:$DEST_DIR/$CONFIG_DIR
    fi
}

function confirm_command
{
    if [ $REMOTE != "y" ]
    then
	read -p "Sure to $2 locally at $DEST_DIR (y/n)?"
	if [ $REPLY == "y" ]
	then
    	    pushd $DEST_DIR 1> /dev/null
	    eval $1
	    popd 1> /dev/null
	fi
    else
	read -p "Sure to $2 remotely at $DEST_USER@$DEST:$DEST_DIR (y/n)?"
	[ "$REPLY" == "y" ] && ssh $DEST_USER@$DEST "cd $DEST_DIR; $1"
    fi
}

function install
{
    update_index
    line=$(cat $CONFIG_DIR/$PACKAGE_LIST | grep "$PACKAGE:")
    BASE=${line%:*}
    FILE_EXT=${line#*:}

    if [ -z $FILE_EXT ]; then
	echo "Module $PACKAGE not found in package list - use '$EXT' extension"
	FILE_EXT=$EXT
    fi
    
    case $FILE_EXT in
	"tar" ) UNPACK_CMD='xvf' ;;
	"tgz" ) UNPACK_CMD='xzvf';;
	"tar.bz2" ) UNPACK_CMD='xjvf';;
	* ) echo unknown extension: $FILE_EXT ; exit 2 ;;
    esac
    confirm_command "tar $UNPACK_CMD $PACKAGE.$FILE_EXT" "unpack $PACKAGE"
}

function remove
{
    confirm_command "xargs rm -f < $CONFIG_DIR/$PACKAGE.lst" "remove $PACKAGE"
}

function touch_files
{
    confirm_command "xargs touch < $CONFIG_DIR/$PACKAGE.lst" "touch all files of the $PACKAGE"
}

function configuration
{
    if [ ! -f $CONFIG_DIR/.config ]
    then
	echo "Destination address and user not configured ($CONFIG_DIR/.config file not found)"
	DEST_DIR="/mnt/C"
	DEST_USER="root"
	DEST="storage"
	read -p "Insert destination directory [$DEST_DIR]: "
	if [ ! -z $REPLY ]; then DEST_DIR=$REPLY; fi 
	read -p "Insert box IP address or hostname (remote packaging only)[$DEST]: "
	if [ ! -z $REPLY ]; then DEST=$REPLY; fi 
	read -p "Insert box user account (remote packaging only) [$DEST_USER]: "
	if [ ! -z $REPLY ]; then DEST_USER=$REPLY; fi 
	
	echo -e "DEST=$DEST\nDEST_USER=$DEST_USER\nDEST_DIR=$DEST_DIR" > $CONFIG_DIR/.config
	echo "Settings stored to $CONFIG_DIR/.config file"
    else
        source $CONFIG_DIR/.config
        echo "Configuration file $CONFIG_DIR/.config file found and used"
    fi
}

###########################################################3

if [ -z $1 ]; then usage; exit 0; fi

eval set -- "$CMD_LINE_PARSER"
while true ; do
    case "$1" in
        -r) REMOTE="y" ; shift ;;
        -g) LISTFMT="html" ; shift ;;
        -R) REUSE="y" ; shift ;;
        -o) OUTFMT=$2 ; shift 2 ;;
        -p) PREFIX=$2 ; shift 2 ;;
        -f) FILTER=$2 ; shift 2 ;;
	--config) rm -f $CONFIG_DIR/.config ; ACTION=$1 ; shift ;;
        --init | --print | --packages | --genlist |\
	--pack | --upload | --install | --remove |\
	--download | --delete | --summary | --touch |\
	--help | --version ) ACTION=$1 ; shift ;; 
        --) 
	    if [ -z $ACTION ] ; then 
		echo "Missing action!"; exit 1 ; 
	    fi
	    shift ; break ;;
        *) echo "Internal error!" ; exit 1 ;;
    esac
done

PACKAGE=$1

case $OUTFMT in
    "tar" ) PACK_CMD='cvvf'; UNPACK_CMD='xvf'; EXT='tar';;
    "tgz" ) PACK_CMD='czvvf'; UNPACK_CMD='xzvf'; EXT='tgz';;
    "tbz2" ) PACK_CMD='cjvvf'; UNPACK_CMD='xjvf'; EXT='tar.bz2';;
    "lst" ) PACK_CMD='cat'; UNPACK_CMD=''; EXT='lst';;
    * ) echo invalid output format: $OUTFMT ;;
esac

if [ -z "$PACKAGE" ] \
&& [ "$ACTION" != "--packages" ] \
&& [ "$ACTION" != "--genlist" ] \
&& [ "$ACTION" != "--summary" ] \
&& [ "$ACTION" != "--config" ] \
&& [ "$ACTION" != "--help" ] \
&& [ "$ACTION" != "--version" ]
then
    echo "Missing package name"
else
    configuration
    mkdir -p $CONFIG_DIR
    case $ACTION in
        --version)  echo $VERSION ;;
        --help)     help;;
        --init)     init;;
	--print)    print;;
	--packages) packages;;
	--genlist)  generate_list $LISTFMT;;
	--pack)     pack;;
	--upload )  upload;;
	--install ) install;;
	--remove )  remove;;
	--download) download;;
	--delete)   delete;;
	--summary ) summary;;
	--touch)    touch_files;;
	--config)   ;;
	* ) echo invalid action: $ACTION ;;
    esac
fi


