1 # include "../hdr/defines.h"
2 # include "pathnames.h"
3 
4 static char Sccsid[] = "@(#)bdiff.c	4.6	11/11/90";
5 
6 /*
7 	This program segments two files into pieces of <= seglim lines
8 	(which is passed as a third argument or defaulted to some number)
9 	and then executes diff upon the pieces. The output of
10 	'diff' is then processed to make it look as if 'diff' had
11 	processed the files whole. The reason for all this is that seglim
12 	is a reasonable upper limit on the size of files that diff can
13 	process.
14 	NOTE -- by segmenting the files in this manner, it cannot be
15 	guaranteed that the 'diffing' of the segments will generate
16 	a minimal set of differences.
17 	This process is most definitely not equivalent to 'diffing'
18 	the files whole, assuming 'diff' could handle such large files.
19 
20 	'diff' is executed by a child process, generated by forking,
21 	and communicates with this program through pipes.
22 */
23 
24 int seglim;	/* limit of size of file segment to be generated */
25 
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 			dup2(pfd[1], 1);
196 			if (pfd[1] != 1)
197 				close(pfd[1]);
198 
199 			/*
200 			Execute 'diff' on the segment files.
201 			*/
202 			execl(_PATH_DIFF,"diff",otmp,ntmp,0);
203 			close(1);
204 			sprintf(Error,"cannot execute '%s' (bd5)",_PATH_DIFF);
205 			fatal(Error);
206 		}
207 		else {			/* parent process */
208 			close(pfd[1]);
209 			pipeinp = fdopen(pfd[0],"r");
210 
211 			/*
212 			Process 'diff' output.
213 			*/
214 			while ((dp = fgets(diffline,BUFSIZ,pipeinp))) {
215 				if (numeric(*dp))
216 					fixnum(diffline);
217 				else
218 					printf("%s",diffline);
219 			}
220 
221 			fclose(pipeinp);
222 
223 			/*
224 			EOF on pipe.
225 			*/
226 			wait(&status);
227 			if (status&~0x100) {
228 				sprintf(Error,"'%s' failed (bd6)",_PATH_DIFF);
229 				fatal(Error);
230 			}
231 		}
232 		linenum += seglim;
233 
234 		/*
235 		Remove temporary files.
236 		*/
237 		unlink(otmp);
238 		unlink(ntmp);
239 	}
240 }
241 
242 
243 /*
244 	Routine to save remainder of a file.
245 */
246 saverest(line,iptr)
247 char *line;
248 FILE *iptr;
249 {
250 	register char *lp;
251 	FILE *temptr;
252 
253 	temptr = maket(tempfile);
254 
255 	lp = line;
256 
257 	while (lp) {
258 		fputs(line,temptr);
259 		linenum++;
260 		lp = fgets(line,BUFSIZ,iptr);
261 	}
262 	fclose(temptr);
263 }
264 
265 
266 /*
267 	Routine to write out data saved by
268 	'saverest' routine and to remove the file.
269 */
270 putsave(line,type)
271 char *line;
272 char type;
273 {
274 	FILE *temptr;
275 
276 	temptr = xfopen(tempfile,0);
277 
278 	while (fgets(line,BUFSIZ,temptr))
279 		printf("%c %s",type,line);
280 
281 	fclose(temptr);
282 
283 	xunlink(tempfile);
284 }
285 
286 
287 fixnum(lp)
288 char *lp;
289 {
290 	int num;
291 
292 	while (*lp) {
293 		switch (*lp) {
294 
295 		case 'a':
296 		case 'c':
297 		case 'd':
298 		case ',':
299 		case '\n':
300 			printf("%c",*lp);
301 			lp++;
302 			break;
303 
304 		default:
305 			lp = satoi(lp,&num);
306 			num += linenum;
307 			printf("%d",num);
308 		}
309 	}
310 }
311 
312 
313 addgen(lp,fp)
314 char *lp;
315 FILE *fp;
316 {
317 	printf("%da%d,",linenum,linenum+1);
318 
319 	/*
320 	Save lines of new file.
321 	*/
322 	saverest(lp,fp);
323 
324 	printf("%d\n",linenum);
325 
326 	/*
327 	Output saved lines, as 'diff' would.
328 	*/
329 	putsave(lp,'>');
330 
331 	exit(0);
332 }
333 
334 
335 delgen(lp,fp)
336 char *lp;
337 FILE *fp;
338 {
339 	int savenum;
340 
341 	printf("%d,",linenum+1);
342 	savenum = linenum;
343 
344 	/*
345 	Save lines of old file.
346 	*/
347 	saverest(lp,fp);
348 
349 	printf("%dd%d\n",linenum,savenum);
350 
351 	/*
352 	Output saved lines, as 'diff' would.
353 	*/
354 	putsave(lp,'<');
355 
356 	exit(0);
357 }
358 
359 
360 clean_up()
361 {
362 	unlink(tempfile);
363 	unlink(otmp);
364 	unlink(ntmp);
365 }
366 
367 
368 maket(file)
369 char *file;
370 {
371 	FILE *iop;
372 
373 	copy(tempskel,file);
374 	iop = xfcreat(mktemp(file),0644);
375 
376 	return(iop);
377 }
378