|
|
1.1 root 1: /*
2: * Parts of diff that depend on the minimal common
3: * subsequence algorithm of Hirschberg.
4: */
5:
6: #include "diff.h"
7: #include <sys/mdata.h>
8:
9: #define NSBRK 512 /* Size of each sbrk -- allocator */
10: #define BADSBRK ((char *)-1) /* fail return from sbrk */
11: /* Marks last of each hash-equivalence class */
12: #define LAST ((unsigned)MININT)
13: /* Assume MININT is a single bit */
14:
15: static char partial[] = "diff: partial line omitted from %s\n";
16: static char jackpot[] = "Jackpot %d %d\n";
17:
18: /*
19: * Tables for the table-driven CRC16 algorithm.
20: * This is used as the hash of the input lines
21: * and should be relatively uniform statistically.
22: */
23: static unsigned short crctab1[] = {
24: 0000000, 0140301, 0140601, 0000500,
25: 0141401, 0001700, 0001200, 0141101,
26: 0143001, 0003300, 0003600, 0143501,
27: 0002400, 0142701, 0142201, 0002100
28: };
29:
30: static unsigned short crctab2[] = {
31: 0000000, 0146001, 0154001, 0012000,
32: 0170001, 0036000, 0024000, 0162001,
33: 0120001, 0066000, 0074000, 0132001,
34: 0050000, 0116001, 0104001, 0043000
35: };
36:
37: typedef struct LINES {
38: unsigned l_num;
39: unsigned short l_hash;
40: } LINES;
41:
42: /*
43: * A K-candidate entry
44: */
45: typedef struct CAND {
46: unsigned a;
47: unsigned b;
48: struct CAND *prev;
49: } CAND;
50:
51: int compar();
52: unsigned short inhash();
53: CAND *candidate();
54:
55: char line1[LSIZE]; /* First file's input line buffer */
56: char line2[LSIZE];
57: char nomem[] = "Out of memory";
58:
59: int *allrp;
60: int *allep;
61:
62: /*
63: * Called to invoke heuristic version of
64: * diff.
65: */
66: diffh(args)
67: char **args;
68: {
69: #ifdef COHERENT
70: execv("/usr/lib/diffh", args);
71: #endif
72: #ifdef MSDOS
73: #define MAXTAIL 128 /* Maximum command tail length */
74: char tail[MAXTAIL];
75:
76: tail[0] = '\0';
77: while (*++args != NULL) {
78: if (strlen(tail) + strlen(*args) + 2 > MAXTAIL)
79: cerr("command tail too long");
80: if (tail[0] != '\0')
81: strcat(tail, " ");
82: strcat(tail, *args);
83: }
84: exit(execall("diffh", tail));
85: #endif
86: cerr("-h doesn't work");
87: }
88:
89: /*
90: * Routine is given two file streams and
91: * it produces the minimal list of
92: * differences by the Hirschberg algorithm
93: */
94: diff(fp1, fp2)
95: FILE *fp1, *fp2;
96: {
97: register unsigned short hash;
98: register unsigned ln1;
99: register unsigned ln2;
100: register LINES *V;
101: register unsigned *P;
102: register unsigned *E;
103: register unsigned *K;
104: register unsigned *J;
105:
106: /*
107: * Calculate the hash tables for
108: * the second file
109: * and sort with hash as primary key,
110: * line-number secondary.
111: */
112: V = (LINES *)alloc(sizeof (LINES));
113: V->l_num = V->l_hash = 0; /* unneeded? */
114: ln2 = 0;
115: while ((hash = inhash(fp2, fn2)) != 0) {
116: register LINES *Vp;
117:
118: Vp = (LINES *)alloc(sizeof (LINES));
119: Vp->l_num = ++ln2;
120: Vp->l_hash = hash;
121: }
122: qsort(V+1, ln2, sizeof (LINES), compar);
123: /*
124: * Read first file, building
125: * a table per line of
126: * pointers to the first elements
127: * of the hash-equivalent list in file2.
128: */
129: P = (unsigned *)alloc(sizeof (unsigned));
130: for (ln1=0; (hash = inhash(fp1, fn1))!=0; ln1++) {
131: register unsigned *Pp;
132: register unsigned lo, mid, hi;
133:
134: Pp = (unsigned *)alloc(sizeof(unsigned));
135: lo = 1;
136: hi = ln2;
137: while (lo <= hi) {
138: mid = (lo+hi)/2;
139: if (hash <= V[mid].l_hash)
140: hi = mid-1;
141: else
142: lo = mid+1;
143: }
144: if (hi!=0 && V[hi].l_hash==V[hi+1].l_hash)
145: cerr("fatal search botch");
146: if (hash == V[++hi].l_hash)
147: *Pp = hi; else
148: *Pp = 0;
149: }
150:
151: /*
152: * Throw away the hash values.
153: * Mark the last line of each hash-equivalent
154: * class of lines and fake line 0.
155: */
156: {
157: register unsigned *Ep, *eEp;
158: register LINES *Vp;
159:
160: E = V;
161: E[0] = LAST;
162: for (Ep=E+1, Vp=V+1, eEp=&E[ln2]; Ep <= eEp; Vp++, Ep++) {
163: *Ep = Vp->l_num;
164: if (Ep==eEp || Vp->l_hash!=(Vp+1)->l_hash)
165: *Ep |= LAST;
166: }
167: }
168: {
169: register int i;
170: register unsigned *Pp;
171:
172: Pp = &E[ln2+3];
173: for (i=1; i<=ln1; i++)
174: Pp[i] = P[i];
175: P = Pp;
176: ralloc(&Pp[ln1+1]);
177: }
178: /*
179: * Set up to build list of K-candidates for
180: * all k. The K table overwrites P (when
181: * we add K[k+2] to the table we no longer
182: * need P[k].
183: */
184: {
185: register int k;
186: register int i;
187: register CAND *cp;
188:
189: K = &E[ln2+1];
190: K[0] = candidate(0, 0, NULL);
191: K[1] = candidate(ln1+1, ln2+1, NULL);
192: k = 0;
193: for (i=1; i<=ln1; i++)
194: if (P[i] != 0)
195: k = merge(K, k, i, E, P[i]);
196:
197: /*
198: * `k' is the length of the maximal
199: * common subsequence, thus K[k] points
200: * to the list of lines in the subsequnce.
201: * Store the list in `J' in a handier
202: * format. E, K (all but K[k]) and P
203: * are redundant so J overlays them.
204: */
205: cp = K[k];
206: J = E;
207: for (i=0; i<=ln1; i++)
208: J[i] = 0;
209: J[ln1+1] = ln2+1; /* fence */
210: while (cp != NULL) {
211: J[cp->a] = cp->b;
212: cp = cp->prev;
213: }
214: }
215: {
216: register unsigned i, j, k, l;
217:
218: /*
219: * Matching lines are now stored as pairs
220: * (i, J[i]), J[i]!=0. Produce output
221: * scripts and look for jackpots.
222: */
223: rewind(fp1);
224: rewind(fp2);
225: fgets(line1, LSIZE, fp1);
226: fgets(line2, LSIZE, fp2);
227: for (i=j=1; i<=ln1 || j<=ln2; ) {
228: if (j == J[i]) {
229: if (!(*equal)(line1, line2)) {
230: /*
231: * This can be avoided at
232: * the expense of another
233: * pass. So we settle for this
234: * potentially non-minimal result.
235: * Possibly this printout could
236: * disappear.
237: */
238: fprintf(stderr, jackpot, i, j);
239: change(i, i, j, j);
240: text1(line1);
241: prsep();
242: text2(line2);
243: prend();
244: } else
245: text(line1);
246: i++;
247: fgets(line1, LSIZE, fp1);
248: j++;
249: fgets(line2, LSIZE, fp2);
250: continue;
251: }
252: for (k=i; J[k]==0; k++)
253: ;
254: l = J[k];
255: if (i == k)
256: append(i, j, l-1);
257: else if (j == l)
258: delete(i, k-1, j);
259: else
260: change(i, k-1, j, l-1);
261: while (i != k) {
262: text1(line1);
263: i++;
264: fgets(line1, LSIZE, fp1);
265: }
266: prsep();
267: while (j != l) {
268: text2(line2);
269: j++;
270: fgets(line2, LSIZE, fp2);
271: }
272: prend();
273: }
274: }
275: }
276:
277: /*
278: * Read input characters and hash for each
279: * line. Return the hash value computed
280: * using CRC-16 methods. A zero value
281: * means EOF or error and thus will not
282: * be returned as a hash value.
283: */
284: unsigned short
285: inhash(fp, fn)
286: register FILE *fp;
287: char *fn;
288: {
289: register int c;
290: register int tmp;
291: register int short hash;
292: register int space;
293:
294: space = hash = 0;
295: while ((c = getc(fp)) != '\n') {
296: if (c == EOF) {
297: if (hash != 0)
298: fprintf(stderr, partial, fn);
299: return (0);
300: }
301: if (bflag && (c==' ' || c=='\t')) {
302: space++;
303: continue;
304: }
305: compute:
306: tmp = c^hash;
307: hash = (hash>>8) ^ crctab1[tmp&017] ^ crctab2[(tmp&0360)>>4];
308: if (space) {
309: c = ' ';
310: space = 0;
311: goto compute;
312: }
313: }
314: if (hash == 0)
315: hash++;
316: return (hash);
317: }
318:
319: /*
320: * Sort comparison routine.
321: * Hash value is primary key,
322: * line number secondary.
323: */
324: compar(a, b)
325: register LINES *a, *b;
326: {
327: if (a->l_hash < b->l_hash)
328: return (-1);
329: if (a->l_hash > b->l_hash)
330: return (1);
331: if (a->l_num < b->l_num)
332: return (-1);
333: if (a->l_num > b->l_num)
334: return (1);
335: return (0);
336: }
337:
338: /*
339: * Build an entry in the K-candidates
340: * table
341: */
342: CAND *
343: candidate(a, b, prev)
344: unsigned a;
345: unsigned b;
346: CAND *prev;
347: {
348: register CAND *v;
349:
350: v = (CAND *)alloc(sizeof(CAND));
351: v->a = a;
352: v->b = b;
353: v->prev = prev;
354: return (v);
355: }
356:
357: /*
358: * The merge step, called for
359: * each index in file 1.
360: * `k' is index of last filled element
361: * of `K', `i' is current index in
362: * file 1, and `p' is first element
363: * of class of lines in file 2
364: * equivalent to line `i' by hash value.
365: */
366: merge(K, k, i, E, p)
367: CAND **K;
368: unsigned k, i;
369: unsigned *E;
370: unsigned p;
371: {
372: register unsigned r;
373: register CAND *c;
374:
375: r = 0;
376: c = K[0];
377: do {
378: register unsigned lo, mid, hi;
379: register unsigned j;
380:
381: j = E[p]&~LAST;
382: lo = r;
383: hi = k;
384: if (K[lo]->b >= j)
385: continue;
386: while (lo <= hi) {
387: mid = (lo+hi)/2;
388: if (j <= K[mid]->b)
389: hi = mid-1; else
390: lo = mid+1;
391: }
392: if (K[hi]->b<j && j<=K[hi+1]->b) {
393: if (j < K[hi+1]->b) {
394: #if SIMUL
395: register CAND *prev;
396: prev = K[hi];
397: #endif
398: K[r] = c;
399: r = hi+1;
400: #if SIMUL
401: c = candidate(i, j, prev);
402: #else
403: c = candidate(i, j, K[hi]);
404: #endif
405: }
406: if (hi == k) {
407: K[k+2] = K[k+1];
408: k++;
409: break;
410: }
411: }
412: } while (!(E[p++] & LAST));
413: K[r] = c;
414: return (k);
415: }
416:
417: /*
418: * This allocator simply does sbrk calls so that
419: * it can grow (easily) the space as it needs without
420: * threading it. It also checks and prints and error
421: * if out of space.
422: */
423: char *
424: alloc(size)
425: register unsigned size;
426: {
427: register int *cp;
428:
429: if ((size = (size+sizeof(int)-1)/sizeof(int)) == 0)
430: cerr(nomem);
431: if (allrp==NULL || allrp+size>=allep) {
432: if ((allep = (int *)sbrk(NSBRK)) == BADSBRK)
433: cerr(nomem);
434: if (allrp == NULL)
435: allrp = allep;
436: allep += NSBRK/sizeof(int);
437: }
438: cp = allrp;
439: allrp += size;
440: return (cp);
441: }
442:
443: /*
444: * Like the brk system call, reset
445: * current end of memory to `ep'.
446: */
447: ralloc(ep)
448: register int *ep;
449: {
450: allrp = ep;
451: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.