#!/bin/sh -e

#
# given a target directory and a linux kernel CVS tree, create
# patches for all the CVS* tags on the Makefile.
#

#-----------------------------------------------#
CVSROOT=${CVSROOT:-/var/cvs}

MODULE=linux
# Used to hide files from web server directory listings
HIDE=.\#
# For faster testing, set TEST=-l otherwise TEST="".  It causes CVS
# to manipulate only the top-level directory of $MODULE
TEST=
TARBALL=false
ALTCVSROOT=
DESTDIR=/tmp
CACHEDIR=

while [ $# != 0 ]
do
    case $1 in
        -m) MODULE=$2; shift 2;;
	-d) CVSROOT=$2; shift 2;;
	-T) TEST=-l; shift 1;;
	-cache) CACHEDIR=$2; shift 2;;
	-dest) DESTDIR=$2; shift 2;;
	-tarball) TARBALL=true; shift 1;;
	-altcvsroot) ALTCVSROOT=$2; shift 2;;
	*) echo "Usage: $0 [-m module] [-d CVSROOT] [-tarball]" >&2
	   exit 2;;
    esac
done

#-----------------------------------------------#

getlock()
{
    typeset LOCKDIR=/tmp/$1.lock
    if mkdir $LOCKDIR 2>&-
    then
	echo $$ > $LOCKDIR/pid
    else
	pid=$(<$LOCKDIR/pid)
	kill -0 $pid 2>&- && exit
	echo 'Stale lockfile'
	echo $$ > $LOCKDIR/pid
    fi
}

releaselock()
{
    rm -r /tmp/$1.lock
}

CVS() {
    cvs -Qfz4 -d$CVSROOT "$@"
}

# print the tag list associated with cvs file $1
show_tags() {
    CVS rlog $1 |
    awk '
	(/symbolic names:$/) {
	    state = 1
	    next
	}
	(state == 1 && !(/^[ 	]/)) {
	    state = 2
	}
	(state == 1) {
	    sub(":", "", $1)
	    print $1
	}'
}

info() {
    echo "info: $@" >&2
}

update() {
    typeset revision=$1

    if [ ! -d $CACHEDIR/$revision/CVS ]
    then
        (cd $CACHEDIR && CVS co -d $revision $TEST -ko -r $revision $MODULE)
    fi

    echo $revision
}

fixpath()
{
    # need to adjust $PATH to contain directory of this script, in which
    # other thins area assumed to live as well
    SCRIPTDIR=$(dirname $0)
    case X$SCRIPTDIR in
	X/) : ;;
	X.) SCRIPTDIR=$PWD ;;
	X.*) SCRIPTDIR=$PWD/$SCRIPTDIR ;;
    esac
    PATH=$SCRIPTDIR:$PATH
}

cleanup() {
    rm -fr $TMPDIR
}

DIFF() {
    ( cd $CACHEDIR && 
	diff -urN --exclude-from=$SCRIPTDIR/dontdiff $1 $2 ) |
	    gzip 
}

create_patch() {
    VERnumeric=$1
    VERcvs=$2
    PATCHNAME=$3

    # This is not a reliable locking scheme...
    # touch $DESTDIR/$HIDE$PATCHNAME

    set -- $(echo ${VERnumeric%-pa*} | version-order)
    set -- $(awk -vv=$1 '($1 == v){print}' < $TMPDIR/linustags.vo)
    upstreamcvstag=$2
    upstreamnumeric=$3

    info creat_patch diff $upstreamcvstag $VERcvs

    old=$(update $upstreamcvstag)
    new=$(update $VERcvs)

    DIFF $old $new > $DESTDIR/$HIDE$PATCHNAME
    mv $DESTDIR/$HIDE$PATCHNAME $DESTDIR/$PATCHNAME
    RESULTS="$RESULTS $DESTDIR/$PATCHNAME"
}

#-----------------------------------------------------------#

info "\$Id: make-kernel-patch,v 1.17 2004/11/16 05:55:53 bame Exp $"

B=$(basename $0)
getlock $B
TMPDIR=/tmp/$B-$$-$RANDOM
[ X$CACHEDIR = X ] && CACHEDIR=$TMPDIR

RESULTS=

START=$PWD
fixpath

mkdir -p $TMPDIR $CACHEDIR

show_tags $MODULE/Makefile > $TMPDIR/alltags
version-order < $TMPDIR/alltags | sort -r > $TMPDIR/alltags.vo
awk '($2 ~ "^LINUS.*[0-9]$")' <$TMPDIR/alltags.vo > $TMPDIR/linustags.vo
awk '($2 ~ "^CVS.*_PA[0-9]*[0-9]$")' <$TMPDIR/alltags.vo > $TMPDIR/patags.vo

while read vo_sortable vo_orig vo_numeric vo_cvs
do
    patchname=patch-$vo_numeric.gz
    if [ ! -f $DESTDIR/$patchname ]
    then
	if [ ! -f $DESTDIR/$HIDE$patchname ]
	then
	    create_patch $vo_numeric $vo_orig $patchname
	fi
    fi

    if $TARBALL
    then
	tarball=linux-$vo_numeric.tar.bz2
	if [ ! -f $DESTDIR/$HIDE$tarball -a ! -f $DESTDIR/$tarball ]
	then
	    info create tarball $tarball
	    d=$(update $vo_orig)
	    ln -s $CACHEDIR/$d $TMPDIR/linux-$vo_numeric
	    if [ ! -z $ALTCVSROOT ]
	    then
		(
		    cd $CACHEDIR/$d
		    find * -name Root |
		    while read f
		    do
			rm $f # to break the link
			echo $ALTCVSROOT > $f
		    done
		)
	    fi
	    (cd $TMPDIR &&
		tar chjf $DESTDIR/$HIDE$tarball linux-$vo_numeric)
	    mv $DESTDIR/$HIDE$tarball $DESTDIR/$tarball
	    RESULTS="$RESULTS $DESTDIR/$tarball"
	fi
    fi

    # could we create differenial patch?
    if [ X$prev_vo_numeric != X ]
    then
	# Note order of new/old is counterintuitive -- note sort -r
	newbase=${prev_vo_numeric%-pa*}
	oldbase=${vo_numeric%-pa*}

	if [ $oldbase = $newbase ]
	then
	    patchname=patch-$prev_vo_numeric${vo_numeric#$oldbase}.gz
	    if [ ! -f $DESTDIR/$patchname -a ! -f $DESTDIR/$HIDE$patchname ]
	    then
		info create_dpatch $patchname
		old=$(update $vo_orig)
		new=$(update $prev_vo_orig)
		DIFF $old $new > $DESTDIR/$HIDE$patchname
		mv $DESTDIR/$HIDE$patchname $DESTDIR/$patchname
		RESULTS="$RESULTS $DESTDIR/$patchname"
	    fi
	fi
    fi

    prev_vo_numeric=$vo_numeric
    prev_vo_orig=$vo_orig
done < $TMPDIR/patags.vo

cleanup
releaselock $B

echo $RESULTS
