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
main(argc,argv)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 */
saverest(line,iptr)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 */
putsave(line,type)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
fixnum(lp)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
addgen(lp,fp)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
delgen(lp,fp)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
clean_up()360 clean_up()
361 {
362 unlink(tempfile);
363 unlink(otmp);
364 unlink(ntmp);
365 }
366
367
maket(file)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