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