diff options
Diffstat (limited to 'lib')
-rwxr-xr-x | lib/apatch.in | 114 | ||||
-rw-r--r-- | lib/backup-files.c | 228 | ||||
-rwxr-xr-x | lib/parse-patch | 46 | ||||
-rw-r--r-- | lib/patchfns.in | 434 | ||||
-rwxr-xr-x | lib/rpatch.in | 148 | ||||
-rwxr-xr-x | lib/spec2series | 223 | ||||
-rwxr-xr-x | lib/touched-by-patch | 69 |
7 files changed, 883 insertions, 379 deletions
diff --git a/lib/apatch.in b/lib/apatch.in index 0e0c366..76d9dc7 100755 --- a/lib/apatch.in +++ b/lib/apatch.in @@ -1,4 +1,4 @@ -#! /bin/bash +#!/bin/bash # This script is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as @@ -6,48 +6,85 @@ # # See the COPYING and AUTHORS files for more details. -if ! [ -r @LIB@/patchfns ] +# Read in library functions +if [ "$(type -t patch_file_name)" != function ] then - echo "Cannot read library @LIB@/patchfns" >&2 - exit 1 + if ! [ -r @LIB@/patchfns ] + then + echo "Cannot read library @LIB@/patchfns" >&2 + exit 1 + fi + . @LIB@/patchfns fi -. @LIB@/patchfns usage() { - echo "Usage: apatch [-fq] patchname" + echo "Usage: $0 [-fqv] patchname" exit 1 } +rollback_patch() +{ + local patch=$1 pc_file=$(pc_file_name $patch) + @LIB@/backup-files $silent_unless_verbose \ + -f $pc_file -B .pc/$patch/ -r + rm -f $(files_in_patch $patch | sed -e 's/$/\.rej/') +} + interrupt() { - local pc_file=$(pc_file_name $1) - @LIB@/backup-files -s -f $pc_file -B .pc/$patch/ -r - echo "apatch interrupted by user" + rollback_patch $1 + echo "Interrupted by user; patch $patch was not applied." exit 1 } apply_patch() { + local patch=$1 + local patch_file=$(patch_file_name $patch) + + if ! [ -s $patch_file ] + then + echo "Patch file $patch_file appears to be empty" + return 0 + fi + + if [ "x${patch_file:(-3)}" = "x.gz" ] + then + gzip -cd $patch_file \ + | patch $(patch_args $patch) --no-backup-if-mismatch \ + -E $silent + elif [ "x${patch_file:(-4)}" = "x.bz2" ] + then + bzip2 -cd $patch_file \ + | patch $(patch_args $patch) --no-backup-if-mismatch \ + -E $silent + else + patch $(patch_args $patch) --no-backup-if-mismatch \ + -E $silent -i $patch_file + fi +} + +apatch() +{ 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" ] + trap "" SIGINT + if ! refresh_file_list $patch then - echo "No patch named $patch found." + echo "refresh_file_list failed" return 1 fi - #if is_applied "$patch" - #then - # echo "$patch" is already applied - # return 1 - #fi - - trap "" SIGINT - refresh_file_list $patch + if ! [ -e $pc_file ] + then + echo "Patch $patch appears to be empty, applied" + add_to_db $patch + return 0 + fi + status=$? if [ $status -eq 2 ] then @@ -57,21 +94,20 @@ apply_patch() return 1 fi - if ! @LIB@/backup-files -s -f $pc_file -B .pc/$patch/ + if ! @LIB@/backup-files $silent_unless_verbose \ + -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 + + apply_patch $patch status=$? + trap "" SIGINT - # Remember date/time of applying so that poppatch can + # Remember date/time of applying so that pop can # avoid reverse applying the patch in the usual cases. touch $pc_file @@ -81,20 +117,20 @@ apply_patch() if [ $status -eq 0 ] then echo "Applied $patch" - rm -f $(pc_file_name $patch)~forced + rm -f $pc_file~refresh else - touch $(pc_file_name $patch)~forced - echo "Applied $patch (forced; needs refpatch)" + touch $pc_file~refresh + echo "Applied $patch (forced; needs refresh)" fi else - @LIB@/backup-files -s -f $pc_file -B .pc/$patch/ -r - echo "Patch $patch does not apply" + rollback_patch $patch + echo "Patch $patch does not apply (enforce with -f)" fi trap - SIGINT return $status } -options=`getopt -o fqh -- "$@"` +options=`getopt -o fqvh -- "$@"` if [ $? -ne 0 ] then @@ -112,6 +148,9 @@ do -q) opt_quiet=1 shift ;; + -v) + opt_verbose=1 + shift ;; -h) usage -h ;; --) @@ -126,16 +165,15 @@ then fi [ -n "$opt_quiet" ] && silent=-s +[ -z "$opt_verbose" ] && silent_unless_verbose=-s patch=$(stripit $1) top=$(top_patch) -if [ -n "$top" -a -e $(pc_file_name $top)~forced ] +if [ -n "$top" -a -e $(pc_file_name $top)~refresh ] then - echo "The topmost patch $top was force applied. Please run" \ - "refpatch before applying other patches." + echo "The topmost patch $top needs to be refreshed first." exit 1 fi -apply_patch $patch - +apatch $patch diff --git a/lib/backup-files.c b/lib/backup-files.c index b9f1b21..c6e0c6d 100644 --- a/lib/backup-files.c +++ b/lib/backup-files.c @@ -1,14 +1,29 @@ -/* backup-files.c - Andreas Gruenbacher, 18 January 2003 +/* + File: backup-files.c + + Copyright (C) 2003 Andreas Gruenbacher <agruen@suse.de> + SuSE Labs, SuSE Linux AG + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. - `patch -b' fails to back up files correctly if a file occurs - more than once in a patch, so we use this utility instead. + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* - - catch SIGINT - - remove backup files and directories -*/ + * Create backup files of a list of files similar to GNU patch. A path + * name prefix and suffix for the backup file can be specified with the + * -B and -Z options. + */ #include <sys/types.h> #include <sys/stat.h> @@ -20,10 +35,18 @@ const char *progname; +enum { what_backup, what_restore, what_remove }; + +const char *opt_prefix="", *opt_suffix="", *opt_file=NULL; +int opt_silent=0, opt_what=what_backup; + +#define LINE_LENGTH 1024 + + void usage(void) { - printf("Usage: %s [-B prefix] [-z suffix] [-f filelist] [-s] [-r|-x]\n" + printf("Usage: %s [-B prefix] [-z suffix] [-f {filelist|-}] [-s] [-r|-x] filename ...\n" "\n" "\tCreate hard linked backup copies of a list of files\n" "\tread from standard input.\n" @@ -60,26 +83,96 @@ create_parents(char *filename) void remove_parents(char *filename) { - char *f; + char *f, *g = NULL; + f = strrchr(filename, '/'); while ((f = strrchr(filename, '/')) != NULL) { - *f = '\0'; + if (g != NULL) + *g = '/'; + g = f; + *f= '\0'; + rmdir(filename); } + if (g != NULL) + *g = '/'; } -enum { what_backup, what_restore, what_remove }; +int +process_file(char *file) +{ + char backup[LINE_LENGTH]; -#define LINE_LENGTH 1024 + if (strlen(opt_prefix) + strlen(file) + + strlen(opt_suffix) >= sizeof(backup)) { + perror("Line buffer too small\n"); + return 1; + } + + snprintf(backup, sizeof(backup), "%s%s%s", + opt_prefix, file, opt_suffix); + + if (opt_what == what_backup) { + int fd; + create_parents(backup); + + unlink(backup); + if (link(file, backup) == 0) { + if (!opt_silent) + printf("Copying %s\n", file); + } else if ((fd = creat(backup, 0666)) != -1) { + close(fd); + if (!opt_silent) + printf("New file %s\n", file); + } else { + perror(backup); + return 1; + } + return 0; + } else if (opt_what == what_restore) { + struct stat st; + create_parents(file); + + if (stat(backup, &st) != 0) { + perror(backup); + return 1; + } + if (st.st_size == 0) { + if (unlink(file) == 0 || errno == ENOENT) { + if (!opt_silent) + printf("Removing %s\n", file); + unlink(backup); + remove_parents(backup); + } else { + perror(file); + return 1; + } + } else { + unlink(file); + if (link(backup, file) == 0) { + if (!opt_silent) + printf("Restoring %s\n", file); + unlink(backup); + remove_parents(backup); + } else { + fprintf(stderr, "Could not restore " + "file `%s' to `%s': %s\n", + backup, file, strerror(errno)); + return 1; + } + } + return 0; + } else if (opt_what == what_remove) { + unlink(backup); + remove_parents(backup); + return 0; + } else + return 1; +} 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]; @@ -117,95 +210,44 @@ main(int argc, char *argv[]) } } - if ((*opt_prefix == '\0' && *opt_suffix == '\0') || optind != argc) { + if ((*opt_prefix == '\0' && *opt_suffix == '\0') || + (opt_file == NULL && 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); + FILE *file; + char line[LINE_LENGTH]; - 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); + if (!strcmp(opt_file, "-")) { + file = stdin; + } else { + if ((file = fopen(opt_file, "r")) == NULL) { + perror(opt_file); return 1; } - } else if (opt_what == what_restore) { - struct stat st; + } - create_parents(orig); + while (fgets(line, sizeof(line), file)) { + char *l = strchr(line, '\0'); - if (stat(backup, &st) != 0) { - perror(backup); - status=1; + if (l > line && *(l-1) == '\n') + *(l-1) = '\0'; + if (*line == '\0') 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 ((status = process_file(line)) != 0) + return status; } - } - if (file != stdin) { - fclose(file); + if (file != stdin) { + fclose(file); + } + } + for (; optind < argc; optind++) { + if ((status = process_file(argv[optind])) != 0) + return status; } return status; diff --git a/lib/parse-patch b/lib/parse-patch index b4dded2..9d38e36 100755 --- a/lib/parse-patch +++ b/lib/parse-patch @@ -1,10 +1,17 @@ #!/usr/bin/perl -w +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# See the COPYING and AUTHORS files for more details. + # Extract or update a section from a combined patch + documentation + # meta information file. use FileHandle; use Getopt::Long; +use File::Temp qw(tempfile); use strict; my $select; @@ -20,7 +27,9 @@ if (!GetOptions("s|select=s" => \$select, foreach my $arg (@ARGV) { my $fh; - if ($arg =~ /\.gz$/) { + if (! -e $arg) { + $fh = new FileHandle("/dev/null"); + } elsif ($arg =~ /\.gz$/) { $fh = new FileHandle("gzip -cd $arg |"); } elsif ($arg =~ /\.bz2$/) { $fh = new FileHandle("bzip2 -cd $arg |"); @@ -53,31 +62,43 @@ foreach my $arg (@ARGV) { } } } elsif (defined $update) { - my $fh2; + my ($fh2, $tempname) = tempfile("$arg.XXXXXX"); if ($arg =~ /\.gz$/) { - $fh2 = new FileHandle("| gzip -c > $arg.parse"); + $fh2->close(); + if (! -e $tempname) { + die "File $tempname disappeared!\n"; + } + $fh2 = new FileHandle("| gzip -c > $tempname"); } elsif ($arg =~ /\.bz2$/) { - $fh2 = new FileHandle("| bzip2 -c > $arg.parse"); - } else { - $fh2 = new FileHandle("$arg.parse", O_CREAT|O_WRONLY); + $fh2->close(); + if (! -e $tempname) { + die "File $tempname disappeared!\n"; + } + $fh2 = new FileHandle("| bzip2 -c > $tempname"); } unless ($fh2) { - die "$arg.parse: $!\n"; + die "$tempname: $!\n"; } # Copy things before updated section + my $last_was_newline=1; # start first section in first line while (<$fh>) { if (/^%(.*)/ && $1 eq $update) { last; } + $last_was_newline = ($_ eq "\n"); print $fh2 $_; } + print $fh2 "\n" + unless ($last_was_newline); + # Create/replace updated section print $fh2 "%$update\n"; while (<STDIN>) { print $fh2 $_; } print $fh2 "\n"; + # Skip obsolete section while (<$fh>) { if (/^%(.*)/) { @@ -93,11 +114,14 @@ foreach my $arg (@ARGV) { die "$arg.patch: $!\n"; } - unlink "$arg~"; - unless (rename $arg, "$arg~") { - die "Failed to rename $arg to $arg~: $!\n"; + if (-e $arg) { + unlink "$arg~"; + unless (rename $arg, "$arg~") { + die "Failed to rename $arg to $arg~: $!\n"; + } } - unless (rename "$arg.parse", $arg) { + unless (rename $tempname, $arg) { + rename("$arg~", $arg); die "Failed to rename $arg.parse to $arg: $!\n"; } } diff --git a/lib/patchfns.in b/lib/patchfns.in index 63fae45..17c5b56 100644 --- a/lib/patchfns.in +++ b/lib/patchfns.in @@ -1,8 +1,10 @@ -debug() -{ - #echo "$@" >&2 - true -} +#!/bin/bash + +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# See the COPYING and AUTHORS files for more details. if [ -n "$PATCHSCRIPTS" ] then @@ -23,20 +25,36 @@ fi DB=".pc/applied-patches" +# Define where diffstat lives (may be missing) +DIFFSTAT=/usr/bin/diffstat + quote_re() { echo "$1" | sed -e 's:\([/.+*\[\\]\):\\\1:g' } +#basename() +#{ +# local path=${1%/} +# echo "${path/*\/}" +#} + +#dirname() +#{ +# local path=${1%/} +#} + 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 + awk '/^'"$(quote_re $patch)"'(|\.patch|\.diff?)(|\.gz|\.bz2)([ \t]|$)/ \ + { printf "'"$P"'patches/%s\n", $1 + exit + } + ' $SERIES fi } @@ -47,13 +65,16 @@ patch_args() if [ -e $SERIES ] then - awk '/^'"$(quote_re $patch)"'(|\.patch|\.diff?)([ \t]|$)/ \ + awk ' + /^'"$(quote_re $patch)"'(|\.patch|\.diff?)(|\.gz|\.bz2)([ \t]|$)/ \ { if (NF >= 2) for (i=2; i <= NF; i++) - print $i ; - else print "-p1" ; - exit }' \ - $SERIES + print $i + else + print "-p1" ; + exit + } + ' $SERIES fi } @@ -87,20 +108,24 @@ change_db_strip_level() 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 + local tmpfile=$(mktemp /tmp/patch-scripts.XXXXXX) + awk ' + /^'"$(quote_re $patch)"'(\.patch|\.diff?)(|\.gz|\.bz2)([ \t]|$)/ \ + { for(i=2; i<=NF; i++) + if ($i ~ /^-p/) { + $i="'"$level"'" + break + } + if (i > NF) + $i="'"$level"'" + } + { print } + ' $SERIES > $tmpfile + if cmp $SERIES $tmpfile >/dev/null 2>/dev/null then - rm -f $tmp + rm -f $tmpfile else - mv -f $tmp $SERIES + mv -f $tmpfile $SERIES fi else return 1 @@ -115,7 +140,7 @@ patch_in_series() then return 1 else - grep -q '^'"$(quote_re $patch)"'\(\|\.patch\|.diff\?\)\([ \t]\|$\)' $SERIES + grep -q '^'"$(quote_re $patch)"'\(\|\.patch\|.diff\?\)\(\|\.gz\|\.bz2\)\([ \t]\|$\)' $SERIES fi } @@ -123,31 +148,54 @@ patch_in_series() insert_in_series() { local patch=$1 patch_args=$2 - local top=$(top_patch) tmp + local top=$(top_patch) tmpfile if [ -n "$patch_args" ] then patch_args=" $patch_args" fi - tmp=$(mktemp /tmp/patch-scripts.XXXXXX) || return 1 + tmpfile=$(mktemp /tmp/patch-scripts.XXXXXX) || return 1 + mkdir -p $(dirname $SERIES) if [ -n "$top" ] then - awk ' { print } - /^'"$(quote_re $top)"'(|\.patch|\.diff?)([ \t]|$)/ \ - { print "'"$patch$patch_args"'" }' \ - < $SERIES \ - > $tmp \ - || return 1 + awk ' + { print } + /^'"$(quote_re $top)"'(|\.patch|\.diff?)(|\.gz|\.bz2)([ \t]|$)/ \ + { print "'"$patch$patch_args"'" } + ' $SERIES > $tmpfile + status=$? + if [ $status -ne 0 ] + then + rm -f $tmpfile + return 1 + fi else - echo $patch$patch_args > $tmp + echo $patch$patch_args > $tmpfile if [ -e $SERIES ] then - cat $SERIES >> $tmp + cat $SERIES >> $tmpfile fi fi - mkdir -p $(dirname $SERIES) - mv -f $tmp $SERIES + mv -f $tmpfile $SERIES +} + +remove_from_series() +{ + local patch=$1 + + tmpfile=$(mktemp /tmp/patch-scripts.XXXXXX) || return 1 + awk ' + ! /^'"$(quote_re $patch)"'(|\.patch|\.diff?)(|\.gz|\.bz2)([ \t]|$)/ \ + { print } + ' $SERIES > $tmpfile + if [ $? -eq 0 ] + then + mv -f $tmpfile $SERIES + else + rm -f $tmpfile + return 1 + fi } pc_file_name() @@ -170,34 +218,23 @@ backup_file_name() done } -__cat_series() +cat_series() { - local series=$1 - if [ -e $series ] + if [ -e $SERIES ] then sed -e '/^#/d' -e 's/^[ \t]*//' -e 's/[ \t].*//' \ - -e 's/\.patch$//' -e 's/\.diff\?//' $series + -e 's/\.gz$//' -e 's/\.bz2$//' \ + -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]*$' @@ -219,17 +256,30 @@ is_applied() patches_before() { local patch=$1 - cat_series \ - | awk '$0 == "'"$patch"'" { exit } - { print }' + + if [ -n "$patch" ] + then + cat_series \ + | awk ' + $0 == "'"$patch"'" { exit } + { print } + ' + fi } patches_after() { local patch=$1 - cat_series \ - | awk 'seen { print } - $0 == "'"$patch"'" { seen=1 }' + if [ -n "$patch" ] + then + cat_series \ + | awk ' + seen { print } + $0 == "'"$patch"'" { seen=1 } + ' + else + cat_series + fi } # List all patches that have been applied on top of patch $1 @@ -237,8 +287,10 @@ patches_on_top_of() { local patch=$1 [ -e $DB ] || return - awk '$0 == "'"$patch"'" { seen=1 ; next } - seen { print }' $DB + awk ' + $0 == "'"$patch"'" { seen=1 ; next } + seen { print } + ' $DB } # Print the name of the patch that modified the file $2 next after @@ -255,30 +307,106 @@ next_patch_for_file() | head -1 \ | sed -e 's:^\.pc/::' -e 's:/\.pc$::' fi + + #modified_files $file -- $patches_on_top \ + #| cut -d $'\t' -f2 \ + #| cut -d ' ' -f1 } -can_apply() +# Create a list of files and the patches that modify them. +refresh_patches_per_file() { - local silent - if [ "x$1" == "x-s" ] + local pc_files=$(pc_file_name $(cat_series)) + local ex_pc_files pc_file + + if [ -e .pc/patches-per-file ] then - silent=-s - shift + local needs_refresh + for pc_file in pc_files + do + if [ .pc/patches-per-file -ot $pc_file ] + then + needs_refresh=1 + break + fi + done + if [ -z "$needs_refresh" ] + then + return 0 + fi 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" ] + for pc_file in $pc_files + do + if [ -e $pc_file ] + then + ex_pc_files[${#ex_pc_files[@]}]=$pc_file + fi + done + + if [ ${#ex_pc_files[@]} -eq 0 ] then - silent=-s - shift + rm -f .pc/patches-per-file + return 0 fi - local patch=$1 - patch -R -p1 --dry-run -f $silent -i $(patch_file_name $patch) < /dev/null + + awk ' + ARGIND!=saved { sub(/^.pc\//, "", FILENAME) + sub(/\/\.pc/, "", FILENAME) + saved=ARGIND + } + { if (files[$0]) + files[$0]=files[$0] " " FILENAME + else + files[$0]=FILENAME + } + END { for (file in files) + printf "%s\t%s\n", file, files[file] + } + ' "${ex_pc_files[@]}" > .pc/patches-per-file +} + +# For a lists of patches and a list of files, compute which patches +# modify which files. Invoked as +# modified_files file ... [-- patch ...] +# +modified_files() +{ + if ! refresh_patches_per_file + then + return 1 + fi + + awk ' + BEGIN { no_files=1 + no_patches=1 + for (i=1; i<ARGC; i++) { + if (ARGV[i]=="--") + break + files[ARGV[i]]=1 + no_files=0 + } + for (i++; i<ARGC; i++) { + patches[ARGV[i]]=1 + no_patches=0 + } + split("", ARGV) # read from standard input + } + no_files || files[$1] { + if (no_patches) { + print + next + } + for (i=2; i<=NF; i++) + if ($i in patches) { + printf "%s\t%s", $1, $i + for (i++; i<=NF; i++) + if ($i in patches) + printf " %s", $i + printf "\n" + } + } + ' "$@" < .pc/patches-per-file } add_to_db() @@ -290,10 +418,13 @@ 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 + if tmpfile=$(mktemp /tmp/patch-scripts.XXXXXX) + then + grep -v "^$(quote_re $patch)\$" $DB > $tmpfile + mv -f $tmpfile $DB + rm -f $tmpfile + [ -s $DB ] || rm -f $DB + fi } stripit() @@ -303,6 +434,7 @@ stripit() echo $1 | sed -e 's/^\(\.\/\)*//' \ -e 's/^'"$P"'patches\///' -e 's/^\.pc\///' \ + -e 's/\.gz$//' -e 's/\.bz2$//' \ -e 's/\.patch$//' -e 's/\.diff\?$//' fi } @@ -322,15 +454,19 @@ files_in_patch() fi } -install_file_in_patch() +touched_by_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 + local strip=$1 patch=$2 + cat_file $(patch_file_name $patch) \ + | awk ' + /^\+\+\+[ \t]/ { + sub(/^\+\+\+[ \t]/, "") + sub(/[ \t].*/, "") + sub(/^\/dev\/null/, "") + for (i=0; i<'$strip'; i++) + sub(/^[^\/]*\//, "") + print + }' } refresh_file_list() @@ -341,40 +477,103 @@ refresh_file_list() if ! [ -e "$patch_file" ] then - echo "Patch file $patch_file does not exist" - return 1 + return 0 fi - if [ ! -e $pc_file -o $pc_file -ot $patch_file ] + if [ ! -e $pc_file -o \ + $pc_file -ot $patch_file -o \ + $pc_file -ot $SERIES ] 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 ] + local tmpfile status + if ! mkdir -p $(dirname $pc_file) || \ + ! tmpfile=$(mktemp /tmp/patch-scripts.XXXXXX) + then + return 1 + fi + + if [ -e $pc_file ] + then + cat $pc_file >> $tmpfile + fi + if ! touched_by_patch $(patch_strip_level $patch) \ + $patch >> $tmpfile then return 1 fi - return 2 + sort $tmpfile | uniq > $pc_file + rm $tmpfile + return 0 fi } -directory_names() +diff_file() +{ + local file=$1 suffix=$2 old_file=$3 new_file=$4 + local old_file new_file + + if [ ! -e "$old_file" -a ! -e "$new_file" ] + then + echo "File $file does not exist" >&2 + return 0 + fi + local old_hdr new_hdr + if [ $opt_strip_level -eq 0 ] + then + old_hdr=$file$suffix + new_hdr=$file + else + local dir=$(basename $PWD) + old_hdr=$dir$suffix/$file + new_hdr=$dir/$file + fi + + echo Index: $new_hdr + diff -Nu $DIFF_OPTS $old_file $new_file | + fix_diff_header $old_hdr $new_hdr +} + +fix_diff_header() { - echo "$@" \ - | awk 'BEGIN {RS=" "} - {sub(/\/?[^\/]*$/, "") ; printf "[%s]\n", $0} - {if (last != $0) - print last - last = $0} - END {print last}' + local from=$1 to=$2 + sed -e 's:^--- [^ \t]*:--- '"$from"':' \ + -e 's:^+++ [^ \t]*:+++ '"$to"':' } -need_file_there() +cat_file() { - if [ ! -e $1 ] + local filename + + for filename in "$@" + do + if [ -e "$filename" ] + then + case "$filename" in + *.gz|*.tgz) + gzip -cd "$filename" ;; + *.bz2) + bzip2 -cd "$filename" ;; + *) + cat "$filename" ;; + esac + fi + done +} + +cat_to_file() +{ + local filename="$1" + + if [ -z "$filename" ] then - echo "File $1 does not exist" - exit 1 + cat + else + case "$filename" in + *.gz|*.tgz) + gzip -c > "$filename" ;; + *.bz2) + bzip2 -c > "$filename" ;; + *) + cat > "$filename" ;; + esac fi } @@ -384,10 +583,15 @@ patch_description() if [ -e "$patch_file" ] then - awk '/^--- / {exit} - diff {print diff ; diff=""} - /^diff / {diff=$0 ; next} - {print}' \ - $patch_file + awk ' + /^--- / { exit } + diff_line { print diff_line + diff_line="" + } + /^diff / { diff_line=$0 + next + } + { print } + ' $patch_file fi } diff --git a/lib/rpatch.in b/lib/rpatch.in index 3c05c0a..85e8408 100755 --- a/lib/rpatch.in +++ b/lib/rpatch.in @@ -1,4 +1,4 @@ -#! /bin/bash +#!/bin/bash # This script is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as @@ -6,16 +6,20 @@ # # See the COPYING and AUTHORS files for more details. -if ! [ -r @LIB@/patchfns ] +# Read in library functions +if [ "$(type -t patch_file_name)" != function ] then - echo "Cannot read library @LIB@/patchfns" >&2 - exit 1 + if ! [ -r @LIB@/patchfns ] + then + echo "Cannot read library @LIB@/patchfns" >&2 + exit 1 + fi + . @LIB@/patchfns fi -. @LIB@/patchfns usage() { - echo "Usage: rpatch [-fRq] patchname" + echo "Usage: $0 [-fRq] patchname" exit 1 } @@ -52,10 +56,17 @@ verify_removal() files_may_have_changed() { local patch=$1 file + local patch_file=$(patch_file_name $patch) local pc_file=$(pc_file_name $patch) + + if ! [ -e $pc_file ] + then + return 1 + fi + local apply_ts=$(date -r $pc_file '+%s') ts - if [ $pc_file -ot $(patch_file_name $patch) ] + if [ -e "$patch_file" -a $pc_file -ot "$patch_file" ] then return 0 fi @@ -76,25 +87,60 @@ files_may_have_changed() return 1 } -abort_patch() +rollback_patch() { - local pc_file=$(pc_file_name $1) - @LIB@/backup-files -s -f $pc_file -z ~rpatch -r - exit 1 + local patch=$1 pc_file=$(pc_file_name $patch) + @LIB@/backup-files $silent_unless_verbose \ + -f $pc_file -z ~rpatch -r + rm -f $(files_in_patch $patch | sed -e 's/$/\.rej/') } interrupt() { local patch=$1 - abort_patch $patch - echo "rpatch interrupted by user" + rollback_patch $patch + echo "Interrupted by user; patch $patch was not removed." exit 1 } -do_remove() +reverse_patch() +{ + local patch=$1 + local patch_file=$(patch_file_name $patch) + + if ! [ -s $patch_file ] + then + echo "Patch file $patch_file appears to be empty" + return 0 + fi + + if [ "x${patch_file:(-3)}" = "x.gz" ] + then + gzip -cd $patch_file \ + | patch $(patch_args $patch) --no-backup-if-mismatch \ + -R -E $silent + elif [ "x${patch_file:(-4)}" = "x.bz2" ] + then + bzip2 -cd $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 +} + +rpatch() { local patch=$1 pc_file=$(pc_file_name $patch) + if ! [ -e $pc_file ] + then + echo "Patch $patch appears to be empty, removed" + remove_from_db $patch + return 0 + fi + trap "" SIGINT if [ -n "$opt_force" ] || \ ( [ -z "$opt_remove" ] && ! files_may_have_changed $patch ) @@ -106,44 +152,53 @@ do_remove() @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 + rm -f $pc_file~refresh + if [ $status != 0 ] + then + exit $status + fi else - local patch_file=$(patch_file_name $patch) - if ! [ -e "$patch_file" ] + if ! @LIB@/backup-files $silent_unless_verbose \ + -f $pc_file -z ~rpatch then - echo "No patch named $patch found." + echo "Failed to create temporary files" >&2 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 + + reverse_patch $patch 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 + @LIB@/backup-files $silent_unless_verbose \ + -f $pc_file -z ~rpatch -x + @LIB@/backup-files $silent_unless_verbose \ + -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" + rollback_patch $patch + echo "Patch $patch does not remove (enforce with -f)" return 1 fi fi trap - SIGINT + + local top=$(top_patch) where + if [ -z "$top" ] + then + where="no patches applied" + else + where="now at $top" + fi + echo "Removed $patch, $where" } -options=`getopt -o fRqh -- "$@"` +options=`getopt -o fRqvh -- "$@"` if [ $? -ne 0 ] then @@ -165,6 +220,9 @@ do -q) opt_quiet=1 shift ;; + -v) + opt_verbose=1 + shift ;; -h) usage -h ;; --) @@ -180,29 +238,13 @@ fi patch=$(stripit $1) [ -n "$opt_quiet" ] && silent=-s +[ -z "$opt_verbose" ] && silent_unless_verbose=-s top=$(top_patch) -if [ -n "$top" -a -e $(pc_file_name $top)~forced -a -z "$opt_force" ] +if [ -n "$top" -a -e $(pc_file_name $top)~refresh -a -z "$opt_force" ] then - echo "The topmost patch $top was force applied. Please run" \ - "refpatch before removing it." + echo "The topmost patch $top needs to be refreshed first." 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" - +rpatch "$patch" || exit 1 diff --git a/lib/spec2series b/lib/spec2series new file mode 100755 index 0000000..685f8e0 --- /dev/null +++ b/lib/spec2series @@ -0,0 +1,223 @@ +#!/bin/bash + +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# See the COPYING and AUTHORS files for more details. + +# defaults +debug=0 +specfile="" +outfile="" + +function usage() { +cat <<EOF +Usage: quilt spec2series [ options ] specfile + +-h print this text +-d debug mode (prints lots additional info + as comments into the output file) +-o <file> + specify output file, stdout if unspecified + +EOF +} + +# parse args +while test "$1" != ""; do + case "$1" in + -h | --help) + usage; exit 0 + ;; + -d | --debug) + debug=1; shift + ;; + -o | --outfile) + outfile="$2"; shift; shift + ;; + *) + specfile="$1" + break; + ;; + esac +done + +if test ! -f "$specfile"; then + usage + exit 1 +fi + +# get absolute patch for specfile location +specdir=`dirname $specfile` +specfile=`basename $specfile` +case "$specdir" in + .) specdir="`pwd`" + ;; + /*) # nothing + ;; + *) specdir="`pwd`/$specdir" + ;; +esac + +# create tmp work dir +WORK="${TMPDIR-/tmp}/rpmlog-$$" +mkdir -p "$WORK" || exit 1 +trap 'rm -rf "$WORK"' EXIT +mkdir -p "$WORK/build" +mkdir -p "$WORK/bin" + +# create md5 sums, also for uncompressed files +echo -n "### md5: " >&2 +(cd $specdir; for file in /dev/null *; do + case "$file" in + ready | bigpack | MD5SUMS | MD5SUMS.meta | *.spec | *.changes) + continue + ;; + esac + type="`file -b $file | cut -d" " -f1`" + case "$type" in + compress*) + echo -n "z" >&2 + set -- `zcat $file | md5sum` + echo "$1 zcat ${file}" + ;; + gzip) + echo -n "g" >&2 + set -- `zcat $file | md5sum` + echo "$1 zcat ${file}" + ;; + bzip2) + echo -n "b" >&2 + set -- `bzcat $file | md5sum` + echo "$1 bzcat ${file}" + ;; + esac + echo -n "." >&2 + set -- `md5sum < $file` + echo "$1 cat ${file}" +done) > $WORK/md5sum +echo " done" >&2 + +# prepare rpm dir fixups and hooks +RPM="rpm --rcfile=/usr/lib/rpm/rpmrc:$WORK/rpmrc" +PATH="$WORK/bin:$PATH" +grep ^macrofiles /usr/lib/rpm/rpmrc \ + | sed -e "/macrofiles/s|$|:$WORK/rpmmacros|" \ + > $WORK/rpmrc +cat <<-EOF > "$WORK/rpmmacros" + %_sourcedir $specdir + %_specdir $specdir + %_builddir $WORK/build +EOF + +# wrapper script for patch and tar +cat <<-'EOF' > "$WORK/bin/patch" + #!/bin/sh + + # save stuff for log + unpackcmd=`basename $0` + unpackdir=`pwd` + unpackargs="$*" + uncompress="false" + unpackfile="[oops]" + + # sort of progress bar + case $unpackcmd in + tar) echo -n "t" >&2;; + patch) echo -n "p" >&2;; + *) echo -n "?" >&2;; + esac + + # find real binary + realcmd="" + test -x "/bin/$unpackcmd" && realcmd="/bin/$unpackcmd" + test -x "/usr/bin/$unpackcmd" && realcmd="/usr/bin/$unpackcmd" + test "$realcmd" = "" && exit 1 + + # put data into tmpfile, exec real cmd, return on failure + WORK=`dirname $RPM_BUILD_DIR` + cat > "$WORK/data" + if test -x /bin/$unpackcmd; then + cmddir="/bin" + fi + $realcmd $* < "$WORK/data" + retval="$?" + test "$retval" != "0" && exit $retval + + # find original data file by md5sum + set -- `md5sum < "$WORK/data"` + unpackfile="[$1]" + set -- `cat "$WORK/md5sum"` + while test "$1" != ""; do + if test "[$1]" = "$unpackfile"; then + uncompress="$2" + unpackfile="$3" + break + fi + shift + done + + # print results + unpackdir=`echo $unpackdir | sed -e "s|$RPM_BUILD_DIR|BUILD|"` + echo -n "# log: [$unpackdir] $uncompress $unpackfile" >>$WORK/cmdlog + echo " | $unpackcmd $unpackargs" >>$WORK/cmdlog + if test "$unpackcmd" = "patch" -a \ + -f "$RPM_SOURCE_DIR/$unpackfile"; then + patchdir="${unpackdir#BUILD/}" + if test ! -f "$WORK/patchdir"; then + echo -n $patchdir > $WORK/patchdir + fi + if test "`cat $WORK/patchdir`" = "$patchdir"; then + level=`echo $unpackargs | tr " " "\n" | grep ^-p` + echo "$unpackfile $level" >> $WORK/patchlog + fi + fi + if test "$unpackcmd" = "tar" -a \ + -f "$RPM_SOURCE_DIR/$unpackfile"; then + echo -n "# Source: $unpackfile" >>$WORK/tarlog + if test "$unpackdir" != "BUILD"; then + echo -n " -C ${unpackdir#BUILD/}" >>$WORK/tarlog + fi + echo "" >>$WORK/tarlog + fi +EOF +chmod 755 "$WORK/bin/patch" +ln -s patch "$WORK/bin/tar" + +# let rpm do all the dirty specfile stuff ... +echo -n "### rpm: " >&2 +touch $WORK/patchlog +$RPM --nodeps --quiet -bp "$specdir/$specfile" </dev/null +echo " done" >&2 + +# print results saved by the wrapper script +( + # header + echo "# Patch series file for quilt, created by $0" + echo "#" + echo "# Sourcedir: $specdir" + echo "# Specfile: $specfile" + if test -f $WORK/patchdir; then + echo "# Patchdir: `cat $WORK/patchdir`" + fi + echo "#" + + # additional info for trouble shooting + if test "$debug" = "1"; then + cat $WORK/md5sum | sed -e 's/^/# md5: /' + echo "#" + + test -f $WORK/cmdlog && cat $WORK/cmdlog && echo "#" + fi + + # list tarballs + patches + test -f $WORK/tarlog && cat $WORK/tarlog && echo "#" + test -f $WORK/patchlog && cat $WORK/patchlog +)|( + if test "$outfile" != ""; then + cat > "$outfile" + else + cat + fi +) diff --git a/lib/touched-by-patch b/lib/touched-by-patch deleted file mode 100755 index bdfc296..0000000 --- a/lib/touched-by-patch +++ /dev/null @@ -1,69 +0,0 @@ -#! /bin/bash -# Extract names of new files from a patch, print them out - -# This script is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# See the COPYING and AUTHORS files for more details. - -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 |