From eb1f8528c11666bf3a5beb9825d3f2696cb30cf9bddd4a04c366eaafca2d0f2e Mon Sep 17 00:00:00 2001 From: OBS User unknown Date: Mon, 18 Dec 2006 23:17:23 +0000 Subject: [PATCH] OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/patch?expand=0&rev=1 --- .gitattributes | 23 + .gitignore | 1 + fail.test | 19 + global-reject-file.diff | 201 +++++ if_else_endif_comments.diff | 75 ++ patch-2.5.9-cat_if_device.diff | 92 ++ patch-2.5.9.tar.bz2 | 3 + patch-man-unified-reject.diff | 11 + patch.changes | 204 +++++ patch.spec | 189 ++++ ready | 0 remember-backup-files.diff | 1507 ++++++++++++++++++++++++++++++++ trailing-cr-fix.diff | 61 ++ unified-reject-files.diff | 305 +++++++ 14 files changed, 2691 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 fail.test create mode 100644 global-reject-file.diff create mode 100644 if_else_endif_comments.diff create mode 100644 patch-2.5.9-cat_if_device.diff create mode 100644 patch-2.5.9.tar.bz2 create mode 100644 patch-man-unified-reject.diff create mode 100644 patch.changes create mode 100644 patch.spec create mode 100644 ready create mode 100644 remember-backup-files.diff create mode 100644 trailing-cr-fix.diff create mode 100644 unified-reject-files.diff diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9b03811 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,23 @@ +## Default LFS +*.7z filter=lfs diff=lfs merge=lfs -text +*.bsp filter=lfs diff=lfs merge=lfs -text +*.bz2 filter=lfs diff=lfs merge=lfs -text +*.gem filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.jar filter=lfs diff=lfs merge=lfs -text +*.lz filter=lfs diff=lfs merge=lfs -text +*.lzma filter=lfs diff=lfs merge=lfs -text +*.obscpio filter=lfs diff=lfs merge=lfs -text +*.oxt filter=lfs diff=lfs merge=lfs -text +*.pdf filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.rpm filter=lfs diff=lfs merge=lfs -text +*.tbz filter=lfs diff=lfs merge=lfs -text +*.tbz2 filter=lfs diff=lfs merge=lfs -text +*.tgz filter=lfs diff=lfs merge=lfs -text +*.ttf filter=lfs diff=lfs merge=lfs -text +*.txz filter=lfs diff=lfs merge=lfs -text +*.whl filter=lfs diff=lfs merge=lfs -text +*.xz filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text +*.zst filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57affb6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.osc diff --git a/fail.test b/fail.test new file mode 100644 index 0000000..deb918d --- /dev/null +++ b/fail.test @@ -0,0 +1,19 @@ + $ mkdir d + $ cd d + + $ mkdir sub + $ echo 1 > f + $ echo 2 > f.new + $ diff -Nu f f.new > f.diff + $ mv f.new f + $ echo 3 > f.new + $ diff -Nu f f.new >> f.diff + $ rm f.new + $ echo 1 > f + $ chmod a=r f + $ strace -o ../log patch -p0 --backup < f.diff + > patching file f + > patching file f + + $ cd .. + $ rm -rf d diff --git a/global-reject-file.diff b/global-reject-file.diff new file mode 100644 index 0000000..918f00c --- /dev/null +++ b/global-reject-file.diff @@ -0,0 +1,201 @@ +Index: patch-2.5.9/patch.man +=================================================================== +--- patch-2.5.9.orig/patch.man ++++ patch-2.5.9/patch.man +@@ -520,6 +520,15 @@ file. + \fB\*=reject\-unified\fP + Produce unified reject files. The default is to produce context type reject files. + .TP ++.BI \*=global\-reject\-file= rejectfile ++Put all rejects into ++.I rejectfile ++instead of creating separate reject files for all files that have rejects. The ++.I rejectfile ++will contain headers that identify which file each reject refers to. Note that ++the global reject file is created even if \-\-dry\-run is specified (while ++non-global reject files will only be created without \-\-dry\-run). ++.TP + \fB\-R\fP or \fB\*=reverse\fP + Assume that this patch was created with the old and new files swapped. + (Yes, I'm afraid that does happen occasionally, human nature being what it +Index: patch-2.5.9/patch.c +=================================================================== +--- patch-2.5.9.orig/patch.c ++++ patch-2.5.9/patch.c +@@ -67,6 +67,7 @@ static bool similar (char const *, size_ + static bool spew_output (struct outstate *); + static char const *make_temp (char); + static int numeric_string (char const *, bool, char const *); ++static void reject_header (const char *filename); + static void abort_hunk (void); + static void cleanup (void); + static void get_some_switches (void); +@@ -98,6 +99,7 @@ static int Argc; + static char * const *Argv; + + static FILE *rejfp; /* reject file pointer */ ++static char *global_reject; + + static char const *patchname; + static char *rejname; +@@ -172,6 +174,10 @@ main (int argc, char **argv) + /* Make sure we clean up in case of disaster. */ + set_signals (false); + ++ /* initialize global reject file */ ++ if (global_reject) ++ init_reject (); ++ + for ( + open_patch_file (patchname); + there_is_another_patch(); +@@ -208,8 +214,9 @@ main (int argc, char **argv) + init_output (TMPOUTNAME, exclusive, &outstate); + } + +- /* initialize reject file */ +- init_reject (); ++ /* initialize per-patch reject file */ ++ if (!global_reject) ++ init_reject (); + + /* find out where all the lines are */ + if (!skip_rest_of_patch) +@@ -278,6 +285,8 @@ main (int argc, char **argv) + + newwhere = pch_newfirst() + last_offset; + if (skip_rest_of_patch) { ++ if (!failed) ++ reject_header(outname); + abort_hunk(); + failed++; + if (verbosity == VERBOSE) +@@ -292,6 +301,8 @@ main (int argc, char **argv) + say ("Patch attempted to create file %s, which already exists.\n", + quotearg (inname)); + ++ if (!failed) ++ reject_header(outname); + abort_hunk(); + failed++; + if (verbosity != SILENT) +@@ -299,6 +310,8 @@ main (int argc, char **argv) + format_linenum (numbuf, newwhere)); + } + else if (! apply_hunk (&outstate, where)) { ++ if (!failed) ++ reject_header(outname); + abort_hunk (); + failed++; + if (verbosity != SILENT) +@@ -332,7 +345,8 @@ main (int argc, char **argv) + fclose (outstate.ofp); + outstate.ofp = 0; + } +- fclose (rejfp); ++ if (!global_reject) ++ fclose (rejfp); + continue; + } + +@@ -412,13 +426,13 @@ main (int argc, char **argv) + } + } + if (diff_type != ED_DIFF) { +- if (fclose (rejfp) != 0) ++ if (!global_reject && fclose (rejfp) != 0) + write_fatal (); + if (failed) { + somefailed = true; + say ("%d out of %d hunk%s %s", failed, hunk, "s" + (hunk == 1), + skip_rest_of_patch ? "ignored" : "FAILED"); +- if (outname) { ++ if (!global_reject && outname) { + char *rej = rejname; + if (!rejname) { + rej = xmalloc (strlen (outname) + 5); +@@ -445,6 +459,20 @@ main (int argc, char **argv) + } + set_signals (true); + } ++ if (global_reject) ++ { ++ if (fclose (rejfp) != 0) ++ write_fatal (); ++ if (somefailed) ++ { ++ say (" -- saving rejects to file %s\n", quotearg (global_reject)); ++ /*if (! dry_run) ++ {*/ ++ move_file (TMPREJNAME, &TMPREJNAME_needs_removal, ++ global_reject, 0644, false); ++ /*}*/ ++ } ++ } + if (outstate.ofp && (ferror (outstate.ofp) || fclose (outstate.ofp) != 0)) + write_fatal (); + cleanup (); +@@ -523,6 +551,7 @@ static struct option const longopts[] = + {"posix", no_argument, NULL, CHAR_MAX + 7}, + {"quoting-style", required_argument, NULL, CHAR_MAX + 8}, + {"unified-reject-files", no_argument, NULL, CHAR_MAX + 9}, ++ {"global-reject-file", required_argument, NULL, CHAR_MAX + 10}, + {NULL, no_argument, NULL, 0} + }; + +@@ -582,6 +611,7 @@ static char const *const option_help[] = + " --dry-run Do not actually change any files; just print what would happen.", + " --posix Conform to the POSIX standard.", + " --unified-reject-files Create unified reject files.", ++" --global-reject-file=file Put all rejects into one file.", + "", + " -d DIR --directory=DIR Change the working directory to DIR first.", + #if HAVE_SETMODE_DOS +@@ -784,6 +814,9 @@ get_some_switches (void) + case CHAR_MAX + 9: + unified_reject_files = true; + break; ++ case CHAR_MAX + 10: ++ global_reject = savestr (optarg); ++ break; + default: + usage (stderr, 2); + } +@@ -933,6 +966,37 @@ locate_hunk (LINENUM fuzz) + } + + static char * ++format_timestamp (char timebuf[37], bool which) ++{ ++ time_t ts = pch_timestamp(which); ++ if (ts != -1) ++ { ++ struct tm *tm = localtime(&ts); ++ strftime(timebuf, 37, "\t%Y-%m-%d %H:%M:%S.000000000 %z", tm); ++ } ++ else ++ timebuf[0] = 0; ++ return timebuf; ++} ++ ++/* Write a header in a reject file that combines multiple hunks. */ ++static void ++reject_header (const char *outname) ++{ ++ char timebuf0[37], timebuf1[37]; ++ if (!global_reject) ++ return; ++ if (diff_type == UNI_DIFF) ++ fprintf(rejfp, "--- %s.orig%s\n+++ %s%s\n", ++ outname, format_timestamp(timebuf0, reverse), ++ outname, format_timestamp(timebuf1, !reverse)); ++ else ++ fprintf(rejfp, "*** %s.orig%s\n--- %s%s\n", ++ outname, format_timestamp(timebuf0, reverse), ++ outname, format_timestamp(timebuf1, !reverse)); ++} ++ ++static char * + format_linerange (char rangebuf[LINENUM_LENGTH_BOUND*2 + 2], + LINENUM first, LINENUM lines) + { diff --git a/if_else_endif_comments.diff b/if_else_endif_comments.diff new file mode 100644 index 0000000..85f5e55 --- /dev/null +++ b/if_else_endif_comments.diff @@ -0,0 +1,75 @@ +Index: patch-2.5.9/patch.c +=================================================================== +--- patch-2.5.9.orig/patch.c 2003-06-05 00:22:56.000000000 +0200 ++++ patch-2.5.9/patch.c 2003-06-05 00:23:16.000000000 +0200 +@@ -91,8 +91,8 @@ static LINENUM last_frozen_line; + static char const *do_defines; /* symbol to patch using ifdef, ifndef, etc. */ + static char const if_defined[] = "\n#ifdef %s\n"; + static char const not_defined[] = "\n#ifndef %s\n"; +-static char const else_defined[] = "\n#else\n"; +-static char const end_defined[] = "\n#endif\n"; ++static char const else_defined[] = "\n#else /* %s */\n"; ++static char const end_defined[] = "\n#endif /* %s */\n"; + + static int Argc; + static char * const *Argv; +@@ -1097,7 +1097,8 @@ apply_hunk (struct outstate *outstate, L + def_state = IN_IFNDEF; + } + else if (def_state == IN_IFDEF) { +- fprintf (fp, outstate->after_newline + else_defined); ++ fprintf (fp, outstate->after_newline + else_defined, ++ R_do_defines); + def_state = IN_ELSE; + } + if (ferror (fp)) +@@ -1116,7 +1117,8 @@ apply_hunk (struct outstate *outstate, L + return false; + if (R_do_defines) { + if (def_state == IN_IFNDEF) { +- fprintf (fp, outstate->after_newline + else_defined); ++ fprintf (fp, outstate->after_newline + else_defined, ++ R_do_defines); + def_state = IN_ELSE; + } + else if (def_state == OUTSIDE) { +@@ -1164,7 +1166,8 @@ apply_hunk (struct outstate *outstate, L + while (pch_char (old) == '!'); + + if (R_do_defines) { +- fprintf (fp, outstate->after_newline + else_defined); ++ fprintf (fp, outstate->after_newline + else_defined, ++ R_do_defines); + if (ferror (fp)) + write_fatal (); + def_state = IN_ELSE; +@@ -1183,7 +1186,8 @@ apply_hunk (struct outstate *outstate, L + old++; + new++; + if (R_do_defines && def_state != OUTSIDE) { +- fprintf (fp, outstate->after_newline + end_defined); ++ fprintf (fp, outstate->after_newline + end_defined, ++ R_do_defines); + if (ferror (fp)) + write_fatal (); + outstate->after_newline = true; +@@ -1201,7 +1205,8 @@ apply_hunk (struct outstate *outstate, L + def_state = IN_IFDEF; + } + else if (def_state == IN_IFNDEF) { +- fprintf (fp, outstate->after_newline + else_defined); ++ fprintf (fp, outstate->after_newline + else_defined, ++ R_do_defines); + def_state = IN_ELSE; + } + if (ferror (fp)) +@@ -1220,7 +1225,8 @@ apply_hunk (struct outstate *outstate, L + while (new <= pat_end && pch_char (new) == '+'); + } + if (R_do_defines && def_state != OUTSIDE) { +- fprintf (fp, outstate->after_newline + end_defined); ++ fprintf (fp, outstate->after_newline + end_defined, ++ R_do_defines); + if (ferror (fp)) + write_fatal (); + outstate->after_newline = true; diff --git a/patch-2.5.9-cat_if_device.diff b/patch-2.5.9-cat_if_device.diff new file mode 100644 index 0000000..0c1ad5d --- /dev/null +++ b/patch-2.5.9-cat_if_device.diff @@ -0,0 +1,92 @@ +Index: patch-2.5.9/patch.c +=================================================================== +--- patch-2.5.9.orig/patch.c ++++ patch-2.5.9/patch.c +@@ -444,7 +444,7 @@ main (int argc, char **argv) + { + move_file (TMPREJNAME, &TMPREJNAME_needs_removal, + rej, instat.st_mode, false); +- if (! inerrno ++ if (! inerrno && !s_is_chrblkfifosock(rej) + && (chmod (rej, (instat.st_mode + & ~(S_IXUSR|S_IXGRP|S_IXOTH))) + != 0)) +Index: patch-2.5.9/util.c +=================================================================== +--- patch-2.5.9.orig/util.c ++++ patch-2.5.9/util.c +@@ -65,6 +65,49 @@ static bool fid_search (const char *, co + FROM_NEEDS_REMOVAL must be nonnull if FROM is nonnull. + Back up TO if BACKUP is true. */ + ++int ++s_is_chrblkfifosock (const char *path) ++{ ++ int r; ++ struct stat st; ++ ++ r = stat(path, &st); ++ if (r < 0) ++ return r; ++ ++ return (((st.st_mode & S_IFMT) == S_IFCHR) || ++ ((st.st_mode & S_IFMT) == S_IFBLK) || ++ ((st.st_mode & S_IFMT) == S_IFIFO) || ++ ((st.st_mode & S_IFMT) == S_IFSOCK)); ++} ++ ++void ++cat_file_to_dev (const char *from, const char *to) ++{ ++ size_t i; ++ int fromfd, tofd; ++ ++ fromfd = open(from, O_RDONLY); ++ if (fromfd < 0) ++ pfatal("could not open %s for reading", quotearg(from)); ++ ++ tofd = open(to, O_WRONLY | O_NONBLOCK); ++ if (tofd < 0) ++ pfatal("could not open %s for writing", quotearg(to)); ++ ++ while ((i = read (fromfd, buf, bufsize)) != 0) ++ { ++ if (i == (size_t) -1) ++ read_fatal (); ++ if (write (tofd, buf, i) != i) ++ write_fatal (); ++ } ++ if (close (fromfd) != 0) ++ read_fatal (); ++ if (close (tofd) != 0) ++ write_fatal (); ++} ++ + void + move_file (char const *from, int volatile *from_needs_removal, + char *to, mode_t mode, bool backup) +@@ -164,6 +207,15 @@ move_file (char const *from, int volatil + goto rename_succeeded; + } + ++ if (errno == EACCES && (s_is_chrblkfifosock(to) > 0)) ++ { ++ cat_file_to_dev (from, to); ++ if (backup) ++ insert_fid (to); ++ unlink(from); ++ return; ++ } ++ + if (errno == EXDEV) + { + if (! backup) +Index: patch-2.5.9/util.h +=================================================================== +--- patch-2.5.9.orig/util.h ++++ patch-2.5.9/util.h +@@ -57,3 +57,4 @@ void remove_prefix (char *, size_t); + void removedirs (char *); + void set_signals (bool); + void write_fatal (void) __attribute__ ((noreturn)); ++int s_is_chrblkfifosock (const char *); diff --git a/patch-2.5.9.tar.bz2 b/patch-2.5.9.tar.bz2 new file mode 100644 index 0000000..3a1fadb --- /dev/null +++ b/patch-2.5.9.tar.bz2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:612a424b80bccf4638059742dc77676387c75a880723fc990a5d3a0ad0799de3 +size 158619 diff --git a/patch-man-unified-reject.diff b/patch-man-unified-reject.diff new file mode 100644 index 0000000..5703e4a --- /dev/null +++ b/patch-man-unified-reject.diff @@ -0,0 +1,11 @@ +--- patch-2.5.9/patch.man ++++ patch-2.5.9/patch.man +@@ -517,7 +517,7 @@ + .B \&.rej + file. + .TP +-\fB\*=reject\-unified\fP ++\fB\*=unified\-reject\-files\fP + Produce unified reject files. The default is to produce context type reject files. + .TP + .BI \*=global\-reject\-file= rejectfile diff --git a/patch.changes b/patch.changes new file mode 100644 index 0000000..c0244a8 --- /dev/null +++ b/patch.changes @@ -0,0 +1,204 @@ +------------------------------------------------------------------- +Wed Jan 25 21:30:47 CET 2006 - mls@suse.de + +- converted neededforbuild to BuildRequires + +------------------------------------------------------------------- +Thu Jan 12 07:52:31 CET 2006 - agruen@suse.de + +- remember-backup-files.diff: Fix case where a patch modifies a + read-only file more than once while --backup is used (test case + in the patch header). + +------------------------------------------------------------------- +Thu Aug 18 18:09:59 CEST 2005 - mmj@suse.de + +- --reject-unified is called --unified-reject-files [#105151] + +------------------------------------------------------------------- +Wed Jun 29 13:50:06 CEST 2005 - mmj@suse.de + +- Don't compile with -f-signed-char [#93883] +- Don't strip explicitly + +------------------------------------------------------------------- +Tue Jan 4 21:11:54 CET 2005 - mmj@suse.de + +- Add patch to make patch able to write reject files to devices, + named pipes and sockets [#45794] + +------------------------------------------------------------------- +Sat Jan 25 02:30:00 CET 2004 - agruen@suse.de + +- Add --reject-unified option to produce unified reject files: + Before, unified reject files were produced if the patch itself + was unified; this could sometimes have been annoying. +- If a patch with C function names (diff -p) has rejects, include + the function names in the reject files. + +------------------------------------------------------------------- +Sun Jan 11 11:17:55 CET 2004 - adrian@suse.de + +- add %defattr + +------------------------------------------------------------------- +Tue Oct 7 15:03:22 CEST 2003 - agruen@suse.de + +- remember-backup-files: Also include the file timestamps in the + hash table for non-POSIX-compliant systems that don't guarantee + that i_dev + i_ino uniquely identifies a file. +- #32031: Create --global-reject-file even if --dry-run is + specified. Add a missing '\n'. + +------------------------------------------------------------------- +Wed Jul 16 15:21:44 CEST 2003 - agruen@suse.de + +- Replace trailing-cr-fix.diff with official upstream version that + fixes this bug differently. + +------------------------------------------------------------------- +Mon Jun 30 13:30:33 CEST 2003 - agruen@suse.de + +- trailing-cr-fix.diff: Fix a bug in carriage return detection + (DOS files) in the unified diff path. +- Disable patch that adds comments to C preprocessor style + merges, as specified by POSIX.1-2001. + +------------------------------------------------------------------- +Wed Jun 25 16:46:41 CEST 2003 - agruen@suse.de + +- Fix bug introduced on June 5 that broke remember-backup-files. + +------------------------------------------------------------------- +Tue Jun 24 17:35:09 CEST 2003 - agruen@suse.de + +- smart-reject-file-format.diff: Work around a special case in + which patches are not terminated with a '^' in the internal + representation. The resulting error message was "internal error + in abort_hunk". + +------------------------------------------------------------------- +Thu Jun 5 09:38:17 CEST 2003 - agruen@suse.de + +- Temporary reject file logic: Ooops, now must only close the + temporary reject file after processing all patches, instead of + after each patch. + +------------------------------------------------------------------- +Thu Jun 5 01:00:38 CEST 2003 - agruen@suse.de + +- Upgrade to 2.5.9: Several fixes, obsoletes + rename-same-file.patch. +- Fix and adapt global-reject-file patch: + + The global reject file included corrupted headers for each + rejected hunk, instead of one header for each file with + rejects. + + Rename --global-reject to --global-reject-file. + + Simplify temporary reject file logic. + + Adapt to unified-reject-files patch (which has different + headers). + + Add entry in man page. +- Fix a bug in smart-reject-file-format: Files that are created + are identified by `-0,0'; before the patch was generating + `-1,0' in reject files. +- Switch remember-backup-files.diff over to use gnulib's hash + tables instead of glibc's binary trees, requested from upstream + to ensure greater portability. +- Add /* SYM */ comment to #endif lines for patch -D SYM, too. + +------------------------------------------------------------------- +Wed Apr 9 19:33:59 CEST 2003 - agruen@suse.de + +- Fix another bug with hard links and backup file generation. +- Fix backup file generation if the same file appears in the + patch more than once. + +------------------------------------------------------------------- +Wed Mar 26 14:00:55 CET 2003 - mmj@suse.de + +- Update to 2.5.8: + · Bugfixes + · patch -D now outputs preprocessor lines without comments, as + required by POSIX 1003.1-2001 + · File names in context patches may now contain spaces, so long + as the context patch headers use a tab to separate the file name + from the time stamp + · Perforce is now supported + · Patch lines beginning with "#" are comments and are ignored + +------------------------------------------------------------------- +Wed Jan 15 16:50:57 CET 2003 - agruen@suse.de + +- Fix a bug with hardlinks (see rename-same-file.patch) + +------------------------------------------------------------------- +Tue Sep 17 17:34:28 CEST 2002 - ro@suse.de + +- removed bogus self-provides + +------------------------------------------------------------------- +Wed Feb 6 12:05:34 CET 2002 - coolo@suse.de + +- called suse_update_config + +------------------------------------------------------------------- +Mon Jun 25 12:46:18 CEST 2001 - uli@suse.de + +- added patch adding "--global-reject" option by ak@suse.de +- bzipped tarball + +------------------------------------------------------------------- +Tue Mar 6 20:36:46 CET 2001 - bk@suse.de + +- update to 2.5.4, added 2 patches from PLD and use buildroot + +------------------------------------------------------------------- +Wed Nov 8 17:41:23 CET 2000 - uli@suse.de + +- now builds with -D_GNU_SOURCE, should avoid miscompilation that + breaks LFS support +- added fix for offset output by Alessandro Rubini +- added fix and enhancement for --ifdef by Pete Buechler + +------------------------------------------------------------------- +Sun Feb 27 20:16:11 CET 2000 - @suse.de + +- added missing CFLAGS quotes. + +------------------------------------------------------------------- +Sun Feb 27 19:07:58 CET 2000 - bk@suse.de + +- added PPC fixes by Uli back again (-fsigned-char, CPPFLAGS) + +------------------------------------------------------------------- +Fri Feb 25 17:44:41 CET 2000 - schwab@suse.de + +- Specfile cleanup, get rid of Makefile.Linux +- /usr/man -> /usr/share/man +- Add group tag. + +------------------------------------------------------------------- +Mon Sep 13 17:23:57 CEST 1999 - bs@suse.de + +- ran old prepare_spec on spec file to switch to new prepare_spec. + +------------------------------------------------------------------- +Wed Aug 18 12:13:35 MEST 1999 - uli@suse.de + +- fixed for PPC (-fsigned-char, CPPFLAGS) + +------------------------------------------------------------------- +Tue Mar 16 14:58:42 MET 1999 - ro@suse.de + +- update to 2.5.3 using diff from jurix + +------------------------------------------------------------------- +Wed Jan 13 15:45:28 MET 1999 - bs@suse.de + +- applied patch from Egbert Eich (problems with non existing files fixed) + +------------------------------------------------------------------- +Thu Nov 5 00:19:13 MET 1998 - ro@suse.de + +- use libc's basename() for glibc + diff --git a/patch.spec b/patch.spec new file mode 100644 index 0000000..dacac16 --- /dev/null +++ b/patch.spec @@ -0,0 +1,189 @@ +# +# spec file for package patch (Version 2.5.9) +# +# Copyright (c) 2006 SUSE LINUX Products GmbH, Nuernberg, Germany. +# This file and all modifications and additions to the pristine +# package are under the same license as the package itself. +# +# Please submit bugfixes or comments via http://bugs.opensuse.org +# + +# norootforbuild + +Name: patch +License: GPL +Group: Productivity/Text/Utilities +Autoreqprov: on +Version: 2.5.9 +Release: 149 +Summary: GNU patch +Source: ftp://prep.ai.mit.edu/pub/gnu/patch/%{name}-%{version}.tar.bz2 +URL: http://www.gnu.org/software/patch/patch.html +Patch: trailing-cr-fix.diff +Patch1: remember-backup-files.diff +Patch2: unified-reject-files.diff +Patch3: global-reject-file.diff +Patch4: if_else_endif_comments.diff +Patch5: patch-2.5.9-cat_if_device.diff +Patch6: patch-man-unified-reject.diff +BuildRoot: %{_tmppath}/%{name}-%{version}-build + +%description +The GNU patch program is used to apply diffs between original and +changed files (generated by the diff command) to the original files. + + + +Authors: +-------- + Larry Wall + Paul Eggert + +%prep +%setup -q +%suse_update_config +%patch0 -p1 +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch5 -p1 +%patch6 -p1 + +%build +aclocal --acdir=m4 +autoheader +autoconf +CFLAGS="$RPM_OPT_FLAGS -D_GNU_SOURCE -Wall -O2 -pipe" \ +./configure \ +%ifarch sparc sparc64 + --disable-largefile \ +%endif + --prefix=%{_prefix} +make CPPFLAGS="" + +%install +make install \ + bindir=$RPM_BUILD_ROOT%{_bindir} \ + man1dir=$RPM_BUILD_ROOT%{_mandir}/man1 + +%files +%defattr(-,root,root) +%doc NEWS README +/usr/bin/patch +%doc %{_mandir}/man1/patch.1.gz + +%changelog -n patch +* Wed Jan 25 2006 - mls@suse.de +- converted neededforbuild to BuildRequires +* Thu Jan 12 2006 - agruen@suse.de +- remember-backup-files.diff: Fix case where a patch modifies a + read-only file more than once while --backup is used (test case + in the patch header). +* Thu Aug 18 2005 - mmj@suse.de +- --reject-unified is called --unified-reject-files [#105151] +* Wed Jun 29 2005 - mmj@suse.de +- Don't compile with -f-signed-char [#93883] +- Don't strip explicitly +* Tue Jan 04 2005 - mmj@suse.de +- Add patch to make patch able to write reject files to devices, + named pipes and sockets [#45794] +* Sun Jan 25 2004 - agruen@suse.de +- Add --reject-unified option to produce unified reject files: + Before, unified reject files were produced if the patch itself + was unified; this could sometimes have been annoying. +- If a patch with C function names (diff -p) has rejects, include + the function names in the reject files. +* Sun Jan 11 2004 - adrian@suse.de +- add %%defattr +* Tue Oct 07 2003 - agruen@suse.de +- remember-backup-files: Also include the file timestamps in the + hash table for non-POSIX-compliant systems that don't guarantee + that i_dev + i_ino uniquely identifies a file. +- #32031: Create --global-reject-file even if --dry-run is + specified. Add a missing '\n'. +* Wed Jul 16 2003 - agruen@suse.de +- Replace trailing-cr-fix.diff with official upstream version that + fixes this bug differently. +* Mon Jun 30 2003 - agruen@suse.de +- trailing-cr-fix.diff: Fix a bug in carriage return detection + (DOS files) in the unified diff path. +- Disable patch that adds comments to C preprocessor style + merges, as specified by POSIX.1-2001. +* Wed Jun 25 2003 - agruen@suse.de +- Fix bug introduced on June 5 that broke remember-backup-files. +* Tue Jun 24 2003 - agruen@suse.de +- smart-reject-file-format.diff: Work around a special case in + which patches are not terminated with a '^' in the internal + representation. The resulting error message was "internal error + in abort_hunk". +* Thu Jun 05 2003 - agruen@suse.de +- Temporary reject file logic: Ooops, now must only close the + temporary reject file after processing all patches, instead of + after each patch. +* Thu Jun 05 2003 - agruen@suse.de +- Upgrade to 2.5.9: Several fixes, obsoletes + rename-same-file.patch. +- Fix and adapt global-reject-file patch: + + The global reject file included corrupted headers for each + rejected hunk, instead of one header for each file with + rejects. + + Rename --global-reject to --global-reject-file. + + Simplify temporary reject file logic. + + Adapt to unified-reject-files patch (which has different + headers). + + Add entry in man page. +- Fix a bug in smart-reject-file-format: Files that are created + are identified by `-0,0'; before the patch was generating + `-1,0' in reject files. +- Switch remember-backup-files.diff over to use gnulib's hash + tables instead of glibc's binary trees, requested from upstream + to ensure greater portability. +- Add /* SYM */ comment to #endif lines for patch -D SYM, too. +* Wed Apr 09 2003 - agruen@suse.de +- Fix another bug with hard links and backup file generation. +- Fix backup file generation if the same file appears in the + patch more than once. +* Wed Mar 26 2003 - mmj@suse.de +- Update to 2.5.8: + · Bugfixes + · patch -D now outputs preprocessor lines without comments, as + required by POSIX 1003.1-2001 + · File names in context patches may now contain spaces, so long + as the context patch headers use a tab to separate the file name + from the time stamp + · Perforce is now supported + · Patch lines beginning with "#" are comments and are ignored +* Wed Jan 15 2003 - agruen@suse.de +- Fix a bug with hardlinks (see rename-same-file.patch) +* Tue Sep 17 2002 - ro@suse.de +- removed bogus self-provides +* Wed Feb 06 2002 - coolo@suse.de +- called suse_update_config +* Mon Jun 25 2001 - uli@suse.de +- added patch adding "--global-reject" option by ak@suse.de +- bzipped tarball +* Tue Mar 06 2001 - bk@suse.de +- update to 2.5.4, added 2 patches from PLD and use buildroot +* Wed Nov 08 2000 - uli@suse.de +- now builds with -D_GNU_SOURCE, should avoid miscompilation that + breaks LFS support +- added fix for offset output by Alessandro Rubini +- added fix and enhancement for --ifdef by Pete Buechler +* Sun Feb 27 2000 - @suse.de +- added missing CFLAGS quotes. +* Sun Feb 27 2000 - bk@suse.de +- added PPC fixes by Uli back again (-fsigned-char, CPPFLAGS) +* Fri Feb 25 2000 - schwab@suse.de +- Specfile cleanup, get rid of Makefile.Linux +- /usr/man -> /usr/share/man +- Add group tag. +* Mon Sep 13 1999 - bs@suse.de +- ran old prepare_spec on spec file to switch to new prepare_spec. +* Wed Aug 18 1999 - uli@suse.de +- fixed for PPC (-fsigned-char, CPPFLAGS) +* Tue Mar 16 1999 - ro@suse.de +- update to 2.5.3 using diff from jurix +* Wed Jan 13 1999 - bs@suse.de +- applied patch from Egbert Eich (problems with non existing files fixed) +* Thu Nov 05 1998 - ro@suse.de +- use libc's basename() for glibc diff --git a/ready b/ready new file mode 100644 index 0000000..473a0f4 diff --git a/remember-backup-files.diff b/remember-backup-files.diff new file mode 100644 index 0000000..ecbc261 --- /dev/null +++ b/remember-backup-files.diff @@ -0,0 +1,1507 @@ +This patch remembers backup file names patch has already created +in a binary tree, and prevents patch from overwriting backup +files it has created before. The bug can be reproduced as follows: + + $ cat > f + < one + + $ cat > f.patch + <--- f.orig 2003-04-09 01:48:01.000000000 +0200 + <+++ f 2003-04-09 01:49:17.000000000 +0200 + <@@ -2 +2 @@ + <-one + <+two + <--- f.orig 2003-04-09 01:48:01.000000000 +0200 + <+++ f 2003-04-09 01:49:17.000000000 +0200 + <@@ -2 +2 @@ + <-two + <+three + + $ patch -p0 < f.patch + > patching file f + > Hunk #1 succeeded at 1 (offset -1 lines). + > patching file f + > Hunk #1 succeeded at 1 (offset -1 lines). + + $ cat f.orig + > one + + $ rm f f.orig f.patch + + +Here is another test case with hard links between source files. + + $ cat > f + < one + + $ ln f g + $ cat > fg.patch + <--- f.orig 2003-04-09 01:48:01.000000000 +0200 + <+++ f 2003-04-09 01:49:17.000000000 +0200 + <@@ -2 +2 @@ + <-one + <+two + <--- g.orig 2003-04-09 01:48:01.000000000 +0200 + <+++ g 2003-04-09 01:49:17.000000000 +0200 + <@@ -2 +2 @@ + <-one + <+two + + $ patch -p0 < fg.patch + > patching file f + > Hunk #1 succeeded at 1 (offset -1 lines). + > patching file g + > Hunk #1 succeeded at 1 (offset -1 lines). + + $ cat f.orig + > one + + $ cat g.orig + > one + + $ rm f f.orig g g.orig fg.patch + +This test case failed with a Permission denied error with a previous +version of this patch: + + $ echo 1 > f + $ echo 2 > f.new + $ diff -Nu f f.new > f.diff + $ mv f.new f + $ echo 3 > f.new + $ diff -Nu f f.new >> f.diff + $ rm f.new + $ echo 1 > f + $ chmod a=r f + $ strace -o ../log patch -p0 --backup < f.diff + > patching file f + > patching file f + +Index: patch-2.5.9/Makefile.in +=================================================================== +--- patch-2.5.9.orig/Makefile.in ++++ patch-2.5.9/Makefile.in +@@ -62,7 +62,7 @@ CONFIG_STATUS = config.status + SHELL = /bin/sh + + LIBSRCS = error.c malloc.c memchr.c mkdir.c \ +- realloc.c rmdir.c strcasecmp.c strncasecmp.c ++ realloc.c rmdir.c strcasecmp.c strncasecmp.c hash.c + SRCS = $(LIBSRCS) \ + addext.c argmatch.c backupfile.c \ + basename.c dirname.c \ +@@ -78,12 +78,12 @@ OBJS = $(LIBOBJS) \ + maketime.$(OBJEXT) partime.$(OBJEXT) \ + patch.$(OBJEXT) pch.$(OBJEXT) \ + quote.$(OBJEXT) quotearg.$(OBJEXT) quotesys.$(OBJEXT) \ +- util.$(OBJEXT) version.$(OBJEXT) xmalloc.$(OBJEXT) ++ util.$(OBJEXT) version.$(OBJEXT) xmalloc.$(OBJEXT) hash.$(OBJEXT) + HDRS = argmatch.h backupfile.h common.h dirname.h \ + error.h getopt.h gettext.h \ + inp.h maketime.h partime.h pch.h \ + quote.h quotearg.h quotesys.h \ +- unlocked-io.h util.h version.h xalloc.h ++ unlocked-io.h util.h version.h xalloc.h hash.h + MISC = AUTHORS COPYING ChangeLog INSTALL Makefile.in NEWS README \ + aclocal.m4 \ + config.hin configure configure.ac \ +Index: patch-2.5.9/configure.ac +=================================================================== +--- patch-2.5.9.orig/configure.ac ++++ patch-2.5.9/configure.ac +@@ -64,6 +64,9 @@ gl_GETOPT + gl_PREREQ_XMALLOC + gl_QUOTE + gl_QUOTEARG ++gl_HASH ++ ++ag_CHECK_NANOSECOND_STAT + + dnl This should be in gnulib, but isn't for some reason. + AC_DEFUN([jm_PREREQ_ADDEXT], +Index: patch-2.5.9/m4/hash.m4 +=================================================================== +--- /dev/null ++++ patch-2.5.9/m4/hash.m4 +@@ -0,0 +1,15 @@ ++# hash.m4 serial 1 ++dnl Copyright (C) 2002 Free Software Foundation, Inc. ++dnl This file is free software, distributed under the terms of the GNU ++dnl General Public License. As a special exception to the GNU General ++dnl Public License, this file may be distributed as part of a program ++dnl that contains a configuration script generated by Autoconf, under ++dnl the same distribution terms as the rest of that program. ++ ++AC_DEFUN([gl_HASH], ++[ ++ dnl Prerequisites of lib/hash.c. ++ AC_CHECK_HEADERS_ONCE(stdlib.h) ++ AC_HEADER_STDBOOL ++ AC_CHECK_DECLS_ONCE(free malloc) ++]) +Index: patch-2.5.9/util.c +=================================================================== +--- patch-2.5.9.orig/util.c ++++ patch-2.5.9/util.c +@@ -45,9 +45,17 @@ + # define raise(sig) kill (getpid (), sig) + #endif + ++#if defined(HAVE_STAT_TIMEVAL) ++#include ++#endif ++ + #include ++#include + + static void makedirs (char *); ++static bool fid_search (const char *, const struct stat *, bool); ++# define fid_exists(name, pst) fid_search (name, pst, false) ++# define insert_fid(name) fid_search (name, NULL, true) + + /* Move a file FROM (where *FROM_NEEDS_REMOVAL is nonzero if FROM + needs removal when cleaning up at the end of execution) +@@ -64,7 +72,7 @@ move_file (char const *from, int volatil + struct stat to_st; + int to_errno = ! backup ? -1 : stat (to, &to_st) == 0 ? 0 : errno; + +- if (backup) ++ if (backup && (to_errno || ! fid_exists (to, &to_st))) + { + int try_makedirs_errno = 0; + char *bakname; +@@ -123,6 +131,7 @@ move_file (char const *from, int volatil + quotearg_n (0, to), quotearg_n (1, bakname)); + while (rename (to, bakname) != 0) + { ++ /* FIXME: copy if errno == EXDEV */ + if (errno != try_makedirs_errno) + pfatal ("Can't rename file %s to %s", + quotearg_n (0, to), quotearg_n (1, bakname)); +@@ -133,6 +142,8 @@ move_file (char const *from, int volatil + + free (bakname); + } ++ else ++ backup = false; + + if (from) + { +@@ -165,6 +176,8 @@ move_file (char const *from, int volatil + if (! to_dir_known_to_exist) + makedirs (to); + copy_file (from, to, 0, mode); ++ if (backup) ++ insert_fid (to); + return; + } + +@@ -173,6 +186,8 @@ move_file (char const *from, int volatil + } + + rename_succeeded: ++ if (backup) ++ insert_fid (to); + /* Do not clear *FROM_NEEDS_REMOVAL if it's possible that the + rename returned zero because FROM and TO are hard links to + the same file. */ +@@ -1011,3 +1026,105 @@ Fseek (FILE *stream, file_offset offset, + if (file_seek (stream, offset, ptrname) != 0) + pfatal ("fseek"); + } ++ ++typedef struct ++{ ++ dev_t fid_dev; ++ ino_t fid_ino; ++ time_t fid_mtime; ++ unsigned long fid_mtimensec; ++} file_id; ++ ++unsigned ++file_id_hasher (file_id *entry, unsigned table_size) ++{ ++ return ((unsigned long) entry->fid_ino + ++ (unsigned long) entry->fid_dev + ++ (unsigned long) entry->fid_mtime + ++ (unsigned long) entry->fid_mtimensec) % table_size; ++} ++ ++bool ++file_id_comparator (file_id *entry1, file_id *entry2) ++{ ++ return (entry1->fid_dev == entry2->fid_dev && ++ entry1->fid_ino == entry2->fid_ino && ++ entry1->fid_mtime == entry2->fid_mtime && ++ entry1->fid_mtimensec == entry2->fid_mtimensec); ++} ++ ++void ++file_id_freer (file_id *entry) ++{ ++ free (entry); ++} ++ ++Hash_table *file_id_hash; ++ ++/* Check if the file identified by FILENAME and PST was already seen. If the ++ file was already seen, returns TRUE. If the file has not yet been seen ++ and INSERT is TRUE, it is inserted. PST or FILENAME may be NULL (but not ++ both of them). */ ++ ++static bool ++fid_search (const char *filename, const struct stat *pst, bool insert) ++{ ++ struct stat st; ++ ++ if (!file_id_hash) ++ { ++ file_id_hash = hash_initialize (0, NULL, (Hash_hasher) file_id_hasher, ++ (Hash_comparator) file_id_comparator, ++ (Hash_data_freer) file_id_freer); ++ if (!file_id_hash) ++ pfatal ("hash_initialize"); ++ } ++ ++ if (!pst) ++ { ++ if (stat (filename, &st) != 0) ++ pfatal ("%s", quotearg (filename)); ++ pst = &st; ++ } ++ ++ if (insert) ++ { ++ file_id *pfid = xmalloc (sizeof (file_id)), *old_pfid; ++ pfid->fid_dev = pst->st_dev; ++ pfid->fid_ino = pst->st_ino; ++ pfid->fid_mtime = pst->st_mtime; ++#if defined(HAVE_STAT_NSEC) ++ pfid->fid_mtimensec = pst->st_mtimensec; ++#elif defined(HAVE_STAT_TIMEVAL) ++ pfid->fid_mtimensec = pst->st_mtim.tv_nsec; ++#else ++ pfid->fid_mtimensec = 0; ++#endif ++ old_pfid = hash_insert (file_id_hash, pfid); ++ if (!old_pfid) ++ pfatal ("hash_insert"); ++ else if (old_pfid != pfid) ++ { ++ free (pfid); ++ return true; ++ } ++ else ++ return false; ++ } ++ else ++ { ++ file_id fid; ++ fid.fid_dev = pst->st_dev; ++ fid.fid_ino = pst->st_ino; ++ fid.fid_mtime = pst->st_mtime; ++#if defined(HAVE_STAT_NSEC) ++ fid.fid_mtimensec = pst->st_mtimensec; ++#elif defined(HAVE_STAT_TIMEVAL) ++ fid.fid_mtimensec = pst->st_mtim.tv_nsec; ++#else ++ fid.fid_mtimensec = 0; ++#endif ++ return hash_lookup (file_id_hash, &fid) != 0; ++ } ++} ++ +Index: patch-2.5.9/hash.c +=================================================================== +--- /dev/null ++++ patch-2.5.9/hash.c +@@ -0,0 +1,1051 @@ ++/* hash - hashing table processing. ++ ++ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Free Software ++ Foundation, Inc. ++ ++ Written by Jim Meyering, 1992. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2, 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 General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software Foundation, ++ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ ++ ++/* A generic hash table package. */ ++ ++/* Define USE_OBSTACK to 1 if you want the allocator to use obstacks instead ++ of malloc. If you change USE_OBSTACK, you have to recompile! */ ++ ++#if HAVE_CONFIG_H ++# include ++#endif ++#if HAVE_STDLIB_H ++# include ++#endif ++ ++#include ++#include ++#include ++ ++#ifndef HAVE_DECL_FREE ++"this configure-time declaration test was not run" ++#endif ++#if !HAVE_DECL_FREE ++void free (); ++#endif ++ ++#ifndef HAVE_DECL_MALLOC ++"this configure-time declaration test was not run" ++#endif ++#if !HAVE_DECL_MALLOC ++char *malloc (); ++#endif ++ ++#if USE_OBSTACK ++# include "obstack.h" ++# ifndef obstack_chunk_alloc ++# define obstack_chunk_alloc malloc ++# endif ++# ifndef obstack_chunk_free ++# define obstack_chunk_free free ++# endif ++#endif ++ ++#include "hash.h" ++ ++struct hash_table ++ { ++ /* The array of buckets starts at BUCKET and extends to BUCKET_LIMIT-1, ++ for a possibility of N_BUCKETS. Among those, N_BUCKETS_USED buckets ++ are not empty, there are N_ENTRIES active entries in the table. */ ++ struct hash_entry *bucket; ++ struct hash_entry *bucket_limit; ++ unsigned n_buckets; ++ unsigned n_buckets_used; ++ unsigned n_entries; ++ ++ /* Tuning arguments, kept in a physicaly separate structure. */ ++ const Hash_tuning *tuning; ++ ++ /* Three functions are given to `hash_initialize', see the documentation ++ block for this function. In a word, HASHER randomizes a user entry ++ into a number up from 0 up to some maximum minus 1; COMPARATOR returns ++ true if two user entries compare equally; and DATA_FREER is the cleanup ++ function for a user entry. */ ++ Hash_hasher hasher; ++ Hash_comparator comparator; ++ Hash_data_freer data_freer; ++ ++ /* A linked list of freed struct hash_entry structs. */ ++ struct hash_entry *free_entry_list; ++ ++#if USE_OBSTACK ++ /* Whenever obstacks are used, it is possible to allocate all overflowed ++ entries into a single stack, so they all can be freed in a single ++ operation. It is not clear if the speedup is worth the trouble. */ ++ struct obstack entry_stack; ++#endif ++ }; ++ ++/* A hash table contains many internal entries, each holding a pointer to ++ some user provided data (also called a user entry). An entry indistinctly ++ refers to both the internal entry and its associated user entry. A user ++ entry contents may be hashed by a randomization function (the hashing ++ function, or just `hasher' for short) into a number (or `slot') between 0 ++ and the current table size. At each slot position in the hash table, ++ starts a linked chain of entries for which the user data all hash to this ++ slot. A bucket is the collection of all entries hashing to the same slot. ++ ++ A good `hasher' function will distribute entries rather evenly in buckets. ++ In the ideal case, the length of each bucket is roughly the number of ++ entries divided by the table size. Finding the slot for a data is usually ++ done in constant time by the `hasher', and the later finding of a precise ++ entry is linear in time with the size of the bucket. Consequently, a ++ larger hash table size (that is, a larger number of buckets) is prone to ++ yielding shorter chains, *given* the `hasher' function behaves properly. ++ ++ Long buckets slow down the lookup algorithm. One might use big hash table ++ sizes in hope to reduce the average length of buckets, but this might ++ become inordinate, as unused slots in the hash table take some space. The ++ best bet is to make sure you are using a good `hasher' function (beware ++ that those are not that easy to write! :-), and to use a table size ++ larger than the actual number of entries. */ ++ ++/* If an insertion makes the ratio of nonempty buckets to table size larger ++ than the growth threshold (a number between 0.0 and 1.0), then increase ++ the table size by multiplying by the growth factor (a number greater than ++ 1.0). The growth threshold defaults to 0.8, and the growth factor ++ defaults to 1.414, meaning that the table will have doubled its size ++ every second time 80% of the buckets get used. */ ++#define DEFAULT_GROWTH_THRESHOLD 0.8 ++#define DEFAULT_GROWTH_FACTOR 1.414 ++ ++/* If a deletion empties a bucket and causes the ratio of used buckets to ++ table size to become smaller than the shrink threshold (a number between ++ 0.0 and 1.0), then shrink the table by multiplying by the shrink factor (a ++ number greater than the shrink threshold but smaller than 1.0). The shrink ++ threshold and factor default to 0.0 and 1.0, meaning that the table never ++ shrinks. */ ++#define DEFAULT_SHRINK_THRESHOLD 0.0 ++#define DEFAULT_SHRINK_FACTOR 1.0 ++ ++/* Use this to initialize or reset a TUNING structure to ++ some sensible values. */ ++static const Hash_tuning default_tuning = ++ { ++ DEFAULT_SHRINK_THRESHOLD, ++ DEFAULT_SHRINK_FACTOR, ++ DEFAULT_GROWTH_THRESHOLD, ++ DEFAULT_GROWTH_FACTOR, ++ false ++ }; ++ ++/* Information and lookup. */ ++ ++/* The following few functions provide information about the overall hash ++ table organization: the number of entries, number of buckets and maximum ++ length of buckets. */ ++ ++/* Return the number of buckets in the hash table. The table size, the total ++ number of buckets (used plus unused), or the maximum number of slots, are ++ the same quantity. */ ++ ++unsigned ++hash_get_n_buckets (const Hash_table *table) ++{ ++ return table->n_buckets; ++} ++ ++/* Return the number of slots in use (non-empty buckets). */ ++ ++unsigned ++hash_get_n_buckets_used (const Hash_table *table) ++{ ++ return table->n_buckets_used; ++} ++ ++/* Return the number of active entries. */ ++ ++unsigned ++hash_get_n_entries (const Hash_table *table) ++{ ++ return table->n_entries; ++} ++ ++/* Return the length of the longest chain (bucket). */ ++ ++unsigned ++hash_get_max_bucket_length (const Hash_table *table) ++{ ++ struct hash_entry *bucket; ++ unsigned max_bucket_length = 0; ++ ++ for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) ++ { ++ if (bucket->data) ++ { ++ struct hash_entry *cursor = bucket; ++ unsigned bucket_length = 1; ++ ++ while (cursor = cursor->next, cursor) ++ bucket_length++; ++ ++ if (bucket_length > max_bucket_length) ++ max_bucket_length = bucket_length; ++ } ++ } ++ ++ return max_bucket_length; ++} ++ ++/* Do a mild validation of a hash table, by traversing it and checking two ++ statistics. */ ++ ++bool ++hash_table_ok (const Hash_table *table) ++{ ++ struct hash_entry *bucket; ++ unsigned n_buckets_used = 0; ++ unsigned n_entries = 0; ++ ++ for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) ++ { ++ if (bucket->data) ++ { ++ struct hash_entry *cursor = bucket; ++ ++ /* Count bucket head. */ ++ n_buckets_used++; ++ n_entries++; ++ ++ /* Count bucket overflow. */ ++ while (cursor = cursor->next, cursor) ++ n_entries++; ++ } ++ } ++ ++ if (n_buckets_used == table->n_buckets_used && n_entries == table->n_entries) ++ return true; ++ ++ return false; ++} ++ ++void ++hash_print_statistics (const Hash_table *table, FILE *stream) ++{ ++ unsigned n_entries = hash_get_n_entries (table); ++ unsigned n_buckets = hash_get_n_buckets (table); ++ unsigned n_buckets_used = hash_get_n_buckets_used (table); ++ unsigned max_bucket_length = hash_get_max_bucket_length (table); ++ ++ fprintf (stream, "# entries: %u\n", n_entries); ++ fprintf (stream, "# buckets: %u\n", n_buckets); ++ fprintf (stream, "# buckets used: %u (%.2f%%)\n", n_buckets_used, ++ (100.0 * n_buckets_used) / n_buckets); ++ fprintf (stream, "max bucket length: %u\n", max_bucket_length); ++} ++ ++/* If ENTRY matches an entry already in the hash table, return the ++ entry from the table. Otherwise, return NULL. */ ++ ++void * ++hash_lookup (const Hash_table *table, const void *entry) ++{ ++ struct hash_entry *bucket ++ = table->bucket + table->hasher (entry, table->n_buckets); ++ struct hash_entry *cursor; ++ ++ if (! (bucket < table->bucket_limit)) ++ abort (); ++ ++ if (bucket->data == NULL) ++ return NULL; ++ ++ for (cursor = bucket; cursor; cursor = cursor->next) ++ if (table->comparator (entry, cursor->data)) ++ return cursor->data; ++ ++ return NULL; ++} ++ ++/* Walking. */ ++ ++/* The functions in this page traverse the hash table and process the ++ contained entries. For the traversal to work properly, the hash table ++ should not be resized nor modified while any particular entry is being ++ processed. In particular, entries should not be added or removed. */ ++ ++/* Return the first data in the table, or NULL if the table is empty. */ ++ ++void * ++hash_get_first (const Hash_table *table) ++{ ++ struct hash_entry *bucket; ++ ++ if (table->n_entries == 0) ++ return NULL; ++ ++ for (bucket = table->bucket; ; bucket++) ++ if (! (bucket < table->bucket_limit)) ++ abort (); ++ else if (bucket->data) ++ return bucket->data; ++} ++ ++/* Return the user data for the entry following ENTRY, where ENTRY has been ++ returned by a previous call to either `hash_get_first' or `hash_get_next'. ++ Return NULL if there are no more entries. */ ++ ++void * ++hash_get_next (const Hash_table *table, const void *entry) ++{ ++ struct hash_entry *bucket ++ = table->bucket + table->hasher (entry, table->n_buckets); ++ struct hash_entry *cursor; ++ ++ if (! (bucket < table->bucket_limit)) ++ abort (); ++ ++ /* Find next entry in the same bucket. */ ++ for (cursor = bucket; cursor; cursor = cursor->next) ++ if (cursor->data == entry && cursor->next) ++ return cursor->next->data; ++ ++ /* Find first entry in any subsequent bucket. */ ++ while (++bucket < table->bucket_limit) ++ if (bucket->data) ++ return bucket->data; ++ ++ /* None found. */ ++ return NULL; ++} ++ ++/* Fill BUFFER with pointers to active user entries in the hash table, then ++ return the number of pointers copied. Do not copy more than BUFFER_SIZE ++ pointers. */ ++ ++unsigned ++hash_get_entries (const Hash_table *table, void **buffer, ++ unsigned buffer_size) ++{ ++ unsigned counter = 0; ++ struct hash_entry *bucket; ++ struct hash_entry *cursor; ++ ++ for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) ++ { ++ if (bucket->data) ++ { ++ for (cursor = bucket; cursor; cursor = cursor->next) ++ { ++ if (counter >= buffer_size) ++ return counter; ++ buffer[counter++] = cursor->data; ++ } ++ } ++ } ++ ++ return counter; ++} ++ ++/* Call a PROCESSOR function for each entry of a hash table, and return the ++ number of entries for which the processor function returned success. A ++ pointer to some PROCESSOR_DATA which will be made available to each call to ++ the processor function. The PROCESSOR accepts two arguments: the first is ++ the user entry being walked into, the second is the value of PROCESSOR_DATA ++ as received. The walking continue for as long as the PROCESSOR function ++ returns nonzero. When it returns zero, the walking is interrupted. */ ++ ++unsigned ++hash_do_for_each (const Hash_table *table, Hash_processor processor, ++ void *processor_data) ++{ ++ unsigned counter = 0; ++ struct hash_entry *bucket; ++ struct hash_entry *cursor; ++ ++ for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) ++ { ++ if (bucket->data) ++ { ++ for (cursor = bucket; cursor; cursor = cursor->next) ++ { ++ if (!(*processor) (cursor->data, processor_data)) ++ return counter; ++ counter++; ++ } ++ } ++ } ++ ++ return counter; ++} ++ ++/* Allocation and clean-up. */ ++ ++/* Return a hash index for a NUL-terminated STRING between 0 and N_BUCKETS-1. ++ This is a convenience routine for constructing other hashing functions. */ ++ ++#if USE_DIFF_HASH ++ ++/* About hashings, Paul Eggert writes to me (FP), on 1994-01-01: "Please see ++ B. J. McKenzie, R. Harries & T. Bell, Selecting a hashing algorithm, ++ Software--practice & experience 20, 2 (Feb 1990), 209-224. Good hash ++ algorithms tend to be domain-specific, so what's good for [diffutils'] io.c ++ may not be good for your application." */ ++ ++unsigned ++hash_string (const char *string, unsigned n_buckets) ++{ ++# define ROTATE_LEFT(Value, Shift) \ ++ ((Value) << (Shift) | (Value) >> ((sizeof (unsigned) * CHAR_BIT) - (Shift))) ++# define HASH_ONE_CHAR(Value, Byte) \ ++ ((Byte) + ROTATE_LEFT (Value, 7)) ++ ++ unsigned value = 0; ++ ++ for (; *string; string++) ++ value = HASH_ONE_CHAR (value, *(const unsigned char *) string); ++ return value % n_buckets; ++ ++# undef ROTATE_LEFT ++# undef HASH_ONE_CHAR ++} ++ ++#else /* not USE_DIFF_HASH */ ++ ++/* This one comes from `recode', and performs a bit better than the above as ++ per a few experiments. It is inspired from a hashing routine found in the ++ very old Cyber `snoop', itself written in typical Greg Mansfield style. ++ (By the way, what happened to this excellent man? Is he still alive?) */ ++ ++unsigned ++hash_string (const char *string, unsigned n_buckets) ++{ ++ unsigned value = 0; ++ ++ while (*string) ++ value = ((value * 31 + (int) *(const unsigned char *) string++) ++ % n_buckets); ++ return value; ++} ++ ++#endif /* not USE_DIFF_HASH */ ++ ++/* Return true if CANDIDATE is a prime number. CANDIDATE should be an odd ++ number at least equal to 11. */ ++ ++static bool ++is_prime (unsigned long candidate) ++{ ++ unsigned long divisor = 3; ++ unsigned long square = divisor * divisor; ++ ++ while (square < candidate && (candidate % divisor)) ++ { ++ divisor++; ++ square += 4 * divisor; ++ divisor++; ++ } ++ ++ return (candidate % divisor ? true : false); ++} ++ ++/* Round a given CANDIDATE number up to the nearest prime, and return that ++ prime. Primes lower than 10 are merely skipped. */ ++ ++static unsigned long ++next_prime (unsigned long candidate) ++{ ++ /* Skip small primes. */ ++ if (candidate < 10) ++ candidate = 10; ++ ++ /* Make it definitely odd. */ ++ candidate |= 1; ++ ++ while (!is_prime (candidate)) ++ candidate += 2; ++ ++ return candidate; ++} ++ ++void ++hash_reset_tuning (Hash_tuning *tuning) ++{ ++ *tuning = default_tuning; ++} ++ ++/* For the given hash TABLE, check the user supplied tuning structure for ++ reasonable values, and return true if there is no gross error with it. ++ Otherwise, definitively reset the TUNING field to some acceptable default ++ in the hash table (that is, the user loses the right of further modifying ++ tuning arguments), and return false. */ ++ ++static bool ++check_tuning (Hash_table *table) ++{ ++ const Hash_tuning *tuning = table->tuning; ++ ++ if (tuning->growth_threshold > 0.0 ++ && tuning->growth_threshold < 1.0 ++ && tuning->growth_factor > 1.0 ++ && tuning->shrink_threshold >= 0.0 ++ && tuning->shrink_threshold < 1.0 ++ && tuning->shrink_factor > tuning->shrink_threshold ++ && tuning->shrink_factor <= 1.0 ++ && tuning->shrink_threshold < tuning->growth_threshold) ++ return true; ++ ++ table->tuning = &default_tuning; ++ return false; ++} ++ ++/* Allocate and return a new hash table, or NULL upon failure. The initial ++ number of buckets is automatically selected so as to _guarantee_ that you ++ may insert at least CANDIDATE different user entries before any growth of ++ the hash table size occurs. So, if have a reasonably tight a-priori upper ++ bound on the number of entries you intend to insert in the hash table, you ++ may save some table memory and insertion time, by specifying it here. If ++ the IS_N_BUCKETS field of the TUNING structure is true, the CANDIDATE ++ argument has its meaning changed to the wanted number of buckets. ++ ++ TUNING points to a structure of user-supplied values, in case some fine ++ tuning is wanted over the default behavior of the hasher. If TUNING is ++ NULL, the default tuning parameters are used instead. ++ ++ The user-supplied HASHER function should be provided. It accepts two ++ arguments ENTRY and TABLE_SIZE. It computes, by hashing ENTRY contents, a ++ slot number for that entry which should be in the range 0..TABLE_SIZE-1. ++ This slot number is then returned. ++ ++ The user-supplied COMPARATOR function should be provided. It accepts two ++ arguments pointing to user data, it then returns true for a pair of entries ++ that compare equal, or false otherwise. This function is internally called ++ on entries which are already known to hash to the same bucket index. ++ ++ The user-supplied DATA_FREER function, when not NULL, may be later called ++ with the user data as an argument, just before the entry containing the ++ data gets freed. This happens from within `hash_free' or `hash_clear'. ++ You should specify this function only if you want these functions to free ++ all of your `data' data. This is typically the case when your data is ++ simply an auxiliary struct that you have malloc'd to aggregate several ++ values. */ ++ ++Hash_table * ++hash_initialize (unsigned candidate, const Hash_tuning *tuning, ++ Hash_hasher hasher, Hash_comparator comparator, ++ Hash_data_freer data_freer) ++{ ++ Hash_table *table; ++ struct hash_entry *bucket; ++ ++ if (hasher == NULL || comparator == NULL) ++ return NULL; ++ ++ table = (Hash_table *) malloc (sizeof (Hash_table)); ++ if (table == NULL) ++ return NULL; ++ ++ if (!tuning) ++ tuning = &default_tuning; ++ table->tuning = tuning; ++ if (!check_tuning (table)) ++ { ++ /* Fail if the tuning options are invalid. This is the only occasion ++ when the user gets some feedback about it. Once the table is created, ++ if the user provides invalid tuning options, we silently revert to ++ using the defaults, and ignore further request to change the tuning ++ options. */ ++ free (table); ++ return NULL; ++ } ++ ++ table->n_buckets ++ = next_prime (tuning->is_n_buckets ? candidate ++ : (unsigned) (candidate / tuning->growth_threshold)); ++ ++ table->bucket = (struct hash_entry *) ++ malloc (table->n_buckets * sizeof (struct hash_entry)); ++ if (table->bucket == NULL) ++ { ++ free (table); ++ return NULL; ++ } ++ table->bucket_limit = table->bucket + table->n_buckets; ++ ++ for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) ++ { ++ bucket->data = NULL; ++ bucket->next = NULL; ++ } ++ table->n_buckets_used = 0; ++ table->n_entries = 0; ++ ++ table->hasher = hasher; ++ table->comparator = comparator; ++ table->data_freer = data_freer; ++ ++ table->free_entry_list = NULL; ++#if USE_OBSTACK ++ obstack_init (&table->entry_stack); ++#endif ++ return table; ++} ++ ++/* Make all buckets empty, placing any chained entries on the free list. ++ Apply the user-specified function data_freer (if any) to the datas of any ++ affected entries. */ ++ ++void ++hash_clear (Hash_table *table) ++{ ++ struct hash_entry *bucket; ++ ++ for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) ++ { ++ if (bucket->data) ++ { ++ struct hash_entry *cursor; ++ struct hash_entry *next; ++ ++ /* Free the bucket overflow. */ ++ for (cursor = bucket->next; cursor; cursor = next) ++ { ++ if (table->data_freer) ++ (*table->data_freer) (cursor->data); ++ cursor->data = NULL; ++ ++ next = cursor->next; ++ /* Relinking is done one entry at a time, as it is to be expected ++ that overflows are either rare or short. */ ++ cursor->next = table->free_entry_list; ++ table->free_entry_list = cursor; ++ } ++ ++ /* Free the bucket head. */ ++ if (table->data_freer) ++ (*table->data_freer) (bucket->data); ++ bucket->data = NULL; ++ bucket->next = NULL; ++ } ++ } ++ ++ table->n_buckets_used = 0; ++ table->n_entries = 0; ++} ++ ++/* Reclaim all storage associated with a hash table. If a data_freer ++ function has been supplied by the user when the hash table was created, ++ this function applies it to the data of each entry before freeing that ++ entry. */ ++ ++void ++hash_free (Hash_table *table) ++{ ++ struct hash_entry *bucket; ++ struct hash_entry *cursor; ++ struct hash_entry *next; ++ ++ /* Call the user data_freer function. */ ++ if (table->data_freer && table->n_entries) ++ { ++ for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) ++ { ++ if (bucket->data) ++ { ++ for (cursor = bucket; cursor; cursor = cursor->next) ++ { ++ (*table->data_freer) (cursor->data); ++ } ++ } ++ } ++ } ++ ++#if USE_OBSTACK ++ ++ obstack_free (&table->entry_stack, NULL); ++ ++#else ++ ++ /* Free all bucket overflowed entries. */ ++ for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) ++ { ++ for (cursor = bucket->next; cursor; cursor = next) ++ { ++ next = cursor->next; ++ free (cursor); ++ } ++ } ++ ++ /* Also reclaim the internal list of previously freed entries. */ ++ for (cursor = table->free_entry_list; cursor; cursor = next) ++ { ++ next = cursor->next; ++ free (cursor); ++ } ++ ++#endif ++ ++ /* Free the remainder of the hash table structure. */ ++ free (table->bucket); ++ free (table); ++} ++ ++/* Insertion and deletion. */ ++ ++/* Get a new hash entry for a bucket overflow, possibly by reclying a ++ previously freed one. If this is not possible, allocate a new one. */ ++ ++static struct hash_entry * ++allocate_entry (Hash_table *table) ++{ ++ struct hash_entry *new; ++ ++ if (table->free_entry_list) ++ { ++ new = table->free_entry_list; ++ table->free_entry_list = new->next; ++ } ++ else ++ { ++#if USE_OBSTACK ++ new = (struct hash_entry *) ++ obstack_alloc (&table->entry_stack, sizeof (struct hash_entry)); ++#else ++ new = (struct hash_entry *) malloc (sizeof (struct hash_entry)); ++#endif ++ } ++ ++ return new; ++} ++ ++/* Free a hash entry which was part of some bucket overflow, ++ saving it for later recycling. */ ++ ++static void ++free_entry (Hash_table *table, struct hash_entry *entry) ++{ ++ entry->data = NULL; ++ entry->next = table->free_entry_list; ++ table->free_entry_list = entry; ++} ++ ++/* This private function is used to help with insertion and deletion. When ++ ENTRY matches an entry in the table, return a pointer to the corresponding ++ user data and set *BUCKET_HEAD to the head of the selected bucket. ++ Otherwise, return NULL. When DELETE is true and ENTRY matches an entry in ++ the table, unlink the matching entry. */ ++ ++static void * ++hash_find_entry (Hash_table *table, const void *entry, ++ struct hash_entry **bucket_head, bool delete) ++{ ++ struct hash_entry *bucket ++ = table->bucket + table->hasher (entry, table->n_buckets); ++ struct hash_entry *cursor; ++ ++ if (! (bucket < table->bucket_limit)) ++ abort (); ++ ++ *bucket_head = bucket; ++ ++ /* Test for empty bucket. */ ++ if (bucket->data == NULL) ++ return NULL; ++ ++ /* See if the entry is the first in the bucket. */ ++ if ((*table->comparator) (entry, bucket->data)) ++ { ++ void *data = bucket->data; ++ ++ if (delete) ++ { ++ if (bucket->next) ++ { ++ struct hash_entry *next = bucket->next; ++ ++ /* Bump the first overflow entry into the bucket head, then save ++ the previous first overflow entry for later recycling. */ ++ *bucket = *next; ++ free_entry (table, next); ++ } ++ else ++ { ++ bucket->data = NULL; ++ } ++ } ++ ++ return data; ++ } ++ ++ /* Scan the bucket overflow. */ ++ for (cursor = bucket; cursor->next; cursor = cursor->next) ++ { ++ if ((*table->comparator) (entry, cursor->next->data)) ++ { ++ void *data = cursor->next->data; ++ ++ if (delete) ++ { ++ struct hash_entry *next = cursor->next; ++ ++ /* Unlink the entry to delete, then save the freed entry for later ++ recycling. */ ++ cursor->next = next->next; ++ free_entry (table, next); ++ } ++ ++ return data; ++ } ++ } ++ ++ /* No entry found. */ ++ return NULL; ++} ++ ++/* For an already existing hash table, change the number of buckets through ++ specifying CANDIDATE. The contents of the hash table are preserved. The ++ new number of buckets is automatically selected so as to _guarantee_ that ++ the table may receive at least CANDIDATE different user entries, including ++ those already in the table, before any other growth of the hash table size ++ occurs. If TUNING->IS_N_BUCKETS is true, then CANDIDATE specifies the ++ exact number of buckets desired. */ ++ ++bool ++hash_rehash (Hash_table *table, unsigned candidate) ++{ ++ Hash_table *new_table; ++ struct hash_entry *bucket; ++ struct hash_entry *cursor; ++ struct hash_entry *next; ++ ++ new_table = hash_initialize (candidate, table->tuning, table->hasher, ++ table->comparator, table->data_freer); ++ if (new_table == NULL) ++ return false; ++ ++ /* Merely reuse the extra old space into the new table. */ ++#if USE_OBSTACK ++ obstack_free (&new_table->entry_stack, NULL); ++ new_table->entry_stack = table->entry_stack; ++#endif ++ new_table->free_entry_list = table->free_entry_list; ++ ++ for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) ++ if (bucket->data) ++ for (cursor = bucket; cursor; cursor = next) ++ { ++ void *data = cursor->data; ++ struct hash_entry *new_bucket ++ = (new_table->bucket ++ + new_table->hasher (data, new_table->n_buckets)); ++ ++ if (! (new_bucket < new_table->bucket_limit)) ++ abort (); ++ ++ next = cursor->next; ++ ++ if (new_bucket->data) ++ { ++ if (cursor == bucket) ++ { ++ /* Allocate or recycle an entry, when moving from a bucket ++ header into a bucket overflow. */ ++ struct hash_entry *new_entry = allocate_entry (new_table); ++ ++ if (new_entry == NULL) ++ return false; ++ ++ new_entry->data = data; ++ new_entry->next = new_bucket->next; ++ new_bucket->next = new_entry; ++ } ++ else ++ { ++ /* Merely relink an existing entry, when moving from a ++ bucket overflow into a bucket overflow. */ ++ cursor->next = new_bucket->next; ++ new_bucket->next = cursor; ++ } ++ } ++ else ++ { ++ /* Free an existing entry, when moving from a bucket ++ overflow into a bucket header. Also take care of the ++ simple case of moving from a bucket header into a bucket ++ header. */ ++ new_bucket->data = data; ++ new_table->n_buckets_used++; ++ if (cursor != bucket) ++ free_entry (new_table, cursor); ++ } ++ } ++ ++ free (table->bucket); ++ table->bucket = new_table->bucket; ++ table->bucket_limit = new_table->bucket_limit; ++ table->n_buckets = new_table->n_buckets; ++ table->n_buckets_used = new_table->n_buckets_used; ++ table->free_entry_list = new_table->free_entry_list; ++ /* table->n_entries already holds its value. */ ++#if USE_OBSTACK ++ table->entry_stack = new_table->entry_stack; ++#endif ++ free (new_table); ++ ++ return true; ++} ++ ++/* If ENTRY matches an entry already in the hash table, return the pointer ++ to the entry from the table. Otherwise, insert ENTRY and return ENTRY. ++ Return NULL if the storage required for insertion cannot be allocated. */ ++ ++void * ++hash_insert (Hash_table *table, const void *entry) ++{ ++ void *data; ++ struct hash_entry *bucket; ++ ++ /* The caller cannot insert a NULL entry. */ ++ if (! entry) ++ abort (); ++ ++ /* If there's a matching entry already in the table, return that. */ ++ if ((data = hash_find_entry (table, entry, &bucket, false)) != NULL) ++ return data; ++ ++ /* ENTRY is not matched, it should be inserted. */ ++ ++ if (bucket->data) ++ { ++ struct hash_entry *new_entry = allocate_entry (table); ++ ++ if (new_entry == NULL) ++ return NULL; ++ ++ /* Add ENTRY in the overflow of the bucket. */ ++ ++ new_entry->data = (void *) entry; ++ new_entry->next = bucket->next; ++ bucket->next = new_entry; ++ table->n_entries++; ++ return (void *) entry; ++ } ++ ++ /* Add ENTRY right in the bucket head. */ ++ ++ bucket->data = (void *) entry; ++ table->n_entries++; ++ table->n_buckets_used++; ++ ++ /* If the growth threshold of the buckets in use has been reached, increase ++ the table size and rehash. There's no point in checking the number of ++ entries: if the hashing function is ill-conditioned, rehashing is not ++ likely to improve it. */ ++ ++ if (table->n_buckets_used ++ > table->tuning->growth_threshold * table->n_buckets) ++ { ++ /* Check more fully, before starting real work. If tuning arguments ++ became invalid, the second check will rely on proper defaults. */ ++ check_tuning (table); ++ if (table->n_buckets_used ++ > table->tuning->growth_threshold * table->n_buckets) ++ { ++ const Hash_tuning *tuning = table->tuning; ++ unsigned candidate ++ = (unsigned) (tuning->is_n_buckets ++ ? (table->n_buckets * tuning->growth_factor) ++ : (table->n_buckets * tuning->growth_factor ++ * tuning->growth_threshold)); ++ ++ /* If the rehash fails, arrange to return NULL. */ ++ if (!hash_rehash (table, candidate)) ++ entry = NULL; ++ } ++ } ++ ++ return (void *) entry; ++} ++ ++/* If ENTRY is already in the table, remove it and return the just-deleted ++ data (the user may want to deallocate its storage). If ENTRY is not in the ++ table, don't modify the table and return NULL. */ ++ ++void * ++hash_delete (Hash_table *table, const void *entry) ++{ ++ void *data; ++ struct hash_entry *bucket; ++ ++ data = hash_find_entry (table, entry, &bucket, true); ++ if (!data) ++ return NULL; ++ ++ table->n_entries--; ++ if (!bucket->data) ++ { ++ table->n_buckets_used--; ++ ++ /* If the shrink threshold of the buckets in use has been reached, ++ rehash into a smaller table. */ ++ ++ if (table->n_buckets_used ++ < table->tuning->shrink_threshold * table->n_buckets) ++ { ++ /* Check more fully, before starting real work. If tuning arguments ++ became invalid, the second check will rely on proper defaults. */ ++ check_tuning (table); ++ if (table->n_buckets_used ++ < table->tuning->shrink_threshold * table->n_buckets) ++ { ++ const Hash_tuning *tuning = table->tuning; ++ unsigned candidate ++ = (unsigned) (tuning->is_n_buckets ++ ? table->n_buckets * tuning->shrink_factor ++ : (table->n_buckets * tuning->shrink_factor ++ * tuning->growth_threshold)); ++ ++ hash_rehash (table, candidate); ++ } ++ } ++ } ++ ++ return data; ++} ++ ++/* Testing. */ ++ ++#if TESTING ++ ++void ++hash_print (const Hash_table *table) ++{ ++ struct hash_entry *bucket; ++ ++ for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) ++ { ++ struct hash_entry *cursor; ++ ++ if (bucket) ++ printf ("%d:\n", bucket - table->bucket); ++ ++ for (cursor = bucket; cursor; cursor = cursor->next) ++ { ++ char *s = (char *) cursor->data; ++ /* FIXME */ ++ if (s) ++ printf (" %s\n", s); ++ } ++ } ++} ++ ++#endif /* TESTING */ +Index: patch-2.5.9/hash.h +=================================================================== +--- /dev/null ++++ patch-2.5.9/hash.h +@@ -0,0 +1,93 @@ ++/* hash - hashing table processing. ++ Copyright (C) 1998, 1999, 2001 Free Software Foundation, Inc. ++ Written by Jim Meyering , 1998. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2, 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 General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software Foundation, ++ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ ++ ++/* A generic hash table package. */ ++ ++/* Make sure USE_OBSTACK is defined to 1 if you want the allocator to use ++ obstacks instead of malloc, and recompile `hash.c' with same setting. */ ++ ++#ifndef HASH_H_ ++# define HASH_H_ ++ ++# ifndef PARAMS ++# if PROTOTYPES || __STDC__ ++# define PARAMS(Args) Args ++# else ++# define PARAMS(Args) () ++# endif ++# endif ++ ++typedef unsigned (*Hash_hasher) PARAMS ((const void *, unsigned)); ++typedef bool (*Hash_comparator) PARAMS ((const void *, const void *)); ++typedef void (*Hash_data_freer) PARAMS ((void *)); ++typedef bool (*Hash_processor) PARAMS ((void *, void *)); ++ ++struct hash_entry ++ { ++ void *data; ++ struct hash_entry *next; ++ }; ++ ++struct hash_tuning ++ { ++ /* This structure is mainly used for `hash_initialize', see the block ++ documentation of `hash_reset_tuning' for more complete comments. */ ++ ++ float shrink_threshold; /* ratio of used buckets to trigger a shrink */ ++ float shrink_factor; /* ratio of new smaller size to original size */ ++ float growth_threshold; /* ratio of used buckets to trigger a growth */ ++ float growth_factor; /* ratio of new bigger size to original size */ ++ bool is_n_buckets; /* if CANDIDATE really means table size */ ++ }; ++ ++typedef struct hash_tuning Hash_tuning; ++ ++struct hash_table; ++ ++typedef struct hash_table Hash_table; ++ ++/* Information and lookup. */ ++unsigned hash_get_n_buckets PARAMS ((const Hash_table *)); ++unsigned hash_get_n_buckets_used PARAMS ((const Hash_table *)); ++unsigned hash_get_n_entries PARAMS ((const Hash_table *)); ++unsigned hash_get_max_bucket_length PARAMS ((const Hash_table *)); ++bool hash_table_ok PARAMS ((const Hash_table *)); ++void hash_print_statistics PARAMS ((const Hash_table *, FILE *)); ++void *hash_lookup PARAMS ((const Hash_table *, const void *)); ++ ++/* Walking. */ ++void *hash_get_first PARAMS ((const Hash_table *)); ++void *hash_get_next PARAMS ((const Hash_table *, const void *)); ++unsigned hash_get_entries PARAMS ((const Hash_table *, void **, unsigned)); ++unsigned hash_do_for_each PARAMS ((const Hash_table *, Hash_processor, void *)); ++ ++/* Allocation and clean-up. */ ++unsigned hash_string PARAMS ((const char *, unsigned)); ++void hash_reset_tuning PARAMS ((Hash_tuning *)); ++Hash_table *hash_initialize PARAMS ((unsigned, const Hash_tuning *, ++ Hash_hasher, Hash_comparator, ++ Hash_data_freer)); ++void hash_clear PARAMS ((Hash_table *)); ++void hash_free PARAMS ((Hash_table *)); ++ ++/* Insertion and deletion. */ ++bool hash_rehash PARAMS ((Hash_table *, unsigned)); ++void *hash_insert PARAMS ((Hash_table *, const void *)); ++void *hash_delete PARAMS ((Hash_table *, const void *)); ++ ++#endif +Index: patch-2.5.9/m4/nanosecond_stat.m4 +=================================================================== +--- /dev/null ++++ patch-2.5.9/m4/nanosecond_stat.m4 +@@ -0,0 +1,35 @@ ++AC_DEFUN([ag_CHECK_NANOSECOND_STAT], ++ [AC_CACHE_CHECK([for nanosecond timestamps in struct stat], ++ [ac_cv_stat_nsec], ++ [AC_TRY_COMPILE( ++ [ ++ #include ++ #include ++ #include ++ struct stat st; ++ ], ++ [ return st.st_atimensec + st.st_mtimensec + st.st_ctimensec; ], ++ [ac_cv_stat_nsec=yes], ++ [ac_cv_stat_nsec=no]) ++ ]) ++ if test $ac_cv_stat_nsec = yes; then ++ AC_DEFINE(HAVE_STAT_NSEC, 1, [Define to 1 if struct stat has nanosecond timestamps.]) ++ fi ++ ++ AC_CACHE_CHECK([for nanosecond timestamps in struct stat], ++ [ac_cv_stat_timeval], ++ [AC_TRY_COMPILE( ++ [ ++ #include ++ #include ++ #include ++ #include ++ struct stat st; ++ ], ++ [ return st.st_atim.tv_nsec + st.st_mtim.tv_nsec + st.st_ctim.tv_nsec; ], ++ [ac_cv_stat_timeval=yes], ++ [ac_cv_stat_timeval=no]) ++ ]) ++ if test $ac_cv_stat_timeval = yes; then ++ AC_DEFINE(HAVE_STAT_TIMEVAL, 1, [Define to 1 if struct stat comtains struct timeval's.]) ++ fi]) diff --git a/trailing-cr-fix.diff b/trailing-cr-fix.diff new file mode 100644 index 0000000..1caeeb5 --- /dev/null +++ b/trailing-cr-fix.diff @@ -0,0 +1,61 @@ +From: "Paul Eggert" +Date: Wed, 2 Jul 2003 15:22:09 -0700 +Subject: Re: patch bug with carriage returns + + > From: Andreas Gruenbacher + > Date: Mon, 30 Jun 2003 13:43:33 +0200 + + > unified diffs with CRLF line endings are not recognized correctly + > anymore in 2.5.9. Here is a fix. + + Thanks for the bug report. Unfortunately that fix doesn't handle the + case correctly where a Unix file contains a trailing CR in a header + line that is output by "diff -p" (trailing CR and all). + + How about the following patch instead? + +--- patch-2.5.9.orig/pch.c 2003/05/20 14:03:17 1.44 ++++ patch-2.5.9/pch.c 2003/07/02 22:19:21 1.45 +@@ -1,6 +1,6 @@ + /* reading patches */ + +-/* $Id: pch.c,v 1.44 2003/05/20 14:03:17 eggert Exp $ */ ++/* $Id: pch.c,v 1.45 2003/07/02 22:19:21 eggert Exp $ */ + + /* Copyright (C) 1986, 1987, 1988 Larry Wall + +@@ -366,10 +366,16 @@ intuit_diff_type (void) + if (!stars_last_line && strnEQ(s, "*** ", 4)) + name[OLD] = fetchname (s+4, strippath, &p_timestamp[OLD]); + else if (strnEQ(s, "+++ ", 4)) ++ { + /* Swap with NEW below. */ + name[OLD] = fetchname (s+4, strippath, &p_timestamp[OLD]); ++ p_strip_trailing_cr = strip_trailing_cr; ++ } + else if (strnEQ(s, "Index:", 6)) ++ { + name[INDEX] = fetchname (s+6, strippath, (time_t *) 0); ++ p_strip_trailing_cr = strip_trailing_cr; ++ } + else if (strnEQ(s, "Prereq:", 7)) { + for (t = s + 7; ISSPACE ((unsigned char) *t); t++) + continue; +@@ -409,6 +415,7 @@ intuit_diff_type (void) + p_timestamp[NEW] = timestamp; + p_rfc934_nesting = (t - s) >> 1; + } ++ p_strip_trailing_cr = strip_trailing_cr; + } + } + if ((diff_type == NO_DIFF || diff_type == ED_DIFF) && +#--- patch-2.5.9.orig/pch.c 2003-06-30 13:28:39.000000000 +0200 +#+++ patch-2.5.9/pch.c 2003-06-30 13:28:03.000000000 +0200 +#@@ -440,6 +440,7 @@ +# if (s[0] == '+' && s[1] == '0' && !ISDIGIT (s[2])) +# p_says_nonexistent[NEW] = 1 + ! p_timestamp[NEW]; +# p_indent = indent; +#+ p_strip_trailing_cr = strip_trailing_cr; +# p_start = this_line; +# p_sline = p_input_line; +# retval = UNI_DIFF; diff --git a/unified-reject-files.diff b/unified-reject-files.diff new file mode 100644 index 0000000..a21ddf6 --- /dev/null +++ b/unified-reject-files.diff @@ -0,0 +1,305 @@ +Generate unified diff style reject files. Also include the C function names +in reject files whenever possible. + + $ cat > f.orig + < a() { + < 2 + < 3 + < + < 5 + < 6 + < } + + $ sed -e 's/5/5a/' f.orig > f + $ diff -U2 -p f.orig f > f.diff + $ sed -e 's/5/5a/' -e 's/6/6x/' f.orig > f + $ ./patch -F0 -s --no-backup-if-mismatch f --reject-unified < f.diff + > 1 out of 1 hunk FAILED -- saving rejects to file f.rej + + $ cat f.rej + > @@ -3,5 +3,5 @@ a() { + > 3 + > + > -5 + > +5a + > 6 + > } + + $ ./patch -F0 -s --no-backup-if-mismatch f < f.diff + > 1 out of 1 hunk FAILED -- saving rejects to file f.rej + + $ cat f.rej + > *************** a() { + > *** 3,7 **** + > 3 + > + > - 5 + > 6 + > } + > --- 3,7 ---- + > 3 + > + > + 5a + > 6 + > } + + $ diff -Nu -p /dev/null f.orig > f2.diff + $ ./patch -F0 -s --no-backup-if-mismatch f --reject-unified < f2.diff + > Patch attempted to create file f, which already exists. + > 1 out of 1 hunk FAILED -- saving rejects to file f.rej + + $ cat f.rej + > @@ -0,0 +1,7 @@ + > +a() { + > +2 + > +3 + > + + > +5 + > +6 + > +} + + $ rm -f f f.orig f.rej f.diff f2.diff + +Index: patch-2.5.9/pch.c +=================================================================== +--- patch-2.5.9.orig/pch.c ++++ patch-2.5.9/pch.c +@@ -68,6 +68,7 @@ static LINENUM p_sline; /* and the lin + static LINENUM p_hunk_beg; /* line number of current hunk */ + static LINENUM p_efake = -1; /* end of faked up lines--don't free */ + static LINENUM p_bfake = -1; /* beg of faked up lines */ ++static char *p_c_function; /* the C function a hunk is in */ + + enum nametype { OLD, NEW, INDEX, NONE }; + +@@ -888,6 +889,19 @@ another_hunk (enum diff difftype, bool r + next_intuit_at(line_beginning,p_input_line); + return chars_read == (size_t) -1 ? -1 : 0; + } ++ s = buf; ++ while (*s == '*') ++ s++; ++ if (*s == ' ') ++ { ++ p_c_function = s; ++ while (*s != '\n') ++ s++; ++ *s = '\0'; ++ p_c_function = savestr (p_c_function); ++ } ++ else ++ p_c_function = NULL; + p_hunk_beg = p_input_line + 1; + while (p_end < p_max) { + chars_read = get_line (); +@@ -1277,8 +1291,18 @@ another_hunk (enum diff difftype, bool r + else + p_repl_lines = 1; + if (*s == ' ') s++; +- if (*s != '@') ++ if (*s++ != '@') + malformed (); ++ if (*s++ == '@' && *s == ' ' && *s != '\0') ++ { ++ p_c_function = s; ++ while (*s != '\n') ++ s++; ++ *s = '\0'; ++ p_c_function = savestr (p_c_function); ++ } ++ else ++ p_c_function = NULL; + if (!p_ptrn_lines) + p_first++; /* do append rather than insert */ + if (!p_repl_lines) +@@ -1884,6 +1908,12 @@ pch_hunk_beg (void) + return p_hunk_beg; + } + ++char const * ++pch_c_function (void) ++{ ++ return p_c_function; ++} ++ + /* Is the newline-terminated line a valid `ed' command for patch + input? If so, return the command character; if not, return 0. + This accepts accepts just a subset of the valid commands, but it's +Index: patch-2.5.9/pch.h +=================================================================== +--- patch-2.5.9.orig/pch.h ++++ patch-2.5.9/pch.h +@@ -25,6 +25,7 @@ + LINENUM pch_end (void); + LINENUM pch_first (void); + LINENUM pch_hunk_beg (void); ++char const *pch_c_function (void); + LINENUM pch_newfirst (void); + LINENUM pch_prefix_context (void); + LINENUM pch_ptrn_lines (void); +Index: patch-2.5.9/patch.man +=================================================================== +--- patch-2.5.9.orig/patch.man ++++ patch-2.5.9/patch.man +@@ -517,6 +517,9 @@ instead of the default + .B \&.rej + file. + .TP ++\fB\*=reject\-unified\fP ++Produce unified reject files. The default is to produce context type reject files. ++.TP + \fB\-R\fP or \fB\*=reverse\fP + Assume that this patch was created with the old and new files swapped. + (Yes, I'm afraid that does happen occasionally, human nature being what it +Index: patch-2.5.9/common.h +=================================================================== +--- patch-2.5.9.orig/common.h ++++ patch-2.5.9/common.h +@@ -146,6 +146,7 @@ XTERN int invc; + XTERN struct stat instat; + XTERN bool dry_run; + XTERN bool posixly_correct; ++XTERN bool unified_reject_files; + + XTERN char const *origprae; + XTERN char const *origbase; +Index: patch-2.5.9/patch.c +=================================================================== +--- patch-2.5.9.orig/patch.c ++++ patch-2.5.9/patch.c +@@ -522,6 +522,7 @@ static struct option const longopts[] = + {"no-backup-if-mismatch", no_argument, NULL, CHAR_MAX + 6}, + {"posix", no_argument, NULL, CHAR_MAX + 7}, + {"quoting-style", required_argument, NULL, CHAR_MAX + 8}, ++ {"unified-reject-files", no_argument, NULL, CHAR_MAX + 9}, + {NULL, no_argument, NULL, 0} + }; + +@@ -580,6 +581,7 @@ static char const *const option_help[] = + " --verbose Output extra information about the work being done.", + " --dry-run Do not actually change any files; just print what would happen.", + " --posix Conform to the POSIX standard.", ++" --unified-reject-files Create unified reject files.", + "", + " -d DIR --directory=DIR Change the working directory to DIR first.", + #if HAVE_SETMODE_DOS +@@ -779,6 +781,9 @@ get_some_switches (void) + (enum quoting_style) i); + } + break; ++ case CHAR_MAX + 9: ++ unified_reject_files = true; ++ break; + default: + usage (stderr, 2); + } +@@ -927,6 +932,24 @@ locate_hunk (LINENUM fuzz) + return 0; + } + ++static char * ++format_linerange (char rangebuf[LINENUM_LENGTH_BOUND*2 + 2], ++ LINENUM first, LINENUM lines) ++{ ++ if (lines == 1) ++ rangebuf = format_linenum (rangebuf, first); ++ else ++ { ++ char *rb; ++ rangebuf = format_linenum (rangebuf + LINENUM_LENGTH_BOUND + 1, lines); ++ rb = rangebuf-1; ++ rangebuf = format_linenum (rangebuf - LINENUM_LENGTH_BOUND - 1, ++ (lines > 0) ? first : 0); ++ *rb = ','; ++ } ++ return rangebuf; ++} ++ + /* We did not find the pattern, dump out the hunk so they can handle it. */ + + static void +@@ -943,8 +966,83 @@ abort_hunk (void) + (int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ****" : ""; + char const *minuses = + (int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ----" : " -----"; ++ char const *function = pch_c_function(); ++ if (function == NULL) ++ function = ""; ++ ++ if (unified_reject_files) ++ { ++ /* produce unified reject files */ ++ char rangebuf0[LINENUM_LENGTH_BOUND*2 + 2]; ++ char rangebuf1[LINENUM_LENGTH_BOUND*2 + 2]; ++ LINENUM j; ++ ++ /* Find the beginning of the remove and insert section. */ ++ for (j = 0; j <= pat_end; j++) ++ if (pch_char (j) == '=') ++ break; ++ for (i = j+1; i <= pat_end; i++) ++ if (pch_char (i) == '^') ++ break; ++ if (pch_char (0) != '*' || j > pat_end || i > pat_end+1) ++ fatal ("internal error in abort_hunk"); ++ i = 1; j++; ++ ++ /* @@ -from,lines +to,lines @@ */ ++ fprintf (rejfp, "@@ -%s +%s @@%s\n", ++ format_linerange (rangebuf0, oldfirst, pch_ptrn_lines()), ++ format_linerange (rangebuf1, newfirst, pch_repl_lines()), ++ function); ++ ++ while ( (i <= pat_end && pch_char (i) != '=') ++ || (j <= pat_end && pch_char (j) != '^')) ++ { ++ if (i <= pat_end ++ && (pch_char (i) == '-' || pch_char (i) == '!')) ++ { ++ fputc('-', rejfp); ++ pch_write_line (i++, rejfp); ++ } ++ else if (j <= pat_end ++ && (pch_char (j) == '+' || pch_char (j) == '!')) ++ { ++ fputc('+', rejfp); ++ pch_write_line (j++, rejfp); ++ } ++ else if ((i <= pat_end ++ && (pch_char (i) == ' ' || pch_char (i) == '\n')) && ++ (j > pat_end ++ || (pch_char (j) == ' ' || pch_char (j) == '\n'))) ++ { ++ /* Unless j is already past the end, lines i and j ++ must be equal here. */ ++ ++ if (pch_char (i) == ' ') ++ fputc(' ', rejfp); ++ pch_write_line (i++, rejfp); ++ if (j <= pat_end) ++ j++; ++ } ++ else if ((j <= pat_end && ++ (pch_char (j) == ' ' || pch_char (j) == '\n')) && ++ (pch_char (i) == '=')) ++ { ++ if (pch_char (j) == ' ') ++ fputc(' ', rejfp); ++ pch_write_line (j++, rejfp); ++ } ++ else ++ fatal ("internal error in abort_hunk"); ++ } ++ ++ if (ferror (rejfp)) ++ write_fatal (); ++ return; ++ } + +- fprintf(rejfp, "***************\n"); ++ /* produce context type reject files */ ++ ++ fprintf(rejfp, "***************%s\n", function); + for (i=0; i<=pat_end; i++) { + char numbuf0[LINENUM_LENGTH_BOUND + 1]; + char numbuf1[LINENUM_LENGTH_BOUND + 1];