#! /bin/sh
#
#@(#)patch_sid.sh 1.1 94/10/31 SMI
#
######################################################################
# This utility facilitates the migration of build fixes from an
# integration machine to the various build machines.  When a build 
# problem gets fixed, the lead release engineer should:
# 
# 1)	login to an INTEGRATION machine
# 
# 2) 	Create the input file '$top/$sid_dir/sidfix', which will be the
#	basis for editing SIDlist on the integration machine, and 
#	extracting/deleting files from the source tree on the build 
#	machines.  This file should list the sources relative to $top 
#	(usually /usr/src), followed by white space and SID number.
# 	eg:
#		subdir/SCCS/s.sourcefile 1.7
#		subdir/subdir2/SCCS/s.sourcefile 1.2
#
#	To earmark a file for deletion, the filename should be preceded by 
#	*D* <<no white space!, SID# optional >> as in:
#
#		*D*subdir/subdir3/SCCS/s.sourcefile
# 
# 3)	Execute 'patch_sid [-n] [-l]'. 
#	-n turns off sending mail to other RE's such as when 
#		lang/boot and libs are first built by one RE.
#	-l creates a list of sidfix entries that show no change
#		from the current SIDlist.  This option is most
#		useful for PSR releases where sidfix entries are
#		generated based on a snapshot of entire directories
#		that have been smerged.  If the -l option is not
#		used, the script asks for confirmation to include
#		sidfix entries that haven't changed.
# 
# After 'patch_sid' successfully executes on the integration machine,
# it sends mail to the release engineers, who should then 
# follow these steps:
# 
# 1)	login to their BUILD machines
# 
# 2)	Create the input file '$top/$sid_dir/sidfix', by
# 	mousing up the contents of the received e-mail.
# 
# 3)	Execute 'patch_sid' to populate their source trees
# 	with the build fixes, or remove files deleted from SIDlist.
# 
# 4)	Rebuild affected directories.
#
# Since the SIDlist build philosophy requires that the SCCS_DIRECTORIES
# not be mounted when a 'make' may be running, a separate entry similar 
# to this is required in the BUILD machine's fstab:
#
# For the ***4.1 BUILD MACHINES ONLY*** the entry is:
# aprime:/usr/bigsrc.41/SCCS_DIRECTORIES /mnt/41src nfs ro,bg,hard,intr  0 0
#
# In general, the script mounts/unmounts the SCCS sources as needed
# and verifies the input file data.  After execution, it renames the
# input file by suffixing it with the new SID of SIDlist, thus 
# providing some kind of audit trail.  Each release engineer can
# check that her/his build fixes are up-to-date by looking for a complete
# set of suffixed files.
# 
# The script also performs these functions, depending on where it 
# is invoked:
# 
#	Integration Machine:  edits the SIDlist using the input
#	file, and checks it back into SCCS with comments entered
#	on the keyboard, followed by a Control-D.  To sidestep
#	various internal size limits in the use of 'sed', sidfix
#	files containing more than 80 lines are split up.
#
#	Build Machines:  extracts/removes the sources listed in
#	the input file.  [Options -n -l are ignored].
#
# NOTE: This script can create and delete entries to SIDlist, as well as
#	change existing ones.  Where a path or filename change is desired,
#	create a 'delete' entry for the old and another one for the new.
#	This script may have to be modified for concurrent use of one set
#	of integration sources by multiple releases, as may be the
#	case for 4.1 and 4.1.1.
#
#
######################################################################

PWD=`pwd`
fix=sidfix			#list of modified files and deltas
bindir=/usr/release/bin
top=/usr/src
reng="pos-build@nova"
bugid='patch_sid'

usage="USAGE:  $0 [-n] [-l]"
if [ $# -gt 1 ] ; then 
	echo "$usage" && exit 1
else
	case $1 in
		-n )	reng=`whoami`@`hostname`;; 
		-l )	list_chg=1;;
		'' ) 	;;			
		*  )	echo $usage && exit 1;;
	esac
fi

tmp=/tmp; sid_dir=$top/SIDlist

if [ ! -f $sid_dir/$fix ] ; then
	echo "You must create '$sid_dir/$fix' by listing the modified"
	echo "source files and deltas required to complete the build."
	exit 1
fi

#Determine which function will be invoked

case `hostname` in

	iridium)
		edit_sid=1    #Integration machines
		mt_dir=/usr/src/SCCS_DIRECTORIES;;
	krypton|halftrack|godzilla)
		get_files=1   #Build machines
		mt_dir=/mnt/413src;;
	* )
		echo "`hostname` is not a known release machine!" && exit 1	
esac

#Get mount state

/etc/mount | grep -s " $mt_dir " || unmounted=1 

