/* * Last Hacked by Johan Widen, jw@sics.se, 17 Jan 88. * Decreased memory use by cleaning up the data types. "new style" context * diffs. File dates are now displayed under UNIX and Amiga-DOS. * Incorporated documentation cleanup and TURBO-C adaptions by mike2@lcuxa. * * Previously Hacked by Robert A. Larson, blarson@ecla.usc.edu, Nov 29 86 * OSK support. * Real context diffs, with a couple of minor problems: * If the first change is deleting leading lines, and the second * such that the context overlaps the deleted lines, the deleted * lines are output as context. This is consistant with other * cases of overlapping context, but patch doesn't like it. It's * not hard to manually fix the diff in this (rare?) case. * At most 9 lines of context is output. * * Previously Hacked by John Gilmore, hoptoad!gnu, 26Sep86. * Compiles and runs under Unix. Much faster since it doesn't reallocate * every data structure in the inner loop(!). Compatible with Unix diff * output format, though it occasionally finds different sets of changed * lines (both are valid). -c option needs work. Also, ftell's in * should be dumped when possible. * From: EVERHART%ARISIA%rca.com@CSNET-RELAY.ARPA Message-Id: <8608201845.AA15181@lll-crg.ARPA> Date: Wed, 20 Aug 86 10:34 EDT To: hoptoad!gnu@LLL-CRG.ARPA Subject: Decus C DIFF source. */ /* * D I F F */ /* For VMS: )BUILD $(TKBOPTIONS) = { TASK = ...DIF } */ /* * OSK changes: since OSK doesn't have realloc, put the size * allocated in the head of each allocated region. This code * assumes int is a maximally alligned type. */ /* * Borland Turbo C instructions. * * 1. Make certain that TURBO is #defined. * 2. Compile in the Large model. * 3. Set the optimizations: * a) to optimize for speed, not size * b) to use register variables * c) to invoke register optimization * c) to invoke jump optimization */ #ifdef TURBO #include #include #include #include #include #include #else #include #include #endif #ifdef unix #include #include #endif #ifdef vms #include #include #define IO_SUCCESS (SS | STS) #define IO_ERROR SS #endif #ifdef AMIGA #ifndef MANX #ifndef LATTICE #define LATTICE 1 #define ANSI 1 #endif #endif #ifdef LATTICE #include #include extern char *_TZ; #endif #endif /* * Note: IO_SUCCESS and IO_ERROR are defined in the Decus C stdio.h file */ #ifndef IO_SUCCESS #define IO_SUCCESS 0 #endif #ifndef IO_ERROR #define IO_ERROR 1 #endif #define EOS 0 #ifdef unix char temfile[L_tmpnam]; char *tmpnam(); #define TEMPFILE (temfile[0]? temfile: tmpnam(temfile)) #else #define TEMPFILE "diff.tmp" #endif #define TRUE 1 #define FALSE 0 #ifdef pdp11 #define short int #endif typedef short LINENO; typedef unsigned short HASH; typedef struct candidate { LINENO b; /* Line in fileB */ LINENO a; /* Line in fileA */ LINENO link; /* Previous candidate */ } CANDIDATE; typedef struct line { HASH hash; /* Hash value etc. */ LINENO serial; /* Line number */ } LINE; #define MAXLINES 32760 /* depends on sizeof(short) */ #define LSIZE_INC 200 /* # of line entries to alloc at once */ LINE *file[2]; /* Hash/line for total file */ #define fileA file[0] #define fileB file[1] LINE *sfile[2]; /* Hash/line after prefix */ #define sfileA sfile[0] #define sfileB sfile[1] int len[2]; /* Actual lines in each file */ #define lenA len[0] #define lenB len[1] int slen[2]; /* Squished lengths */ #define slenA slen[0] #define slenB slen[1] int prefix; /* Identical lines at start */ int suffix; /* Identical lines at end */ FILE *infd[2] = { NULL, NULL }; /* Input file identifiers */ FILE *tempfd; /* Temp for input redirection */ #ifndef LATTICE extern long ftell(); extern FILE *fopen(); #ifdef TURBO extern void *malloc(); #else extern char *malloc(); #endif #endif #ifdef ANSI int error(char *,); char *fgetss(char *, int, FILE *); HASH hash(char *); HASH newcand(LINENO, LINENO, LINENO); unsigned search(unsigned, unsigned, LINENO); unsigned subseq(); #else char *fgetss(); HASH hash(); HASH newcand(); unsigned search(); unsigned subseq(); #endif /* * The following vectors overlay the area defined by fileA */ HASH *class; /* Unsorted line numbers */ HASH *klist; /* Index of element in clist */ CANDIDATE *clist; /* Storage pool for candidates */ int clength = 0; /* Number of active candidates */ #define CSIZE_INC 50 /* How many to allocate each time we have to */ int csize = CSIZE_INC; /* Current size of storage pool */ LINENO *match; /* Longest subsequence */ long *oldseek; /* Seek position in file A */ /* * The following vectors overlay the area defined by fileB */ LINENO *member; /* Concatenated equiv. classes */ long *newseek; /* Seek position in file B */ char *textb; /* Input from file2 for check */ /* * Global variables */ int eflag = FALSE; /* Edit script requested */ int bflag = FALSE; /* Blank supress requested */ int cflag = FALSE; /* Context printout */ int iflag = FALSE; /* Ignore case requested */ char text[257]; /* Input line from file1 */ extern char *myalloc(); /* Storage allocator */ extern char *compact(); /* Storage compactor */ #ifdef DEBUG #ifndef OSK #define TIMING #endif #endif #ifdef TIMING extern long time(); extern char *358mend; long totaltime; long sectiontime; char *mstart; #endif main(argc, argv) int argc; char **argv; /* * Diff main program */ { register int i; register char *ap; #ifdef OSK extern int _memmins; _memmins = 16 * 1024; /* tell OSK we will malloc a lot */ #endif #ifdef TIMING sectiontime = time(&totaltime); #endif #ifdef vms argc = getredirection(argc,argv); #endif while (argc > 1 && *(ap = argv[1]) == '-' && *++ap != EOS) { while (*ap != EOS) { switch ((*ap++)) { case 'b': bflag++; break; case 'c': if (*ap > '0' && *ap <= '9') cflag = *ap++ - '0'; else cflag = 3; break; case 'e': eflag++; break; case 'i': iflag++; break; default: fprintf(stderr, "Warning, bad option '%c'\n", ap[-1]); break; } } argc--; argv++; } if (argc != 3) error("Usage: diff [-options] file1 file2"); if (cflag && eflag) { fprintf(stderr, "Warning, -c and -e are incompatible, -c supressed.\n"); cflag = FALSE; } argv++; for (i = 0; i <= 1; i++) { if (argv[i][0] == '-' && argv[i][1] == EOS) { infd[i] = stdin; if ((tempfd = fopen(TEMPFILE, "w")) == NULL) cant(TEMPFILE, "work", 1); } else { infd[i] = fopen(argv[i], "r"); if (!infd[i]) cant(argv[i], "input", 2); /* Fatal error */ } } if (infd[0] == stdin && infd[1] == stdin) error("Can't diff two things both on standard input."); if (infd[0] == NULL && infd[1] == NULL) { cant(argv[0], "input", 0); cant(argv[1], "input", 1); } #ifdef vms else if (infd[1] == NULL) opendir(1, &argv[1], infd[0]); else if (infd[0] == NULL) opendir(0, &argv[0], infd[1]); #endif /* * Read input, building hash tables. */ input(0); input(1); if(lenA > MAXLINES || lenB > MAXLINES) error("Can't handle files with more than %d lines.", MAXLINES); squish(); #ifdef DEBUG printf("before sort\n"); for (i = 1; i <= slenA; i++) printf("sfileA[%d] = %6d %06o\n", i, sfileA[i].serial, sfileA[i].hash); for (i = 1; i <= slenB; i++) printf("sfileB[%d] = %6d %06o\n", i, sfileB[i].serial, sfileB[i].hash); #endif sort(sfileA, slenA); sort(sfileB, slenB); #ifdef TIMING ptime("input"); #endif #ifdef DEBUG printf("after sort\n"); for (i = 1; i <= slenA; i++) printf("sfileA[%d] = %6d %06o\n", i, sfileA[i].serial, sfileB[i].hash); for (i = 1; i <= slenB; i++) printf("sfileB[%d] = %6d %06o\n", i, sfileB[i].serial, sfileB[i].hash); #endif /* * Build equivalence classes. */ member = (LINENO *)fileB; equiv(); /* This will overwrite fileB */ member = (LINENO *)compact((char *)member, (slenB + 2) * sizeof (LINENO), "squeezing member vector"); /* sfileB and fileB are no longer valid */ /* * Reorder equivalence classes into array class[] */ class = (HASH *)fileA; unsort(); /* This will overwrite fileA */ class = (HASH *)compact((char *)class, (slenA + 2) * sizeof (HASH), "compacting class vector"); /* sfileA and fileA are no longer valid */ #ifdef TIMING ptime("equiv/unsort"); #endif /* * Find longest subsequences */ klist = (HASH *)myalloc((slenA + 2) * sizeof (HASH), "klist"); clist = (CANDIDATE *)myalloc(csize * sizeof (CANDIDATE), "clist"); i = subseq(); #ifndef OSK free((char *)member); free((char *)class); #else free((char *)member - sizeof(int)); free((char *)class - sizeof(int)); #endif match = (LINENO *)myalloc((lenA + 2) * sizeof (LINENO), "match"); unravel(klist[i]); #ifndef OSK free((char *)clist); free((char *)klist); #else free((char *)clist - sizeof(int)); free((char *)klist - sizeof(int)); #endif #ifdef TIMING ptime("subsequence/unravel"); #endif /* * Check for fortuitous matches and output differences */ oldseek = (long *)myalloc((lenA + 2) * sizeof (* oldseek), "oldseek"); newseek = (long *)myalloc((lenB + 2) * sizeof (* newseek), "newseek"); textb = myalloc(sizeof text, "textbuffer"); check(argv[0], argv[1]); #ifdef TIMING ptime("check"); #endif output(argv[0], argv[1]); #ifdef TIMING ptime("output"); printf("%ld seconds required\n", sectiontime - totaltime); #endif if (tempfd != NULL) { fclose(tempfd); #ifdef vms remove(TEMPFILE); #else unlink(TEMPFILE); #endif } } input(which) int which; /* 0 or 1 to redefine infd[] */ /* * Read the file, building hash table */ { register LINE *lentry; register int linect = 0; FILE *fd; int lsize = LSIZE_INC; lentry = (LINE *)myalloc(sizeof(LINE) * (lsize+3), "line"); fd = infd[which]; while (!getline(fd, text)) { if (++linect >= lsize) { lsize += LSIZE_INC; lentry = (LINE *)compact((char *)lentry, (lsize + 3) * sizeof(LINE), "extending line vector"); } lentry[linect].hash = hash(text); } /* * If input was from stdin ("-" command), finish off the temp file. */ if (fd == stdin) { fclose(tempfd); tempfd = infd[which] = fopen(TEMPFILE, "r"); } /* If we wanted to be stingy with memory, we could realloc lentry down * to its exact size (+3 for some odd reason) here. No need? */ len[which] = linect; file[which] = lentry; } squish() /* * Look for initial and trailing sequences that have identical hash values. * Don't bother building them into the candidate vector. */ { register int i; register LINE *ap; register LINE *bp; int j; int k; /* * prefix -> first line (from start) that doesn't hash identically */ i = 0; ap = &fileA[1]; bp = &fileB[1]; while (i < lenA && i < lenB && ap->hash == bp->hash) { i++; ap++; bp++; } prefix = i; /* * suffix -> first line (from end) that doesn't hash identically */ j = lenA - i; k = lenB - i; ap = &fileA[lenA]; bp = &fileB[lenB]; i = 0; while (i < j && i < k && ap->hash == bp->hash) { i++; ap--; bp--; } suffix = i; /* * Tuck the counts away */ for (k = 0; k <= 1; k++) { sfile[k] = file[k] + prefix; slen[k] = len[k] - prefix - suffix; for (i = 0, ap = sfile[k]; i <= slen[k]; i++, ap++) { ap->serial = i; } } } sort(vector, vecsize) LINE *vector; /* What to sort */ int vecsize; /* How many to sort */ /* * Sort hash entries */ { register int j; register LINE *aim; register LINE *ai; int mid; int k; LINE work; for (j = 1; j <= vecsize; j *= 2); mid = (j - 1); while ((mid /= 2) != 0) { k = vecsize - mid; for (j = 1; j <= k; j++) { for (ai = &vector[j]; ai > vector; ai -= mid) { aim = &ai[mid]; if (aim->hash > ai->hash || aim->hash == ai->hash && aim->serial > ai->serial) break; work.hash = ai->hash; ai->hash = aim->hash; aim->hash = work.hash; work.serial = ai->serial; ai->serial = aim->serial; aim->serial = work.serial; } } } } equiv() /* * Build equivalence class vector */ { register LINE *ap; register LINE *bp; register LINENO *mp; register int j; LINE *atop, *btop; #ifdef DEBUG printf("equiv entry\n"); for (j = 1; j <= slenA; j++) printf("sfileA[%d] = %6d %06o\n", j, sfileA[j].serial, sfileA[j].hash); for (j = 1; j <= slenB; j++) printf("sfileB[%d] = %6d %06o\n", j, sfileB[j].serial, sfileB[j].hash); #endif j = 1; ap = &sfileA[1]; bp = &sfileB[1]; atop = &sfileA[slenA]; while (ap <= atop && j <= slenB) { if (ap->hash < bp->hash) { ap->hash = 0; ap++; } else if (ap->hash == bp->hash) { ap->hash = j; ap++; } else { bp++; j++; } } while (ap <= atop) { ap->hash = 0; ap++; } sfileB[slenB + 1].hash = 0; #ifdef DEBUG printf("equiv exit\n"); for (j = 1; j <= slenA; j++) printf("sfileA[%d] = %6d %06o\n", j, sfileA[j].serial, sfileA[j].hash); for (j = 1; j <= slenB; j++) printf("sfileB[%d] = %6d %06o\n", j, sfileB[j].serial, sfileB[j].hash); #endif bp = sfileB; btop = &sfileB[slenB]; mp = member; while (++bp <= btop) { *(++mp) = -(bp->serial); while (bp[1].hash == bp->hash) { bp++; *(++mp) = bp->serial; } } *(++mp) = -1; #ifdef DEBUG for (j = 0; j <= slenB; j++) printf("member[%d] = %d\n", j, member[j]); #endif } unsort() /* * Build class vector */ { HASH *temp; register HASH *tp; register LINE *ap; register HASH *cp; LINE *evec; HASH *eclass; #ifdef DEBUG int i; #endif tp = temp = (HASH *)myalloc((slenA + 1) * sizeof(HASH), "unsort scratch"); ap = &sfileA[1]; evec = &sfileA[slenA]; while (ap <= evec) { #ifdef DEBUG printf("temp[%2d] := %06o\n", ap->serial, ap->hash); #endif tp[ap->serial] = ap->hash; ap++; } /* * Copy into class vector and free work space */ cp = &class[1]; eclass = &class[slenA]; tp = &temp[1]; while (cp <= eclass) *cp++ = *tp++; #ifndef OSK free((char *) temp); #else free((char *)temp - sizeof(int)); #endif #ifdef DEBUG printf("unsort exit\n"); for (i = 1; i <= slenA; i++) printf("class[%d] = %d %06o\n", i, class[i], class[i]); #endif } unsigned subseq() /* * Generate maximum common subsequence chain in clist[] */ { int a; register unsigned ktop; register LINENO b; register unsigned s; unsigned r; HASH i; HASH cand; klist[0] = newcand(0, 0, -1); klist[1] = newcand((LINENO) (slenA + 1), (LINENO) (slenB + 1), -1); ktop = 1; /* -> guard entry */ for (a = 1; a <= slenA; a++) { /* * For each non-zero element in fileA ... */ if ((i = class[a]) == 0) continue; cand = klist[0]; /* No candidate now */ r = 0; /* Current r-candidate */ do { #ifdef DEBUG printf("a = %d, i = %d, b = %d\n", a, i, member[i]); #endif /* * Perform the merge algorithm */ if ((b = member[i]) < 0) b = -b; #ifdef DEBUG printf("search(%d, %d, %d) -> %d\n", r, ktop, b, search(r, ktop, b)); #endif if (s = search(r, ktop, b)) { if (clist[klist[s]].b > b) { klist[r] = cand; r = s; cand = newcand((LINENO) a, b, klist[s-1]); #ifdef DEBUG dumpklist(ktop, "klist[s-1]->b > b"); #endif } if (s >= ktop) { klist[ktop + 1] = klist[ktop]; ktop++; #ifdef DEBUG klist[r] = cand; dumpklist(ktop, "extend"); #endif break; } } } while (member[++i] > 0); klist[r] = cand; } #ifdef DEBUG printf("last entry = %d\n", ktop - 1); #endif return(ktop - 1); /* Last entry found */ } HASH newcand(a, b, pred) LINENO a; /* Line in fileA */ LINENO b; /* Line in fileB */ LINENO pred; /* Link to predecessor, index in cand[] */ { register CANDIDATE *new; clength++; if (++clength >= csize) { csize += CSIZE_INC; clist = (CANDIDATE *)compact((char *)clist, csize * sizeof (CANDIDATE), "extending clist"); } new = &clist[clength - 1]; new->a = a; new->b = b; new->link = pred; return((HASH) (clength - 1)); } unsigned search(low, high, b) register unsigned low; register unsigned high; register LINENO b; /* * Search klist[low..top] (inclusive) for b. If klist[low]->b >= b, * return zero. Else return s such that klist[s-1]->b < b and * klist[s]->b >= b. Note that the algorithm presupposes the two * preset "fence" elements, (0, 0) and (slenA, slenB). */ { register LINENO temp; register unsigned mid; if (clist[klist[low]].b >= b) return(0); while ((mid = (low + high) / 2) > low) { if ((temp = clist[klist[mid]].b) > b) high = mid; else if (temp < b) low = mid; else { return(mid); } } return(mid + 1); } unravel(k) register int k; { register int i; register CANDIDATE *cp; int first_trailer; int difference; first_trailer = lenA - suffix; difference = lenB - lenA; #ifdef DEBUG printf("first trailer = %d, difference = %d\n", first_trailer, difference); #endif for (i = 0; i <= lenA; i++) { match[i] = (i <= prefix) ? i : (i > first_trailer) ? i + difference : 0; } #ifdef DEBUG printf("unravel\n"); #endif while (k != -1) { cp = &clist[k]; #ifdef DEBUG if (k < 0 || k >= clength) error("Illegal link -> %d", k); printf("match[%d] := %d\n", cp->a + prefix, cp->b + prefix); #endif match[cp->a + prefix] = cp->b + prefix; k = cp->link; } } /* * Check for hash matches (jackpots) and collect random access indices to * the two files. * * It should be possible to avoid doing most of the ftell's by noticing * that we are not doing a context diff and noticing that if a line * compares equal to the other file, we will not ever need to know its * file position. FIXME. */ check(fileAname, fileBname) char *fileAname; char *fileBname; { register int a; /* Current line in file A */ register int b; /* Current line in file B */ int jackpot; /* * The VAX C ftell() returns the address of the CURRENT record, not the * next one (as in DECUS C or, effectively, other C's). Hence, the values * are "off by one" in the array. OFFSET compensates for this. */ #ifdef vms #define OFFSET (-1) #else #define OFFSET 0 #endif b = 1; rewind(infd[0]); rewind(infd[1]); /* * See above; these would be over-written on VMS anyway. */ #ifndef vms oldseek[0] = ftell(infd[0]); newseek[0] = ftell(infd[1]); #endif jackpot = 0; #ifdef DEBUG printf("match vector\n"); for (a = 0; a <= lenA; a++) printf("match[%d] = %d\n", a, match[a]); #endif for (a = 1; a <= lenA; a++) { if (match[a] == 0) { /* Unique line in A */ oldseek[a+OFFSET] = ftell(infd[0]); getline(infd[0], text); continue; } while (b < match[a]) { /* Skip over unique lines in B */ newseek[b+OFFSET] = ftell(infd[1]); getline(infd[1], textb); b++; } /* * Compare the two, supposedly matching, lines. * Unless we are going to print these lines, don't bother to * remember where they are. We only print matching lines * if a context diff is happening, or if a jackpot occurs. */ if (cflag) { oldseek[a+OFFSET] = ftell(infd[0]); newseek[b+OFFSET] = ftell(infd[1]); } getline(infd[0], text); getline(infd[1], textb); if (strcmp(text, textb)) { #ifdef DEBUG fprintf(stderr, "Spurious match:\n"); fprintf(stderr, "line %d in %s, \"%s\"\n", a, fileAname, text); fprintf(stderr, "line %d in %s, \"%s\"\n", b, fileBname, textb); #endif match[a] = 0; jackpot++; } b++; } for (; b <= lenB; b++) { newseek[b+OFFSET] = ftell(infd[1]); getline(infd[1], textb); } /* * The logical converse to the code up above, for NON-VMS systems, to * store away an fseek() pointer at the beginning of the file. For VMS, * we need one at EOF... */ #ifdef vms oldseek[lenA] = ftell(infd[0]); getline(infd[0],text); /* Will hit EOF... */ newseek[lenB] = ftell(infd[1]); getline(infd[1],textb); /* Will hit EOF... */ #endif return(jackpot); } output(fileAname, fileBname) char *fileAname, *fileBname; { register int astart; register int aend; int bstart; register int bend; rewind(infd[0]); rewind(infd[1]); match[0] = 0; match[lenA+1] = lenB + 1; if (!eflag) { if (cflag) { coutput(fileAname, fileBname); return; } else { /* * Normal printout */ for (astart = 1; astart <= lenA; astart = aend + 1) { /* * New subsequence, skip over matching stuff */ while (astart <= lenA && match[astart] == (match[astart - 1] + 1)) astart++; /* * Found a difference, setup range and print it */ bstart = match[astart - 1] + 1; aend = astart - 1; while (aend < lenA && match[aend + 1] == 0) aend++; bend = match[aend + 1] - 1; match[aend] = bend; change(astart, aend, bstart, bend); } } } else { /* * Edit script output -- differences are output "backwards" * for the benefit of a line-oriented editor. */ for (aend = lenA; aend >= 1; aend = astart - 1) { while (aend >= 1 && match[aend] == (match[aend + 1] - 1) && match[aend] != 0) aend--; bend = match[aend + 1] - 1; astart = aend + 1; while (astart > 1 && match[astart - 1] == 0) astart--; bstart = match[astart - 1] + 1; match[astart] = bstart; change(astart, aend, bstart, bend); } } if (lenA == 0) change(1, 0, 1, lenB); } coutput(fileAname, fileBname) char *fileAname, *fileBname; { int astart; int aend; int bstart; int bend = 0; int change, ctmp, cFirst, cOld; int a1start, a1end, b1start, b1end, a2start, a2end, b2start, b2end; int astartLast, aendLast, bstartLast, bendLast; int low; int numDifferences = 0; #define CINSERTION 1 #define CDELETION 2 #define CCHANGE 4 for (astart = 1; astart <= lenA; astart = aend + 1) { if(!(change = findentry(&astart, &aend, &bstart, &bend))) break; /* find last entry to print in this diff */ cFirst = change; astartLast = astart; aendLast = a1end = aend; bstartLast = bstart; bendLast = b1end = bend; for(a1start = aendLast + 1; a1start <= lenA; a1start = a1end + 1) { if(!(ctmp = findentry(&a1start, &a1end, &b1start, &b1end)) || ((a1start - aendLast) >= 2*cflag && (b1start - bendLast) >= 2*cflag)) break; change |= ctmp; astartLast = a1start; aendLast = a1end; bstartLast = b1start; bendLast = b1end; } if(!numDifferences++) printHeader(fileAname, fileBname); fputs("***************\n*** ", stdout); range(astart, aendLast, 0); fputs(" ****\n", stdout); if (change & (CDELETION | CCHANGE)) { a1start = astart; a1end = aend; b2end = bend; cOld = cFirst; low = astart - cflag; while (a1start < astartLast) { a2start = a1end + 1; ctmp = findentry(&a2start, &a2end, &b2start, &b2end); fetch(oldseek, a1start, a1end, low, a2start - 1, lenA, infd[0], (cOld & CDELETION) ? "- " : "! "); cOld = ctmp; low = a2start; a1start = a2start; a1end = a2end; } fetch(oldseek, astartLast, aendLast, low, aendLast + cflag, lenA, infd[0], (cOld & CDELETION) ? "- " : "! "); } fputs("--- ", stdout); range(bstart, bendLast, 1); fputs(" ----\n", stdout); if (change & (CINSERTION | CCHANGE)) { a2start = astart; a2end = aend; b1start = bstart; b1end = b2end = bend; cOld = cFirst; low = bstart - cflag; while (a2start < astartLast) { a2start = a2end + 1; ctmp = findentry(&a2start, &a2end, &b2start, &b2end); fetch(newseek, b1start, b1end, low, b2start - 1, lenB, infd[1], (cOld & CINSERTION) ? "+ " : "! "); cOld = ctmp; low = b2start; b1start = b2start; b1end = b2end; } fetch(newseek, bstartLast, bendLast, low, bendLast + cflag, lenB, infd[1], (cOld & CINSERTION) ? "+ " : "! "); } aend = aendLast; bend = bendLast; } if (lenA == 0 && lenB > 0) { numDifferences++; printHeader(fileAname, fileBname); cchange(1, 0, 1, lenB); } if (!numDifferences) printf("No differences encountered\n"); } printHeader(fileAname, fileBname) char *fileAname, *fileBname; { long fileDate; #ifdef unix struct stat statbuf; #endif #ifdef AMIGA #ifdef LATTICE _TZ = "GMT0"; #endif if(infd[0] == tempfd) fileDate = time(0); else fileDate = getft(fileAname); printf("*** %s\t%s", fileAname, ctime(&fileDate)); if(infd[1] == tempfd) fileDate = time(0); else fileDate = getft(fileBname); printf("--- %s\t%s", fileBname, ctime(&fileDate)); #else #ifdef unix if(infd[0] == tempfd) fileDate = time(0); else { fstat(fileno(infd[0]), &statbuf); fileDate = statbuf.st_mtime; } printf("*** %s\t%s", fileAname, ctime(&fileDate)); if(infd[1] == tempfd) fileDate = time(0); else { fstat(fileno(infd[1]), &statbuf); fileDate = statbuf.st_mtime; } printf("--- %s\t%s", fileBname, ctime(&fileDate)); #else printf("*** %s\n--- %s\n", fileAname, fileBname); #endif #endif } int findentry(astartp, aendp, bstartp, bendp) int *astartp, *aendp, *bstartp, *bendp; { int save; register int astart = *astartp; register int aend; if(astart > lenA) return(0); save = match[astart - 1]; match[astart - 1] = *bendp; /* Skip over matching stuff */ while (astart <= lenA && match[astart] == (match[astart - 1] + 1)) astart++; *bstartp = match[astart - 1] + 1; aend = astart - 1; while (aend < lenA && match[aend + 1] == 0) aend++; *bendp = match[aend + 1] - 1; match[*astartp - 1] = save; *astartp = astart; *aendp = aend; if (astart > aend) { if (*bstartp > *bendp) return(0); else return(CINSERTION); } else if (*bstartp > *bendp) return(CDELETION); else return(CCHANGE); } /* * Output a non context diff change entry: * fileA[astart..aend] changed to fileB[bstart..bend] */ change(astart, aend, bstart, bend) int astart; int aend; int bstart; int bend; { char c; /* * This catches a "dummy" last entry */ if (astart > aend && bstart > bend) return; c = (astart > aend) ? 'a' : (bstart > bend) ? 'd' : 'c'; if (c == 'a') range(astart-1, astart-1, 0); /* Addition: just print one odd # */ else range(astart, aend, 0); /* Print both, if different */ putchar(c); if (!eflag) { if (c == 'd') range(bstart-1, bstart-1, 1); /* Deletion: just print one odd # */ else range(bstart, bend, 1); /* Print both, if different */ } putchar('\n'); if (c != 'a') { fetch(oldseek, astart, aend, 0, 0, lenA, infd[0], "< "); if (astart <= aend && bstart <= bend) printf("---\n"); } if (c != 'd') fetch(newseek, bstart, bend, 0, 0, lenB, infd[1], eflag ? "" : "> "); if (eflag && bstart <= bend) printf(".\n"); } /* * Output a context diff change entry: * fileA[astart..aend] changed to fileB[bstart..bend] */ cchange(astart, aend, bstart, bend) int astart; int aend; int bstart; int bend; { char c; /* * This catches a "dummy" last entry */ if (astart > aend && bstart > bend) return; c = (astart > aend) ? 'a' : (bstart > bend) ? 'd' : 'c'; fputs("***************\n*** ", stdout); range(astart, aend, 0); fputs(" ****\n", stdout); fetch(oldseek, astart, aend, astart - cflag, aend + cflag, lenA, infd[0], c=='d' ? "- " : "! "); fputs("--- ", stdout); range(bstart, bend, 1); fputs(" ----\n", stdout); fetch(newseek, bstart, bend, bstart - cflag, bend + cflag, lenB, infd[1], c=='a' ? "+ " : "! "); } range(from, to, w) int from; int to; int w; /* * Print a range */ { if (cflag) { if((from -= cflag) <= 0) from = 1; if((to += cflag) > len[w]) to = len[w]; if(!to) from = 0; } if (to > from) { printf("%d,%d", from, to); } else if (to < from) { printf("%d,%d", to, from); } else { printf("%d", from); } } fetch(seekvec, start, end, low, high, trueend, fd, pfx) long *seekvec; register int start; register int end; int low; int high; int trueend; FILE *fd; char *pfx; /* * Print the appropriate text */ { register int i; register int first; register int last; if (cflag) { if((first = low) <= 0) first = 1; if((last = high) > trueend) last = trueend; } else { first = start; last = end; } if (first > last) return; if (fseek(fd, seekvec[first], 0) != 0) { printf("?Can't read line %d at %08lx (hex) in file%c\n", start, seekvec[first], (fd == infd[0]) ? 'A' : 'B'); } else { for (i = first; i <= last; i++) { if (fgetss(text, sizeof text, fd) == NULL) { printf("** Unexpected end of file\n"); break; } #ifdef DEBUG printf("%5d: %s%s\n", i, pfx, text); #else fputs((cflag && (iend)) ? " " : pfx, stdout); fputs(text, stdout); putchar('\n'); #endif } } } /* * Input routine, read one line to buffer[], return TRUE on eof, else FALSE. * The terminating newline is always removed. If "-b" was given, trailing * whitespace (blanks and tabs) are removed and strings of blanks and * tabs are replaced by a single blank. Getline() does all hacking for * redirected input files. */ int getline(fd, buffer) FILE *fd; char *buffer; { register char *top; register char *fromp; register char c; if (fgetss(buffer, sizeof text, fd) == NULL) { *buffer = EOS; return(TRUE); } if (fd == stdin) { fputs(buffer, tempfd); putc('\n', tempfd); } if (bflag || iflag) { top = buffer; fromp = buffer; while ((c = *fromp++) != EOS) { if (bflag && (c == ' ' || c == '\t')) { c = ' '; while (*fromp == ' ' || *fromp == '\t') fromp++; } if (iflag) c = tolower(c); *top++ = c; } if (bflag && top[-1] == ' ') top--; *top = EOS; } return(FALSE); } static unsigned short crc16a[] = { 0000000, 0140301, 0140601, 0000500, 0141401, 0001700, 0001200, 0141101, 0143001, 0003300, 0003600, 0143501, 0002400, 0142701, 0142201, 0002100, }; static unsigned short crc16b[] = { 0000000, 0146001, 0154001, 0012000, 0170001, 0036000, 0024000, 0162001, 0120001, 0066000, 0074000, 0132001, 0050000, 0116001, 0104001, 0043000, }; unsigned short hash(buffer) char *buffer; /* * Return the CRC16 hash code for the buffer * Algorithm from Stu Wecker (Digital memo 130-959-002-00). */ { register unsigned short crc; register char *tp; register short temp; crc = 0; for (tp = buffer; *tp != EOS;) { temp = *tp++ ^ crc; /* XOR crc with new char */ crc = (crc >> 8) ^ crc16a[(temp & 0017)] ^ crc16b[(temp & 0360) >> 4]; } #ifdef DEBUG_ALL printf("%06o: %s\n", (crc == 0) ? 1 : crc, buffer); #endif return((crc == 0) ? (unsigned short) 1 : crc); } #ifdef vms opendir(which, arg, okfd) int which; /* Which file to open (0 or 1) */ char **arg; /* File name argument, &argv[which] */ FILE *okfd; /* File name (already open) */ { register char *tp; register int c; register char *newname; fgetname(okfd, text); /* * Skip over device name */ for (tp = text; (c = *tp) && c != ':'; tp++); if (c) tp++; else tp = text; /* * Skip over [UIC] or [PPN] if present */ if (*tp == '[' || *tp == '(') { while ((c = *tp++) && c != ']' && c != ')'); if (c == 0) { fprintf(stderr, "?Bug: bad file name \"%s\"\n", text); tp--; } } strcpy(text, tp); /* * Don't include version */ for (tp = text; (c = *tp) && c != ';'; tp++); *tp = 0; /* * Now, text has the file name, tp - text, its length, * and *arg the (possible) directory name. Create a new * file name for opening. */ #ifndef OSK if ((newname = malloc(tp - text + strlen(*arg) + 1)) == NULL) error("Out of space at start"); #else newname = myalloc(tp - text + strlen(*arg) + 1, "Out of space at start"); #endif concat(newname, *arg, text, NULL); if ((infd[which] = fopen(newname, "r")) == NULL) cant(*arg, "constructed input", 1); else *arg = newname; } #endif char * myalloc(amount, why) int amount; char *why; /* * Allocate or crash. */ { register char *pointer; #ifdef OSK amount += sizeof(int); #endif if ((pointer = malloc((unsigned) amount)) == NULL) noroom(why); #ifdef OSK *((int *)pointer) = amount; pointer += sizeof(int); #ifdef DEBUG fprintf(stderr, "Myalloc: %d at %06o\n", amount, pointer); #endif #endif return (pointer); } /* * Reallocate pointer, compacting storage * * The "compacting storage" part is probably not relevant any more. * There used to be horrid code here that malloc'd one byte and freed * it at magic times, to cause garbage collection of the freespace * or something. It's safely gone now, you didn't have to see it. * -- John Gilmore, Nebula Consultants, Sept 26, 1986 */ char * compact(pointer, new_amount, why) char *pointer; int new_amount; char *why; { register char *new_pointer; #ifndef OSK #ifdef TURBO extern void *realloc(); #else extern char *realloc(); #endif if ((new_pointer = realloc(pointer, (unsigned) new_amount)) == NULL){ noroom(why); } #else register int old_amount; new_amount += sizeof(int); if((new_pointer = malloc(new_amount)) == NULL) noroom(why); *(int *)new_pointer = new_amount; new_pointer += sizeof(int); old_amount = *(((int *)pointer)-1); /* _strass is like bcopy with the first two arguments reversted */ _strass(new_pointer, pointer, (new_amount <= old_amount ? new_amount : old_amount) - sizeof(int)); #ifdef DEBUG fprintf(stderr, "compact %d to %d from %06o to %06o\n", old_amount, new_amount, pointer, new_pointer); #endif free(pointer - sizeof(int)); #endif #ifndef OSK #ifdef DEBUG if (new_pointer != pointer) { fprintf(stderr, "moved from %06o to %06o\n", pointer, new_pointer); } /* rdump(new_pointer, why); */ #endif #endif return (new_pointer); } noroom(why) char *why; { fprintf(stderr, "?DIFF-F-out of room when %s\n", why); exit(IO_ERROR); } #ifdef DEBUG rdump(pointer, why) int *pointer; char *why; /* * Dump memory block */ { int *last; int count; last = ((int **)pointer)[-1]; fprintf(stderr, "dump %s of %06o -> %06o, %d words", why, pointer, last, last - pointer); last = (int *)(((int) last) & ~1); for (count = 0; pointer < last; ++count) { if ((count & 07) == 0) { fprintf(stderr, "\n%06o", pointer); } fprintf(stderr, "\t%06o", *pointer); pointer++; } fprintf(stderr, "\n"); } #endif cant(filename, what, fatalflag) char *filename; char *what; int fatalflag; /* * Can't open file message */ { fprintf(stderr, "Can't open %s file \"%s\": ", what, filename); #ifndef OSK perror((char *)NULL); #else prerr(0, errno); #endif if (fatalflag) { exit(fatalflag); } } #ifdef DEBUG dump(d_linep, d_len, d_which) LINE *d_linep; { register int i; printf("Dump of file%c, %d elements\n", "AB"[d_which], d_len); printf("linep @ %06o\n", d_linep); for (i = 0; i <= d_len; i++) { printf("%3d %6d %06o\n", i, d_linep[i].serial, d_linep[i].hash); } } dumpklist(kmax, why) int kmax; char *why; /* * Dump klist */ { register int i; register CANDIDATE *cp; register int count; printf("\nklist[0..%d] %s, clength = %d\n", kmax, why, clength); for (i = 0; i <= kmax; i++) { cp = &clist[klist[i]]; printf("%2d %2d", i, klist[i]); if (cp >= &clist[0] && cp < &clist[clength]) printf(" (%2d %2d -> %2d)\n", cp->a, cp->b, cp->link); else if (klist[i] == -1) printf(" End of chain\n"); else printf(" illegal klist element\n"); } for (i = 0; i <= kmax; i++) { count = -1; for (cp = (CANDIDATE *)klist[i]; cp > &clist[0]; cp = (CANDIDATE *)&cp->link) { if (++count >= 6) { printf("\n "); count = 0; } printf(" (%2d: %2d,%2d -> %d)", cp-clist, cp->a, cp->b, cp->link); } printf("\n"); } printf("*\n"); } #endif #ifdef TIMING ptime(why) char *why; /* * Dump time buffer */ { long ttemp; ttemp = time(NULL); printf("%ld seconds for %s\n", ttemp - sectiontime, why); sectiontime = ttemp; } #endif /* * s t r e q . c */ /*)LIBRARY */ #ifdef DOCUMENTATION title streq String Equality Test index String equality test synopsis .s.nf streq(a, b); char *a; char *b; .s.f Description Return TRUE if the strings are equal. Bugs #endif /* #define EOS 0 #define FALSE 0 #define TRUE 1 */ int streq(s1, s2) register char *s1; register char *s2; /* * TRUE if strings are identical */ { while (*s1++ == *s2) { if (*s2++ == EOS) return (TRUE); } return (FALSE); } /* * e r r o r . c */ /*)LIBRARY */ #ifdef DOCUMENTATION title error Fatal Error Exit index Fatal error exit synopsis .s.nf _error() error(format, args) char *format; .s.f documentation Fatal error exits. _error() halts, error() prints something on stderr and then halts. bugs Not a real vararg function. Only handles up to a fixed number of args. #endif /* VARARGS */ error(format, arg1, arg2) char *format; int arg1, arg2; /* * Error message before retiring. */ { fprintf(stderr, format, arg1, arg2); putc('\n', stderr); _error(); } _error() { exit(1); } /* * Fgetss() is like fgets() except that the terminating newline * is removed. */ char *fgetss(s, n, iop) char *s; FILE *iop; { int len; s[n - 1] = 0; if(fgets(s, n-1, iop) == NULL) return(NULL); len = strlen(s); if(s[len - 1] == '\n') s[len - 1] = 0; return(s); }