1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 31 #pragma ident "%Z%%M% %I% %E% SMI" 32 33 /* 34 * sdiff [-l] [-s] [-w #] [-o output] file1 file2 35 * does side by side diff listing 36 * -l leftside only for identical lines 37 * -s silent; only print differences 38 * -w # width of output 39 * -o output interactive creation of new output commands: 40 * s silent; do not print identical lines 41 * v turn off silent 42 * l copy left side to output 43 * r copy right side to output 44 * e l call ed with left side 45 * e r call ed with right side 46 * e b call ed with cat of left and right 47 * e call ed with empty file 48 * q exit from program 49 * 50 * functions: 51 * cmd decode diff commands 52 * put1 output left side 53 * put2 output right side 54 * putmid output gutter 55 * putline output n chars to indicated file 56 * getlen calculate length of strings with tabs 57 * cmdin read and process interactive cmds 58 * cpp copy from file to file 59 * edit call ed with file 60 */ 61 62 #include <stdio.h> 63 #include <ctype.h> 64 #include <signal.h> 65 #include <sys/types.h> 66 #include <sys/stat.h> 67 #include <sys/wait.h> 68 #include <unistd.h> 69 #include <stdlib.h> 70 #include <locale.h> 71 #include <limits.h> 72 #include <string.h> 73 #include <wchar.h> 74 75 #define LMAX BUFSIZ 76 #define BMAX BUFSIZ 77 #define STDOUT 1 78 #define WGUTTER 6 79 #define WLEN (WGUTTER * 2 + WGUTTER + 2) 80 #define PROMPT '%' 81 82 static const char twoblanks[3] = " "; 83 84 static const char *DIFF = "diff -b "; 85 static char diffcmd[BMAX]; 86 static char inbuf[10]; 87 88 static int llen = 130; /* Default maximum line length written out */ 89 static int hlen; /* Half line length with space for gutter */ 90 static int len1; /* Calculated length of left side */ 91 static int nchars; /* Number of characters in left side - */ 92 /* used for tab expansion */ 93 static char change = ' '; 94 static int leftonly = 0; /* if set print left side only for */ 95 /* identical lines */ 96 static int silent = 0; /* if set do not print identical lines */ 97 static int midflg = 0; /* set after middle was output */ 98 static int rcode = 0; /* return code */ 99 100 101 static char *file1; 102 static FILE *fdes1; 103 104 static char *file2; 105 static FILE *fdes2; 106 107 static FILE *diffdes; 108 109 static int oflag; 110 static char *ofile; 111 static FILE *odes; 112 113 static char *ltemp; 114 static FILE *left; 115 116 static char *rtemp; 117 static FILE *right; 118 119 static FILE *tempdes; 120 static char *temp; 121 122 /* decoded diff cmd- left side from to; right side from, to */ 123 124 static int from1, to1, from2, to2; 125 126 static int num1, num2; /* line count for left side file and right */ 127 static int tempfd = -1; 128 129 static char *filename(char *, char *); 130 static char *fgetline(FILE *); 131 static int put1(void); 132 static int put2(void); 133 static void putline(FILE *, char *, int); 134 static int cmd(char *); 135 static int getlen(int, char *); 136 static void putmid(int); 137 static void error(char *, char *); 138 static void onintr(void); 139 static void sremove(void); 140 static void cmdin(void); 141 static void cpp(char *, FILE *, FILE *); 142 static void edit(char *); 143 144 int 145 main(int argc, char **argv) 146 { 147 int com; 148 int n1, n2, n; 149 char *bp; 150 int lfd = -1; 151 int rfd = -1; 152 153 if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 154 (void) signal((int)SIGHUP, (void (*)(int))onintr); 155 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 156 (void) signal((int)SIGINT, (void (*)(int))onintr); 157 if (signal(SIGPIPE, SIG_IGN) != SIG_IGN) 158 (void) signal((int)SIGPIPE, (void (*)(int))onintr); 159 if (signal(SIGTERM, SIG_IGN) != SIG_IGN) 160 (void) signal((int)SIGTERM, (void (*)(int))onintr); 161 162 (void) setlocale(LC_ALL, ""); 163 #if !defined(TEXT_DOMAIN) 164 #define TEXT_DOMAIN "SYS_TEST" 165 #endif 166 (void) textdomain(TEXT_DOMAIN); 167 168 while (--argc > 1 && **++argv == '-') { 169 switch (*++*argv) { 170 171 case 'w': 172 /* -w# instead of -w # */ 173 if (*++*argv) 174 llen = atoi(*argv); 175 else { 176 argc--; 177 llen = atoi(*++argv); 178 } 179 if (llen < WLEN) 180 error(gettext("Wrong line length %s"), *argv); 181 if (llen > LMAX) 182 llen = LMAX; 183 break; 184 185 case 'l': 186 leftonly++; 187 break; 188 189 case 's': 190 silent++; 191 break; 192 case 'o': 193 oflag++; 194 argc--; 195 ofile = *++argv; 196 break; 197 default: 198 error(gettext("Illegal argument: %s"), *argv); 199 } 200 } 201 if (argc != 2) { 202 (void) fprintf(stderr, gettext( 203 "Usage: sdiff [-l] [-s] [-o output] [-w #] file1 file2\n")); 204 return (2); 205 } 206 207 file1 = *argv++; 208 file2 = *argv; 209 file1 = filename(file1, file2); 210 file2 = filename(file2, file1); 211 hlen = (llen - WGUTTER +1)/2; 212 213 if ((fdes1 = fopen(file1, "r")) == NULL) 214 error(gettext("Cannot open: %s"), file1); 215 216 if ((fdes2 = fopen(file2, "r")) == NULL) 217 error(gettext("Cannot open: %s"), file2); 218 219 if (oflag) { 220 if (tempfd == -1) { 221 temp = strdup("/tmp/sdiffXXXXXX"); 222 tempfd = mkstemp(temp); 223 if (tempfd == -1) { 224 error(gettext( 225 "Cannot open/create temp %s"), temp); 226 free(temp); 227 temp = 0; 228 } 229 } 230 ltemp = strdup("/tmp/sdifflXXXXXX"); 231 if ((lfd = mkstemp(ltemp)) == -1 || 232 (left = fdopen(lfd, "w")) == NULL) 233 error(gettext( 234 "Cannot open/create temp %s"), 235 ltemp); 236 rtemp = strdup("/tmp/sdiffrXXXXXX"); 237 if ((rfd = mkstemp(rtemp)) == -1 || 238 (right = fdopen(rfd, "w")) == NULL) 239 error(gettext( 240 "Cannot open/create temp file %s"), 241 rtemp); 242 if ((odes = fopen(ofile, "w")) == NULL) 243 error(gettext("Cannot open output %s"), ofile); 244 } 245 /* Call DIFF command */ 246 (void) strcpy(diffcmd, DIFF); 247 (void) strcat(diffcmd, file1); 248 (void) strcat(diffcmd, " "); 249 (void) strcat(diffcmd, file2); 250 diffdes = popen(diffcmd, "r"); 251 252 num1 = num2 = 0; 253 254 /* 255 * Read in diff output and decode commands 256 * "change" is used to determine character to put in gutter 257 * num1 and num2 counts the number of lines in file1 and 2 258 */ 259 260 n = 0; 261 while ((bp = fgetline(diffdes)) != NULL) { 262 change = ' '; 263 com = cmd(bp); 264 265 /* 266 * handles all diff output that is not cmd 267 * lines starting with <, >, ., --- 268 */ 269 if (com == 0) 270 continue; 271 272 /* Catch up to from1 and from2 */ 273 rcode = 1; 274 n1 = from1 - num1; 275 n2 = from2 - num2; 276 n = n1 > n2 ? n2 : n1; 277 if (com == 'c' && n > 0) 278 n--; 279 if (silent) 280 (void) fputs(bp, stdout); 281 while (n-- > 0) { 282 (void) put1(); 283 (void) put2(); 284 if (!silent) 285 (void) putc('\n', stdout); 286 midflg = 0; 287 } 288 289 /* Process diff cmd */ 290 switch (com) { 291 292 case 'a': 293 change = '>'; 294 while (num2 < to2) { 295 (void) put2(); 296 (void) putc('\n', stdout); 297 midflg = 0; 298 } 299 break; 300 301 case 'd': 302 change = '<'; 303 while (num1 < to1) { 304 (void) put1(); 305 (void) putc('\n', stdout); 306 midflg = 0; 307 } 308 break; 309 310 case 'c': 311 n1 = to1 - from1; 312 n2 = to2 - from2; 313 n = n1 > n2 ? n2 : n1; 314 change = '|'; 315 do { 316 (void) put1(); 317 (void) put2(); 318 (void) putc('\n', stdout); 319 midflg = 0; 320 } while (n--); 321 322 change = '<'; 323 while (num1 < to1) { 324 (void) put1(); 325 (void) putc('\n', stdout); 326 midflg = 0; 327 } 328 329 change = '>'; 330 while (num2 < to2) { 331 (void) put2(); 332 (void) putc('\n', stdout); 333 midflg = 0; 334 } 335 break; 336 337 default: 338 (void) fprintf(stderr, gettext( 339 "%c: cmd not found\n"), cmd); 340 break; 341 } 342 343 if (oflag == 1 && com != 0) { 344 cmdin(); 345 if ((left = fopen(ltemp, "w")) == NULL) 346 error(gettext( 347 "main: Cannot open temp %s"), ltemp); 348 if ((right = fopen(rtemp, "w")) == NULL) 349 error(gettext( 350 "main: Cannot open temp %s"), rtemp); 351 } 352 } 353 /* put out remainder of input files */ 354 355 while (put1()) { 356 (void) put2(); 357 if (!silent) 358 (void) putc('\n', stdout); 359 midflg = 0; 360 } 361 if (odes) 362 (void) fclose(odes); 363 sremove(); 364 return (rcode); 365 } 366 367 static int 368 put1(void) 369 { 370 /* len1 = length of left side */ 371 /* nchars = num of chars including tabs */ 372 373 char *bp; 374 375 376 if ((bp = fgetline(fdes1)) != NULL) { 377 len1 = getlen(0, bp); 378 if ((!silent || change != ' ') && len1 != 0) 379 putline(stdout, bp, nchars); 380 381 if (oflag) { 382 /* 383 * put left side either to output file 384 * if identical to right 385 * or left temp file if not 386 */ 387 388 if (change == ' ') 389 putline(odes, bp, strlen(bp)); 390 else 391 putline(left, bp, strlen(bp)); 392 } 393 if (change != ' ') 394 putmid(1); 395 num1++; 396 return (1); 397 } else 398 return (0); 399 } 400 401 static int 402 put2(void) 403 { 404 char *bp; 405 406 if ((bp = fgetline(fdes2)) != NULL) { 407 (void) getlen((hlen + WGUTTER) % 8, bp); 408 409 /* 410 * if the left and right are different they are always 411 * printed. 412 * If the left and right are identical 413 * right is only printed if leftonly is not specified 414 * or silent mode is not specified 415 * or the right contains other than white space (len1 !=0) 416 */ 417 if (change != ' ') { 418 419 /* 420 * put right side to right temp file only 421 * because left side was written to output for 422 * identical lines 423 */ 424 425 if (oflag) 426 putline(right, bp, strlen(bp)); 427 428 if (midflg == 0) 429 putmid(1); 430 putline(stdout, bp, nchars); 431 } else 432 if (!silent && !leftonly && len1 != 0) { 433 if (midflg == 0) 434 putmid(1); 435 putline(stdout, bp, nchars); 436 } 437 num2++; 438 len1 = 0; 439 return (1); 440 } else { 441 len1 = 0; 442 return (0); 443 } 444 } 445 446 static void 447 putline(FILE *file, char *start, int num) 448 { 449 char *cp, *end; 450 int i, len, d_col; 451 wchar_t wc; 452 453 cp = start; 454 end = cp + num; 455 while (cp < end) { 456 if (isascii(*cp)) { 457 (void) putc(*cp++, file); 458 continue; 459 } 460 461 if ((len = end - cp) > MB_LEN_MAX) 462 len = MB_LEN_MAX; 463 464 if ((len = mbtowc(&wc, cp, len)) <= 0) { 465 (void) putc(*cp++, file); 466 continue; 467 } 468 469 if ((d_col = wcwidth(wc)) <= 0) 470 d_col = len; 471 472 if ((cp + d_col) > end) 473 return; 474 475 for (i = 0; i < len; i++) 476 (void) putc(*cp++, file); 477 } 478 } 479 480 static int 481 cmd(char *start) 482 { 483 unsigned char *cp; 484 char *cps; 485 int com; 486 487 if (*start == '>' || *start == '<' || *start == '-' || *start == '.') 488 return (0); 489 490 cp = (unsigned char *)start; 491 cps = start; 492 while (isdigit(*cp)) 493 cp++; 494 from1 = atoi(cps); 495 to1 = from1; 496 if (*cp == ',') { 497 cp++; 498 cps = (char *)cp; 499 while (isdigit(*cp)) 500 cp++; 501 to1 = atoi(cps); 502 } 503 504 com = *cp++; 505 cps = (char *)cp; 506 507 while (isdigit(*cp)) 508 cp++; 509 from2 = atoi(cps); 510 to2 = from2; 511 if (*cp == ',') { 512 cp++; 513 cps = (char *)cp; 514 while (isdigit(*cp)) 515 cp++; 516 to2 = atoi(cps); 517 } 518 return (com); 519 } 520 521 static int 522 getlen(int startpos, char *buffer) 523 { 524 /* 525 * get the length of the string in buffer 526 * expand tabs to next multiple of 8 527 */ 528 unsigned char *cp; 529 int slen, tlen, len, d_col; 530 int notspace; 531 wchar_t wc; 532 533 nchars = 0; 534 notspace = 0; 535 tlen = startpos; 536 for (cp = (unsigned char *)buffer; (*cp != '\n') && (*cp); cp++) { 537 if (*cp == '\t') { 538 slen = tlen; 539 tlen += 8 - (tlen % 8); 540 if (tlen >= hlen) { 541 tlen = slen; 542 break; 543 } 544 nchars++; 545 continue; 546 } 547 548 if (isascii(*cp)) { 549 slen = tlen; 550 tlen++; 551 if (tlen >= hlen) { 552 tlen = slen; 553 break; 554 } 555 if (!isspace(*cp)) 556 notspace = 1; 557 nchars++; 558 continue; 559 } 560 561 if ((len = mbtowc(&wc, (char *)cp, MB_LEN_MAX)) <= 0) { 562 slen = tlen; 563 tlen++; 564 if (tlen >= hlen) { 565 tlen = slen; 566 break; 567 } 568 notspace = 1; 569 nchars++; 570 continue; 571 } 572 573 if ((d_col = wcwidth(wc)) <= 0) 574 d_col = len; 575 576 slen = tlen; 577 tlen += d_col; 578 if (tlen > hlen) { 579 tlen = slen; 580 break; 581 } 582 notspace = 1; 583 cp += len - 1; 584 nchars += len; 585 } 586 return (notspace ? tlen : 0); 587 } 588 589 static void 590 putmid(int bflag) 591 { 592 int i; 593 594 /* 595 * len1 set by getlen to the possibly truncated 596 * length of left side 597 * hlen is length of half line 598 */ 599 600 midflg = 1; 601 if (bflag) { 602 for (i = 0; i < hlen - len1; i++) 603 (void) putc(' ', stdout); 604 } 605 (void) fputs(twoblanks, stdout); 606 (void) putc((int)change, stdout); 607 (void) fputs(twoblanks, stdout); 608 } 609 610 static void 611 error(char *s1, char *s2) 612 { 613 (void) fprintf(stderr, "sdiff: "); 614 (void) fprintf(stderr, s1, s2); 615 (void) putc('\n', stderr); 616 sremove(); 617 exit(2); 618 } 619 620 static void 621 onintr(void) 622 { 623 sremove(); 624 exit(rcode); 625 } 626 627 static void 628 sremove(void) 629 { 630 if (ltemp) { 631 (void) unlink(ltemp); 632 free(ltemp); 633 } 634 if (rtemp) { 635 (void) unlink(rtemp); 636 free(rtemp); 637 } 638 if (temp) { 639 (void) unlink(temp); 640 free(temp); 641 } 642 } 643 644 static void 645 cmdin(void) 646 { 647 char *cp, *ename; 648 int notacc; 649 650 (void) fclose(left); 651 (void) fclose(right); 652 notacc = 1; 653 while (notacc) { 654 (void) putc(PROMPT, stdout); 655 if ((cp = fgets(inbuf, 10, stdin)) == NULL) { 656 (void) putc('\n', stdout); 657 break; 658 } 659 switch (*cp) { 660 661 case 's': 662 silent = 1; 663 break; 664 665 case 'v': 666 silent = 0; 667 break; 668 669 case 'q': 670 sremove(); 671 exit(rcode); 672 /* NOTREACHED */ 673 break; 674 675 case 'l': 676 cpp(ltemp, left, odes); 677 notacc = 0; 678 break; 679 680 case 'r': 681 cpp(rtemp, right, odes); 682 notacc = 0; 683 break; 684 685 case 'e': 686 while (*++cp == ' ') 687 ; 688 switch (*cp) { 689 case 'l': 690 case '<': 691 notacc = 0; 692 ename = ltemp; 693 edit(ename); 694 break; 695 696 case 'r': 697 case '>': 698 notacc = 0; 699 ename = rtemp; 700 edit(ename); 701 break; 702 703 case 'b': 704 case '|': 705 if ((tempdes = fopen(temp, "w")) == NULL) 706 error(gettext( 707 "Cannot open temp file %s"), 708 temp); 709 cpp(ltemp, left, tempdes); 710 cpp(rtemp, right, tempdes); 711 (void) fclose(tempdes); 712 notacc = 0; 713 ename = temp; 714 edit(ename); 715 break; 716 717 case '\n': 718 if ((tempdes = fopen(temp, "w")) == NULL) 719 error(gettext( 720 "Cannot open temp file %s"), 721 temp); 722 (void) fclose(tempdes); 723 notacc = 0; 724 ename = temp; 725 edit(ename); 726 break; 727 default: 728 (void) fprintf(stderr, gettext( 729 "Illegal command %s reenter\n"), 730 cp); 731 break; 732 } 733 if (notacc == 0) 734 cpp(ename, tempdes, odes); 735 break; 736 737 default: 738 (void) fprintf(stderr, gettext( 739 "Illegal command reenter\n")); 740 break; 741 } 742 } 743 } 744 745 static void 746 cpp(char *from, FILE *fromdes, FILE *todes) 747 { 748 char tempbuf[BMAX + 1]; 749 750 if ((fromdes = fopen(from, "r")) == NULL) 751 error(gettext( 752 "cpp: Cannot open %s"), from); 753 while ((fgets(tempbuf, BMAX, fromdes) != NULL)) 754 (void) fputs(tempbuf, todes); 755 (void) fclose(fromdes); 756 } 757 758 static void 759 edit(char *file) 760 { 761 int i; 762 pid_t pid; 763 void (*oldintr)(int); 764 765 switch (pid = fork()) { 766 case (pid_t)-1: 767 error(gettext("Cannot fork"), NULL); 768 /* NOTREACHED */ 769 break; 770 case (pid_t)0: 771 (void) execl("/usr/bin/ed", "ed", file, NULL); 772 } 773 774 oldintr = signal(SIGINT, SIG_IGN); /* ignore interrupts in ed */ 775 while (pid != wait(&i)) 776 ; 777 /* restore previous interrupt proc */ 778 (void) signal(SIGINT, oldintr); 779 } 780 781 static char * 782 filename(char *pa1, char *pa2) 783 { 784 int c; 785 char *a1, *b1, *a2; 786 struct stat stbuf; 787 a1 = pa1; 788 a2 = pa2; 789 if (stat(a1, &stbuf) != -1 && ((stbuf.st_mode&S_IFMT) == S_IFDIR)) { 790 b1 = pa1 = (char *)malloc(strlen(a1) + strlen(a2) + 2); 791 while (*b1++ = *a1++); 792 b1[-1] = '/'; 793 a1 = b1; 794 while (*a1++ = *a2++) 795 if (*a2 && *a2 != '/' && a2[-1] == '/') 796 a1 = b1; 797 } else if (a1[0] == '-' && a1[1] == 0 && temp == 0) { 798 if (fstat(fileno(stdin), &stbuf) == -1) 799 error(gettext("Cannot process stdin"), NULL); 800 pa1 = temp = strdup("/tmp/sdiffXXXXXX"); 801 if ((tempfd = mkstemp(temp)) == -1 || 802 (tempdes = fdopen(tempfd, "w")) == NULL) 803 error(gettext("Cannot open/create temp %s"), 804 temp); 805 while ((c = getc(stdin)) != EOF) 806 (void) putc(c, tempdes); 807 (void) fclose(tempdes); 808 } 809 return (pa1); 810 } 811 812 /* 813 * like fgets, but reads upto and including a newline, 814 * the data is stored in a reusable dynamic buffer that grows to fit 815 * the largest line in the file, the buffer is NULL terminated 816 * returns a pointer to the dynamic buffer. 817 */ 818 static char * 819 fgetline(FILE *fp) 820 { 821 static char *bp = NULL; 822 static int blen = 0; 823 int sl; 824 825 if (bp == NULL) { 826 /* allocate it for the first time */ 827 bp = (char *)malloc(BUFSIZ); 828 if (bp == NULL) 829 error(gettext("fgetline: malloc failed"), NULL); 830 blen = BUFSIZ; 831 } 832 833 /* check for error or nothing read */ 834 if (fgets(bp, blen, fp) == NULL) 835 return (NULL); 836 837 if (feof(fp)) 838 return (bp); 839 840 while ((sl = strlen(bp)) == blen-1 && *(bp+blen-2) != '\n') { 841 /* still more data, grow the buffer */ 842 blen *= 2; 843 bp = (char *)realloc(bp, blen); 844 if (bp == NULL) 845 error(gettext("fgetline: realloc failed"), NULL); 846 /* continue reading and add to end of buffer */ 847 (void) fgets(bp+sl, blen-sl, fp); 848 } 849 return (bp); 850 } 851