#Screen yes/no reponses

get_yn='(while read yn; do
	case "$yn" in
		[Yy]* ) exit 0;;
		[Nn]* ) exit 1;;
		    * ) echo -n "Please answer y or n:  ";;
	esac
done)'

#Mount and unmount partitions

get_mtstat='( [ "$do_mt" = "0" ] && cmd=".mountit" || cmd="unmount"
done=""
while :
do
	/etc/mount|grep -s " $mt_dir "
	[ "$?" = "$do_mt" ] && exit 0
	if [ "$done" ]; then
		echo -n "Unable to $cmd $mt_dir!  Try again? (y/n)  "
		eval "$get_yn" && continue || exit 1
	fi
	${bindir}/${cmd} $mt_dir
	done=1
done )'

#Clean up before exiting

sv_errs=$tmp/patch_sid.errs; rm -f $sv_errs
nochg=$tmp/no_change; rm -f $nochg
errs=$tmp/err_$$

cleanup='( [ "$edit_sid" ] && [ -f $sid_dir/SCCS/p.${sid} ] && \
	sccs unedit $sid;
	if [ -s $errs ]; then
		cat $errs >  $sv_errs; echo "ERROR!"
		echo "Errors are in $sv_errs."
	fi
	if [ -s $nochg ]; then 
		echo "Any files listed in $nochg should be investigated."
		echo "They match entries already in $sid."
	fi
rm -f $tmp/*$$*; cd $PWD
if [ "$unmounted" ]; then
	do_mt=1
	eval "$get_mtstat" && exit 0 || exit 1
fi )'

do_mt=0
eval "$get_mtstat" || exit 1

trap 'echo ABORTED!; eval "$cleanup"; exit 1' 1 2 3 15

#NOTE:  Need to change this for branched releases!!      <<<<<<<<<<<<<

if [ "$edit_sid" ]; then
	(cd $top/sys/conf.common; sccs get -s RELEASE)   
else
	(cd $top/sys/conf.common 
	sccs get -s ${mt_dir}/sys/conf.common/RELEASE)
fi
release=`cat $top/sys/conf.common/RELEASE`
sid=${release}.SID

#Check if $sid is editable; also clean up $fix

cd $sid_dir
if [ "$edit_sid" ]; then
	if [ -f SCCS/p.${sid} ]; then
		echo "Writable $sid exists!"
		edit_sid=""
		eval "$cleanup"; exit 1
	fi
	sed -e '/^[ 	]*$/d' \
	-e "/^SIDlist\/SCCS\/s.$sid /d" $fix | sort |uniq > \
	$tmp/${fix}_$$
else
	sed -e '/^[ 	]*$/d' $fix | sort |uniq > $tmp/${fix}_$$
fi

#Validate $fix; separately process delete/change/add

echo "Verifying '$fix' entries..."
cat $tmp/${fix}_$$ | \
awk '{ if (NF > 2 ) printf ("Too many fields: %s\n", $0) }' > $errs

egrep -v 'SCCS/s.' $tmp/${fix}_$$ > $tmp/$$
if [ -s $tmp/$$ ]; then
	echo "These entries should be in 'SCCS/s.' format:" >> $errs
	cat $tmp/$$ >> $errs
fi

if [ -s $errs ] ; then
	eval "$cleanup"; exit 1
fi

[ "$edit_sid" ] && sccs get $sid 
del=$tmp/del_$$; rest=$tmp/rest_$$; add=$tmp/add_$$; chg=$tmp/chg_$$
current=$tmp/current_$$ todo=$tmp/todo_$$
grep '^*D*' $tmp/${fix}_$$ | sed 's/*D*//g' | awk '{print $1}'  > $del
grep -v '^*D*' $tmp/${fix}_$$ | awk '{print $1, $2}' > $rest

notfound='No such file: '; rm -f $errs; ask=$tmp/ask_$$

#Get entries that are the same in SIDlist-- build a list
#if '-l' option used; also verify that SID really exists.

if [ "$edit_sid" ]; then
	cat $rest | while read line; do
		if [ "$list_chg" ]; then
			egrep "^${line}$" $sid >> $nochg && continue
		else
			egrep "^${line}$" $sid >> $ask && continue
		fi
	set $line
	sccs get -g -r$2 ${mt_dir}/$1  1>> /dev/null 2>> $errs && \
	echo "${line}" >> $todo && continue
	done	
else
	cp -p $rest $todo
fi

#If '-l' not used, ask if ok to process entries that haven't changed.

if [ -s $ask ] ; then
	echo "Line(s) below reflect no change from current SIDlist."
	cat $ask |more
	echo -n "Do you wish to process them anyway? (y/n)  "
	if eval "$get_yn"; then
		cat $ask > $chg 
	else
		echo "" >> $errs
		echo "Remove line(s) that show no change from $sid" >> $errs
		cat $ask >> $errs
		echo "" >> $errs
		echo "Verifying remaining entries..."
	fi
