111894Speter/* Compare RCS revisions. */ 29Sjkh 311894Speter/* Copyright 1982, 1988, 1989 Walter Tichy 411894Speter Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert 59Sjkh Distributed under license by the Free Software Foundation, Inc. 69Sjkh 79SjkhThis file is part of RCS. 89Sjkh 99SjkhRCS is free software; you can redistribute it and/or modify 109Sjkhit under the terms of the GNU General Public License as published by 119Sjkhthe Free Software Foundation; either version 2, or (at your option) 129Sjkhany later version. 139Sjkh 149SjkhRCS is distributed in the hope that it will be useful, 159Sjkhbut WITHOUT ANY WARRANTY; without even the implied warranty of 169SjkhMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 179SjkhGNU General Public License for more details. 189Sjkh 199SjkhYou should have received a copy of the GNU General Public License 2011894Speteralong with RCS; see the file COPYING. 2111894SpeterIf not, write to the Free Software Foundation, 2211894Speter59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 239Sjkh 249SjkhReport problems and direct all questions to: 259Sjkh 269Sjkh rcs-bugs@cs.purdue.edu 279Sjkh 289Sjkh*/ 299Sjkh 3011894Speter/* 3111894Speter * Revision 5.19 1995/06/16 06:19:24 eggert 3211894Speter * Update FSF address. 338858Srgrimes * 3411894Speter * Revision 5.18 1995/06/01 16:23:43 eggert 3511894Speter * (main): Pass "--binary" if -kb and if --binary makes a difference. 3611894Speter * Don't treat + options specially. 3711894Speter * 3811894Speter * Revision 5.17 1994/03/17 14:05:48 eggert 3911894Speter * Specify subprocess input via file descriptor, not file name. Remove lint. 4011894Speter * 4111894Speter * Revision 5.16 1993/11/09 17:40:15 eggert 4211894Speter * -V now prints version on stdout and exits. Don't print usage twice. 4311894Speter * 4411894Speter * Revision 5.15 1993/11/03 17:42:27 eggert 4511894Speter * Add -z. Ignore -T. Pass -Vn to `co'. Add Name keyword. 4611894Speter * Put revision numbers in -c output. Improve quality of diagnostics. 4711894Speter * 4811894Speter * Revision 5.14 1992/07/28 16:12:44 eggert 4911894Speter * Add -V. Use co -M for better dates with traditional diff -c. 5011894Speter * 5111894Speter * Revision 5.13 1992/02/17 23:02:23 eggert 5211894Speter * Output more readable context diff headers. 5311894Speter * Suppress needless checkout and comparison of identical revisions. 5411894Speter * 5511894Speter * Revision 5.12 1992/01/24 18:44:19 eggert 5611894Speter * Add GNU diff 1.15.2's new options. lint -> RCS_lint 5711894Speter * 5811894Speter * Revision 5.11 1992/01/06 02:42:34 eggert 5911894Speter * Update usage string. 6011894Speter * 619Sjkh * Revision 5.10 1991/10/07 17:32:46 eggert 629Sjkh * Remove lint. 639Sjkh * 649Sjkh * Revision 5.9 1991/08/19 03:13:55 eggert 659Sjkh * Add RCSINIT, -r$. Tune. 669Sjkh * 679Sjkh * Revision 5.8 1991/04/21 11:58:21 eggert 689Sjkh * Add -x, RCSINIT, MS-DOS support. 699Sjkh * 709Sjkh * Revision 5.7 1990/12/13 06:54:07 eggert 719Sjkh * GNU diff 1.15 has -u. 729Sjkh * 739Sjkh * Revision 5.6 1990/11/01 05:03:39 eggert 749Sjkh * Remove unneeded setid check. 759Sjkh * 769Sjkh * Revision 5.5 1990/10/04 06:30:19 eggert 779Sjkh * Accumulate exit status across files. 789Sjkh * 799Sjkh * Revision 5.4 1990/09/27 01:31:43 eggert 809Sjkh * Yield 1, not EXIT_FAILURE, when diffs are found. 819Sjkh * 829Sjkh * Revision 5.3 1990/09/11 02:41:11 eggert 839Sjkh * Simplify -kkvl test. 849Sjkh * 859Sjkh * Revision 5.2 1990/09/04 17:07:19 eggert 869Sjkh * Diff's argv was too small by 1. 879Sjkh * 889Sjkh * Revision 5.1 1990/08/29 07:13:55 eggert 899Sjkh * Add -kkvl. 909Sjkh * 919Sjkh * Revision 5.0 1990/08/22 08:12:46 eggert 929Sjkh * Add -k, -V. Don't use access(). Add setuid support. 939Sjkh * Remove compile-time limits; use malloc instead. 949Sjkh * Don't pass arguments with leading '+' to diff; GNU DIFF treats them as options. 959Sjkh * Add GNU diff's flags. Make lock and temp files faster and safer. 969Sjkh * Ansify and Posixate. 979Sjkh * 989Sjkh * Revision 4.6 89/05/01 15:12:27 narten 999Sjkh * changed copyright header to reflect current distribution rules 1008858Srgrimes * 1019Sjkh * Revision 4.5 88/08/09 19:12:41 eggert 1029Sjkh * Use execv(), not system(); yield exit status like diff(1)s; allow cc -R. 1038858Srgrimes * 1049Sjkh * Revision 4.4 87/12/18 11:37:46 narten 1059Sjkh * changes Jay Lepreau made in the 4.3 BSD version, to add support for 1069Sjkh * "-i", "-w", and "-t" flags and to permit flags to be bundled together, 1079Sjkh * merged in. 1088858Srgrimes * 1099Sjkh * Revision 4.3 87/10/18 10:31:42 narten 1109Sjkh * Updating version numbers. Changes relative to 1.1 actually 1119Sjkh * relative to 4.1 1128858Srgrimes * 1139Sjkh * Revision 1.3 87/09/24 13:59:21 narten 1148858Srgrimes * Sources now pass through lint (if you ignore printf/sprintf/fprintf 1159Sjkh * warnings) 1168858Srgrimes * 1179Sjkh * Revision 1.2 87/03/27 14:22:15 jenkins 1189Sjkh * Port to suns 1198858Srgrimes * 1209Sjkh * Revision 4.1 83/05/03 22:13:19 wft 1219Sjkh * Added default branch, option -q, exit status like diff. 1229Sjkh * Added fterror() to replace faterror(). 1238858Srgrimes * 1249Sjkh * Revision 3.6 83/01/15 17:52:40 wft 1259Sjkh * Expanded mainprogram to handle multiple RCS files. 1269Sjkh * 1279Sjkh * Revision 3.5 83/01/06 09:33:45 wft 1289Sjkh * Fixed passing of -c (context) option to diff. 1299Sjkh * 1309Sjkh * Revision 3.4 82/12/24 15:28:38 wft 1319Sjkh * Added call to catchsig(). 1329Sjkh * 1339Sjkh * Revision 3.3 82/12/10 16:08:17 wft 1349Sjkh * Corrected checking of return code from diff; improved error msgs. 1359Sjkh * 1369Sjkh * Revision 3.2 82/12/04 13:20:09 wft 1379Sjkh * replaced getdelta() with gettree(). Changed diagnostics. 1389Sjkh * 1399Sjkh * Revision 3.1 82/11/28 19:25:04 wft 1409Sjkh * Initial revision. 1419Sjkh * 1429Sjkh */ 1439Sjkh#include "rcsbase.h" 1449Sjkh 1459Sjkh#if DIFF_L 1469Sjkhstatic char const *setup_label P((struct buf*,char const*,char const[datesize])); 1479Sjkh#endif 1489Sjkhstatic void cleanup P((void)); 1499Sjkh 1509Sjkhstatic int exitstatus; 1519Sjkhstatic RILE *workptr; 1529Sjkhstatic struct stat workstat; 1539Sjkh 15450472SpetermainProg(rcsdiffId, "rcsdiff", "$FreeBSD: releng/11.0/gnu/usr.bin/rcs/rcsdiff/rcsdiff.c 50472 1999-08-27 23:37:10Z peter $") 1559Sjkh{ 1569Sjkh static char const cmdusage[] = 15711894Speter "\nrcsdiff usage: rcsdiff -ksubst -q -rrev1 [-rrev2] -Vn -xsuff -zzone [diff options] file ..."; 1589Sjkh 1599Sjkh int revnums; /* counter for revision numbers given */ 1609Sjkh char const *rev1, *rev2; /* revision numbers from command line */ 1619Sjkh char const *xrev1, *xrev2; /* expanded revision numbers */ 16211894Speter char const *expandarg, *lexpandarg, *suffixarg, *versionarg, *zonearg; 1639Sjkh#if DIFF_L 1649Sjkh static struct buf labelbuf[2]; 1659Sjkh int file_labels; 1669Sjkh char const **diff_label1, **diff_label2; 1679Sjkh char date2[datesize]; 1689Sjkh#endif 16911894Speter char const *cov[10 + !DIFF_L]; 17011894Speter char const **diffv, **diffp, **diffpend; /* argv for subsidiary diff */ 1719Sjkh char const **pp, *p, *diffvstr; 1729Sjkh struct buf commarg; 1739Sjkh struct buf numericrev; /* expanded revision number */ 1749Sjkh struct hshentries *gendeltas; /* deltas to be generated */ 1759Sjkh struct hshentry * target; 1769Sjkh char *a, *dcp, **newargv; 17711894Speter int no_diff_means_no_output; 1789Sjkh register c; 1799Sjkh 1809Sjkh exitstatus = DIFF_SUCCESS; 1819Sjkh 1829Sjkh bufautobegin(&commarg); 1839Sjkh bufautobegin(&numericrev); 1849Sjkh revnums = 0; 18511894Speter rev1 = rev2 = xrev2 = 0; 1869Sjkh#if DIFF_L 1879Sjkh file_labels = 0; 1889Sjkh#endif 18911894Speter expandarg = suffixarg = versionarg = zonearg = 0; 19011894Speter no_diff_means_no_output = true; 1919Sjkh suffixes = X_DEFAULT; 1929Sjkh 19311894Speter /* 19411894Speter * Room for runv extra + args [+ --binary] [+ 2 labels] 19511894Speter * + 1 file + 1 trailing null. 19611894Speter */ 19711894Speter diffv = tnalloc(char const*, 1 + argc + !!OPEN_O_BINARY + 2*DIFF_L + 2); 19811894Speter diffp = diffv + 1; 1999Sjkh *diffp++ = DIFF; 2009Sjkh 2019Sjkh argc = getRCSINIT(argc, argv, &newargv); 2029Sjkh argv = newargv; 2039Sjkh while (a = *++argv, 0<--argc && *a++=='-') { 2049Sjkh dcp = a; 20511894Speter while ((c = *a++)) switch (c) { 2069Sjkh case 'r': 2079Sjkh switch (++revnums) { 2089Sjkh case 1: rev1=a; break; 2099Sjkh case 2: rev2=a; break; 21011894Speter default: error("too many revision numbers"); 2119Sjkh } 2129Sjkh goto option_handled; 21311894Speter case '-': case 'D': 21411894Speter no_diff_means_no_output = false; 21511894Speter /* fall into */ 21611894Speter case 'C': case 'F': case 'I': case 'L': case 'W': 2179Sjkh#if DIFF_L 21813512Smpp if (c == 'L' && file_labels++ == 2) 2199Sjkh faterror("too many -L options"); 2209Sjkh#endif 2219Sjkh *dcp++ = c; 2229Sjkh if (*a) 2239Sjkh do *dcp++ = *a++; 2249Sjkh while (*a); 2259Sjkh else { 2269Sjkh if (!--argc) 2279Sjkh faterror("-%c needs following argument%s", 2289Sjkh c, cmdusage 2299Sjkh ); 2309Sjkh *diffp++ = *argv++; 2319Sjkh } 2329Sjkh break; 23311894Speter case 'y': 23411894Speter no_diff_means_no_output = false; 23511894Speter /* fall into */ 23611894Speter case 'B': case 'H': 2379Sjkh case '0': case '1': case '2': case '3': case '4': 2389Sjkh case '5': case '6': case '7': case '8': case '9': 2399Sjkh case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': 2409Sjkh case 'h': case 'i': case 'n': case 'p': 2419Sjkh case 't': case 'u': case 'w': 2429Sjkh *dcp++ = c; 2439Sjkh break; 2449Sjkh case 'q': 2459Sjkh quietflag=true; 2469Sjkh break; 2479Sjkh case 'x': 24811894Speter suffixarg = *argv; 2499Sjkh suffixes = *argv + 2; 2509Sjkh goto option_handled; 25111894Speter case 'z': 25211894Speter zonearg = *argv; 25311894Speter zone_set(*argv + 2); 25411894Speter goto option_handled; 25511894Speter case 'T': 25611894Speter /* Ignore -T, so that RCSINIT can contain -T. */ 25711894Speter if (*a) 25811894Speter goto unknown; 25911894Speter break; 2609Sjkh case 'V': 2619Sjkh versionarg = *argv; 2629Sjkh setRCSversion(versionarg); 2639Sjkh goto option_handled; 2649Sjkh case 'k': 2659Sjkh expandarg = *argv; 2669Sjkh if (0 <= str2expmode(expandarg+2)) 2679Sjkh goto option_handled; 2689Sjkh /* fall into */ 2699Sjkh default: 27011894Speter unknown: 27111894Speter error("unknown option: %s%s", *argv, cmdusage); 2729Sjkh }; 2739Sjkh option_handled: 2749Sjkh if (dcp != *argv+1) { 2759Sjkh *dcp = 0; 2769Sjkh *diffp++ = *argv; 2779Sjkh } 2789Sjkh } /* end of option processing */ 2799Sjkh 28011894Speter for (pp = diffv+2, c = 0; pp<diffp; ) 2819Sjkh c += strlen(*pp++) + 1; 2829Sjkh diffvstr = a = tnalloc(char, c + 1); 28311894Speter for (pp = diffv+2; pp<diffp; ) { 2849Sjkh p = *pp++; 2859Sjkh *a++ = ' '; 2869Sjkh while ((*a = *p++)) 2879Sjkh a++; 2889Sjkh } 2899Sjkh *a = 0; 2909Sjkh 2919Sjkh#if DIFF_L 29211894Speter diff_label1 = diff_label2 = 0; 2939Sjkh if (file_labels < 2) { 2949Sjkh if (!file_labels) 2959Sjkh diff_label1 = diffp++; 2969Sjkh diff_label2 = diffp++; 2979Sjkh } 2989Sjkh#endif 29911894Speter diffpend = diffp; 3009Sjkh 30111894Speter cov[1] = CO; 30211894Speter cov[2] = "-q"; 30311894Speter# if !DIFF_L 30411894Speter cov[3] = "-M"; 30511894Speter# endif 3069Sjkh 30711894Speter /* Now handle all pathnames. */ 30811894Speter if (nerror) 30911894Speter cleanup(); 31011894Speter else if (argc < 1) 31111894Speter faterror("no input file%s", cmdusage); 31211894Speter else 31311894Speter for (; 0 < argc; cleanup(), ++argv, --argc) { 3149Sjkh ffree(); 3159Sjkh 31611894Speter if (pairnames(argc, argv, rcsreadopen, true, false) <= 0) 3179Sjkh continue; 31811894Speter diagnose("===================================================================\nRCS file: %s\n",RCSname); 3199Sjkh if (!rev2) { 3209Sjkh /* Make sure work file is readable, and get its status. */ 32111894Speter if (!(workptr = Iopen(workname, FOPEN_R_WORK, &workstat))) { 32211894Speter eerror(workname); 3239Sjkh continue; 3249Sjkh } 3259Sjkh } 3269Sjkh 3279Sjkh 3289Sjkh gettree(); /* reads in the delta tree */ 3299Sjkh 33011894Speter if (!Head) { 33111894Speter rcserror("no revisions present"); 3329Sjkh continue; 3339Sjkh } 3349Sjkh if (revnums==0 || !*rev1) 3359Sjkh rev1 = Dbranch ? Dbranch : Head->num; 3369Sjkh 3379Sjkh if (!fexpandsym(rev1, &numericrev, workptr)) continue; 33811894Speter if (!(target=genrevs(numericrev.string,(char *)0,(char *)0,(char *)0,&gendeltas))) continue; 3399Sjkh xrev1=target->num; 3409Sjkh#if DIFF_L 3419Sjkh if (diff_label1) 3429Sjkh *diff_label1 = setup_label(&labelbuf[0], target->num, target->date); 3439Sjkh#endif 3449Sjkh 3459Sjkh lexpandarg = expandarg; 3469Sjkh if (revnums==2) { 3479Sjkh if (!fexpandsym( 3489Sjkh *rev2 ? rev2 : Dbranch ? Dbranch : Head->num, 3499Sjkh &numericrev, 3509Sjkh workptr 3519Sjkh )) 3529Sjkh continue; 35311894Speter if (!(target=genrevs(numericrev.string,(char *)0,(char *)0,(char *)0,&gendeltas))) continue; 3549Sjkh xrev2=target->num; 35511894Speter if (no_diff_means_no_output && xrev1 == xrev2) 35611894Speter continue; 3579Sjkh } else if ( 3589Sjkh target->lockedby 3599Sjkh && !lexpandarg 3609Sjkh && Expand == KEYVAL_EXPAND 3619Sjkh && WORKMODE(RCSstat.st_mode,true) == workstat.st_mode 3629Sjkh ) 3639Sjkh lexpandarg = "-kkvl"; 3649Sjkh Izclose(&workptr); 3659Sjkh#if DIFF_L 3669Sjkh if (diff_label2) 3679Sjkh if (revnums == 2) 3689Sjkh *diff_label2 = setup_label(&labelbuf[1], target->num, target->date); 3699Sjkh else { 3709Sjkh time2date(workstat.st_mtime, date2); 37111894Speter *diff_label2 = setup_label(&labelbuf[1], (char*)0, date2); 3729Sjkh } 3739Sjkh#endif 3749Sjkh 3759Sjkh diagnose("retrieving revision %s\n", xrev1); 3769Sjkh bufscpy(&commarg, "-p"); 37711894Speter bufscat(&commarg, rev1); /* not xrev1, for $Name's sake */ 3789Sjkh 37911894Speter pp = &cov[3 + !DIFF_L]; 3809Sjkh *pp++ = commarg.string; 38111894Speter if (lexpandarg) *pp++ = lexpandarg; 38211894Speter if (suffixarg) *pp++ = suffixarg; 38311894Speter if (versionarg) *pp++ = versionarg; 38411894Speter if (zonearg) *pp++ = zonearg; 38511894Speter *pp++ = RCSname; 3869Sjkh *pp = 0; 3879Sjkh 38811894Speter diffp = diffpend; 38911894Speter# if OPEN_O_BINARY 39011894Speter if (Expand == BINARY_EXPAND) 39111894Speter *diffp++ = "--binary"; 39211894Speter# endif 39311894Speter diffp[0] = maketemp(0); 39411894Speter if (runv(-1, diffp[0], cov)) { 39511894Speter rcserror("co failed"); 3969Sjkh continue; 3979Sjkh } 3989Sjkh if (!rev2) { 39911894Speter diffp[1] = workname; 40011894Speter if (*workname == '-') { 40111894Speter char *dp = ftnalloc(char, strlen(workname)+3); 4029Sjkh diffp[1] = dp; 4039Sjkh *dp++ = '.'; 4049Sjkh *dp++ = SLASH; 40511894Speter VOID strcpy(dp, workname); 4069Sjkh } 4079Sjkh } else { 4089Sjkh diagnose("retrieving revision %s\n",xrev2); 4099Sjkh bufscpy(&commarg, "-p"); 41011894Speter bufscat(&commarg, rev2); /* not xrev2, for $Name's sake */ 41111894Speter cov[3 + !DIFF_L] = commarg.string; 41211894Speter diffp[1] = maketemp(1); 41311894Speter if (runv(-1, diffp[1], cov)) { 41411894Speter rcserror("co failed"); 4159Sjkh continue; 4169Sjkh } 4179Sjkh } 4189Sjkh if (!rev2) 41911894Speter diagnose("diff%s -r%s %s\n", diffvstr, xrev1, workname); 4209Sjkh else 4219Sjkh diagnose("diff%s -r%s -r%s\n", diffvstr, xrev1, xrev2); 4229Sjkh 42311894Speter diffp[2] = 0; 42411894Speter switch (runv(-1, (char*)0, diffv)) { 4259Sjkh case DIFF_SUCCESS: 4269Sjkh break; 4279Sjkh case DIFF_FAILURE: 4289Sjkh if (exitstatus == DIFF_SUCCESS) 4299Sjkh exitstatus = DIFF_FAILURE; 4309Sjkh break; 4319Sjkh default: 43211894Speter workerror("diff failed"); 4339Sjkh } 43411894Speter } 4359Sjkh 4369Sjkh tempunlink(); 4379Sjkh exitmain(exitstatus); 4389Sjkh} 4399Sjkh 4409Sjkh static void 4419Sjkhcleanup() 4429Sjkh{ 4439Sjkh if (nerror) exitstatus = DIFF_TROUBLE; 4449Sjkh Izclose(&finptr); 4459Sjkh Izclose(&workptr); 4469Sjkh} 4479Sjkh 44811894Speter#if RCS_lint 4499Sjkh# define exiterr rdiffExit 4509Sjkh#endif 45111894Speter void 4529Sjkhexiterr() 4539Sjkh{ 4549Sjkh tempunlink(); 4559Sjkh _exit(DIFF_TROUBLE); 4569Sjkh} 4579Sjkh 4589Sjkh#if DIFF_L 4599Sjkh static char const * 46011894Spetersetup_label(b, num, date) 4619Sjkh struct buf *b; 46211894Speter char const *num; 4639Sjkh char const date[datesize]; 4649Sjkh{ 4659Sjkh char *p; 46611894Speter char datestr[datesize + zonelenmax]; 46711894Speter VOID date2str(date, datestr); 46811894Speter bufalloc(b, 46911894Speter strlen(workname) 47011894Speter + sizeof datestr + 4 47111894Speter + (num ? strlen(num) : 0) 47211894Speter ); 4739Sjkh p = b->string; 47411894Speter if (num) 47511894Speter VOID sprintf(p, "-L%s\t%s\t%s", workname, datestr, num); 47611894Speter else 47711894Speter VOID sprintf(p, "-L%s\t%s", workname, datestr); 4789Sjkh return p; 4799Sjkh} 4809Sjkh#endif 481