diffdir.c (23544B)
1 /* 2 * This code contains changes by 3 * Gunnar Ritter, Freiburg i. Br., Germany, March 2003. All rights reserved. 4 * 5 * Conditions 1, 2, and 4 and the no-warranty notice below apply 6 * to these changes. 7 * 8 * 9 * Copyright (c) 1991 10 * The Regents of the University of California. All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by the University of 23 * California, Berkeley and its contributors. 24 * 4. Neither the name of the University nor the names of its contributors 25 * may be used to endorse or promote products derived from this software 26 * without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38 * SUCH DAMAGE. 39 * 40 * 41 * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved. 42 * 43 * Redistribution and use in source and binary forms, with or without 44 * modification, are permitted provided that the following conditions 45 * are met: 46 * Redistributions of source code and documentation must retain the 47 * above copyright notice, this list of conditions and the following 48 * disclaimer. 49 * Redistributions in binary form must reproduce the above copyright 50 * notice, this list of conditions and the following disclaimer in the 51 * documentation and/or other materials provided with the distribution. 52 * All advertising materials mentioning features or use of this software 53 * must display the following acknowledgement: 54 * This product includes software developed or owned by Caldera 55 * International, Inc. 56 * Neither the name of Caldera International, Inc. nor the names of 57 * other contributors may be used to endorse or promote products 58 * derived from this software without specific prior written permission. 59 * 60 * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA 61 * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR 62 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 63 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 64 * ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE 65 * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR 66 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 67 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 68 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 69 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 70 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 71 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 72 */ 73 74 /* Sccsid @(#)diffdir.c 1.30 (gritter) 1/22/06> */ 75 /* from 4.3BSD diffdir.c 4.9 (Berkeley) 8/28/84 */ 76 77 #include "diff.h" 78 #include <sys/types.h> 79 #include <sys/stat.h> 80 #include <sys/wait.h> 81 #include <fcntl.h> 82 #include <unistd.h> 83 #include <time.h> 84 #include <signal.h> 85 #include "sigset.h" 86 #include "pathconf.h" 87 88 #ifdef __GLIBC__ /* old glibcs don't know _XOPEN_SOURCE=600L yet */ 89 #ifndef S_IFSOCK 90 #ifdef __S_IFSOCK 91 #define S_IFSOCK __S_IFSOCK 92 #endif /* __S_IFSOCK */ 93 #endif /* !S_IFSOCK */ 94 #endif /* __GLIBC__ */ 95 96 /* 97 * diff - directory comparison 98 */ 99 #define d_flags d_ino 100 101 #define ONLY 1 /* Only in this directory */ 102 #define SAME 2 /* Both places and same */ 103 #define DIFFER 4 /* Both places and different */ 104 #define DIRECT 8 /* Directory */ 105 106 struct dir { 107 unsigned long long d_ino; 108 char *d_entry; 109 }; 110 111 static int header; 112 static char *title, *etitle; 113 static size_t titlesize; 114 static char procself[40]; 115 116 static void setfile(char **, char **, const char *); 117 static void scanpr(register struct dir *, int, const char *, const char *, 118 const char *, const char *, const char *); 119 static void only(struct dir *, int); 120 static struct dir *setupdir(const char *); 121 static int entcmp(const struct dir *, const struct dir *); 122 static void compare(struct dir *, char **); 123 static void calldiff(const char *, char **); 124 static int useless(register const char *); 125 static const char *mtof(mode_t mode); 126 static void putN(const char *, const char *, const char *, int); 127 static void putNreg(const char *, const char *, time_t, int); 128 static void putNnorm(FILE *, const char *, const char *, 129 FILE *, long long, int); 130 static void putNedit(FILE *, const char *, const char *, 131 FILE *, long long, int, int); 132 static void putNcntx(FILE *, const char *, const char *, 133 time_t, FILE *, long long, int); 134 static void putNunif(FILE *, const char *, const char *, 135 time_t, FILE *, long long, int); 136 static void putNhead(FILE *, const char *, const char *, time_t, int, 137 const char *, const char *); 138 static void putNdata(FILE *, FILE *, int, int); 139 static void putNdir(const char *, const char *, int); 140 static long long linec(const char *, FILE *); 141 static char *mkpath(const char *, const char *); 142 static void mktitle(void); 143 static int xclude(const char *); 144 145 void 146 diffdir(char **argv) 147 { 148 register struct dir *d1, *d2; 149 struct dir *dir1, *dir2; 150 register int i, n; 151 int cmp; 152 153 if (opt == D_IFDEF) { 154 fprintf(stderr, "%s: can't specify -I with directories\n", 155 progname); 156 done(); 157 } 158 status = 0; 159 if (opt == D_EDIT && (sflag || lflag)) 160 fprintf(stderr, 161 "%s: warning: shouldn't give -s or -l with -e\n", 162 progname); 163 for (n = 6, i = 1; diffargv[i+2]; i++) 164 n += strlen(diffargv[i]) + 1; 165 if (n > titlesize) 166 title = ralloc(title, titlesize = n); 167 title[0] = 0; 168 strcpy(title, "diff "); 169 for (i = 1; diffargv[i+2]; i++) { 170 if (!strcmp(diffargv[i], "-")) 171 continue; /* was -S, dont look silly */ 172 strcat(title, diffargv[i]); 173 strcat(title, " "); 174 } 175 for (etitle = title; *etitle; etitle++) 176 ; 177 /* 178 * This works around a bug present in (at least) Solaris 8 and 179 * 9: If exec() is called with /proc/self/object/a.out, the 180 * process hangs. It is possible, though, to use the executable 181 * of another process. So the parent diff is used instead of the 182 * forked child. 183 */ 184 i = getpid(); 185 snprintf(procself, sizeof procself, 186 #if defined (__linux__) 187 "/proc/%d/exe", 188 #elif defined (__FreeBSD__) || defined (__DragonFly__) || defined (__APPLE__) 189 "/proc/%d/file", 190 #else /* !__linux__, !__FreeBSD__, !__APPLE__ */ 191 "/proc/%d/object/a.out", 192 #endif /* !__linux__, !__FreeBSD__, !__APPLE__ */ 193 i); 194 setfile(&file1, &efile1, file1); 195 setfile(&file2, &efile2, file2); 196 argv[0] = file1; 197 argv[1] = file2; 198 dir1 = setupdir(file1); 199 dir2 = setupdir(file2); 200 d1 = dir1; d2 = dir2; 201 while (d1->d_entry != 0 || d2->d_entry != 0) { 202 if (d1->d_entry && useless(d1->d_entry)) { 203 d1++; 204 continue; 205 } 206 if (d2->d_entry && useless(d2->d_entry)) { 207 d2++; 208 continue; 209 } 210 if (d1->d_entry == 0) 211 cmp = 1; 212 else if (d2->d_entry == 0) 213 cmp = -1; 214 else 215 cmp = strcmp(d1->d_entry, d2->d_entry); 216 if (cmp < 0) { 217 if (lflag && !(Nflag&1)) 218 d1->d_flags |= ONLY; 219 else if (Nflag&1 || opt == D_NORMAL || 220 opt == D_CONTEXT || opt == D_UNIFIED) 221 only(d1, 1); 222 d1++; 223 } else if (cmp == 0) { 224 compare(d1, argv); 225 d1++; 226 d2++; 227 } else { 228 if (lflag && !(Nflag&2)) 229 d2->d_flags |= ONLY; 230 else if (Nflag&2 || opt == D_NORMAL || 231 opt == D_CONTEXT || opt == D_UNIFIED) 232 only(d2, 2); 233 d2++; 234 } 235 } 236 if (lflag) { 237 scanpr(dir1, ONLY, "Only in %.*s", file1, efile1, 0, 0); 238 scanpr(dir2, ONLY, "Only in %.*s", file2, efile2, 0, 0); 239 scanpr(dir1, SAME, "Common identical files in %.*s and %.*s", 240 file1, efile1, file2, efile2); 241 scanpr(dir1, DIFFER, "Binary files which differ in %.*s and %.*s", 242 file1, efile1, file2, efile2); 243 scanpr(dir1, DIRECT, "Common subdirectories of %.*s and %.*s", 244 file1, efile1, file2, efile2); 245 } 246 if (rflag) { 247 if (header && lflag) 248 printf("\f"); 249 for (d1 = dir1; d1->d_entry; d1++) { 250 if ((d1->d_flags & DIRECT) == 0) 251 continue; 252 strcpy(efile1, d1->d_entry); 253 strcpy(efile2, d1->d_entry); 254 calldiff(0, argv); 255 } 256 } 257 } 258 259 static void 260 setfile(char **fpp, char **epp, const char *file) 261 { 262 register char *cp; 263 int n; 264 265 if ((n = pathconf(file, _PC_PATH_MAX)) < 1024) 266 n = 1024; 267 *fpp = dalloc(strlen(file) + 2 + n); 268 if (*fpp == 0) { 269 oomsg(": ran out of memory\n"); 270 exit(1); 271 } 272 strcpy(*fpp, file); 273 for (cp = *fpp; *cp; cp++) 274 continue; 275 *cp++ = '/'; 276 *cp = '\0'; 277 *epp = cp; 278 } 279 280 static void 281 scanpr(register struct dir *dp, int test, const char *title, 282 const char *file1, const char *efile1, 283 const char *file2, const char *efile2) 284 { 285 int titled = 0; 286 287 for (; dp->d_entry; dp++) { 288 if ((dp->d_flags & test) == 0) 289 continue; 290 if (titled == 0) { 291 if (header == 0) 292 header = 1; 293 else 294 printf("\n"); 295 printf(title, 296 efile1 - file1 - 1, file1, 297 efile2 - file2 - 1, file2); 298 printf(":\n"); 299 titled = 1; 300 } 301 printf("\t%s\n", dp->d_entry); 302 } 303 } 304 305 static void 306 only(struct dir *dp, int which) 307 { 308 char *file = which == 1 ? file1 : file2; 309 char *other = which == 1 ? file2 : file1; 310 char *efile = which == 1 ? efile1 : efile2; 311 char *eother = which == 1 ? efile2 : efile1; 312 313 if (Nflag&which) { 314 char c = file[efile - file - 1]; 315 char d = other[eother - other - 1]; 316 file[efile - file - 1] = '\0'; 317 other[eother - other - 1] = '\0'; 318 putN(file, other, dp->d_entry, which); 319 file[efile - file - 1] = c; 320 other[eother - other - 1] = d; 321 } else 322 printf("Only in %.*s: %s\n", (int)(efile - file - 1), file, 323 dp->d_entry); 324 status = 1; 325 } 326 327 static struct dir * 328 setupdir(const char *cp) 329 { 330 register struct dir *dp = 0, *ep; 331 register struct dirent *rp; 332 register int nitems; 333 DIR *dirp; 334 335 dirp = opendir(cp); 336 if (dirp == NULL) { 337 fprintf(stderr, "%s: %s: %s\n", progname, cp, strerror(errno)); 338 done(); 339 } 340 nitems = 0; 341 dp = dalloc(sizeof (struct dir)); 342 if (dp == 0) { 343 oomsg(": ran out of memory\n"); 344 status = 2; 345 done(); 346 } 347 while (rp = readdir(dirp)) { 348 if (xflag && xclude(rp->d_name)) 349 continue; 350 ep = &dp[nitems++]; 351 ep->d_entry = 0; 352 ep->d_flags = 0; 353 ep->d_entry = dalloc(strlen(rp->d_name) + 1); 354 if (ep->d_entry == 0) { 355 oomsg(": out of memory\n"); 356 status = 2; 357 done(); 358 } 359 strcpy(ep->d_entry, rp->d_name); 360 dp = ralloc(dp, (nitems + 1) * sizeof (struct dir)); 361 } 362 dp[nitems].d_entry = 0; /* delimiter */ 363 closedir(dirp); 364 qsort(dp, nitems, sizeof (struct dir), 365 (int (*)(const void *, const void *))entcmp); 366 return (dp); 367 } 368 369 static int 370 entcmp(const struct dir *d1, const struct dir *d2) 371 { 372 return (strcmp(d1->d_entry, d2->d_entry)); 373 } 374 375 static void 376 compare(struct dir *dp, char **argv) 377 { 378 register int i, j; 379 int f1 = -1, f2 = -1; 380 mode_t fmt1, fmt2; 381 struct stat stb1, stb2; 382 char buf1[BUFSIZ], buf2[BUFSIZ]; 383 384 strcpy(efile1, dp->d_entry); 385 strcpy(efile2, dp->d_entry); 386 if (stat(file1, &stb1) < 0 || (fmt1 = stb1.st_mode&S_IFMT) == S_IFREG && 387 (f1 = open(file1, O_RDONLY)) < 0) { 388 perror(file1); 389 status = 2; 390 return; 391 } 392 if (stat(file2, &stb2) < 0 || (fmt2 = stb2.st_mode&S_IFMT) == S_IFREG && 393 (f2 = open(file2, O_RDONLY)) < 0) { 394 perror(file2); 395 close(f1); 396 status = 2; 397 return; 398 } 399 if (fmt1 != S_IFREG || fmt2 != S_IFREG) { 400 if (fmt1 == fmt2) { 401 switch (fmt1) { 402 case S_IFDIR: 403 dp->d_flags = DIRECT; 404 if (lflag || opt == D_EDIT) 405 goto closem; 406 if (opt != D_UNIFIED) 407 printf("Common subdirectories: " 408 "%s and %s\n", 409 file1, file2); 410 goto closem; 411 case S_IFBLK: 412 case S_IFCHR: 413 if (stb1.st_rdev == stb2.st_rdev) 414 goto same; 415 printf("Special files %s and %s differ\n", 416 file1, file2); 417 break; 418 case S_IFIFO: 419 if (stb1.st_dev == stb2.st_dev && 420 stb1.st_ino == stb2.st_ino) 421 goto same; 422 printf("Named pipes %s and %s differ\n", 423 file1, file2); 424 break; 425 default: 426 printf("Don't know how to compare " 427 "%ss %s and %s\n", 428 mtof(fmt1), file1, file2); 429 } 430 } else 431 printf("File %s is a %s while file %s is a %s\n", 432 file1, mtof(fmt1), file2, mtof(fmt2)); 433 if (lflag) 434 dp->d_flags |= DIFFER; 435 status = 1; 436 goto closem; 437 } 438 if (stb1.st_size != stb2.st_size) 439 goto notsame; 440 if (stb1.st_dev == stb2.st_dev && stb1.st_ino == stb2.st_ino) 441 goto same; 442 for (;;) { 443 i = read(f1, buf1, BUFSIZ); 444 j = read(f2, buf2, BUFSIZ); 445 if (i < 0 || j < 0 || i != j) 446 goto notsame; 447 if (i == 0 && j == 0) 448 goto same; 449 for (j = 0; j < i; j++) 450 if (buf1[j] != buf2[j]) 451 goto notsame; 452 } 453 same: 454 if (sflag == 0) 455 goto closem; 456 if (lflag) 457 dp->d_flags = SAME; 458 else 459 printf("Files %s and %s are identical\n", file1, file2); 460 goto closem; 461 notsame: 462 if (!aflag && (!ascii(f1) || !ascii(f2))) { 463 if (lflag) 464 dp->d_flags |= DIFFER; 465 else if (opt == D_NORMAL || opt == D_CONTEXT || 466 opt == D_UNIFIED) 467 printf("Binary files %s and %s differ\n", 468 file1, file2); 469 status = 1; 470 goto closem; 471 } 472 close(f1); close(f2); 473 anychange = 1; 474 if (lflag) 475 calldiff(title, argv); 476 else { 477 if (opt == D_EDIT) { 478 printf("ed - %s << '-*-END-*-'\n", dp->d_entry); 479 calldiff(0, argv); 480 } else { 481 printf("%s%s %s\n", title, file1, file2); 482 calldiff(0, argv); 483 } 484 if (opt == D_EDIT) 485 printf("w\nq\n-*-END-*-\n"); 486 } 487 return; 488 closem: 489 close(f1); close(f2); 490 } 491 492 static void 493 stackdiff(char **argv) 494 { 495 int oanychange; 496 char *ofile1, *ofile2, *oefile1, *oefile2; 497 struct stat ostb1, ostb2; 498 struct stackblk *ocurstack; 499 char *oargv[2]; 500 int oheader; 501 char *otitle, *oetitle; 502 size_t otitlesize; 503 jmp_buf orecenv; 504 505 (void)&oargv; 506 recdepth++; 507 oanychange = anychange; 508 ofile1 = file1; 509 ofile2 = file2; 510 oefile1 = efile1; 511 oefile2 = efile2; 512 ostb1 = stb1; 513 ostb2 = stb2; 514 ocurstack = curstack; 515 oargv[0] = argv[0]; 516 oargv[1] = argv[1]; 517 oheader = header; 518 otitle = title; 519 oetitle = etitle; 520 otitlesize = titlesize; 521 memcpy(orecenv, recenv, sizeof orecenv); 522 523 anychange = 0; 524 file1 = argv[0]; 525 file2 = argv[1]; 526 efile1 = NULL; 527 efile2 = NULL; 528 curstack = NULL; 529 header = 0; 530 title = NULL; 531 etitle = NULL; 532 titlesize = 0; 533 534 if (setjmp(recenv) == 0) 535 diffany(argv); 536 purgestack(); 537 538 anychange = oanychange; 539 file1 = ofile1; 540 file2 = ofile2; 541 efile1 = oefile1; 542 efile2 = oefile2; 543 stb1 = ostb1; 544 stb2 = ostb2; 545 curstack = ocurstack; 546 argv[0] = oargv[0]; 547 argv[1] = oargv[1]; 548 header = oheader; 549 title = otitle; 550 etitle = oetitle; 551 titlesize = otitlesize; 552 memcpy(recenv, orecenv, sizeof recenv); 553 recdepth--; 554 } 555 556 static const char *prargs[] = { "pr", "-h", 0, "-f", 0, 0 }; 557 558 static void 559 calldiff(const char *wantpr, char **argv) 560 { 561 int pid, cstatus, cstatus2, pv[2]; 562 563 if (wantpr == NULL && hflag == 0) { 564 stackdiff(argv); 565 return; 566 } 567 prargs[2] = wantpr; 568 fflush(stdout); 569 if (wantpr) { 570 mktitle(); 571 pipe(pv); 572 pid = fork(); 573 if (pid == -1) { 574 fprintf(stderr, "No more processes\n"); 575 done(); 576 } 577 if (pid == 0) { 578 close(0); 579 dup(pv[0]); 580 close(pv[0]); 581 close(pv[1]); 582 execvp(pr, (char **)prargs); 583 perror(pr); 584 done(); 585 } 586 } 587 pid = fork(); 588 if (pid == -1) { 589 fprintf(stderr, "%s: No more processes\n", progname); 590 done(); 591 } 592 if (pid == 0) { 593 if (wantpr) { 594 close(1); 595 dup(pv[1]); 596 close(pv[0]); 597 close(pv[1]); 598 } 599 execv(procself, diffargv); 600 execv(argv0, diffargv); 601 execvp(diff, diffargv); 602 perror(diff); 603 done(); 604 } 605 if (wantpr) { 606 close(pv[0]); 607 close(pv[1]); 608 } 609 while (wait(&cstatus) != pid) 610 continue; 611 if (cstatus != 0) { 612 if (WIFEXITED(cstatus) && WEXITSTATUS(cstatus) == 1) 613 status = 1; 614 else 615 status = 2; 616 } 617 while (wait(&cstatus2) != -1) 618 continue; 619 /* 620 if ((status >> 8) >= 2) 621 done(); 622 */ 623 } 624 625 int 626 ascii(int f) 627 { 628 char buf[BUFSIZ]; 629 register int cnt; 630 register char *cp; 631 632 lseek(f, 0, 0); 633 cnt = read(f, buf, BUFSIZ); 634 cp = buf; 635 while (--cnt >= 0) 636 if (*cp++ == '\0') 637 return (0); 638 return (1); 639 } 640 641 /* 642 * THIS IS CRUDE. 643 */ 644 static int 645 useless(register const char *cp) 646 { 647 648 if (cp[0] == '.') { 649 if (cp[1] == '\0') 650 return (1); /* directory "." */ 651 if (cp[1] == '.' && cp[2] == '\0') 652 return (1); /* directory ".." */ 653 } 654 if (start && strcmp(start, cp) > 0) 655 return (1); 656 return (0); 657 } 658 659 static const char * 660 mtof(mode_t mode) 661 { 662 switch (mode) { 663 case S_IFDIR: 664 return "directory"; 665 case S_IFCHR: 666 return "character special file"; 667 case S_IFBLK: 668 return "block special file"; 669 case S_IFREG: 670 return "plain file"; 671 case S_IFIFO: 672 return "named pipe"; 673 #ifdef S_IFSOCK 674 case S_IFSOCK: 675 return "socket"; 676 #endif /* S_IFSOCK */ 677 default: 678 return "unknown type"; 679 } 680 } 681 682 static void 683 putN(const char *dir, const char *odir, const char *file, int which) 684 { 685 struct stat st; 686 char *path; 687 char *opath; 688 689 path = mkpath(dir, file); 690 opath = mkpath(odir, file); 691 if (stat(path, &st) < 0) { 692 fprintf(stderr, "%s: %s: %s\n", progname, path, 693 strerror(errno)); 694 status = 2; 695 goto out; 696 } 697 switch (st.st_mode & S_IFMT) { 698 case S_IFREG: 699 putNreg(path, opath, st.st_mtime, which); 700 break; 701 case S_IFDIR: 702 putNdir(path, opath, which); 703 break; 704 default: 705 printf("Only in %s: %s\n", dir, file); 706 } 707 out: tfree(path); 708 tfree(opath); 709 } 710 711 static void 712 putNreg(const char *fn, const char *on, time_t mtime, int which) 713 { 714 long long lines; 715 FILE *fp; 716 FILE *op; 717 void (*opipe)(int) = SIG_DFL; 718 pid_t pid = 0; 719 720 if ((fp = fopen(fn, "r")) == NULL) { 721 fprintf(stderr, "%s: %s: %s\n", progname, fn, strerror(errno)); 722 status = 2; 723 return; 724 } 725 if ((lines = linec(fn, fp)) == 0 || fseek(fp, 0, SEEK_SET) != 0) 726 goto out; 727 if (lflag) { 728 int pv[2]; 729 opipe = sigset(SIGPIPE, SIG_IGN); 730 fflush(stdout); 731 prargs[2] = title; 732 pipe(pv); 733 switch (pid = fork()) { 734 case -1: 735 fprintf(stderr, "No more processes\n"); 736 done(); 737 /*NOTREACHED*/ 738 case 0: 739 close(0); 740 dup(pv[0]); 741 close(pv[0]); 742 close(pv[1]); 743 execvp(pr, (char **)prargs); 744 perror(pr); 745 done(); 746 } 747 close(pv[0]); 748 op = fdopen(pv[1], "w"); 749 } else 750 op = stdout; 751 fprintf(op, "%.*s %s %s\n", (int)(etitle - title - 1), title, 752 which == 1 ? fn : on, 753 which == 1 ? on : fn); 754 switch (opt) { 755 case D_NORMAL: 756 putNnorm(op, fn, on, fp, lines, which); 757 break; 758 case D_EDIT: 759 putNedit(op, fn, on, fp, lines, which, 0); 760 break; 761 case D_REVERSE: 762 putNedit(op, fn, on, fp, lines, which, 1); 763 break; 764 case D_CONTEXT: 765 putNcntx(op, fn, on, mtime, fp, lines, which); 766 break; 767 case D_NREVERSE: 768 putNedit(op, fn, on, fp, lines, which, 2); 769 break; 770 case D_UNIFIED: 771 putNunif(op, fn, on, mtime, fp, lines, which); 772 break; 773 } 774 if (lflag) { 775 fclose(op); 776 while (wait(NULL) != pid); 777 sigset(SIGPIPE, opipe); 778 } 779 out: fclose(fp); 780 } 781 782 static void 783 putNnorm(FILE *op, const char *fn, const char *on, 784 FILE *fp, long long lines, int which) 785 { 786 int pfx; 787 788 if (which == 1) { 789 fprintf(op, "1,%lldd0\n", lines); 790 pfx = '<'; 791 } else { 792 fprintf(op, "0a1,%lld\n", lines); 793 pfx = '>'; 794 } 795 putNdata(op, fp, pfx, ' '); 796 } 797 798 static void 799 putNedit(FILE *op, const char *fn, const char *on, 800 FILE *fp, long long lines, int which, int reverse) 801 { 802 switch (reverse) { 803 case 0: 804 if (which == 1) 805 fprintf(op, "1,%lldd\n", lines); 806 else { 807 fprintf(op, "0a\n"); 808 putNdata(op, fp, 0, 0); 809 fprintf(op, ".\n"); 810 } 811 break; 812 case 1: 813 if (which == 1) 814 fprintf(op, "d1 %lld\n", lines); 815 else { 816 fprintf(op, "a0\n"); 817 putNdata(op, fp, 0, 0); 818 fprintf(op, ".\n"); 819 } 820 break; 821 case 2: 822 if (which == 1) 823 fprintf(op, "d1 %lld\n", lines); 824 else { 825 fprintf(op, "a0 %lld\n", lines); 826 putNdata(op, fp, 0, 0); 827 } 828 break; 829 } 830 } 831 832 static void 833 putNcntx(FILE *op, const char *fn, const char *on, time_t mtime, 834 FILE *fp, long long lines, int which) 835 { 836 putNhead(op, fn, on, mtime, which, "***", "---"); 837 fprintf(op, "***************\n*** "); 838 if (which == 1) 839 fprintf(op, "1,%lld", lines); 840 else 841 putc('0', op); 842 fprintf(op, " ****\n"); 843 if (which != 1) 844 fprintf(op, "--- 1,%lld ----\n", lines); 845 putNdata(op, fp, which == 1 ? '-' : '+', ' '); 846 if (which == 1) 847 fprintf(op, "--- 0 ----\n"); 848 } 849 850 static void 851 putNunif(FILE *op, const char *fn, const char *on, time_t mtime, 852 FILE *fp, long long lines, int which) 853 { 854 putNhead(op, fn, on, mtime, which, "---", "+++"); 855 fprintf(op, "@@ "); 856 fprintf(op, which == 1 ? "-1,%lld +0,0" : "-0,0 +1,%lld", lines); 857 fprintf(op, " @@\n"); 858 putNdata(op, fp, which == 1 ? '-' : '+', 0); 859 } 860 861 static void 862 putNhead(FILE *op, const char *fn, const char *on, time_t mtime, int which, 863 const char *p1, const char *p2) 864 { 865 time_t t1, t2; 866 const char *f1, *f2; 867 868 t1 = which == 1 ? mtime : 0; 869 t2 = which == 1 ? 0 : mtime; 870 f1 = which == 1 ? fn : on; 871 f2 = which == 1 ? on : fn; 872 fprintf(op, "%s %s\t%s", p1, f1, ctime(&t1)); 873 fprintf(op, "%s %s\t%s", p2, f2, ctime(&t2)); 874 } 875 876 static void 877 putNdata(FILE *op, FILE *fp, int pfx, int sec) 878 { 879 int c, lastc = '\n', col = 0; 880 881 while ((c = getc(fp)) != EOF) { 882 if (lastc == '\n') { 883 col = 0; 884 if (pfx) 885 putc(pfx, op); 886 if (sec) 887 putc(sec, op); 888 } 889 if (c == '\t' && tflag) { 890 do 891 putc(' ', op); 892 while (++col & 7); 893 } else { 894 putc(c, op); 895 col++; 896 } 897 lastc = c; 898 } 899 if (lastc != '\n') { 900 if (aflag) 901 fprintf(op, "\n\\ No newline at end of file\n"); 902 else 903 putc('\n', op); 904 } 905 } 906 907 static void 908 putNdir(const char *fn, const char *on, int which) 909 { 910 DIR *Dp; 911 struct dirent *dp; 912 913 if ((Dp = opendir(fn)) == NULL) { 914 fprintf(stderr, "%s: %s: %s\n", progname, fn, strerror(errno)); 915 status = 2; 916 return; 917 } 918 while ((dp = readdir(Dp)) != NULL) { 919 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || 920 dp->d_name[1] == '.' && 921 dp->d_name[2] == '\0')) 922 continue; 923 if (xflag && xclude(dp->d_name)) 924 continue; 925 putN(fn, on, dp->d_name, which); 926 } 927 closedir(Dp); 928 } 929 930 static long long 931 linec(const char *fn, FILE *fp) 932 { 933 int c, lastc = '\n'; 934 long long cnt = 0; 935 936 while ((c = getc(fp)) != EOF) { 937 if (c == '\n') 938 cnt++; 939 lastc = c; 940 } 941 if (lastc != '\n') { 942 if (!aflag) 943 fprintf(stderr, 944 "Warning: missing newline at end of file %s\n", 945 fn); 946 cnt++; 947 } 948 return cnt; 949 } 950 951 static char * 952 mkpath(const char *dir, const char *file) 953 { 954 char *path, *pp; 955 const char *cp; 956 957 pp = path = talloc(strlen(dir) + strlen(file) + 2); 958 for (cp = dir; *cp; cp++) 959 *pp++ = *cp; 960 if (pp > path && pp[-1] != '/') 961 *pp++ = '/'; 962 for (cp = file; *cp; cp++) 963 *pp++ = *cp; 964 *pp = '\0'; 965 return path; 966 } 967 968 static void 969 mktitle(void) 970 { 971 int n; 972 973 n = strlen(file1) + strlen(file2) + 2; 974 if (etitle - title + n < titlesize) { 975 titlesize = n; 976 n = etitle - title; 977 title = ralloc(title, titlesize); 978 etitle = &title[n]; 979 } 980 sprintf(etitle, "%s %s", file1, file2); 981 } 982 983 static int 984 xclude(const char *fn) 985 { 986 extern int gmatch(const char *, const char *); 987 struct xclusion *xp; 988 989 for (xp = xflag; xp; xp = xp->x_nxt) 990 if (gmatch(fn, xp->x_pat)) 991 return 1; 992 return 0; 993 }