fi

cat $rest| awk '{ if ($2 !~ /^[0-9]+\.[0-9]+/)
	printf("Bad format: %s %s\n", $1, $2) }' >> $errs

#Get confirmation about new files to be added

if [ -s $todo ] ; then
   for i in `cat $todo | awk '{print $1}'`; do
	if [ "$edit_sid" ]; then 	#check vs. current $sid (patience!)
		if egrep -s "^$i[ 	]" $sid; then
			egrep "^$i[ 	]" $todo >> $chg;  continue
		fi
		echo "This file is not in $sid:" 
		echo "  $i"
		echo -n "Add new entry?  (y/n)  "
		eval "$get_yn" && egrep "^$i[ 	]" $todo >> $add && continue
		echo "Remove entry:  $i " >> $errs
	else				#OK to check vs SCCS tree (faster)
		[ -f ${mt_dir}/$i ] && continue
		echo "$notfound $i" >> $errs
	fi
   done
fi

#Check deletes

if [ -s $del ] && [ "$edit_sid" ]; then
	for i in `cat $del`; do
		[ -f $top/$i ] && continue
		egrep -s "^$i[ 	]" $sid && continue
		echo "$notfound $i" >> $errs
	done
fi

if [ -s $errs ] || [ -s $nochg ] ; then 
   if [ "$edit_sid" ] ; then
	ok=/tmp/patch_sid.pass; rm -f $ok
	for i in $chg $del $add; do
		if [ "$i" = $del ] && [ -s $del ] ; then
			for j in `cat $del`; do
				egrep -s "[ 	]$j$" $errs && continue
				echo "*D*$j" >> $ok && continue
			done
		else
			[ -s $i ] && cat $i >> $ok
		fi
	done
	echo ""
	eval "$cleanup"
	[ -s $ok ] && echo "Entries that have passed verification are in '$ok'."
	exit 1
   else
	eval "$cleanup"; exit 1
   fi
fi

#Get next SID for $sid; use it to rename $fix 

if [ "$edit_sid" ]; then
	comfile=$tmp/comment_$$
	echo \
	"Enter delta comments below; type Control-D when done:"
	while read comment; do
		echo "$comment">> $comfile
	done
	[ "$bugid" ] && echo "$bugid" >> $comfile
	echo "Editing large file '$sid'... "
	trap 'echo ABORTED!; eval "$cleanup"; exit 1' 1 2 3 15
	if sccs edit $sid > $tmp/ed_$$ ; then
		:
	else
		eval "$cleanup"; exit 1
	fi
	new_sid=`fgrep 'new delta' $tmp/ed_$$ | awk '{print $3}'`
	echo "New version is $new_sid..."
else
	new_sid=`fgrep "SIDlist/SCCS/s.$release.SID" $todo |\
	awk '{print $2}'`
	if [ ! "$new_sid" ] ; then 
		echo "ERROR: Input file should include $sid file."
		eval "$cleanup"; exit 1
	fi
fi

#$newfix shouldn't already exist; check it just the same 

newfix=${sid_dir}/${fix}_${new_sid}
if [ -s $newfix ]; then 
	echo -n "$newfix exists; Clobber? (y/n)  "
	if eval "$get_yn"; then
		rm -f $newfix
	else
		[ "$edit_sid" ] && eval "$cleanup"; exit 1
	fi
fi

#If invoked on integration system, edit $sid; fix format of new files;
#split huge sedfiles to avoid overflow;
#append $bugid to sccs delta comments; Send mail

