|
|
1.1 root 1: /*
2: * Half-hearted version of diff (doesn't
3: * use the complicated algorithm).
4: * This algorithm works on arbitrary files
5: * but is only intended where changes are
6: * minimal and well-spaced. There is another
7: * algorithm which also takes space but is at
8: * least guaranteed to be linear in time and space (unlike
9: * Least Common Subsequence) and sometimes produces
10: * more realistic results (and sometimes it doesn't)
11: * Consult: "A Technique for Isolating Diffeences between
12: * Files", Paul Heckel, CACM, November 1978.
13: * This shares code with the normal
14: * diff though.
15: */
16:
17: #include "diff.h"
18:
19: #define NPEND 200 /* Default # pending lines */
20: #define NMATCH 2 /* Minimum lines in a row to have a match */
21:
22: /*
23: * Structure for each line
24: * This is referenced by the
25: * structure for each file
26: * below.
27: */
28: typedef struct LINE {
29: unsigned l_num; /* Line number */
30: struct LINE *l_next; /* Pointer for ungetting lines */
31: char l_text[]; /* Data of line */
32: } LINE;
33:
34: /*
35: * Structure of pending lines
36: * and ungot lines stored for
37: * each of the input files.
38: */
39: typedef struct PENDING {
40: FILE *p_fp; /* I/O stream for input */
41: int p_eof; /* Reached EOF on this input */
42: char *p_fn; /* File name */
43: LINE *p_master; /* Master input buffer */
44: LINE *p_work; /* Current working line */
45: LINE *p_ungot; /* Ungot lines */
46: LINE *p_lines[NPEND]; /* Pending lines */
47: } PENDING;
48:
49: PENDING p1, p2;
50:
51: LINE *lcopy();
52:
53: /*
54: * A NOP routine so that diff()
55: * gets invoked.
56: */
57: /* ARGSUSED */
58: diffh(args)
59: char **args;
60: {
61: }
62:
63: /*
64: * The actual code to produce list
65: * of differences by a stupider
66: * (but cheaper) algorithm.
67: */
68: diff(fp1, fp2)
69: FILE *fp1;
70: FILE *fp2;
71: {
72: register int top, bot;
73: register int i;
74:
75: p1.p_master = p1.p_work = (LINE *)alloc(sizeof(LINE) + LSIZE);
76: p2.p_master = p2.p_work = (LINE *)alloc(sizeof(LINE) + LSIZE);
77: p1.p_fp = fp1;
78: p2.p_fp = fp2;
79: p1.p_fn = fn1;
80: p2.p_fn = fn2;
81: while (lget(&p1) | lget(&p2)) {
82: register char *s1, *s2;
83:
84: s1 = p1.p_work->l_text;
85: s2 = p2.p_work->l_text;
86: if (*s1=='\0' && *s2=='\0')
87: continue;
88: if ((*equal)(s1, s2)) {
89: text(s1);
90: continue;
91: }
92: top = 0;
93: lpend(&p1, top);
94: lpend(&p2, top++);
95: do {
96: if (top >= NPEND)
97: cerr("Out of memory, lines %d and %d",
98: p1.p_lines[0]->l_num, p2.p_lines[0]->l_num);
99: /*
100: * Not checked, lget has
101: * to stop going over EOF
102: * by itself.
103: */
104: lget(&p1);
105: lget(&p2);
106: lpend(&p1, top);
107: lpend(&p2, top++);
108: bot = top-NMATCH;
109: for (i=0; i<bot; i++) {
110: if (lsearch(i, bot)) {
111: lunget(&p1, i+NMATCH, top);
112: break;
113: }
114: if (lsearch(bot, i)) {
115: lunget(&p2, i+NMATCH, top);
116: break;
117: }
118: }
119: } while (i>bot || i==bot && !lsearch(i, i));
120: }
121: }
122:
123: /*
124: * Get a line and put it into the current
125: * working position.
126: * Return 0 at EOF.
127: */
128: lget(pp)
129: register PENDING *pp;
130: {
131: if (pp->p_master != pp->p_work)
132: free(pp->p_work);
133: if (pp->p_ungot != NULL) {
134: pp->p_work = pp->p_ungot;
135: pp->p_ungot = pp->p_ungot->l_next;
136: return (1);
137: }
138: pp->p_work = pp->p_master;
139: return (lgets(pp));
140: }
141:
142: /*
143: * Unget pending lines between
144: * `beg' and `end' that are
145: * not already dealt with.
146: */
147: lunget(pp, beg, end)
148: register PENDING *pp;
149: register int beg, end;
150: {
151: while (--end >= beg) {
152: pp->p_lines[end]->l_next = pp->p_ungot;
153: pp->p_ungot = pp->p_lines[end];
154: pp->p_lines[end] = NULL;
155: }
156: }
157:
158: /*
159: * Copy a line to a new (allocated)
160: * line space.
161: */
162: LINE *
163: lcopy(lp)
164: register LINE *lp;
165: {
166: register LINE *rlp;
167: register char *cp;
168:
169: for (cp = lp->l_text; *cp != '\0'; cp++)
170: ;
171: rlp = (LINE *)alloc(cp-lp->l_text+1 + sizeof(LINE));
172: rlp->l_num = lp->l_num;
173: rlp->l_next = NULL;
174: strcpy(rlp->l_text, lp->l_text);
175: return (rlp);
176: }
177:
178: /*
179: * Save line away in a pending line
180: * position, specified by `pos'.
181: */
182: lpend(pp, pos)
183: register PENDING *pp;
184: register int pos;
185: {
186: if (pp->p_lines[pos] != NULL)
187: free(pp->p_lines[pos]);
188: if (pp->p_master != pp->p_work) {
189: pp->p_lines[pos] = pp->p_work;
190: pp->p_work = pp->p_master;
191: } else
192: pp->p_lines[pos] = lcopy(pp->p_work);
193: }
194:
195: /*
196: * Read a string from input (specified
197: * by the PENDING pointer) and throw away
198: * partial lines.
199: */
200: lgets(pp)
201: register PENDING *pp;
202: {
203: register int c;
204: register char *s;
205: register unsigned lim = LSIZE;
206:
207: s = pp->p_master->l_text;
208: if (pp->p_eof) {
209: *s = '\0';
210: return (0);
211: }
212: while (--lim > 0 && (c = getc(pp->p_fp)) != EOF)
213: if ((*s++ = c) == '\n')
214: break;
215: *s = '\0';
216: if (c == EOF) {
217: if (s != pp->p_master->l_text)
218: fprintf(stderr, "diff: partial line omitted from %s\n",
219: pp->p_fn);
220: pp->p_eof++;
221: return (0);
222: }
223: pp->p_master->l_num++;
224: return (1);
225: }
226:
227: /*
228: * Search for a match in the pending lines.
229: * The two parameters are `pos1' and `pos2'
230: * for the position to start searching in
231: * file1 and file2, respectively. The
232: * search looks for at least NMATCH lines.
233: */
234: lsearch(pos1, pos2)
235: int pos1;
236: int pos2;
237: {
238: register LINE **lpp1, **lpp2;
239: register int i;
240: register char *s1, *s2;
241: register int ln11, ln12, ln21, ln22;
242:
243: lpp1 = &p1.p_lines[pos1];
244: lpp2 = &p2.p_lines[pos2];
245: /*
246: * Empty strings are actually
247: * dummies for after EOF.
248: */
249: for (i=0; i<NMATCH; i++) {
250: s1 = lpp1[i]->l_text;
251: s2 = lpp2[i]->l_text;
252: if (*s1!='\0' || *s2!='\0')
253: if (!(*equal)(s1, s2))
254: break;
255: }
256: if (i < NMATCH)
257: return (0);
258: lpp1 = p1.p_lines;
259: lpp2 = p2.p_lines;
260: ln11 = lpp1[0]->l_num;
261: ln12 = lpp1[pos1-1]->l_num;
262: ln21 = lpp2[0]->l_num;
263: ln22 = lpp2[pos2-1]->l_num;
264: if (pos1 == 0)
265: append(ln11, ln21, ln22);
266: else if (pos2 == 0)
267: delete(ln11, ln12, ln21);
268: else
269: change(ln11, ln12, ln21, ln22);
270: for (i = 0; i < pos1; i++)
271: text1(lpp1[i]->l_text);
272: prsep();
273: for (i = 0; i < pos2; i++)
274: text2(lpp2[i]->l_text);
275: prend();
276: for (i=0; i<NMATCH; i++)
277: if (*(s1 = lpp1[i+pos1]->l_text) != '\0')
278: text(s1);
279: return (1);
280: }
281:
282: /*
283: * Allocator that also checks for
284: * an prints a message if out of space.
285: */
286: char *
287: alloc(nb)
288: unsigned nb;
289: {
290: register char *rp;
291:
292: if ((rp = calloc(nb, 1)) == NULL)
293: cerr("out of memory");
294: return (rp);
295: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.