Subject: v24i023: GNU Diff, version 1.15, Part08/08 Newsgroups: comp.sources.unix Approved: rsalz@uunet.UU.NET X-Checksum-Snefru: fe64831c 7eba4177 12fe286e cdb0fff2 Submitted-by: Paul Eggert Posting-number: Volume 24, Issue 23 Archive-name: gnudiff1.15/part08 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'diff3.c' <<'END_OF_FILE' X/* Three-way file comparison program (diff3) for Project GNU X Copyright (C) 1988, 1989 Free Software Foundation, Inc. X X This program is free software; you can redistribute it and/or modify X it under the terms of the GNU General Public License as published by X the Free Software Foundation; either version 1, or (at your option) X any later version. X X This program is distributed in the hope that it will be useful, X but WITHOUT ANY WARRANTY; without even the implied warranty of X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the X GNU General Public License for more details. X X You should have received a copy of the GNU General Public License X along with this program; if not, write to the Free Software X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ X X X/* Written by Randy Smith */ X X#ifdef __STDC__ X#define VOID void X#else X#define VOID char X#endif X X/* X * Include files. X */ X#include X#include X#include X#include X X#ifdef USG X#include X X/* Define needed BSD functions in terms of sysV library. */ X X#define bcmp(s1,s2,n) memcmp((s1),(s2),(n)) X#define bzero(s,n) memset((s),0,(n)) X X#ifndef XENIX X#define dup2(f,t) (close(t),fcntl((f),F_DUPFD,(t))) X#endif X X#define vfork fork X X#else /* not USG */ X#include X#endif /* not USG */ X X#ifndef WEXITSTATUS X#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) X#undef WIFEXITED /* Avoid 4.3BSD incompatibility with Posix. */ X#endif X#ifndef WIFEXITED X#define WIFEXITED(stat_val) (((stat_val) & 255) == 0) X#endif X X#ifdef sparc X/* vfork clobbers registers on the Sparc, so don't use it. */ X#define vfork fork X#endif X X/* X * Internal data structures and macros for the diff3 program; includes X * data structures for both diff3 diffs and normal diffs. X */ X X/* X * Different files within a diff X */ X#define FILE0 0 X#define FILE1 1 X#define FILE2 2 X X/* X * Three way diffs are build out of two two-way diffs; the file which X * the two two-way diffs share is: X */ X#define FILEC FILE0 X X/* The ranges are indexed by */ X#define START 0 X#define END 1 X Xenum diff_type { X ERROR, /* Should not be used */ X ADD, /* Two way diff add */ X CHANGE, /* Two way diff change */ X DELETE, /* Two way diff delete */ X DIFF_ALL, /* All three are different */ X DIFF_1ST, /* Only the first is different */ X DIFF_2ND, /* Only the second */ X DIFF_3RD /* Only the third */ X}; X X/* Two-way diff */ Xstruct diff_block { X int ranges[2][2]; /* Ranges are inclusive */ X char **lines[2]; /* The actual lines (may contain nulls) */ X int *lengths[2]; /* Line lengths (including newlines, if any) */ X struct diff_block *next; X}; X X/* Three-way diff */ X Xstruct diff3_block { X enum diff_type correspond; /* Type of diff */ X int ranges[3][2]; /* Ranges are inclusive */ X char **lines[3]; /* The actual lines (may contain nulls) */ X int *lengths[3]; /* Line lengths (including newlines, if any) */ X struct diff3_block *next; X}; X X/* X * Access the ranges on a diff block. X */ X#define D_LOWLINE(diff, filenum) \ X ((diff)->ranges[filenum][START]) X#define D_HIGHLINE(diff, filenum) \ X ((diff)->ranges[filenum][END]) X#define D_NUMLINES(diff, filenum) \ X (D_HIGHLINE((diff), (filenum)) - D_LOWLINE((diff), (filenum)) + 1) X X/* X * Access the line numbers in a file in a diff by relative line X * numbers (i.e. line number within the diff itself). Note that these X * are lvalues and can be used for assignment. X */ X#define D_RELNUM(diff, filenum, linenum) \ X (*((diff)->lines[filenum] + linenum)) X#define D_RELLEN(diff, filenum, linenum) \ X (*((diff)->lengths[filenum] + linenum)) X X/* X * And get at them directly, when that should be necessary. X */ X#define D_LINEARRAY(diff, filenum) \ X ((diff)->lines[filenum]) X#define D_LENARRAY(diff, filenum) \ X ((diff)->lengths[filenum]) X X/* X * Next block. X */ X#define D_NEXT(diff) ((diff)->next) X X/* X * Access the type of a diff3 block. X */ X#define D3_TYPE(diff) ((diff)->correspond) X X/* X * Line mappings based on diffs. The first maps off the top of the X * diff, the second off of the bottom. X */ X#define D_HIGH_MAPLINE(diff, fromfile, tofile, lineno) \ X ((lineno) \ X - D_HIGHLINE ((diff), (fromfile)) \ X + D_HIGHLINE ((diff), (tofile))) X X#define D_LOW_MAPLINE(diff, fromfile, tofile, lineno) \ X ((lineno) \ X - D_LOWLINE ((diff), (fromfile)) \ X + D_LOWLINE ((diff), (tofile))) X X/* X * General memory allocation function. X */ X#define ALLOCATE(number, type) \ X (type *) xmalloc ((number) * sizeof (type)) X X/* X * Options variables for flags set on command line. X * X * ALWAYS_TEXT: Treat all files as text files; never treat as binary. X * X * EDSCRIPT: Write out an ed script instead of the standard diff3 format. X * X * FLAGGING: Indicates that in the case of overlapping diffs (type X * DIFF_ALL), the lines which would normally be deleted from file 1 X * should be preserved with a special flagging mechanism. X * X * DONT_WRITE_OVERLAP: 1 if information for overlapping diffs should X * not be output. X * X * DONT_WRITE_SIMPLE: 1 if information for non-overlapping diffs X * should not be output. X * X * FINALWRITE: 1 if a :wq should be included at the end of the script X * to write out the file being edited. X * X * MERGE: output a merged file. X */ Xint always_text; Xint edscript; Xint flagging; Xint dont_write_overlap; Xint dont_write_simple; Xint finalwrite; Xint merge; X Xextern int optind; X Xchar *argv0; X X/* X * Forward function declarations. X */ Xstruct diff_block *process_diff (); Xstruct diff3_block *make_3way_diff (); Xvoid output_diff3 (); Xint output_diff3_edscript (); Xint output_diff3_merge (); Xvoid usage (); X Xstruct diff3_block *using_to_diff3_block (); Xint copy_stringlist (); Xstruct diff3_block *create_diff3_block (); Xint compare_line_list (); X Xchar *read_diff (); Xenum diff_type process_diff_control (); Xchar *scan_diff_line (); X Xstruct diff3_block *reverse_diff3_blocklist (); X XVOID *xmalloc (); XVOID *xrealloc (); X Xchar diff_program[] = DIFF_PROGRAM; X X/* X * Main program. Calls diff twice on two pairs of input files, X * combines the two diffs, and outputs them. X */ Xmain (argc, argv) X int argc; X char **argv; X{ X int c, i; X int mapping[3]; X int rev_mapping[3]; X int incompat; X int overlaps_found; X struct diff_block *thread1, *thread2; X struct diff3_block *diff; X int tag_count = 0; X /* Element 0 is for file 0, element 1 is for file 2. */ X char *tag_strings[2]; X extern char *optarg; X char *commonname; X struct stat statb; X X incompat = 0; X tag_strings[0] = tag_strings[1] = 0; X X argv0 = argv[0]; X X while ((c = getopt (argc, argv, "aeimx3EXL:")) != EOF) X { X switch (c) X { X case 'a': X always_text = 1; X break; X case 'x': X dont_write_simple = 1; X incompat++; X break; X case '3': X dont_write_overlap = 1; X incompat++; X break; X case 'i': X finalwrite = 1; X break; X case 'm': X merge = 1; X break; X case 'X': X dont_write_simple = 1; X /* Falls through */ X case 'E': X flagging = 1; X /* Falls through */ X case 'e': X incompat++; X break; X case 'L': X /* Handle one or two -L arguments. */ X if (tag_count < 2) X { X tag_strings[tag_count++] = optarg; X break; X } X /* Falls through */ X case '?': X default: X usage (); X /* NOTREACHED */ X } X } X X edscript = incompat & ~merge; /* -eExX3 without -m implies ed script. */ X flagging |= ~incompat & merge; /* -m without -eExX3 implies -E. */ X X if (incompat > 1 /* Ensure at most one of -eExX3. */ X || finalwrite & (~incompat | merge) X /* -i needs one of -eExX3; -i -m would rewrite input file. */ X || tag_count && ! flagging /* -L requires one of -EX. */ X || argc - optind != 3) X usage (); X X if (tag_strings[0] == 0) X tag_strings[0] = argv[optind]; X if (tag_strings[1] == 0) X tag_strings[1] = argv[optind + 2]; X X if (*argv[optind] == '-' && *(argv[optind] + 1) == '\0') X { X /* Sigh. We've got standard input as the first arg. We can't */ X /* call diff twice on stdin */ X if (! strcmp (argv[optind + 1], "-") || ! strcmp (argv[optind + 2], "-")) X fatal ("`-' specified for more than one input file"); X mapping[0] = 1; X mapping[1] = 2; X mapping[2] = 0; X rev_mapping[1] = 0; X rev_mapping[2] = 1; X rev_mapping[0] = 2; X } X else X { X /* Normal, what you'd expect */ X mapping[0] = 0; X mapping[1] = 1; X mapping[2] = 2; X rev_mapping[0] = 0; X rev_mapping[1] = 1; X rev_mapping[2] = 2; X } X X for (i = 0; i < 3; i++) X if (argv[optind + i][0] != '-' || argv[optind + i][1] != '\0') X if (stat (argv[optind + i], &statb) < 0) X perror_with_exit (argv[optind + i]); X else if ((statb.st_mode & S_IFMT) == S_IFDIR) X { X fprintf (stderr, "%s: %s: Is a directory\n", argv0, X argv[optind + i]); X exit (2); X } X X X commonname = argv[optind + rev_mapping[0]]; X thread1 = process_diff (commonname, argv[optind + rev_mapping[1]]); X thread2 = process_diff (commonname, argv[optind + rev_mapping[2]]); X diff = make_3way_diff (thread1, thread2); X if (edscript) X overlaps_found X = output_diff3_edscript (stdout, diff, mapping, rev_mapping, X tag_strings[0], argv[optind+1], tag_strings[1]); X else if (merge) X { X if (! freopen (commonname, "r", stdin)) X perror_with_exit (commonname); X overlaps_found X = output_diff3_merge (stdin, stdout, diff, mapping, rev_mapping, X tag_strings[0], argv[optind+1], tag_strings[1]); X if (ferror (stdin)) X fatal ("read error"); X } X else X { X output_diff3 (stdout, diff, mapping, rev_mapping); X overlaps_found = 0; X } X X if (ferror (stdout) || fflush (stdout) != 0) X fatal ("write error"); X exit (overlaps_found); X} X X/* X * Explain, patiently and kindly, how to use this program. Then exit. X */ Xvoid Xusage () X{ X fprintf (stderr, "Usage:\t%s [-exEX3 [-i | -m] [-L label1 -L label3]] file1 file2 file3\n", X argv0); X fprintf (stderr, "\tOnly one of [exEX3] allowed\n"); X exit (2); X} X X/* X * Routines that combine the two diffs together into one. The X * algorithm used follows: X * X * File0 is shared in common between the two diffs. X * Diff01 is the diff between 0 and 1. X * Diff02 is the diff between 0 and 2. X * X * 1) Find the range for the first block in File0. X * a) Take the lowest of the two ranges (in File0) in the two X * current blocks (one from each diff) as being the low X * water mark. Assign the upper end of this block as X * being the high water mark and move the current block up X * one. Mark the block just moved over as to be used. X * b) Check the next block in the diff that the high water X * mark is *not* from. X * X * *If* the high water mark is above X * the low end of the range in that block, X * X * mark that block as to be used and move the current X * block up. Set the high water mark to the max of X * the high end of this block and the current. Repeat b. X * X * 2) Find the corresponding ranges in Files1 (from the blocks X * in diff01; line per line outside of diffs) and in File2. X * Create a diff3_block, reserving space as indicated by the ranges. X * X * 3) Copy all of the pointers for file0 in. At least for now, X * do bcmp's between corresponding strings in the two diffs. X * X * 4) Copy all of the pointers for file1 and 2 in. Get what you X * need from file0 (when there isn't a diff block, it's X * identical to file0 within the range between diff blocks). X * X * 5) If the diff blocks you used came from only one of the two X * strings of diffs, then that file (i.e. the one other than X * file 0 in that diff) is the odd person out. If you used X * diff blocks from both sets, check to see if files 1 and 2 match: X * X * Same number of lines? If so, do a set of bcmp's (if a X * bcmp matches; copy the pointer over; it'll be easier later X * if you have to do any compares). If they match, 1 & 2 are X * the same. If not, all three different. X * X * Then you do it again, until you run out of blocks. X * X */ X X/* X * This routine makes a three way diff (chain of diff3_block's) from two X * two way diffs (chains of diff_block's). It is assumed that each of X * the two diffs passed are off of the same file (i.e. that each of the X * diffs were made "from" the same file). The three way diff pointer X * returned will have numbering 0--the common file, 1--the other file X * in diff1, and 2--the other file in diff2. X */ Xstruct diff3_block * Xmake_3way_diff (thread1, thread2) X struct diff_block *thread1, *thread2; X{ X/* X * This routine works on the two diffs passed to it as threads. X * Thread number 0 is diff1, thread number 1 is diff2. The USING X * array is set to the base of the list of blocks to be used to X * construct each block of the three way diff; if no blocks from a X * particular thread are to be used, that element of the using array X * is set to 0. The elements LAST_USING array are set to the last X * elements on each of the using lists. X * X * The HIGH_WATER_MARK is set to the highest line number in File 0 X * described in any of the diffs in either of the USING lists. The X * HIGH_WATER_THREAD names the thread. Similarly the BASE_WATER_MARK X * and BASE_WATER_THREAD describe the lowest line number in File 0 X * described in any of the diffs in either of the USING lists. The X * HIGH_WATER_DIFF is the diff from which the HIGH_WATER_MARK was X * taken. X * X * The HIGH_WATER_DIFF should always be equal to LAST_USING X * [HIGH_WATER_THREAD]. The OTHER_DIFF is the next diff to check for X * higher water, and should always be equal to X * CURRENT[HIGH_WATER_THREAD ^ 0x1]. The OTHER_THREAD is the thread X * in which the OTHER_DIFF is, and hence should always be equal to X * HIGH_WATER_THREAD ^ 0x1. X * X * The variable LAST_DIFF is kept set to the last diff block produced X * by this routine, for line correspondence purposes between that diff X * and the one currently being worked on. It is initialized to X * ZERO_DIFF before any blocks have been created. X */ X X struct diff_block X *using[2], X *last_using[2], X *current[2]; X X int X high_water_mark; X X int X high_water_thread, X base_water_thread, X other_thread; X X struct diff_block X *high_water_diff, X *other_diff; X X struct diff3_block X *result, X *tmpblock, X *result_last, X *last_diff; X X static struct diff3_block zero_diff = { X ERROR, X { {0, 0}, {0, 0}, {0, 0} }, X { (char **) 0, (char **) 0, (char **) 0 }, X { (int *) 0, (int *) 0, (int *) 0 }, X (struct diff3_block *) 0 X }; X X /* Initialization */ X result = result_last = (struct diff3_block *) 0; X current[0] = thread1; current[1] = thread2; X last_diff = &zero_diff; X X /* Sniff up the threads until we reach the end */ X X while (current[0] || current[1]) X { X using[0] = using[1] = last_using[0] = last_using[1] = X (struct diff_block *) 0; X X /* Setup low and high water threads, diffs, and marks. */ X if (!current[0]) X base_water_thread = 1; X else if (!current[1]) X base_water_thread = 0; X else X base_water_thread = X (D_LOWLINE (current[0], FILE0) > D_LOWLINE (current[1], FILE0)); X X high_water_thread = base_water_thread; X X high_water_diff = current[high_water_thread]; X X#if 0 X /* low and high waters start off same diff */ X base_water_mark = D_LOWLINE (high_water_diff, FILE0); X#endif X X high_water_mark = D_HIGHLINE (high_water_diff, FILE0); X X /* Make the diff you just got info from into the using class */ X using[high_water_thread] X = last_using[high_water_thread] X = high_water_diff; X current[high_water_thread] = high_water_diff->next; X last_using[high_water_thread]->next X = (struct diff_block *) 0; X X /* And mark the other diff */ X other_thread = high_water_thread ^ 0x1; X other_diff = current[other_thread]; X X /* Shuffle up the ladder, checking the other diff to see if it X needs to be incorporated */ X while (other_diff X && D_LOWLINE (other_diff, FILE0) <= high_water_mark + 1) X { X X /* Incorporate this diff into the using list. Note that X this doesn't take it off the current list */ X if (using[other_thread]) X last_using[other_thread]->next = other_diff; X else X using[other_thread] = other_diff; X last_using[other_thread] = other_diff; X X /* Take it off the current list. Note that this following X code assumes that other_diff enters it equal to X current[high_water_thread ^ 0x1] */ X current[other_thread] X = current[other_thread]->next; X other_diff->next X = (struct diff_block *) 0; X X /* Set the high_water stuff X If this comparison is equal, then this is the last pass X through this loop; since diff blocks within a given X thread cannot overlap, the high_water_mark will be X *below* the range_start of either of the next diffs. */ X X if (high_water_mark < D_HIGHLINE (other_diff, FILE0)) X { X high_water_thread ^= 1; X high_water_diff = other_diff; X high_water_mark = D_HIGHLINE (other_diff, FILE0); X } X X /* Set the other diff */ X other_thread = high_water_thread ^ 0x1; X other_diff = current[other_thread]; X } X X /* The using lists contain a list of all of the blocks to be X included in this diff3_block. Create it. */ X X tmpblock = using_to_diff3_block (using, last_using, X base_water_thread, high_water_thread, X last_diff); X X if (!tmpblock) X fatal ("internal: screwup in format of diff blocks"); X X /* Put it on the list */ X if (result) X result_last->next = tmpblock; X else X result = tmpblock; X result_last = tmpblock; X X /* Setup corresponding lines correctly */ X last_diff = tmpblock; X } X return result; X} X X/* X * using_to_diff3_block: X * This routine takes two lists of blocks (from two separate diff X * threads) and puts them together into one diff3 block. X * It then returns a pointer to this diff3 block or 0 for failure. X * X * All arguments besides using are for the convenience of the routine; X * they could be derived from the using array. X * LAST_USING is a pair of pointers to the last blocks in the using X * structure. X * LOW_THREAD and HIGH_THREAD tell which threads contain the lowest X * and highest line numbers for File0. X * last_diff contains the last diff produced in the calling routine. X * This is used for lines mappings which would still be identical to X * the state that diff ended in. X * X * A distinction should be made in this routine between the two diffs X * that are part of a normal two diff block, and the three diffs that X * are part of a diff3_block. X */ Xstruct diff3_block * Xusing_to_diff3_block (using, last_using, low_thread, high_thread, last_diff) X struct diff_block X *using[2], X *last_using[2]; X int low_thread, high_thread; X struct diff3_block *last_diff; X{ X int lowc, highc, low1, high1, low2, high2; X struct diff3_block *result; X struct diff_block *ptr; X int i; X int current0line; X X /* Find the range in file0 */ X lowc = using[low_thread]->ranges[0][START]; X highc = last_using[high_thread]->ranges[0][END]; X X /* Find the ranges in the other files. X If using[x] is null, that means that the file to which that diff X refers is equivalent to file 0 over this range */ X X if (using[0]) X { X low1 = D_LOW_MAPLINE (using[0], FILE0, FILE1, lowc); X high1 = D_HIGH_MAPLINE (last_using[0], FILE0, FILE1, highc); X } X else X { X low1 = D_HIGH_MAPLINE (last_diff, FILEC, FILE1, lowc); X high1 = D_HIGH_MAPLINE (last_diff, FILEC, FILE1, highc); X } X X /* X * Note that in the following, we use file 1 relative to the diff, X * and file 2 relative to the corresponding lines struct. X */ X if (using[1]) X { X low2 = D_LOW_MAPLINE (using[1], FILE0, FILE1, lowc); X high2 = D_HIGH_MAPLINE (last_using[1], FILE0, FILE1, highc); X } X else X { X low2 = D_HIGH_MAPLINE (last_diff, FILEC, FILE2, lowc); X high2 = D_HIGH_MAPLINE (last_diff, FILEC, FILE2, highc); X } X X /* Create a block with the appropriate sizes */ X result = create_diff3_block (lowc, highc, low1, high1, low2, high2); X X /* Copy over all of the information for File 0. Return with a zero X if any of the compares failed. */ X for (ptr = using[0]; ptr; ptr = D_NEXT (ptr)) X { X int result_offset = D_LOWLINE (ptr, FILE0) - lowc; X int copy_size X = D_HIGHLINE (ptr, FILE0) - D_LOWLINE (ptr, FILE0) + 1; X X if (!copy_stringlist (D_LINEARRAY (ptr, FILE0), X D_LENARRAY (ptr, FILE0), X D_LINEARRAY (result, FILEC) + result_offset, X D_LENARRAY (result, FILEC) + result_offset, X copy_size)) X return 0; X } X X for (ptr = using[1]; ptr; ptr = D_NEXT (ptr)) X { X int result_offset = D_LOWLINE (ptr, FILEC) - lowc; X int copy_size X = D_HIGHLINE (ptr, FILEC) - D_LOWLINE (ptr, FILEC) + 1; X X if (!copy_stringlist (D_LINEARRAY (ptr, FILE0), X D_LENARRAY (ptr, FILE0), X D_LINEARRAY (result, FILEC) + result_offset, X D_LENARRAY (result, FILEC) + result_offset, X copy_size)) X return 0; X } X X /* Copy stuff for file 1. First deal with anything that might be X before the first diff. */ X X for (i = 0; X i + low1 < (using[0] ? D_LOWLINE (using[0], FILE1) : high1 + 1); X i++) X { X D_RELNUM (result, FILE1, i) = D_RELNUM (result, FILEC, i); X D_RELLEN (result, FILE1, i) = D_RELLEN (result, FILEC, i); X } X X for (ptr = using[0]; ptr; ptr = D_NEXT (ptr)) X { X int result_offset = D_LOWLINE (ptr, FILE1) - low1; X int copy_size X = D_HIGHLINE (ptr, FILE1) - D_LOWLINE (ptr, FILE1) + 1; X X if (!copy_stringlist (D_LINEARRAY (ptr, FILE1), X D_LENARRAY (ptr, FILE1), X D_LINEARRAY (result, FILE1) + result_offset, X D_LENARRAY (result, FILE1) + result_offset, X copy_size)) X return 0; X X /* Catch the lines between here and the next diff */ X current0line = D_HIGHLINE (ptr, FILE0) + 1 - lowc; X for (i = D_HIGHLINE (ptr, FILE1) + 1 - low1; X i < (D_NEXT (ptr) ? X D_LOWLINE (D_NEXT (ptr), FILE1) : X high1 + 1) - low1; X i++) X { X D_RELNUM (result, FILE1, i) X = D_RELNUM (result, FILEC, current0line); X D_RELLEN (result, FILE1, i) X = D_RELLEN (result, FILEC, current0line++); X } X } X X /* Copy stuff for file 2. First deal with anything that might be X before the first diff. */ X X for (i = 0; X i + low2 < (using[1] ? D_LOWLINE (using[1], FILE1) : high2 + 1); X i++) X { X D_RELNUM (result, FILE2, i) = D_RELNUM (result, FILEC, i); X D_RELLEN (result, FILE2, i) = D_RELLEN (result, FILEC, i); X } X X for (ptr = using[1]; ptr; ptr = D_NEXT (ptr)) X { X int result_offset = D_LOWLINE (ptr, FILE1) - low2; X int copy_size X = D_HIGHLINE (ptr, FILE1) - D_LOWLINE (ptr, FILE1) + 1; X X if (!copy_stringlist (D_LINEARRAY (ptr, FILE1), X D_LENARRAY (ptr, FILE1), X D_LINEARRAY (result, FILE2) + result_offset, X D_LENARRAY (result, FILE2) + result_offset, X copy_size)) X return 0; X X /* Catch the lines between here and the next diff */ X current0line = D_HIGHLINE (ptr, FILE0) + 1 - lowc; X for (i = D_HIGHLINE (ptr, FILE1) + 1 - low2; X i < (D_NEXT (ptr) ? X D_LOWLINE (D_NEXT (ptr), FILE1) : X high2 + 1) - low2; X i++) X { X D_RELNUM (result, FILE2, i) X = D_RELNUM (result, FILEC, current0line); X D_RELLEN (result, FILE2, i) X = D_RELLEN (result, FILEC, current0line++); X } X } X X /* Set correspond */ X if (!using[0]) X D3_TYPE (result) = DIFF_3RD; X else if (!using[1]) X D3_TYPE (result) = DIFF_2ND; X else X { X int nl1 X = D_HIGHLINE (result, FILE1) - D_LOWLINE (result, FILE1) + 1; X int nl2 X = D_HIGHLINE (result, FILE2) - D_LOWLINE (result, FILE2) + 1; X X if (nl1 != nl2 X || !compare_line_list (D_LINEARRAY (result, FILE1), X D_LENARRAY (result, FILE1), X D_LINEARRAY (result, FILE2), X D_LENARRAY (result, FILE2), X nl1)) X D3_TYPE (result) = DIFF_ALL; X else X D3_TYPE (result) = DIFF_1ST; X } X X return result; X} X X/* X * This routine copies pointers from a list of strings to a different list X * of strings. If a spot in the second list is already filled, it X * makes sure that it is filled with the same string; if not it X * returns 0, the copy incomplete. X * Upon successful completion of the copy, it returns 1. X */ Xint Xcopy_stringlist (fromptrs, fromlengths, toptrs, tolengths, copynum) X char *fromptrs[], *toptrs[]; X int *fromlengths, *tolengths; X int copynum; X{ X register char X **f = fromptrs, X **t = toptrs; X register int X *fl = fromlengths, X *tl = tolengths; X X while (copynum--) X { X if (*t) X { if (*fl != *tl || bcmp (*f, *t, *fl)) return 0; } X else X { *t = *f ; *tl = *fl; } X X t++; f++; tl++; fl++; X } X return 1; X} X X/* X * Create a diff3_block, with ranges as specified in the arguments. X * Allocate the arrays for the various pointers (and zero them) based X * on the arguments passed. Return the block as a result. X */ Xstruct diff3_block * Xcreate_diff3_block (low0, high0, low1, high1, low2, high2) X register int low0, high0, low1, high1, low2, high2; X{ X struct diff3_block *result = ALLOCATE (1, struct diff3_block); X int numlines; X X D3_TYPE (result) = ERROR; X D_NEXT (result) = 0; X X /* Assign ranges */ X D_LOWLINE (result, FILE0) = low0; X D_HIGHLINE (result, FILE0) = high0; X D_LOWLINE (result, FILE1) = low1; X D_HIGHLINE (result, FILE1) = high1; X D_LOWLINE (result, FILE2) = low2; X D_HIGHLINE (result, FILE2) = high2; X X /* Allocate and zero space */ X numlines = D_NUMLINES (result, FILE0); X if (numlines) X { X D_LINEARRAY (result, FILE0) = ALLOCATE (numlines, char *); X D_LENARRAY (result, FILE0) = ALLOCATE (numlines, int); X bzero (D_LINEARRAY (result, FILE0), (numlines * sizeof (char *))); X bzero (D_LENARRAY (result, FILE0), (numlines * sizeof (int))); X } X else X { X D_LINEARRAY (result, FILE0) = (char **) 0; X D_LENARRAY (result, FILE0) = (int *) 0; X } X X numlines = D_NUMLINES (result, FILE1); X if (numlines) X { X D_LINEARRAY (result, FILE1) = ALLOCATE (numlines, char *); X D_LENARRAY (result, FILE1) = ALLOCATE (numlines, int); X bzero (D_LINEARRAY (result, FILE1), (numlines * sizeof (char *))); X bzero (D_LENARRAY (result, FILE1), (numlines * sizeof (int))); X } X else X { X D_LINEARRAY (result, FILE1) = (char **) 0; X D_LENARRAY (result, FILE1) = (int *) 0; X } X X numlines = D_NUMLINES (result, FILE2); X if (numlines) X { X D_LINEARRAY (result, FILE2) = ALLOCATE (numlines, char *); X D_LENARRAY (result, FILE2) = ALLOCATE (numlines, int); X bzero (D_LINEARRAY (result, FILE2), (numlines * sizeof (char *))); X bzero (D_LENARRAY (result, FILE2), (numlines * sizeof (int))); X } X else X { X D_LINEARRAY (result, FILE2) = (char **) 0; X D_LENARRAY (result, FILE2) = (int *) 0; X } X X /* Return */ X return result; X} X X/* X * Compare two lists of lines of text. X * Return 1 if they are equivalent, 0 if not. X */ Xint Xcompare_line_list (list1, lengths1, list2, lengths2, nl) X char *list1[], *list2[]; X int *lengths1, *lengths2; X int nl; X{ X char X **l1 = list1, X **l2 = list2; X int X *lgths1 = lengths1, X *lgths2 = lengths2; X X while (nl--) X if (!*l1 || !*l2 || *lgths1 != *lgths2++ X || bcmp (*l1++, *l2++, *lgths1++)) X return 0; X return 1; X} X X/* X * Routines to input and parse two way diffs. X */ X Xextern char **environ; X X#define DIFF_CHUNK_SIZE 10000 X Xstruct diff_block * Xprocess_diff (filea, fileb) X char *filea, *fileb; X{ X char *diff_contents; X char *diff_limit; X char *scan_diff; X enum diff_type dt; X int i; X struct diff_block *block_list, *block_list_end, *bptr; X X diff_limit = read_diff (filea, fileb, &diff_contents); X scan_diff = diff_contents; X bptr = block_list_end = block_list = (struct diff_block *) 0; X X while (scan_diff < diff_limit) X { X bptr = ALLOCATE (1, struct diff_block); X bptr->next = 0; X bptr->lines[0] = bptr->lines[1] = (char **) 0; X bptr->lengths[0] = bptr->lengths[1] = (int *) 0; X X dt = process_diff_control (&scan_diff, bptr); X if (dt == ERROR || *scan_diff != '\n') X { X fprintf (stderr, "%s: diff error: ", argv0); X do X { X putc (*scan_diff, stderr); X } X while (*scan_diff++ != '\n'); X exit (2); X } X scan_diff++; X X /* Force appropriate ranges to be null, if necessary */ X switch (dt) X { X case ADD: X bptr->ranges[0][0]++; X break; X case DELETE: X bptr->ranges[1][0]++; X break; X case CHANGE: X break; X default: X fatal ("internal: Bad diff type in process_diff"); X break; X } X X /* Allocate space for the pointers for the lines from filea, and X parcel them out among these pointers */ X if (dt != ADD) X { X bptr->lines[0] = ALLOCATE ((bptr->ranges[0][END] X - bptr->ranges[0][START] + 1), X char *); X bptr->lengths[0] = ALLOCATE ((bptr->ranges[0][END] X - bptr->ranges[0][START] + 1), X int); X for (i = 0; i <= (bptr->ranges[0][END] X - bptr->ranges[0][START]); i++) X scan_diff = scan_diff_line (scan_diff, X &(bptr->lines[0][i]), X &(bptr->lengths[0][i]), X diff_limit, X '<'); X } X X /* Get past the separator for changes */ X if (dt == CHANGE) X { X if (strncmp (scan_diff, "---\n", 4)) X fatal ("Bad diff format: bad change separator"); X scan_diff += 4; X } X X /* Allocate space for the pointers for the lines from fileb, and X parcel them out among these pointers */ X if (dt != DELETE) X { X bptr->lines[1] = ALLOCATE ((bptr->ranges[1][END] X - bptr->ranges[1][START] + 1), X char *); X bptr->lengths[1] = ALLOCATE ((bptr->ranges[1][END] X - bptr->ranges[1][START] + 1), X int); X for (i = 0; i <= (bptr->ranges[1][END] X - bptr->ranges[1][START]); i++) X scan_diff = scan_diff_line (scan_diff, X &(bptr->lines[1][i]), X &(bptr->lengths[1][i]), X diff_limit, X '>'); X } X X /* Place this block on the blocklist */ X if (block_list_end) X block_list_end->next = bptr; X else X block_list = bptr; X X block_list_end = bptr; X X } X X return block_list; X} X X/* X * This routine will parse a normal format diff control string. It X * returns the type of the diff (ERROR if the format is bad). All of X * the other important information is filled into to the structure X * pointed to by db, and the string pointer (whose location is passed X * to this routine) is updated to point beyond the end of the string X * parsed. Note that only the ranges in the diff_block will be set by X * this routine. X * X * If some specific pair of numbers has been reduced to a single X * number, then both corresponding numbers in the diff block are set X * to that number. In general these numbers are interpetted as ranges X * inclusive, unless being used by the ADD or DELETE commands. It is X * assumed that these will be special cased in a superior routine. X */ X Xenum diff_type Xprocess_diff_control (string, db) X char **string; X struct diff_block *db; X{ X char *s = *string; X int holdnum; X enum diff_type type; X X/* These macros are defined here because they can use variables X defined in this function. Don't try this at home kids, we're X trained professionals! X X Also note that SKIPWHITE only recognizes tabs and spaces, and X that READNUM can only read positive, integral numbers */ X X#define SKIPWHITE(s) { while (*s == ' ' || *s == '\t') s++; } X#define READNUM(s, num) \ X { if (!isdigit (*s)) return ERROR; holdnum = 0; \ X do { holdnum = (*s++ - '0' + holdnum * 10); } \ X while (isdigit (*s)); (num) = holdnum; } X X /* Read first set of digits */ X SKIPWHITE (s); X READNUM (s, db->ranges[0][START]); X X /* Was that the only digit? */ X SKIPWHITE(s); X if (*s == ',') X { X /* Get the next digit */ X s++; X READNUM (s, db->ranges[0][END]); X } X else X db->ranges[0][END] = db->ranges[0][START]; X X /* Get the letter */ X SKIPWHITE (s); X switch (*s) X { X case 'a': X type = ADD; X break; X case 'c': X type = CHANGE; X break; X case 'd': X type = DELETE; X break; X default: X return ERROR; /* Bad format */ X } X s++; /* Past letter */ X X /* Read second set of digits */ X SKIPWHITE (s); X READNUM (s, db->ranges[1][START]); X X /* Was that the only digit? */ X SKIPWHITE(s); X if (*s == ',') X { X /* Get the next digit */ X s++; X READNUM (s, db->ranges[1][END]); X SKIPWHITE (s); /* To move to end */ X } X else X db->ranges[1][END] = db->ranges[1][START]; X X *string = s; X return type; X} X Xchar * Xread_diff (filea, fileb, output_placement) X char *filea, *fileb; X char **output_placement; X{ X char *argv[6]; X char **ap; X int fds[2]; X char *diff_result; X int current_chunk_size; X int bytes; X int total; X int pid, w; X int wstatus; X X ap = argv; X *ap++ = diff_program; X if (always_text) X *ap++ = "-a"; X *ap++ = "--"; X *ap++ = filea; X *ap++ = fileb; X *ap = (char *) 0; X X if (pipe (fds) < 0) X perror_with_exit ("Pipe failed"); X X pid = vfork (); X if (pid == 0) X { X /* Child */ X close (fds[0]); X if (fds[1] != fileno (stdout)) X { X dup2 (fds[1], fileno (stdout)); X close (fds[1]); X } X execve (diff_program, argv, environ); X /* Avoid stdio, because the parent process's buffers are inherited. */ X write (fileno (stderr), diff_program, strlen (diff_program)); X write (fileno (stderr), ": not found\n", 12); X _exit (2); X } X X if (pid == -1) X perror_with_exit ("Fork failed"); X X close (fds[1]); /* Prevent erroneous lack of EOF */ X current_chunk_size = DIFF_CHUNK_SIZE; X diff_result = (char *) xmalloc (current_chunk_size); X total = 0; X do { X bytes = myread (fds[0], X diff_result + total, X current_chunk_size - total); X total += bytes; X if (total == current_chunk_size) X diff_result = (char *) xrealloc (diff_result, (current_chunk_size *= 2)); X } while (bytes); X X if (total != 0 && diff_result[total-1] != '\n') X fatal ("bad diff format; incomplete last line"); X X *output_placement = diff_result; X X do X if ((w = wait (&wstatus)) == -1) X perror_with_exit ("Wait failed"); X while (w != pid); X X if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 2)) X fatal ("Subsidiary diff failed"); X X return diff_result + total; X} X X X/* X * Scan a regular diff line (consisting of > or <, followed by a X * space, followed by text (including nulls) up to a newline. X * X * This next routine began life as a macro and many parameters in it X * are used as call-by-reference values. X */ Xchar * Xscan_diff_line (scan_ptr, set_start, set_length, limit, firstchar) X char *scan_ptr, **set_start; X int *set_length; X char *limit; X char firstchar; X{ X char *line_ptr; X X if (!(scan_ptr[0] == (firstchar) X && scan_ptr[1] == ' ')) X fatal ("Bad diff format; incorrect leading line chars"); X X *set_start = line_ptr = scan_ptr + 2; X while (*line_ptr++ != '\n') X ; X X /* Include newline if the original line ended in a newline, X or if an edit script is being generated. X Copy any missing newline message to stderr if an edit script is being X generated, because edit scripts cannot handle missing newlines. X Return the beginning of the next line. */ X *set_length = line_ptr - *set_start; X if (line_ptr < limit && *line_ptr == '\\') X { X if (edscript) X fprintf (stderr, "%s:", argv0); X else X --*set_length; X line_ptr++; X do X { X if (edscript) X putc (*line_ptr, stderr); X } X while (*line_ptr++ != '\n'); X } X X return line_ptr; X} X X/* X * This routine outputs a three way diff passed as a list of X * diff3_block's. X * The argument MAPPING is indexed by external file number (in the X * argument list) and contains the internal file number (from the X * diff passed). This is important because the user expects his X * outputs in terms of the argument list number, and the diff passed X * may have been done slightly differently (if the first argument in X * the argument list was the standard input, for example). X * REV_MAPPING is the inverse of MAPPING. X */ Xvoid Xoutput_diff3 (outputfile, diff, mapping, rev_mapping) X FILE *outputfile; X struct diff3_block *diff; X int mapping[3], rev_mapping[3]; X{ X int i; X int oddoneout; X char *cp; X struct diff3_block *ptr; X int line; X int length; X int dontprint; X static int skew_increment[3] = { 2, 3, 1 }; /* 0==>2==>1==>3 */ X X for (ptr = diff; ptr; ptr = D_NEXT (ptr)) X { X char x[2]; X X switch (ptr->correspond) X { X case DIFF_ALL: X x[0] = '\0'; X dontprint = 3; /* Print them all */ X oddoneout = 3; /* Nobody's odder than anyone else */ X break; X case DIFF_1ST: X case DIFF_2ND: X case DIFF_3RD: X oddoneout = rev_mapping[(int) ptr->correspond - (int) DIFF_1ST]; X X x[0] = oddoneout + '1'; X x[1] = '\0'; X dontprint = oddoneout==0; X break; X default: X fatal ("internal: Bad diff type passed to output"); X } X fprintf (outputfile, "====%s\n", x); X X /* Go 0, 2, 1 if the first and third outputs are equivalent. */ X for (i = 0; i < 3; X i = (oddoneout == 1 ? skew_increment[i] : i + 1)) X { X int realfile = mapping[i]; X int X lowt = D_LOWLINE (ptr, realfile), X hight = D_HIGHLINE (ptr, realfile); X X fprintf (outputfile, "%d:", i + 1); X switch (lowt - hight) X { X case 1: X fprintf (outputfile, "%da\n", lowt - 1); X break; X case 0: X fprintf (outputfile, "%dc\n", lowt); X break; X default: X fprintf (outputfile, "%d,%dc\n", lowt, hight); X break; X } X X if (i == dontprint) continue; X X for (line = 0; line < hight - lowt + 1; line++) X { X fprintf (outputfile, " "); X cp = D_RELNUM (ptr, realfile, line); X length = D_RELLEN (ptr, realfile, line); X fwrite (cp, sizeof (char), length, outputfile); X } X if (line != 0 && cp[length - 1] != '\n') X fprintf (outputfile, "\n\\ No newline at end of file\n"); X } X } X} X X/* X * This routine outputs a diff3 set of blocks as an ed script. This X * script applies the changes between file's 2 & 3 to file 1. It X * takes the precise format of the ed script to be output from global X * variables set during options processing. Note that it does X * destructive things to the set of diff3 blocks it is passed; it X * reverses their order (this gets around the problems involved with X * changing line numbers in an ed script). X * X * Note that this routine has the same problem of mapping as the last X * one did; the variable MAPPING maps from file number according to X * the argument list to file number according to the diff passed. All X * files listed below are in terms of the argument list. X * REV_MAPPING is the inverse of MAPPING. X * X * The arguments FILE0, FILE1 and FILE2 are the strings to print X * as the names of the three files. These may be the actual names, X * or may be the arguments specified with -L. X * X * Returns 1 if overlaps were found. X */ X Xint Xoutput_diff3_edscript (outputfile, diff, mapping, rev_mapping, X file0, file1, file2) X FILE *outputfile; X struct diff3_block *diff; X int mapping[3], rev_mapping[3]; X char *file0, *file1, *file2; X{ X int i; X int leading_dot; X int overlaps_found = 0; X struct diff3_block *newblock, *thisblock; X X leading_dot = 0; X X newblock = reverse_diff3_blocklist (diff); X X for (thisblock = newblock; thisblock; thisblock = thisblock->next) X { X /* Must do mapping correctly. */ X enum diff_type type X = ((thisblock->correspond == DIFF_ALL) ? X DIFF_ALL : X ((enum diff_type) X (((int) DIFF_1ST) X + rev_mapping[(int) thisblock->correspond - (int) DIFF_1ST]))); X X /* If we aren't supposed to do this output block, skip it */ X if (type == DIFF_2ND || type == DIFF_1ST X || (type == DIFF_3RD && dont_write_simple) X || (type == DIFF_ALL && dont_write_overlap)) X continue; X X if (flagging && type == DIFF_ALL) X /* Do special flagging */ X { X X /* Put in lines from FILE2 with bracket */ X fprintf (outputfile, "%da\n", X D_HIGHLINE (thisblock, mapping[FILE0])); X fprintf (outputfile, "=======\n"); X for (i = 0; X i < D_NUMLINES (thisblock, mapping[FILE2]); X i++) X { X if (D_RELNUM (thisblock, mapping[FILE2], i)[0] == '.') X { leading_dot = 1; fprintf(outputfile, "."); } X fwrite (D_RELNUM (thisblock, mapping[FILE2], i), sizeof (char), X D_RELLEN (thisblock, mapping[FILE2], i), outputfile); X } X fprintf (outputfile, ">>>>>>> %s\n.\n", file2); X overlaps_found = 1; X X /* Add in code to take care of leading dots, if necessary. */ X if (leading_dot) X { X fprintf (outputfile, "%d,%ds/^\\.\\./\\./\n", X D_HIGHLINE (thisblock, mapping[FILE0]) + 1, X (D_HIGHLINE (thisblock, mapping[FILE0]) X + D_NUMLINES (thisblock, mapping[FILE2]))); X leading_dot = 0; X } X X /* Put in code to do initial bracket of lines from FILE0 */ X fprintf (outputfile, "%da\n<<<<<<< %s\n.\n", X D_LOWLINE (thisblock, mapping[FILE0]) - 1, X file0); X } X else if (D_NUMLINES (thisblock, mapping[FILE2]) == 0) X /* Write out a delete */ X { X if (D_NUMLINES (thisblock, mapping[FILE0]) == 1) X fprintf (outputfile, "%dd\n", X D_LOWLINE (thisblock, mapping[FILE0])); X else X fprintf (outputfile, "%d,%dd\n", X D_LOWLINE (thisblock, mapping[FILE0]), X D_HIGHLINE (thisblock, mapping[FILE0])); X } X else X /* Write out an add or change */ X { X switch (D_NUMLINES (thisblock, mapping[FILE0])) X { X case 0: X fprintf (outputfile, "%da\n", X D_HIGHLINE (thisblock, mapping[FILE0])); X break; X case 1: X fprintf (outputfile, "%dc\n", X D_HIGHLINE (thisblock, mapping[FILE0])); X break; X default: X fprintf (outputfile, "%d,%dc\n", X D_LOWLINE (thisblock, mapping[FILE0]), X D_HIGHLINE (thisblock, mapping[FILE0])); X break; X } X for (i = 0; X i < D_NUMLINES (thisblock, mapping[FILE2]); X i++) X { X if (D_RELNUM (thisblock, mapping[FILE2], i)[0] == '.') X { leading_dot = 1; fprintf (outputfile, "."); } X fwrite (D_RELNUM (thisblock, mapping[FILE2], i), sizeof (char), X D_RELLEN (thisblock, mapping[FILE2], i), outputfile); X } X fprintf (outputfile, ".\n"); X X /* Add in code to take care of leading dots, if necessary. */ X if (leading_dot) X { X fprintf (outputfile, "%d,%ds/^\\.\\./\\./\n", X D_HIGHLINE (thisblock, mapping[FILE0]) + 1, X (D_HIGHLINE (thisblock, mapping[FILE0]) X + D_NUMLINES (thisblock, mapping[FILE2]))); X leading_dot = 0; X } X } X } X if (finalwrite) fprintf (outputfile, "w\nq\n"); X return overlaps_found; X} X X/* X * Read from COMMONFILE and output to OUTPUTFILE a set of diff3_ blocks DIFF X * as a merged file. This acts like 'ed file0 <[output_diff3_edscript]', X * except that it works even for binary data or incomplete lines. X * X * As before, MAPPING maps from arg list file number to diff file number, X * REV_MAPPING is its inverse, X * and FILE0, FILE1, and FILE2 are the names of the files. X * X * Returns 1 if overlaps were found. X */ X Xint Xoutput_diff3_merge (commonfile, outputfile, diff, mapping, rev_mapping, X file0, file1, file2) X FILE *commonfile, *outputfile; X struct diff3_block *diff; X int mapping[3], rev_mapping[3]; X char *file0, *file1, *file2; X{ X int c, i; X int overlaps_found = 0; X struct diff3_block *b; X int linesread = 0; X X for (b = diff; b; b = b->next) X { X /* Must do mapping correctly */ X enum diff_type type X = ((b->correspond == DIFF_ALL) ? X DIFF_ALL : X ((enum diff_type) X (((int) DIFF_1ST) X + rev_mapping[(int) b->correspond - (int) DIFF_1ST]))); X X /* If we aren't supposed to do this output block, skip it. */ X if (type == DIFF_2ND || type == DIFF_1ST X || (type == DIFF_3RD && dont_write_simple) X || (type == DIFF_ALL && dont_write_overlap)) X continue; X X /* Copy I lines from common file. */ X i = D_LOWLINE (b, FILE0) - linesread - 1; X linesread += i; X while (0 <= --i) X { X while ((c = getc(commonfile)) != '\n') X { X if (c == EOF) X fatal ("input file shrank"); X putc (c, outputfile); X } X putc (c, outputfile); X } X X if (flagging && type == DIFF_ALL) X /* Do special flagging. */ X { X /* Put in lines from FILE0 with bracket. */ X fprintf (outputfile, "<<<<<<< %s\n", file0); X for (i = 0; X i < D_NUMLINES (b, mapping[FILE0]); X i++) X fwrite (D_RELNUM (b, mapping[FILE0], i), sizeof (char), X D_RELLEN (b, mapping[FILE0], i), outputfile); X fprintf (outputfile, "=======\n"); X overlaps_found = 1; X } X X /* Put in lines from FILE2. */ X for (i = 0; X i < D_NUMLINES (b, mapping[FILE2]); X i++) X fwrite (D_RELNUM (b, mapping[FILE2], i), sizeof (char), X D_RELLEN (b, mapping[FILE2], i), outputfile); X X if (flagging && type == DIFF_ALL) X fprintf (outputfile, ">>>>>>> %s\n", file2); X X /* Skip I lines in common file. */ X i = D_NUMLINES (b, FILE0); X linesread += i; X while (0 <= --i) X while ((c = getc(commonfile)) != '\n') X if (c == EOF) X { X if (i || b->next) X fatal ("input file shrank"); X return overlaps_found; X } X } X /* Copy rest of common file. */ X while ((c = getc (commonfile)) != EOF) X putc (c, outputfile); X return overlaps_found; X} X X/* X * Reverse the order of the list of diff3 blocks. X */ Xstruct diff3_block * Xreverse_diff3_blocklist (diff) X struct diff3_block *diff; X{ X register struct diff3_block *tmp, *next, *prev; X X for (tmp = diff, prev = (struct diff3_block *) 0; X tmp; tmp = next) X { X next = tmp->next; X tmp->next = prev; X prev = tmp; X } X X return prev; X} X Xint Xmyread (fd, ptr, size) X int fd, size; X char *ptr; X{ X int result = read (fd, ptr, size); X if (result < 0) X perror_with_exit ("Read failed"); X return result; X} X XVOID * Xxmalloc (size) X int size; X{ X VOID *result = (VOID *) malloc (size ? size : 1); X if (!result) X fatal ("Malloc failed"); X return result; X} X XVOID * Xxrealloc (ptr, size) X VOID *ptr; X int size; X{ X VOID *result = (VOID *) realloc (ptr, size ? size : 1); X if (!result) X fatal ("Malloc failed"); X return result; X} X Xfatal (string) X char *string; X{ X fprintf (stderr, "%s: %s\n", argv0, string); X exit (2); X} X Xperror_with_exit (string) X char *string; X{ X perror (string); X exit (2); X} END_OF_FILE if test 46642 -ne `wc -c <'diff3.c'`; then echo shar: \"'diff3.c'\" unpacked with wrong size! fi # end of 'diff3.c' fi echo shar: End of archive 8 \(of 8\). cp /dev/null ark8isdone MISSING="" for I in 1 2 3 4 5 6 7 8 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 8 archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 exit 0 # Just in case...