if [ "$edit_sid" ]; then
	stars='********************'; sedit=$tmp/sedit_$$; sidtmp=$tmp/sid_$$
	if [ -s $chg ]; then
		cat $chg | \
		awk '{printf ("s@^\(%s \)\(.*\)@\1%s@\n", $1, $2)}' > $sedit
	fi
	if [ -s $del ]; then
		cat $del | \
		awk '{printf ("s@^\(%s \)\(.*\)@@g\n", $1 )}' >> $sedit
		echo '/^[ \t]*$/d' >> $sedit
	fi
	if [ -s $sedit ]; then
		maxsz=80
		sz=`wc -l $sedit | awk '{print $1}'`
		if [ $sz -gt $maxsz ] ; then
		   subset=$tmp/split$$_; nu=$tmp/newcmd$$
		   split -$maxsz $sedit $subset
		   sets=`ls $subset*`; first=1
		   for i in $sets; do
			sfx=`echo $i | sed 's@^.*_@@g'`
			interm=$tmp/${sfx}_$$
			if [ "$first" ]; then
			   echo "sed -f $i $sid 2>> $errs >> $interm" >> $nu
			   echo 'sync; sync' >> $nu
			   echo "rm -f $sid" >> $nu
			   first=
			else	
			   echo "sed -f $i $nextsid 2>> $errs >> $interm" \
			   >> $nu
			   echo 'sync; sync' >> $nu
			   echo "rm -f $nextsid" >> $nu
			fi
			nextsid=$interm
		   done
		   chmod +x $nu; . $nu 2>> $errs ; sync; sync
		   [ ! -s $errs ] && mv $interm $sidtmp 2>> $errs 
		else
		   sed -f $sedit $sid > $sidtmp 2>> $errs
		fi
	else
		cp -p $sid $sidtmp 2>> $errs
	fi
	if [ -s $add ]; then
		cat $add | awk '{print $1, $2}' >> $sidtmp 2>> $errs
	fi
	sync; sync
	[ -s "$errs" ] && echo "EDIT failed!" && eval "$cleanup" && exit 1
	mv $sidtmp $sid; sync; sync
	trap '' 1 2 3 15
	sed -e 's/$/ \\/g' $comfile | sccs delget $sid
	if [ ! "$?" = 0 ] ; then
		echo "^GUnable to delta $sid!" 
		eval "$cleanup"; exit 1
	fi
	rm -f ${fix}_save; mv $fix ${fix}_save
	cp -p $tmp/${fix}_$$ $fix
	trap 'echo ABORTED!; eval "$cleanup"; exit 1' 1 2 3 15
	echo -n "Include special instructions in e-mail? (y/n)  "
	eval "$get_yn" && do_mesg=1
	ed $tmp/${fix}_$$ > /dev/null 2>&1 << jlt
1i
Please follow these steps to populate your source tree
with the latest build fixes for $release.

1.  create '$sid_dir/$fix' using the entries 
    below the dotted line
2.  patch_sid
3.  rebuild affected directories.

After extracting the SCCS files using 'patch_sid', '~/$fix' 
on your build machine will be moved to '~/${fix}_$new_sid'.  Please 
check that we are in sync by verifying that you have the 
complete set of suffixed files in $sid_dir.

`whoami`@`hostname`
---------------------cut here-------------------------
SIDlist/SCCS/s.${sid} ${new_sid}
.
w
q
jlt
	mesg=$tmp/mesg_$$
	if [ "$do_mesg" ] ; then
		echo "${stars}SPECIAL INSTRUCTIONS${stars}
" 		> $mesg
		echo "${stars}${stars}${stars}
		">> $mesg
		echo "SIDlist delta comments are:" >> $mesg
		cat $comfile >> $mesg
		echo "" >> $mesg
		cat $tmp/${fix}_$$ >> $mesg
		echo -n \
"Press <RETURN> to invoke 'vi'; your text will be prepended to usual mail: "
		read return
		vi +2 $mesg
	else
		echo "SIDlist delta comments are:" >> $mesg
		cat $comfile >> $mesg
		echo "" >> $mesg
		cat $tmp/${fix}_$$ >> $mesg
	fi
	echo "Sending mail to:"
	echo "	$reng"
	/usr/ucb/mail -s "NEW sidfix $new_sid" $reng < $mesg
	mv $fix $newfix; chmod +w $newfix
fi

#If invoked from a build machine, extract/delete the sources in $fix

if [ "$get_files" ]; then
	cd $top
	getit=$tmp/getit_$$; bad=$tmp/bad_$$;
	echo "cd $top" > $getit
	cat $todo | \
	sed -e 's/\/SCCS\/s\./  /g' -e 's/^SCCS\/s\./\. /g' |\
        awk '{printf ("(cd %s; sccs get -r%s ${mt_dir}/%s/%s) 2>>$errs \n",\
	$1, $3, $1, $2 )}' >> $getit
	if [ -s $del ]; then
		cat $del | \
		sed -e 's/\/SCCS\/s\./  /g' -e 's/^SCCS\/s\./\. /g' |\
		awk '{printf ("( [ -d %s ] && cd %s && rm -f %s ) 2>> $errs\n",\
		$1, $1, $2 )}' >> $getit
	fi
	chmod +x $getit
	trap '' 1 2 3 15
	. $getit
	[ -s $errs ] && grep -v 'No id keywords' $errs > $bad
	if [ -s $bad ]; then
		mv $bad $errs
	else
		rm -f $errs
		mv ${sid_dir}/${fix} $newfix; chmod +w $newfix
	fi
fi

[ -s $newfix ] && touch $newfix && chmod 444 $newfix
eval "$cleanup" && exit 0 || exit 1

