[gnu.utils.bug] Problems with GNU diff3

merrick@UUNET.UU.NET (01/20/90)

Hello

In testing RCS merging using GNU diff3, I have encountered the following 
problems:

	* For some sets of input files, I get a core dump.

	* For some others, diff3 prints the following:

		diff3: internal: screwup in format of diff blocks

	* diff3's fatal errors (like the above) get printed on stdout


I was able to trace the core dumps to the fact that the program's internal
linked lists were not being NULL-terminated ie the "next" pointer at the
end of the list was never set to NULL. I fixed this.

I traced the second problem to an integrity check inside of copy_stringlist().
This function checks to see if a destination pointer already contains something
before copying over it. Part way through my test case it finds trash at this 
location, so it bubbles the failure back up the stack to make_3way_diff()
which aborts with the above error message. I haven't been able to figure out
why this is so (and I've spent too much time trying). In desperation I ifdef'd
out the checking code and to my surprise, everything now works (so far). I
modified the code so that defining "SKIPCHECKS" to the preprocessor 
ignores this checking code. This is obviously an unreliable hack...

Finally, I ensured that any error messages go to stderr (and don't wind up
in your editing script when disaster strikes).

I've included below my test cases to run against the original diff3 to 
illustrate the problems. The files "core[012]" cause diff3 to dump core 
when run as "diff3 core0 core1 core2".  The files "file[012]" cause the 
internal screwup error when run as "diff3 file0 file1 file2".

Also included is "diff3-viarfix", the diffs for my fixes. These fixes
include a patch supplied to me by Daniel Trinkle - trinkle@cs.purdue.edu - 
(Thanks!!!) to set the program's exit status to the number of overlaps 
detected - something needed by RCS merge to report to the user.

If someone comes up with the *real* fix to the "internal screwup", I'd
really appreciate it if you could mail it to me (no access to the news
right now...).


				Phillip Merrick {uunet!viar1!merrick}
				Viar and Co., Alexandria, VA.

#!/bin/sh
# shar:	Shell Archiver  (v1.22)
#
#	Run the following text with /bin/sh to create:
#	  core0
#	  core1
#	  core2
#	  file0
#	  file1
#	  file2
#	  diff3-viar-fix
#
sed 's/^X//' << 'SHAR_EOF' > core0 &&
XLeading chunk for file0.
X
XThis is the common chunk of text. Just one line.
X
XTrailing chunk for file0.
SHAR_EOF
chmod 0644 core0 || echo "restore of core0 fails"
sed 's/^X//' << 'SHAR_EOF' > core1 &&
XThis is the common chunk of text. Just one line.
SHAR_EOF
chmod 0644 core1 || echo "restore of core1 fails"
sed 's/^X//' << 'SHAR_EOF' > core2 &&
XThis is the leading chunk of text for file2.
XIt seems that to induce the error, this chunk
Xmust be larger than the leading chunk for file0.
X
XThis is the common chunk of text. Just one line.
X
XThis is the trailing chunk for file2.
SHAR_EOF
chmod 0644 core2 || echo "restore of core2 fails"
sed 's/^X//' << 'SHAR_EOF' > file0 &&
XB
X
XA
X
XY
SHAR_EOF
chmod 0644 file0 || echo "restore of file0 fails"
sed 's/^X//' << 'SHAR_EOF' > file1 &&
XA
SHAR_EOF
chmod 0644 file1 || echo "restore of file1 fails"
sed 's/^X//' << 'SHAR_EOF' > file2 &&
XC
X
XC
X
XA
X
XZ
SHAR_EOF
chmod 0644 file2 || echo "restore of file2 fails"
sed 's/^X//' << 'SHAR_EOF' > diff3-viar-fix &&
X*** diff3.c.orig	Fri Jul 21 18:09:40 1989
X--- diff3.c	Fri Jan 19 19:55:21 1990
X***************
X*** 32,37 ****
X--- 32,38 ----
X  #define bzero(s,n)	memset((s),0,(n))
X  
X  #ifndef XENIX
X+ #include <fcntl.h>
X  #define dup2(f,t)	(close(t),fcntl((f),F_DUPFD,(t)))
X  #endif
X  
X***************
X*** 192,198 ****
X  struct diff_block *process_diff ();
X  struct diff3_block *make_3way_diff ();
X  void output_diff3 ();
X! void output_diff3_edscript ();
X  void usage ();
X  
X  struct diff3_block *using_to_diff3_block ();
X--- 193,199 ----
X  struct diff_block *process_diff ();
X  struct diff3_block *make_3way_diff ();
X  void output_diff3 ();
X! int output_diff3_edscript ();
X  void usage ();
X  
X  struct diff3_block *using_to_diff3_block ();
X***************
X*** 229,234 ****
X--- 230,236 ----
X    int mapping [3];
X    int shiftmap;
X    int incompat;
X+   int overlap_count;
X    struct diff_block *thread1, *thread2;
X    struct diff3_block *diff;
X  
X***************
X*** 308,319 ****
X      }
X    diff = make_3way_diff (thread1, thread2);
X    if (edscript)
X!     output_diff3_edscript (stdout, diff, mapping, argv[optind],
X  			   argv[optind + 1], argv[optind + 2]);
X    else
X      output_diff3 (stdout, diff, mapping);
X  
X!   exit (0);
X  }
X        
X  /*
X--- 310,324 ----
X      }
X    diff = make_3way_diff (thread1, thread2);
X    if (edscript)
X!     overlap_count = output_diff3_edscript (stdout, diff, mapping, argv[optind],
X  			   argv[optind + 1], argv[optind + 2]);
X    else
X      output_diff3 (stdout, diff, mapping);
X  
X!   if (edscript)
X!     exit(overlap_count);
X!   else
X!     exit (0);
X  }
X        
X  /*
X***************
X*** 786,794 ****
X--- 791,801 ----
X    
X    while (copynum--)
X      {
X+ #ifndef SKIPCHECKS
X        if (*t)
X  	{ if (*fl != *tl || bcmp (*f, *t, *fl)) return 0; }
X        else
X+ #endif
X  	{ *t = *f ; *tl = *fl; }
X  
X        t++; f++; tl++; fl++;
X***************
X*** 809,814 ****
X--- 816,822 ----
X    int numlines;
X  
X    D3_TYPE (result) = ERROR;
X+   result->next = (struct diff3_block *) 0;
X  
X    /* Assign ranges */
X    D_LOWLINE (result, FILE0) = low0;
X***************
X*** 917,922 ****
X--- 925,931 ----
X        bptr = ALLOCATE (1, struct diff_block);
X        bptr->lines[0] = bptr->lines[1] = (char **) 0;
X        bptr->lengths[0] = bptr->lengths[1] = (int *) 0;
X+       bptr->next = (struct diff_block *) 0;
X        
X        dt = process_diff_control (&scan_diff, bptr);
X        if (dt == ERROR) fatal ("Bad format in diff output");
X***************
X*** 1285,1294 ****
X   * on which it works.  Thus file0, file1, and file2 are the filenames
X   * passed on the command line.
X   *
X   * See options.h for documentation on the global variables which this
X   * routine pays attention to.
X   */
X! void
X  output_diff3_edscript (outputfile, diff, mapping, file0, file1, file2)
X       FILE *outputfile;
X       struct diff3_block *diff;
X--- 1294,1305 ----
X   * on which it works.  Thus file0, file1, and file2 are the filenames
X   * passed on the command line.
X   *
X+  * Returns the number of overlaps.
X+  *
X   * See options.h for documentation on the global variables which this
X   * routine pays attention to.
X   */
X! int
X  output_diff3_edscript (outputfile, diff, mapping, file0, file1, file2)
X       FILE *outputfile;
X       struct diff3_block *diff;
X***************
X*** 1298,1307 ****
X--- 1309,1320 ----
X    int rev_mapping[3];
X    int i;
X    int leading_dot;
X+   int overlap_count;
X    struct diff3_block *newblock, *thisblock;
X    char *cp;
X  
X    leading_dot = 0;
X+   overlap_count = 0;
X  
X    for (i = 0; i < 3; i++)
X      rev_mapping [mapping [i]] = i;
X***************
X*** 1346,1351 ****
X--- 1359,1365 ----
X  	      putc ('\n', outputfile);
X  	    }
X  	  fprintf (outputfile, ">>>>>>> %s\n.\n", file2);
X+ 	  overlap_count++;
X  
X  	  /* Add in code to take care of leading dots, if necessary. */
X  	  if (leading_dot)
X***************
X*** 1419,1424 ****
X--- 1433,1439 ----
X  	}
X      }
X    if (finalwrite) fprintf (outputfile, "w\nq\n");
X+   return(overlap_count);
X  }
X  
X  /*
X***************
X*** 1476,1482 ****
X  fatal (string)
X       char *string;
X  {
X!   printf("%s: %s",argv0, string);
X    exit (1);
X  }
X  
X--- 1491,1497 ----
X  fatal (string)
X       char *string;
X  {
X!   fprintf(stderr, "%s: %s\n",argv0, string);
X    exit (1);
X  }
X  
SHAR_EOF
chmod 0664 diff3-viar-fix || echo "restore of diff3-viar-fix fails"
exit 0