summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rwxr-xr-xlib/apatch.in135
-rw-r--r--lib/backup-files.c212
-rwxr-xr-xlib/parse-patch105
-rw-r--r--lib/patchfns.in393
-rwxr-xr-xlib/rpatch.in202
-rwxr-xr-xlib/touched-by-patch63
6 files changed, 1110 insertions, 0 deletions
diff --git a/lib/apatch.in b/lib/apatch.in
new file mode 100755
index 0000000..a14d219
--- /dev/null
+++ b/lib/apatch.in
@@ -0,0 +1,135 @@
+#!/bin/sh
+
+if ! [ -r @LIB@/patchfns ]
+then
+ echo "Cannot read library @LIB@/patchfns" >&2
+ exit 1
+fi
+. @LIB@/patchfns
+
+usage()
+{
+ echo "Usage: apatch [-fq] patchname"
+ exit 1
+}
+
+interrupt()
+{
+ local pc_file=$(pc_file_name $1)
+ @LIB@/backup-files -s -f $pc_file -B .pc/$patch/ -r
+ echo "apatch interrupted by user"
+ exit 1
+}
+
+apply_patch()
+{
+ local patch=$(stripit $1)
+ local pc_file=$(pc_file_name $patch)
+ local patch_file=$(patch_file_name $patch)
+ local file status
+
+ if ! [ -e "$patch_file" ]
+ then
+ echo "No patch named $patch found."
+ return 1
+ fi
+
+ #if is_applied "$patch"
+ #then
+ # echo "$patch" is already applied
+ # return 1
+ #fi
+
+ trap "" SIGINT
+ refresh_file_list $patch
+ status=$?
+ if [ $status -eq 2 ]
+ then
+ [ -z "$opt_quiet" ] && echo "Recreated file list for $patch"
+ elif [ $status -ne 0 ]
+ then
+ return 1
+ fi
+
+ if ! @LIB@/backup-files -s -f $pc_file -B .pc/$patch/
+ then
+ exit 1
+ fi
+
+ trap "interrupt $patch" SIGINT
+ # (patch -b fails if a file appears more than once in a patch!)
+ #patch $(patch_args $patch) --no-backup-if-mismatch \
+ # -E $silent -i $patch_file
+ patch $(patch_args $patch) --no-backup-if-mismatch \
+ -E $silent -i $patch_file
+ status=$?
+ trap "" SIGINT
+
+ # Remember date/time of applying so that poppatch can
+ # avoid reverse applying the patch in the usual cases.
+ touch $pc_file
+
+ if [ $status -eq 0 -o -n "$opt_force" ]
+ then
+ add_to_db $patch
+ if [ $status -eq 0 ]
+ then
+ echo "Applied $patch"
+ rm -f $(pc_file_name $patch)~forced
+ else
+ touch $(pc_file_name $patch)~forced
+ echo "Applied $patch (forced; needs refpatch)"
+ fi
+ else
+ @LIB@/backup-files -s -f $pc_file -B .pc/$patch/ -r
+ echo "Patch $patch does not apply"
+ fi
+ trap - SIGINT
+ return $status
+}
+
+options=`getopt -o fqh -- "$@"`
+
+if [ $? -ne 0 ]
+then
+ usage
+fi
+
+eval set -- "$options"
+
+while true
+do
+ case "$1" in
+ -f)
+ opt_force=1
+ shift ;;
+ -q)
+ opt_quiet=1
+ shift ;;
+ -h)
+ usage -h ;;
+ --)
+ shift
+ break ;;
+ esac
+done
+
+if [ $# -ne 1 ]
+then
+ usage
+fi
+
+[ -n "$opt_quiet" ] && silent=-s
+
+patch=$(stripit $1)
+
+top=$(top_patch)
+if [ -n "$top" -a -e $(pc_file_name $top)~forced ]
+then
+ echo "The topmost patch $top was force applied. Please run" \
+ "refpatch before applying other patches."
+ exit 1
+fi
+
+apply_patch $patch
+
diff --git a/lib/backup-files.c b/lib/backup-files.c
new file mode 100644
index 0000000..b9f1b21
--- /dev/null
+++ b/lib/backup-files.c
@@ -0,0 +1,212 @@
+/* backup-files.c
+ Andreas Gruenbacher, 18 January 2003
+
+ `patch -b' fails to back up files correctly if a file occurs
+ more than once in a patch, so we use this utility instead.
+*/
+
+/*
+ - catch SIGINT
+ - remove backup files and directories
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+const char *progname;
+
+void
+usage(void)
+{
+ printf("Usage: %s [-B prefix] [-z suffix] [-f filelist] [-s] [-r|-x]\n"
+ "\n"
+ "\tCreate hard linked backup copies of a list of files\n"
+ "\tread from standard input.\n"
+ "\n"
+ "\t-r\tRestore the backup\n"
+ "\t-x\tRemove backup files and empty parent directories\n"
+ "\t-B\tPath name prefix for backup files\n"
+ "\t-z\tPath name suffix for backup files\n"
+ "\t-s\tSilent operation; only print error messages\n\n",
+ progname);
+}
+
+void
+create_parents(char *filename)
+{
+ struct stat st;
+ char *f = strchr(filename, '/');
+
+ if (f == NULL)
+ return;
+ *f = '\0';
+ if (stat(f, &st) != 0) {
+ while (f != NULL) {
+ *f = '\0';
+ mkdir(filename, 0777);
+ *f = '/';
+ f = strchr(f+1, '/');
+ }
+ } else {
+ *f = '/';
+ }
+}
+
+void
+remove_parents(char *filename)
+{
+ char *f;
+
+ while ((f = strrchr(filename, '/')) != NULL) {
+ *f = '\0';
+ rmdir(filename);
+ }
+}
+
+enum { what_backup, what_restore, what_remove };
+
+#define LINE_LENGTH 1024
+
+int
+main(int argc, char *argv[])
+{
+ char orig[LINE_LENGTH], *o, backup[LINE_LENGTH];
+ FILE *file;
+
+ const char *opt_prefix="", *opt_suffix="", *opt_file=NULL;
+ int opt_silent=0, opt_what=what_backup;
+ int opt, status=0;
+
+ progname = argv[0];
+
+ while ((opt = getopt(argc, argv, "rxB:z:f:sh")) != -1) {
+ switch(opt) {
+ case 'r':
+ opt_what = what_restore;
+ break;
+
+ case 'x':
+ opt_what = what_remove;
+ break;
+
+ case 'B':
+ opt_prefix = optarg;
+ break;
+
+ case 'f':
+ opt_file = optarg;
+ break;
+
+ case 'z':
+ opt_suffix = optarg;
+ break;
+
+ case 's':
+ opt_silent = 1;
+ break;
+
+ case 'h':
+ default:
+ usage();
+ return 0;
+ }
+ }
+
+ if ((*opt_prefix == '\0' && *opt_suffix == '\0') || optind != argc) {
+ usage();
+ return 1;
+ }
+
+ if (opt_file != NULL) {
+ if ((file = fopen(opt_file, "r")) == NULL) {
+ perror(opt_file);
+ return 1;
+ }
+ } else {
+ file = stdin;
+ }
+
+ while (fgets(orig, sizeof(orig), file)) {
+ if (strlen(opt_prefix) + strlen(orig) + strlen(opt_suffix) >=
+ sizeof(backup)) {
+ perror("Line buffer too small\n");
+ return 1;
+ }
+
+ o = strchr(orig, '\0');
+ if (o > orig && *(o-1) == '\n')
+ *(o-1) = '\0';
+ if (*orig == '\0')
+ continue;
+
+ snprintf(backup, sizeof(backup), "%s%s%s",
+ opt_prefix, orig, opt_suffix);
+
+ if (opt_what == what_backup) {
+ int fd;
+ create_parents(backup);
+
+ if (link(orig, backup) == 0) {
+ if (!opt_silent)
+ printf("Copying %s\n", orig);
+ } else if ((fd = creat(backup, 0666)) != -1) {
+ close(fd);
+ if (!opt_silent)
+ printf("New file %s\n", orig);
+ } else {
+ perror(backup);
+ return 1;
+ }
+ } else if (opt_what == what_restore) {
+ struct stat st;
+
+ create_parents(orig);
+
+ if (stat(backup, &st) != 0) {
+ perror(backup);
+ status=1;
+ continue;
+ }
+ if (st.st_size == 0) {
+ if (unlink(orig) == 0 || errno == ENOENT) {
+ if (!opt_silent)
+ printf("Removing %s\n", orig);
+ unlink(backup);
+ remove_parents(backup);
+ } else {
+ if (errno != ENOENT) {
+ perror(orig);
+ status=1;
+ }
+ }
+ } else {
+ unlink(orig);
+ if (link(backup, orig) == 0) {
+ if (!opt_silent)
+ printf("Restoring %s\n", orig);
+ unlink(backup);
+ remove_parents(backup);
+ } else {
+ fprintf(stderr, "Could not restore "
+ "file `%s' to `%s': %s\n",
+ backup, orig, strerror(errno));
+ status=1;
+ }
+ }
+ } else {
+ unlink(backup);
+ remove_parents(backup);
+ }
+ }
+
+ if (file != stdin) {
+ fclose(file);
+ }
+
+ return status;
+}
diff --git a/lib/parse-patch b/lib/parse-patch
new file mode 100755
index 0000000..b4dded2
--- /dev/null
+++ b/lib/parse-patch
@@ -0,0 +1,105 @@
+#!/usr/bin/perl -w
+
+# Extract or update a section from a combined patch + documentation +
+# meta information file.
+
+use FileHandle;
+use Getopt::Long;
+use strict;
+
+my $select;
+my $update;
+
+if (!GetOptions("s|select=s" => \$select,
+ "u|update=s" => \$update) ||
+ (!defined $select && !defined $update)) {
+ print STDERR "USAGE: $0 {-s|-u} section file [< replacement]\n";
+ exit 1;
+}
+
+foreach my $arg (@ARGV) {
+ my $fh;
+
+ if ($arg =~ /\.gz$/) {
+ $fh = new FileHandle("gzip -cd $arg |");
+ } elsif ($arg =~ /\.bz2$/) {
+ $fh = new FileHandle("bzip2 -cd $arg |");
+ } else {
+ $fh = new FileHandle("< $arg");
+ }
+
+ unless ($fh) {
+ print STDERR "$arg: $!\n";
+ next;
+ }
+
+ if (defined $select) {
+ my $section = "head";
+ my $newline = "";
+ while (<$fh>) {
+ if (/^%(.*)/) {
+ last if $section eq $select;
+ $section = $1;
+ next;
+ }
+ if ($section eq $select) {
+ print $newline;
+ if ($_ eq "\n") {
+ $newline = $_;
+ } else {
+ $newline="";
+ print;
+ }
+ }
+ }
+ } elsif (defined $update) {
+ my $fh2;
+ if ($arg =~ /\.gz$/) {
+ $fh2 = new FileHandle("| gzip -c > $arg.parse");
+ } elsif ($arg =~ /\.bz2$/) {
+ $fh2 = new FileHandle("| bzip2 -c > $arg.parse");
+ } else {
+ $fh2 = new FileHandle("$arg.parse", O_CREAT|O_WRONLY);
+ }
+ unless ($fh2) {
+ die "$arg.parse: $!\n";
+ }
+
+ # Copy things before updated section
+ while (<$fh>) {
+ if (/^%(.*)/ && $1 eq $update) {
+ last;
+ }
+ print $fh2 $_;
+ }
+ # Create/replace updated section
+ print $fh2 "%$update\n";
+ while (<STDIN>) {
+ print $fh2 $_;
+ }
+ print $fh2 "\n";
+ # Skip obsolete section
+ while (<$fh>) {
+ if (/^%(.*)/) {
+ print $fh2 $_;
+ last;
+ }
+ }
+ # Copy things after updated section
+ while (<$fh>) {
+ print $fh2 $_;
+ }
+ unless (close $fh2) {
+ die "$arg.patch: $!\n";
+ }
+
+ unlink "$arg~";
+ unless (rename $arg, "$arg~") {
+ die "Failed to rename $arg to $arg~: $!\n";
+ }
+ unless (rename "$arg.parse", $arg) {
+ die "Failed to rename $arg.parse to $arg: $!\n";
+ }
+ }
+ close $fh;
+}
diff --git a/lib/patchfns.in b/lib/patchfns.in
new file mode 100644
index 0000000..63fae45
--- /dev/null
+++ b/lib/patchfns.in
@@ -0,0 +1,393 @@
+debug()
+{
+ #echo "$@" >&2
+ true
+}
+
+if [ -n "$PATCHSCRIPTS" ]
+then
+ P=$PATCHSCRIPTS/
+else
+ unset P
+fi
+
+if [ -e .pc/series ]
+then
+ SERIES=.pc/series
+elif [ -e series ]
+then
+ SERIES=series
+else
+ SERIES=${P}patches/series
+fi
+
+DB=".pc/applied-patches"
+
+quote_re()
+{
+ echo "$1" | sed -e 's:\([/.+*\[\\]\):\\\1:g'
+}
+
+patch_file_name()
+{
+ local patch=$1
+
+ if [ -e $SERIES ]
+ then
+ awk '/^'"$(quote_re $patch)"'(|\.patch|\.diff?)([ \t]|$)/ \
+ { printf "'"$P"'patches/%s\n", $1; exit }' \
+ $SERIES
+ fi
+}
+
+# The -pN option and possibly others that should be passed to patch.
+patch_args()
+{
+ local patch=$1
+
+ if [ -e $SERIES ]
+ then
+ awk '/^'"$(quote_re $patch)"'(|\.patch|\.diff?)([ \t]|$)/ \
+ { if (NF >= 2)
+ for (i=2; i <= NF; i++)
+ print $i ;
+ else print "-p1" ;
+ exit }' \
+ $SERIES
+ fi
+}
+
+patch_strip_level()
+{
+ local patch=$1 i
+ for i in $(patch_args $patch)
+ do
+ case $i in
+ -p)
+ echo $2
+ return ;;
+ -p*)
+ echo ${i:2}
+ return ;;
+ esac
+ done
+ echo "1"
+}
+
+change_db_strip_level()
+{
+ local level=$1 patch=$2
+
+ if [ x$level != x-p1 ]
+ then
+ level="$level"
+ else
+ level=""
+ fi
+
+ if [ -e $SERIES ]
+ then
+ local tmp=$(mktemp /tmp/patch-scripts.XXXXXX)
+ awk '/^'"$(quote_re $patch)"'(\.patch|\.diff?)([ \t]|$)/ \
+ {for(i=2; i<=NF; i++)
+ if ($i ~ /^-p/)
+ {$i="'"$level"'" ; break}
+ if (i > NF)
+ $i="'"$level"'"}
+ {print}' \
+ $SERIES > $tmp
+ if cmp $SERIES $tmp >/dev/null 2>/dev/null
+ then
+ rm -f $tmp
+ else
+ mv -f $tmp $SERIES
+ fi
+ else
+ return 1
+ fi
+}
+
+patch_in_series()
+{
+ local patch=$1
+
+ if ! [ -e $SERIES ]
+ then
+ return 1
+ else
+ grep -q '^'"$(quote_re $patch)"'\(\|\.patch\|.diff\?\)\([ \t]\|$\)' $SERIES
+ fi
+}
+
+# Insert new patch after topmost patch
+insert_in_series()
+{
+ local patch=$1 patch_args=$2
+ local top=$(top_patch) tmp
+
+ if [ -n "$patch_args" ]
+ then
+ patch_args=" $patch_args"
+ fi
+
+ tmp=$(mktemp /tmp/patch-scripts.XXXXXX) || return 1
+ if [ -n "$top" ]
+ then
+ awk ' { print }
+ /^'"$(quote_re $top)"'(|\.patch|\.diff?)([ \t]|$)/ \
+ { print "'"$patch$patch_args"'" }' \
+ < $SERIES \
+ > $tmp \
+ || return 1
+ else
+ echo $patch$patch_args > $tmp
+ if [ -e $SERIES ]
+ then
+ cat $SERIES >> $tmp
+ fi
+ fi
+ mkdir -p $(dirname $SERIES)
+ mv -f $tmp $SERIES
+}
+
+pc_file_name()
+{
+ while [ $# -gt 0 ]
+ do
+ echo ".pc/$1/.pc"
+ shift
+ done
+
+}
+
+backup_file_name()
+{
+ local patch=$1
+ while [ $# -gt 1 ]
+ do
+ echo ".pc/$patch/$2"
+ shift
+ done
+}
+
+__cat_series()
+{
+ local series=$1
+ if [ -e $series ]
+ then
+ sed -e '/^#/d' -e 's/^[ \t]*//' -e 's/[ \t].*//' \
+ -e 's/\.patch$//' -e 's/\.diff\?//' $series
+ else
+ return 1
+ fi
+}
+
+cat_series()
+{
+ __cat_series $SERIES
+}
+
+top_patch()
+{
+ [ -e $DB ] && tail -1 $DB
+}
+
+die()
+{
+ echo "error: $*"
+ exit 1
+}
+
+is_numeric()
+{
+ echo $1 | grep -q -e '^[0-9]*$'
+}
+
+is_applied_last()
+{
+ local patch=$1
+ [ "$(top_patch)" == $patch ]
+}
+
+is_applied()
+{
+ local patch=$1
+ [ -e $DB ] || return 1
+ grep -q "^$patch\$" $DB
+}
+
+patches_before()
+{
+ local patch=$1
+ cat_series \
+ | awk '$0 == "'"$patch"'" { exit }
+ { print }'
+}
+
+patches_after()
+{
+ local patch=$1
+ cat_series \
+ | awk 'seen { print }
+ $0 == "'"$patch"'" { seen=1 }'
+}
+
+# List all patches that have been applied on top of patch $1
+patches_on_top_of()
+{
+ local patch=$1
+ [ -e $DB ] || return
+ awk '$0 == "'"$patch"'" { seen=1 ; next }
+ seen { print }' $DB
+}
+
+# Print the name of the patch that modified the file $2 next after
+# patch $1, or print nothing if patch $1 is on top.
+next_patch_for_file()
+{
+ local patch=$1 file=$2
+ local patches_on_top=$(patches_on_top_of $patch)
+
+ if [ -n "$patches_on_top" ]
+ then
+ grep -l -e "^$(quote_re $file)\$" \
+ $(pc_file_name $patches_on_top) \
+ | head -1 \
+ | sed -e 's:^\.pc/::' -e 's:/\.pc$::'
+ fi
+}
+
+can_apply()
+{
+ local silent
+ if [ "x$1" == "x-s" ]
+ then
+ silent=-s
+ shift
+ fi
+ local patch=$1
+ patch -p1 --dry-run -f $silent -i $(patch_file_name $patch) < /dev/null
+}
+
+can_remove()
+{
+ local silent
+ if [ "x$1" == "x-s" ]
+ then
+ silent=-s
+ shift
+ fi
+ local patch=$1
+ patch -R -p1 --dry-run -f $silent -i $(patch_file_name $patch) < /dev/null
+}
+
+add_to_db()
+{
+ echo $1 >> $DB
+}
+
+remove_from_db()
+{
+ local patch=$1
+ local tmpfile
+ tmpfile=$(mktemp /tmp/patch-scripts.XXXXXX)
+ grep -v "^$patch\$" $DB > $tmpfile
+ mv -f $tmpfile $DB
+ [ -s $DB ] || rm -f $DB
+}
+
+stripit()
+{
+ if [ -n "$1" ]
+ then
+ echo $1 |
+ sed -e 's/^\(\.\/\)*//' \
+ -e 's/^'"$P"'patches\///' -e 's/^\.pc\///' \
+ -e 's/\.patch$//' -e 's/\.diff\?$//'
+ fi
+}
+
+file_in_patch()
+{
+ local file=$1 patch=$2
+ files_in_patch $patch | grep -q "^$file\$"
+}
+
+files_in_patch()
+{
+ local pc_file=$(pc_file_name $1)
+ if [ -e "$pc_file" ]
+ then
+ cat $pc_file
+ fi
+}
+
+install_file_in_patch()
+{
+ local file=$1 patch=$2 bup_file=$(backup_file_name $patch $file)
+
+ if ! echo $file  @LIB@/backup-file -s -B .pc/$patch/ || \
+ ! echo $file >> $(pc_file_name $patch)
+ then
+ return 1
+ fi
+}
+
+refresh_file_list()
+{
+ local patch=$1
+ local pc_file=$(pc_file_name $patch)
+ local patch_file=$(patch_file_name $patch)
+
+ if ! [ -e "$patch_file" ]
+ then
+ echo "Patch file $patch_file does not exist"
+ return 1
+ fi
+ if [ ! -e $pc_file -o $pc_file -ot $patch_file ]
+ then
+ mkdir -p $(dirname $pc_file)
+ echo "touched-by-patch $(patch_strip_level $patch) $patch_file > $pc_file" >&2
+ @LIB@/touched-by-patch -p$(patch_strip_level $patch) \
+ $patch_file > $pc_file
+ if [ $? -ne 0 ]
+ then
+ return 1
+ fi
+ return 2
+ fi
+}
+
+directory_names()
+{
+ echo "$@" \
+ | awk 'BEGIN {RS=" "}
+ {sub(/\/?[^\/]*$/, "") ; printf "[%s]\n", $0}
+ {if (last != $0)
+ print last
+ last = $0}
+ END {print last}'
+}
+
+need_file_there()
+{
+ if [ ! -e $1 ]
+ then
+ echo "File $1 does not exist"
+ exit 1
+ fi
+}
+
+patch_description()
+{
+ local patch_file=$1
+
+ if [ -e "$patch_file" ]
+ then
+ awk '/^--- / {exit}
+ diff {print diff ; diff=""}
+ /^diff / {diff=$0 ; next}
+ {print}' \
+ $patch_file
+ fi
+}
diff --git a/lib/rpatch.in b/lib/rpatch.in
new file mode 100755
index 0000000..d25695b
--- /dev/null
+++ b/lib/rpatch.in
@@ -0,0 +1,202 @@
+#!/bin/sh
+
+if ! [ -r @LIB@/patchfns ]
+then
+ echo "Cannot read library @LIB@/patchfns" >&2
+ exit 1
+fi
+. @LIB@/patchfns
+
+usage()
+{
+ echo "Usage: rpatch [-fRq] patchname"
+ exit 1
+}
+
+verify_removal()
+{
+ local patch=$1
+ local bup file status=0
+ local dir=$(basename $PWD) suffix=${patch//\//_}
+
+ # Check if all changes of the patch are undone
+ for file in $(files_in_patch $patch)
+ do
+ bup=$(backup_file_name $patch $file)
+ if ! [ -s $file -o -s $bup ] || \
+ cmp $file $bup > /dev/null 2> /dev/null
+ then
+ continue
+ fi
+
+ if [ $status -eq 0 ]
+ then
+ echo "Patch does not remove changes:"
+ fi
+
+ diff -Nu $DIFF_OPTS $bup $file \
+ | sed -e 's:^--- [^ \t]*:--- '"$dir/$file.orig"':' \
+ -e 's:^+++ [^ \t]*:+++ '"$dir/$file"':'
+
+ status=1
+ done
+ return $status
+}
+
+files_may_have_changed()
+{
+ local patch=$1 file
+ local pc_file=$(pc_file_name $patch)
+ local apply_ts=$(date -r $pc_file '+%s') ts
+
+ if [ $pc_file -ot $(patch_file_name $patch) ]
+ then
+ return 0
+ fi
+
+ for file in $(files_in_patch $patch)
+ do
+ if ! [ -e $file ]
+ then
+ return 0 # file is missing
+ fi
+
+ ts=$(date -r $file '+%s')
+ if [ $? -ne 0 -o $ts -gt $apply_ts ]
+ then
+ return 0 # file has changed
+ fi
+ done
+ return 1
+}
+
+abort_patch()
+{
+ local pc_file=$(pc_file_name $1)
+ @LIB@/backup-files -s -f $pc_file -z ~rpatch -r
+ exit 1
+}
+
+interrupt()
+{
+ local patch=$1
+ abort_patch $patch
+ echo "rpatch interrupted by user"
+ exit 1
+}
+
+do_remove()
+{
+ local patch=$1 pc_file=$(pc_file_name $patch)
+
+ trap "" SIGINT
+ if [ -n "$opt_force" ] || \
+ ( [ -z "$opt_remove" ] && ! files_may_have_changed $patch )
+ then
+ # Optimize: Force remove if the patch has not been
+ # modified since it was applied. (Forced remove is
+ # faster!)
+
+ @LIB@/backup-files $silent -f $pc_file -B .pc/$patch/ -r
+ status=$?
+ remove_from_db $patch
+ rm -f $(pc_file_name $patch)~forced
+ return $status
+ else
+ local patch_file=$(patch_file_name $patch)
+ if ! [ -e "$patch_file" ]
+ then
+ echo "No patch named $patch found."
+ return 1
+ fi
+
+ trap "interrupt $patch" SIGINT
+ #if [ ! -e $patch_file ] || grep -q '^%patch$' $patch_file
+ #then
+ # @LIB@/parse-patch -s patch $patch_file \
+ # | patch $(patch_args $patch) --no-backup-if-mismatch \
+ # -R -E $silent
+ #else
+ patch $(patch_args $patch) --no-backup-if-mismatch \
+ -R -E $silent -i $patch_file
+ #fi
+ status=$?
+ trap "" SIGINT
+ if [ $status -eq 0 ] && verify_removal $patch
+ then
+ @LIB@/backup-files -s -f $pc_file -z ~rpatch -x
+ @LIB@/backup-files -s -f $pc_file -B .pc/$patch/ -x
+
+ remove_from_db $patch
+ else
+ @LIB@/backup-files -s -f $pc_file -z ~rpatch -r
+ echo "Patch $patch does not remove cleanly"
+ return 1
+ fi
+ fi
+ trap - SIGINT
+}
+
+options=`getopt -o fRqh -- "$@"`
+
+if [ $? -ne 0 ]
+then
+ usage
+fi
+
+eval set -- "$options"
+
+while true
+do
+ case "$1" in
+ -f)
+ opt_force=1
+ shift ;;
+ -R)
+ opt_remove=1 # remove properly with patch -R; no tricks
+ unset opt_force
+ shift ;;
+ -q)
+ opt_quiet=1
+ shift ;;
+ -h)
+ usage -h ;;
+ --)
+ shift
+ break ;;
+ esac
+done
+
+if [ $# -ne 1 ]
+then
+ usage
+fi
+
+patch=$(stripit $1)
+[ -n "$opt_quiet" ] && silent=-s
+
+top=$(top_patch)
+if [ -n "$top" -a -e $(pc_file_name $top)~forced -a -z "$opt_force" ]
+then
+ echo "The topmost patch $top was force applied. Please run" \
+ "refpatch before removing it."
+ exit 1
+fi
+
+#if is_applied "$patch"
+#then
+ do_remove "$patch" || exit 1
+#else
+# echo "Patch $patch is not applied"
+# exit 1
+#fi
+
+top=$(top_patch)
+if [ -z "$top" ]
+then
+ where="no patches applied"
+else
+ where="now at $top"
+fi
+echo "Removed $patch, $where"
+
diff --git a/lib/touched-by-patch b/lib/touched-by-patch
new file mode 100755
index 0000000..f26469e
--- /dev/null
+++ b/lib/touched-by-patch
@@ -0,0 +1,63 @@
+#!/bin/sh
+# Extract names of new files from a patch, print them out
+
+usage()
+{
+ echo "Usage: touched-by-patch [-p num] patchname"
+ exit 1
+}
+
+options=`getopt -o p:h -- "$@"`
+
+if [ $? -ne 0 ]
+then
+ usage
+fi
+
+eval set -- "$options"
+
+while true
+do
+ case "$1" in
+ -p)
+ opt_p=$2
+ shift 2 ;;
+ -h)
+ usage ;;
+ --)
+ shift
+ break ;;
+ esac
+done
+
+if [ $# -ne 1 ]
+then
+ usage
+fi
+
+patch_file=$1
+[ -z "$opt_p" ] && opt_p=1
+
+case "$patch_file" in
+*.bz2)
+ command="bzip2 -cd $patch_file";;
+*.gz)
+ command="gzip -cd $patch_file";;
+*)
+ command="cat $patch_file";;
+esac
+
+# Neither `+++' nor `---' works for all patches; patch looks at the
+# file system to determine which file name to use.
+
+eval $command \
+| awk '/^\+\+\+[ \t]/ {
+ sub(/^\+\+\+[ \t]/, "")
+ sub(/[ \t].*/, "")
+ sub(/^\/dev\/null/, "")
+ for (i=0; i<'$opt_p'; i++)
+ sub(/^[^\/]*\//, "")
+ print
+ }' \
+| sort \
+| uniq