a first cut at an atom patch system modeled on patch(1), and the patch system from the old nix system. there is no manual page yet, as this is not yet announced and the design is still in flux. Reference: /n/atom/patch/applied2013/apatch Date: Thu May 30 00:42:30 CES 2013 Signed-off-by: quanstro@quanstro.net --- /rc/bin/apatch/Apply Thu Jan 1 00:00:00 1970 +++ /rc/bin/apatch/Apply Thu May 30 00:42:24 2013 @@ -0,0 +1,31 @@ +#!/bin/rc + +rfork e +. /rc/bin/apatch/defs +mountfs -a +mountdist -w # writable + +echo applying to [something] ... + +test -e /n/dist/lib/patch.skip && bind /dev/null /n/dist/lib/patch.skip +test -e /n/dist/lib/patch.skipfile && bind /dev/null /n/dist/lib/patch.skipfile + +errs=/tmp/apply.$pid +if($patch/apply $* |[2] tee $errs){ + patch=$1 + if(test -d $PD/$patch.*){ + echo '#you could:' + echo rm -rf $PD/$patch.* + } + $patch/applied $1 + echo patch has been applied. hit enter to notify $M or del to skip + read + @{cd $PD/applied/$1 && $patch/diffemail ; echo ------; cat $errs} | + mail -s 'applied patch: '^$PD/applied/$1 $M + rm -f $errs +} + +if(test -x /sys/lib/dist/update){ + echo '#run this to update the replica db.' + echo '# /sys/lib/dist/update' +} --- /rc/bin/apatch/applied Thu Jan 1 00:00:00 1970 +++ /rc/bin/apatch/applied Thu May 30 00:42:24 2013 @@ -0,0 +1,18 @@ +#!/bin/rc + +if(~ $#* 0){ + echo 'usage: $patch/applied patch-name' >[1=2] + exit usage +} +. /rc/bin/apatch/defs +mountfs + +test -e /n/dist/386 || bind / /n/dist || fail no dist + +if(~ 0 1){ + must mkdir -p /n/dist/dist/patch + + echo $1 >>/n/dist/dist/$patch/applied + cd .. + rm -rf /n/dist/dist/$patch/$1 +} --- /rc/bin/apatch/apply Thu Jan 1 00:00:00 1970 +++ /rc/bin/apatch/apply Thu May 30 00:42:24 2013 @@ -0,0 +1,165 @@ +#!/bin/rc + +rfork e +. /rc/bin/apatch/defs + +args='patch-name' +flagfmt='' +if(! eval `'' {aux/getflags $*} || ! ~ $#* 1){ + aux/usage + exit usage +} +mountfs -a +mountdist -w # writable + +d=$PD/$1 +if(! $patch/okay $d){ + echo 'bad patch: '$status >[1=2] + exit badpatch +} + +if(test -e /n/dist/lib/patch.skip && grep -s '^'^$1^'$' /n/dist/lib/patch.skip){ + echo patch $1 is a skip. done. >[1=2] + exit '' +} + +echo /dist/patch/$1 + +if(! test -d /n/dist/dist/patch/$1){ + must mkdir -p /n/dist/dist/patch/$1 + must dircp $d /n/dist/dist/patch/$1 +} +d=/n/dist/dist/patch/$1 +must cd $d + +echo -n merge... >[1=2] +fn xxx { + rm -rf $2.new + if(! test -e /n/dist$1 && test -e $2.orig){ + echo $1 'locally removed (ignoring)'>[1=2] + } + if(test -e /n/dist$1 && ! test -e $2.orig){ + echo fail: $1 locally created. remove and retry>[1=2] + applyfailed=locally + } + if(test -f /n/dist$1 && test -d $2.orig){ + echo $1 is now a file.>[1=2] + applyfailed=file + } + if(test -d /n/dist$1 && test -f $2.orig){ + echo $1 is now a directory>[1=2] + applyfailed=directory + } + if(test -f /n/dist$1 && test -f $2.orig){ + errs=/tmp/apply.$pid + ape/diff3 -m /n/dist$1 $2.orig $2 >$2.new >[2]$errs + if(grep -s 'Binary' $errs){ + rm -f $2.new + if(! cmp /n/dist$1 $2.orig>[2]/dev/null){ + echo conflicts in binary file $1';' see `{pwd} >[1=2] + applyfailed=binary + } + } + if not{ + if(grep -s '^<<<<' $2.new){ + echo conflicts. see `{pwd}^/$2.new >[1=2] + echo diff -n `{pwd}^/$2 $1 + echo diff -n `{pwd}^/$2.orig $1 + applyfailed=text + } + } + rm -f $errs + } + if(test -d /n/dist$1 && test -d $2.orig){ + diff -ncr /n/dist$1 $2.orig >$2.diffs + if(grep -s '^diff ' $2.diffs){ + echo conflicts merging $1';' see `{pwd}^/$2.diffs >[1=2] + applyfailed=merge + } + if not { + mkdir -p $2.new + dircp $2.orig $2.new + dircp $2 $2.new + } + } +} + +applyfailed=() + +{ + if(test -e /lib/patch.skipfile) + grep -v -f /lib/patch.skipfile files + if not + cat files +} | sed 's/^/xxx /' | rc + +if(! ~ $#applyfailed 0){ + echo exiting without changes >[1=2] + exit failed +} + +echo -n backup... >[1=2] + +fn xxx { + rm -rf $2.backup + test -e /n/dist$1 && cpfile /n/dist$1 $2.backup +} + +if(test -e /lib/patch.skipfile){ + grep -v -f /lib/patch.skipfile files | sed 's/^/xxx /' | rc +} +if not { + cat files | sed 's/^/xxx /' | rc +} +if(~ 0 never){ + echo exiting without changes cannot backup: `{cat failed}>[1=2] + exit failed +} + +echo copy... >[1=2] +fn xxx { + fdir=`{basename -d /n/dist$1} + if(! test -d $fdir){ + echo mkdir $fdir >[1=2] + mkdir -p $fdir + } + echo cpfile $2 /n/dist$1 >[1=2] + if(test -e $2.new) + cpfile $2.new /n/dist$1 + if not + cpfile $2 /n/dist$1 + #BUG: update /n/dist/$1 permissions by looking at those of $2.orig +} +if(test -e /lib/patch.skipfile){ + grep -v -f /lib/patch.skipfile files | sed 's/^/xxx /' | rc +} +if not { + cat files | sed 's/^/xxx /' | rc +} + +fn xxx { + # echo cp $2.backup $1 + if(test -e $2.backup) + cpfile $2.backup /n/dist$1 + if not + rm -rf /n/dist$1 +} +if(~ 0 never){ + echo copying failed, restoring backups >[1=2] + if(test -e /lib/patch.skipfile){ + grep -v -f /lib/patch.skipfile files | sed 's/^/xxx /' | rc + } + if not { + cat files | sed 's/^/xxx /' | rc + } + exit failed +} + +mkdir $PD/applied/$1 && dircp $PD/$1 $PD/applied/$1 && rm -r $PD/$1 + +if(test -s removed){ + echo '#' remove these files if you want. I will not remove them for you >[1=2] + echo '# ($patch/undo will not restore them)'>[1=2] + sed 's|^|rm -r /n/dist|' < removed +} +echo done>[1=2] --- /rc/bin/apatch/create Thu Jan 1 00:00:00 1970 +++ /rc/bin/apatch/create Thu May 30 00:42:24 2013 @@ -0,0 +1,175 @@ +#!/bin/rc +rfork e + +. /rc/bin/apatch/defs + +fn xchmod { + chmod $* >[2]/dev/null +} + +fn validate{ + if(! echo $2 | grep -s $3){ + echo bad $1: $3 only >[1=2]; + exit usage + } +} + +flagfmt='f,v,y,Y ndays' +args='name email file ... [[1=2] + exit fail +} +c=$changed +changed=() +for(i in $c){ + if(test -d $i) + changed = ($changed $i) + if not{ + if(cmp -s $i $source/$i) + echo unchanged: $i >[1=2] + if not + changed = ($changed $i) + } +} +c=() + +r=$removed +removed=() +for(i in $r){ + if(test -e $source/$i) + removed=($removed $i) + if not + echo not removed: $i >[1=2] +} + +if(~ $#changed 0 && ~ $#removed 0) + fail patch not created: nothing changed, nothing added, nothing deleted + +if(~ $flagv 1){ + echo removed + echo -n ' ' + echo $removed | sed 's: :\n :g' + echo changed + echo -n ' ' + echo $changed | sed 's: :\n :g' +} + +~ $flagv 1 && echo patchd $PD/$patch >[1=2] + +if(test -e $PD/$patch){ + if(~ $force n){ + echo patch $patch already exists. + echo Use flag -f to force and redefine it + exit fail + } >[1=2] + for(i in `{seq 1 100}){ + if(! test -e $PD/$patch.$i){ + patch = $patch.$i + break + } + } + echo new name: $patch>[1=2] +} +d=$PD/$patch +fn sigexit sigint{ + echo removing $PD/$patch >[1=2] + rm -rf $PD/$patch +} +if(! mkdir $d){ + echo mkdir $d failed >[1=2] + exit mkdir +} + +xchmod o-w $d +echo $removed | tr ' ' \xa > $d/removed +echo $email >$d/email +touch $d/^(readme files) + +fn genu { + echo $changed | tr ' ' \xa | + awk '{ + base = $0 + sub("/$", "", base) # already have cleanname + sub(".*/", "", base) + i = length(a[base]); + if(i > 0) + print $0 "\t" base "." i + else + print $0 "\t" base + a[base]++; + }' +} + +~ $flagv 1 && genu + +uniqlist = `{genu} +genu >> $d/files +while(! ~ $#uniqlist 0){ + file=$uniqlist(1) + base=$uniqlist(2) + uniqlist = $uniqlist(3-) + test -e $SD/$file && cpfile $SD/$file $d/$base.orig + cpfile $file $d/$base || fail cpfile # we need to back out + +} + +@{builtin cd $d && xchmod ug+rw * && xchmod a+r *} + +if(~ `{cat /proc/$pid/fd | awk 'NR==2{print $NF}'} */dev/cons && test -w /dev/consctl){ + >/dev/consctl { + echo holdon + cat >$d/readme + } +} +if not + cat >$d/readme + +if(! test -s $d/readme) + fail no description given + +# add the patch name to the applied list, so that it is not pull +# when it's $patch/Applied in the future. +must mkdir -p /dist/apatch +echo $patch >> /dist/apatch/applied +echo $d locally applied. + +fn sigexit sigint + +exit '' --- /rc/bin/apatch/defs Thu Jan 1 00:00:00 1970 +++ /rc/bin/apatch/defs Thu May 30 00:42:24 2013 @@ -0,0 +1,52 @@ +P=atom +S=atom +M=sources@9atom.org +MS=sources@9atom.org +PD=/n/$P/patch +SD=/n/$S/plan9 +applied=$PD/applied +patch=/rc/bin/apatch + +fn fail { + echo $0: aborted: $* >[1=2] + exit $"* +} + +fn mountfs { + if(! test -d $PD || ! test -d $SD){ + rm -f /srv/^($P $S) + test -d $PD || 9fs $* $P || fail no patch dir + test -d $SD || 9fs $* $S || fail no source dir + } +} + +fn mountdist { + test -e /n/dist/386 || bind $SD /n/dist || fail no dist + test -e /n/dist/386 || fail /root/386 does not exist + + if(~ $1 -w){ + { + touch /n/dist/lib/xxx && + rm -f /n/dist/lib/xxx + } >[2]/dev/null || fail no permission to write /n/dist/lib + } +} + +fn cpfile { + if(! test -e $1){ + echo $1 does not exist >[1=2] || fail exist + } + # was cp -x + if(test -f $1) + cp $1 $2 || fail cp + if(test -d $1){ + mkdir -p $2 || fail mkdir $2 + rm -rf $2/* + dircp $1 $2 || fail dircp $1 $2 + } + ~ 1 1 +} + +fn must { + $* || fail $* +} --- /rc/bin/apatch/diff Thu Jan 1 00:00:00 1970 +++ /rc/bin/apatch/diff Thu May 30 00:42:24 2013 @@ -0,0 +1,37 @@ +#!/bin/rc +# $patch/diff [-w] patch-name +rfork e +fn usage { + echo 'usage: $patch/diff patch-name' >[1=2] + exit usage +} + +flags=-ncr +if(! ~ $#* 0 && ~ $1 -*){ + flags=() + while (! ~ $#* 0 && ~ $1 -*) { + flags=($flags $1) + shift + } +} +if(! ~ $#* 1) + usage +. /rc/bin/apatch/defs +mountfs + +d=$PD/$1 +cd $d || fail no dir $d + +fn mkdiff { + if(test -e $2.orig){ + echo $1: + diff $flags $2.orig $2 + } + if not + echo new: $1 +} + +{ + cat removed | sed 's/^/removed: /' + mkdiff `{cat files} +} | tee $d/diffs --- /rc/bin/apatch/diffemail Thu Jan 1 00:00:00 1970 +++ /rc/bin/apatch/diffemail Thu May 30 00:42:24 2013 @@ -0,0 +1,20 @@ +#!/bin/rc +rfork en + +f=(email readme notes removed files) +for(i in $f){ + if(test -e $i){ + echo $i + sed 's/^/ /g'<$i + } +} +nl=' +' +for(i in `$nl {cat files}){ + eval j '=(' $i ')' + if(test -e $j(2)^.orig){ + echo + echo $i + diff -c $j(2)^.orig $j(2) + } +} --- /rc/bin/apatch/list Thu Jan 1 00:00:00 1970 +++ /rc/bin/apatch/list Thu May 30 00:42:24 2013 @@ -0,0 +1,59 @@ +#!/bin/rc + +rfork e +. /rc/bin/apatch/defs +mountfs + +cd $PD || fail no $PD +all=no +verb=no + +while(! ~ $#* 0 && ~ $1 -*){ + if(~ $1 *a*) + all=yes + if(~ $1 *v*) + verb=yes + shift +} + +dirs=$* +if(~ $#dirs 0){ + dirs=(`{ + for(p in `{ls -tr | grep -v '\.[0-9]+$'}){ + if(test -d $p){ + if(~ $all yes || ! grep -s '^'^$p^'$' $SD^/dist/$patch/applied) + echo $p + } + } + }) +} +echo dirs >[1=2] + + +for(i in $dirs){ + if($patch/okay $i){ + dt=`{ls -ld $i | awk '{print $7, $8, $9}'} + if(~ $verb yes){ + if(grep -s '^'^$i^'$' $SD^/dist/$patch/applied) + echo applied: $i + if not + echo name: $i + echo date: $dt + cat $i/readme| sed 's/^/ /' + echo from: `{cat $i/email} + echo files: + cat $i/files | sed 's/^/ /' + echo removed: + cat $i/removed | sed 's/^/ /' + echo + } + if not{ + if(grep -s '^'^$i^'$' $SD^/dist/$patch/applied) + echo $dt applied $i: `{grep . <$i/readme | sed 1q} + if not + echo $dt $i: `{grep . <$i/readme | sed 1q} + } + } + if not + echo ' 'bad patch: $status >[2=1] +} --- /rc/bin/apatch/notify Thu Jan 1 00:00:00 1970 +++ /rc/bin/apatch/notify Thu May 30 00:42:24 2013 @@ -0,0 +1,29 @@ +#!/bin/rc +# $patch/notify +rfork e +. /rc/bin/apatch/defs +mountfs + +cd $PD || fail no $PD +for(i in [a-zA-Z]*){ + if(! test -e $i/diffs && test -e $i/done ){ + echo new patch $i >[1=2] + $patch/diff $i >/dev/null >[2=1] + @{ + echo From: $MS + echo Subject: 'new patch: '^$"i + echo + + # echo http://$P ^ '/magic/webls?dir=/patches/'^$i + # echo + + cat $i/readme + echo files: + cat $i/files | sed 's/^/ /' + echo removed: + cat $i/removed | sed 's/^/ /' + echo + cat $i/diffs + } | mail `{cat $i/email} $M + } +} --- /rc/bin/apatch/okay Thu Jan 1 00:00:00 1970 +++ /rc/bin/apatch/okay Thu May 30 00:42:24 2013 @@ -0,0 +1,24 @@ +#!/bin/rc + +rfork e +if(! ~ $#* 1){ + echo usage: $patch/okay dir >[1=2] + exit usage +} + +i=$1 +files=(files removed readme email) +for(f in $files) + if(! test -e $i/$f ){ + echo 'missing file: '^$i/$f >[1=2] + exit 'missing file: '^$i/$f + } +if(grep -v '^/[_a-zA-Z0-9.\-+/:]+ [_a-zA-Z0-9.\-+:]+$' <$i/files){ + echo $i: bad file list >[1=2] + exit 'bad file list' +} +if(grep -v '^(/[_a-zA-Z0-9.\-+/:]+)|$' $i/removed){ + echo $i: bad removed list >[1=2] + exit 'bad removed list' +} +exit '' --- /rc/bin/apatch/undo Thu Jan 1 00:00:00 1970 +++ /rc/bin/apatch/undo Thu May 30 00:42:24 2013 @@ -0,0 +1,27 @@ +#!/bin/rc + +if(! ~ $#* 1){ + echo 'usage: $patch/undo patch-name' >[1=2] + exit usage +} +. /rc/bin/apatch/defs +mountfs + +test -e /n/dist/386 || bind / /n/dist || fail no dist + +d=/n/dist/dist/$patch/$1 +must cd $d + +fn xxx { + if(test -e $2.backup){ + echo cp $2.backup $1 >[1=2] + cpfile $2.backup /n/dist$1 + } + if not{ + echo rm -rf /n/dist$1 + rm -rf /n/dist$1 + } +} + +cat files | sed 's/^/xxx /' |rc +rm -rf /n/dist/dist/$patch/$1