1 # include "../hdr/defines.h"
2 
3 SCCSID(@(#)bdiff.c	4.1);
4 
5 /*
6 	This program segments two files into pieces of <= seglim lines
7 	(which is passed as a third argument or defaulted to some number)
8 	and then executes diff upon the pieces. The output of
9 	'diff' is then processed to make it look as if 'diff' had
10 	processed the files whole. The reason for all this is that seglim
11 	is a reasonable upper limit on the size of files that diff can
12 	process.
13 	NOTE -- by segmenting the files in this manner, it cannot be
14 	guaranteed that the 'diffing' of the segments will generate
15 	a minimal set of differences.
16 	This process is most definitely not equivalent to 'diffing'
17 	the files whole, assuming 'diff' could handle such large files.
18 
19 	'diff' is executed by a child process, generated by forking,
20 	and communicates with this program through pipes.
21 */
22 
23 int seglim;	/* limit of size of file segment to be generated */
24 
25 char diff[]	"/bin/diff";
26 char tempskel[] "/tmp/bdXXXXX";		/* used to generate temp file names */
27 char tempfile[32];
28 char otmp[32], ntmp[32];
29 int linenum;
30 
31 main(argc,argv)
32 int argc;
33 char *argv[];
34 {
35 	FILE *poldfile, *pnewfile, *tptr;
36 	char oline[BUFSIZ], nline[BUFSIZ], diffline[BUFSIZ];
37 	char *olp, *nlp, *dp;
38 	int i, otcnt, ntcnt;
39 	int pfd[2];
40 	FILE *poldtemp, *pnewtemp, *pipeinp;
41 	int status;
42 
43 	/*
44 	Set flags for 'fatal' so that it will clean up,
45 	produce a message, and terminate.
46 	*/
47 	Fflags = FTLMSG | FTLCLN | FTLEXIT;
48 
49 	setsig();
50 
51 	if (argc < 3 || argc > 5)
52 		fatal("arg count (bd1)");
53 
54 	if (equal(argv[1],"-") && equal(argv[2],"-"))
55 		fatal("both files standard input (bd2)");
56 	if (equal(argv[1],"-"))
57 		poldfile = stdin;
58 	else
59 		poldfile = xfopen(argv[1],0);
60 	if (equal(argv[2],"-"))
61 		pnewfile = stdin;
62 	else
63 		pnewfile = xfopen(argv[2],0);
64 
65 	seglim = 3500;
66 
67 	if (argc > 3) {
68 		if (argv[3][0] == '-' && argv[3][1] == 's')
69 			Fflags =& ~FTLMSG;
70 		else {
71 			if ((seglim = patoi(argv[3])) == -1)
72 				fatal("non-numeric limit (bd4)");
73 			if (argc == 5 && argv[4][0] == '-' &&
74 					argv[4][1] == 's')
75 				Fflags =& ~FTLMSG;
76 		}
77 	}
78 
79 	linenum = 0;
80 
81 	/*
82 	The following while-loop will prevent any lines
83 	common to the beginning of both files from being
84 	sent to 'diff'. Since the running time of 'diff' is
85 	non-linear, this will help improve performance.
86 	If, during this process, both files reach EOF, then
87 	the files are equal and the program will terminate.
88 	If either file reaches EOF before the other, the
89 	program will generate the appropriate 'diff' output
90 	itself, since this can be easily determined and will
91 	avoid executing 'diff' completely.
92 	*/
93 	while (1) {
94 		olp = fgets(oline,BUFSIZ,poldfile);
95 		nlp = fgets(nline,BUFSIZ,pnewfile);
96 
97 		if (!olp && !nlp)	/* files are equal */
98 			exit(0);
99 
100 		if (!olp) {
101 			/*
102 			The entire old file is a prefix of the
103 			new file. Generate the appropriate "append"
104 			'diff'-like output, which is of the form:
105 					nan,n
106 			where 'n' represents a line-number.
107 			*/
108 			addgen(nline,pnewfile);
109 		}
110 
111 		if (!nlp) {
112 			/*
113 			The entire new file is a prefix of the
114 			old file. Generate the appropriate "delete"
115 			'diff'-like output, which is of the form:
116 					n,ndn
117 			where 'n' represents a line-number.
118 			*/
119 			delgen(oline,poldfile);
120 		}
121 
122 		if (equal(olp,nlp))
123 			linenum++;
124 		else
125 			break;
126 	}
127 
128 	/*
129 	Here, first 'linenum' lines are equal.
130 	The following while-loop segments both files into
131 	seglim segments, forks and executes 'diff' on the
132 	segments, and processes the resulting output of
133 	'diff', which is read from a pipe.
134 	*/
135 	while (1) {
136 		/*
137 		If both files are at EOF, everything is done.
138 		*/
139 		if (!olp && !nlp)	/* finished */
140 			exit(0);
141 
142 		if (!olp) {
143 			/*
144 			Generate appropriate "append"
145 			output without executing 'diff'.
146 			*/
147 			addgen(nline,pnewfile);
148 		}
149 
150 		if (!nlp) {
151 			/*
152 			Generate appropriate "delete"
153 			output without executing 'diff'.
154 			*/
155 			delgen(oline,poldfile);
156 		}
157 
158 		/*
159 		Create a temporary file to hold a segment
160 		from the old file, and write it.
161 		*/
162 		poldtemp = maket(otmp);
163 		otcnt = 0;
164 		while(olp && otcnt < seglim) {
165 			fputs(oline,poldtemp);
166 			olp = fgets(oline,BUFSIZ,poldfile);
167 			otcnt++;
168 		}
169 		fclose(poldtemp);
170 
171 		/*
172 		Create a temporary file to hold a segment
173 		from the new file, and write it.
174 		*/
175 		pnewtemp = maket(ntmp);
176 		ntcnt = 0;
177 		while(nlp && ntcnt < seglim) {
178 			fputs(nline,pnewtemp);
179 			nlp = fgets(nline,BUFSIZ,pnewfile);
180 			ntcnt++;
181 		}
182 		fclose(pnewtemp);
183 
184 		/*
185 		Create pipes and fork.
186 		*/
187 		xpipe(pfd);
188 		if ((i = fork()) < 0) {
189 			close(pfd[0]);
190 			close(pfd[1]);
191 			fatal("cannot fork, try again (bd3)");
192 		}
193 		else if (i == 0) {	/* child process */
194 			close(pfd[0]);
195 			close(1);
196 			dup(pfd[1]);
197 			close(pfd[1]);
198 
199 			/*
200 			Execute 'diff' on the segment files.
201 			*/
202 			execl(diff,diff,otmp,ntmp,0);
203 			close(1);
204 			fatal(sprintf(Error,"cannot execute '%s' (bd5)",diff));
205 		}
206 		else {			/* parent process */
207 			close(pfd[1]);
208 			pipeinp = fdfopen(pfd[0],0);
209 
210 			/*
211 			Process 'diff' output.
212 			*/
213 			while ((dp = fgets(diffline,BUFSIZ,pipeinp))) {
214 				if (numeric(*dp))
215 					fixnum(diffline);
216 				else
217 					printf("%s",diffline);
218 			}
219 
220 			fclose(pipeinp);
221 
222 			/*
223 			EOF on pipe.
224 			*/
225 			wait(&status);
226 			if (status&~0x100)
227 				fatal(sprintf(Error,"'%s' failed (bd6)",diff));
228 		}
229 		linenum =+ seglim;
230 
231 		/*
232 		Remove temporary files.
233 		*/
234 		unlink(otmp);
235 		unlink(ntmp);
236 	}
237 }
238 
239 
240 /*
241 	Routine to save remainder of a file.
242 */
243 saverest(line,iptr)
244 char *line;
245 FILE *iptr;
246 {
247 	register char *lp;
248 	FILE *temptr;
249 
250 	temptr = maket(tempfile);
251 
252 	lp = line;
253 
254 	while (lp) {
255 		fputs(line,temptr);
256 		linenum++;
257 		lp = fgets(line,BUFSIZ,iptr);
258 	}
259 	fclose(temptr);
260 }
261 
262 
263 /*
264 	Routine to write out data saved by
265 	'saverest' routine and to remove the file.
266 */
267 putsave(line,type)
268 char *line;
269 char type;
270 {
271 	FILE *temptr;
272 
273 	temptr = xfopen(tempfile,0);
274 
275 	while (fgets(line,BUFSIZ,temptr))
276 		printf("%c %s",type,line);
277 
278 	fclose(temptr);
279 
280 	xunlink(tempfile);
281 }
282 
283 
284 fixnum(lp)
285 char *lp;
286 {
287 	int num;
288 
289 	while (*lp) {
290 		switch (*lp) {
291 
292 		case 'a':
293 		case 'c':
294 		case 'd':
295 		case ',':
296 		case '\n':
297 			printf("%c",*lp);
298 			lp++;
299 			break;
300 
301 		default:
302 			lp = satoi(lp,&num);
303 			num =+ linenum;
304 			printf("%d",num);
305 		}
306 	}
307 }
308 
309 
310 addgen(lp,fp)
311 char *lp;
312 FILE *fp;
313 {
314 	printf("%da%d,",linenum,linenum+1);
315 
316 	/*
317 	Save lines of new file.
318 	*/
319 	saverest(lp,fp);
320 
321 	printf("%d\n",linenum);
322 
323 	/*
324 	Output saved lines, as 'diff' would.
325 	*/
326 	putsave(lp,'>');
327 
328 	exit(0);
329 }
330 
331 
332 delgen(lp,fp)
333 char *lp;
334 FILE *fp;
335 {
336 	int savenum;
337 
338 	printf("%d,",linenum+1);
339 	savenum = linenum;
340 
341 	/*
342 	Save lines of old file.
343 	*/
344 	saverest(lp,fp);
345 
346 	printf("%dd%d\n",linenum,savenum);
347 
348 	/*
349 	Output saved lines, as 'diff' would.
350 	*/
351 	putsave(lp,'<');
352 
353 	exit(0);
354 }
355 
356 
357 clean_up()
358 {
359 	unlink(tempfile);
360 	unlink(otmp);
361 	unlink(ntmp);
362 }
363 
364 
365 maket(file)
366 char *file;
367 {
368 	FILE *iop;
369 
370 	copy(tempskel,file);
371 	iop = xfcreat(mktemp(file),0644);
372 
373 	return(iop);
374 }
375