diff options
author | Martin Quinson <mquinson@debian.org> | 2003-01-21 09:49:29 +0000 |
---|---|---|
committer | Martin Quinson <mquinson@debian.org> | 2003-01-21 09:49:29 +0000 |
commit | 02dc4a5f8c1979e9a23f7b6851f693d29b640c4d (patch) | |
tree | 2f397f5df89f4529b89fd0e7a021238d0f290007 /lib | |
parent | 4d11e99bfc25e0261111d202a3a2afdf97daea5c (diff) | |
download | quilt-02dc4a5f8c1979e9a23f7b6851f693d29b640c4d.tar.gz |
Version 0.11, from Andreas Gruenbacher
Diffstat (limited to 'lib')
-rwxr-xr-x | lib/apatch.in | 135 | ||||
-rw-r--r-- | lib/backup-files.c | 212 | ||||
-rwxr-xr-x | lib/parse-patch | 105 | ||||
-rw-r--r-- | lib/patchfns.in | 393 | ||||
-rwxr-xr-x | lib/rpatch.in | 202 | ||||
-rwxr-xr-x | lib/touched-by-patch | 63 |
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 |