rcsdiff.c revision 8858
19Sjkh/* 29Sjkh * RCS rcsdiff operation 39Sjkh */ 49Sjkh/***************************************************************************** 59Sjkh * generate difference between RCS revisions 69Sjkh ***************************************************************************** 79Sjkh */ 89Sjkh 99Sjkh/* Copyright (C) 1982, 1988, 1989 Walter Tichy 109Sjkh Copyright 1990, 1991 by Paul Eggert 119Sjkh Distributed under license by the Free Software Foundation, Inc. 129Sjkh 139SjkhThis file is part of RCS. 149Sjkh 159SjkhRCS is free software; you can redistribute it and/or modify 169Sjkhit under the terms of the GNU General Public License as published by 179Sjkhthe Free Software Foundation; either version 2, or (at your option) 189Sjkhany later version. 199Sjkh 209SjkhRCS is distributed in the hope that it will be useful, 219Sjkhbut WITHOUT ANY WARRANTY; without even the implied warranty of 229SjkhMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 239SjkhGNU General Public License for more details. 249Sjkh 259SjkhYou should have received a copy of the GNU General Public License 269Sjkhalong with RCS; see the file COPYING. If not, write to 279Sjkhthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 289Sjkh 299SjkhReport problems and direct all questions to: 309Sjkh 319Sjkh rcs-bugs@cs.purdue.edu 329Sjkh 339Sjkh*/ 349Sjkh 359Sjkh 369Sjkh 379Sjkh 389Sjkh/* $Log: rcsdiff.c,v $ 398858Srgrimes * Revision 1.1.1.1 1993/06/18 04:22:16 jkh 408858Srgrimes * Updated GNU utilities 418858Srgrimes * 429Sjkh * Revision 5.10 1991/10/07 17:32:46 eggert 439Sjkh * Remove lint. 449Sjkh * 459Sjkh * Revision 5.9 1991/08/19 03:13:55 eggert 469Sjkh * Add RCSINIT, -r$. Tune. 479Sjkh * 489Sjkh * Revision 5.8 1991/04/21 11:58:21 eggert 499Sjkh * Add -x, RCSINIT, MS-DOS support. 509Sjkh * 519Sjkh * Revision 5.7 1990/12/13 06:54:07 eggert 529Sjkh * GNU diff 1.15 has -u. 539Sjkh * 549Sjkh * Revision 5.6 1990/11/01 05:03:39 eggert 559Sjkh * Remove unneeded setid check. 569Sjkh * 579Sjkh * Revision 5.5 1990/10/04 06:30:19 eggert 589Sjkh * Accumulate exit status across files. 599Sjkh * 609Sjkh * Revision 5.4 1990/09/27 01:31:43 eggert 619Sjkh * Yield 1, not EXIT_FAILURE, when diffs are found. 629Sjkh * 639Sjkh * Revision 5.3 1990/09/11 02:41:11 eggert 649Sjkh * Simplify -kkvl test. 659Sjkh * 669Sjkh * Revision 5.2 1990/09/04 17:07:19 eggert 679Sjkh * Diff's argv was too small by 1. 689Sjkh * 699Sjkh * Revision 5.1 1990/08/29 07:13:55 eggert 709Sjkh * Add -kkvl. 719Sjkh * 729Sjkh * Revision 5.0 1990/08/22 08:12:46 eggert 739Sjkh * Add -k, -V. Don't use access(). Add setuid support. 749Sjkh * Remove compile-time limits; use malloc instead. 759Sjkh * Don't pass arguments with leading '+' to diff; GNU DIFF treats them as options. 769Sjkh * Add GNU diff's flags. Make lock and temp files faster and safer. 779Sjkh * Ansify and Posixate. 789Sjkh * 799Sjkh * Revision 4.6 89/05/01 15:12:27 narten 809Sjkh * changed copyright header to reflect current distribution rules 818858Srgrimes * 829Sjkh * Revision 4.5 88/08/09 19:12:41 eggert 839Sjkh * Use execv(), not system(); yield exit status like diff(1)s; allow cc -R. 848858Srgrimes * 859Sjkh * Revision 4.4 87/12/18 11:37:46 narten 869Sjkh * changes Jay Lepreau made in the 4.3 BSD version, to add support for 879Sjkh * "-i", "-w", and "-t" flags and to permit flags to be bundled together, 889Sjkh * merged in. 898858Srgrimes * 909Sjkh * Revision 4.3 87/10/18 10:31:42 narten 919Sjkh * Updating version numbers. Changes relative to 1.1 actually 929Sjkh * relative to 4.1 938858Srgrimes * 949Sjkh * Revision 1.3 87/09/24 13:59:21 narten 958858Srgrimes * Sources now pass through lint (if you ignore printf/sprintf/fprintf 969Sjkh * warnings) 978858Srgrimes * 989Sjkh * Revision 1.2 87/03/27 14:22:15 jenkins 999Sjkh * Port to suns 1008858Srgrimes * 1019Sjkh * Revision 4.1 83/05/03 22:13:19 wft 1029Sjkh * Added default branch, option -q, exit status like diff. 1039Sjkh * Added fterror() to replace faterror(). 1048858Srgrimes * 1059Sjkh * Revision 3.6 83/01/15 17:52:40 wft 1069Sjkh * Expanded mainprogram to handle multiple RCS files. 1079Sjkh * 1089Sjkh * Revision 3.5 83/01/06 09:33:45 wft 1099Sjkh * Fixed passing of -c (context) option to diff. 1109Sjkh * 1119Sjkh * Revision 3.4 82/12/24 15:28:38 wft 1129Sjkh * Added call to catchsig(). 1139Sjkh * 1149Sjkh * Revision 3.3 82/12/10 16:08:17 wft 1159Sjkh * Corrected checking of return code from diff; improved error msgs. 1169Sjkh * 1179Sjkh * Revision 3.2 82/12/04 13:20:09 wft 1189Sjkh * replaced getdelta() with gettree(). Changed diagnostics. 1199Sjkh * 1209Sjkh * Revision 3.1 82/11/28 19:25:04 wft 1219Sjkh * Initial revision. 1229Sjkh * 1239Sjkh */ 1249Sjkh#include "rcsbase.h" 1259Sjkh 1269Sjkh#if DIFF_L 1279Sjkhstatic char const *setup_label P((struct buf*,char const*,char const[datesize])); 1289Sjkh#endif 1299Sjkhstatic void cleanup P((void)); 1309Sjkh 1319Sjkhstatic int exitstatus; 1329Sjkhstatic RILE *workptr; 1339Sjkhstatic struct stat workstat; 1349Sjkh 1358858SrgrimesmainProg(rcsdiffId, "rcsdiff", "$Id: rcsdiff.c,v 1.1.1.1 1993/06/18 04:22:16 jkh Exp $") 1369Sjkh{ 1379Sjkh static char const cmdusage[] = 1389Sjkh "\nrcsdiff usage: rcsdiff [-q] [-rrev1 [-rrev2]] [-Vn] [diff options] file ..."; 1399Sjkh 1409Sjkh int revnums; /* counter for revision numbers given */ 1419Sjkh char const *rev1, *rev2; /* revision numbers from command line */ 1429Sjkh char const *xrev1, *xrev2; /* expanded revision numbers */ 1439Sjkh char const *expandarg, *lexpandarg, *versionarg; 1449Sjkh#if DIFF_L 1459Sjkh static struct buf labelbuf[2]; 1469Sjkh int file_labels; 1479Sjkh char const **diff_label1, **diff_label2; 1489Sjkh char date2[datesize]; 1499Sjkh#endif 1509Sjkh char const *cov[9]; 1519Sjkh char const **diffv, **diffp; /* argv for subsidiary diff */ 1529Sjkh char const **pp, *p, *diffvstr; 1539Sjkh struct buf commarg; 1549Sjkh struct buf numericrev; /* expanded revision number */ 1559Sjkh struct hshentries *gendeltas; /* deltas to be generated */ 1569Sjkh struct hshentry * target; 1579Sjkh char *a, *dcp, **newargv; 1589Sjkh register c; 1599Sjkh 1609Sjkh exitstatus = DIFF_SUCCESS; 1619Sjkh 1629Sjkh bufautobegin(&commarg); 1639Sjkh bufautobegin(&numericrev); 1649Sjkh revnums = 0; 1659Sjkh rev1 = rev2 = xrev2 = nil; 1669Sjkh#if DIFF_L 1679Sjkh file_labels = 0; 1689Sjkh#endif 1699Sjkh expandarg = versionarg = 0; 1709Sjkh suffixes = X_DEFAULT; 1719Sjkh 1729Sjkh /* Room for args + 2 i/o [+ 2 labels] + 1 file + 1 trailing null. */ 1739Sjkh diffp = diffv = tnalloc(char const*, argc + 4 + 2*DIFF_L); 1749Sjkh *diffp++ = nil; 1759Sjkh *diffp++ = nil; 1769Sjkh *diffp++ = DIFF; 1779Sjkh 1789Sjkh argc = getRCSINIT(argc, argv, &newargv); 1799Sjkh argv = newargv; 1809Sjkh while (a = *++argv, 0<--argc && *a++=='-') { 1819Sjkh dcp = a; 1829Sjkh while (c = *a++) switch (c) { 1839Sjkh case 'r': 1849Sjkh switch (++revnums) { 1859Sjkh case 1: rev1=a; break; 1869Sjkh case 2: rev2=a; break; 1879Sjkh default: faterror("too many revision numbers"); 1889Sjkh } 1899Sjkh goto option_handled; 1909Sjkh#if DIFF_L 1919Sjkh case 'L': 1929Sjkh if (++file_labels == 2) 1939Sjkh faterror("too many -L options"); 1949Sjkh /* fall into */ 1959Sjkh#endif 1969Sjkh case 'C': case 'D': case 'F': case 'I': 1979Sjkh *dcp++ = c; 1989Sjkh if (*a) 1999Sjkh do *dcp++ = *a++; 2009Sjkh while (*a); 2019Sjkh else { 2029Sjkh if (!--argc) 2039Sjkh faterror("-%c needs following argument%s", 2049Sjkh c, cmdusage 2059Sjkh ); 2069Sjkh *diffp++ = *argv++; 2079Sjkh } 2089Sjkh break; 2099Sjkh case 'B': case 'H': case 'T': 2109Sjkh case '0': case '1': case '2': case '3': case '4': 2119Sjkh case '5': case '6': case '7': case '8': case '9': 2129Sjkh case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': 2139Sjkh case 'h': case 'i': case 'n': case 'p': 2149Sjkh case 't': case 'u': case 'w': 2159Sjkh *dcp++ = c; 2169Sjkh break; 2179Sjkh case 'q': 2189Sjkh quietflag=true; 2199Sjkh break; 2209Sjkh case 'x': 2219Sjkh suffixes = *argv + 2; 2229Sjkh goto option_handled; 2239Sjkh case 'V': 2249Sjkh versionarg = *argv; 2259Sjkh setRCSversion(versionarg); 2269Sjkh goto option_handled; 2279Sjkh case 'k': 2289Sjkh expandarg = *argv; 2299Sjkh if (0 <= str2expmode(expandarg+2)) 2309Sjkh goto option_handled; 2319Sjkh /* fall into */ 2329Sjkh default: 2339Sjkh faterror("unknown option: %s%s", *argv, cmdusage); 2349Sjkh }; 2359Sjkh option_handled: 2369Sjkh if (dcp != *argv+1) { 2379Sjkh *dcp = 0; 2389Sjkh *diffp++ = *argv; 2399Sjkh } 2409Sjkh } /* end of option processing */ 2419Sjkh 2429Sjkh if (argc<1) faterror("no input file%s", cmdusage); 2439Sjkh 2449Sjkh for (pp = diffv+3, c = 0; pp<diffp; ) 2459Sjkh c += strlen(*pp++) + 1; 2469Sjkh diffvstr = a = tnalloc(char, c + 1); 2479Sjkh for (pp = diffv+3; pp<diffp; ) { 2489Sjkh p = *pp++; 2499Sjkh *a++ = ' '; 2509Sjkh while ((*a = *p++)) 2519Sjkh a++; 2529Sjkh } 2539Sjkh *a = 0; 2549Sjkh 2559Sjkh#if DIFF_L 2569Sjkh diff_label1 = diff_label2 = nil; 2579Sjkh if (file_labels < 2) { 2589Sjkh if (!file_labels) 2599Sjkh diff_label1 = diffp++; 2609Sjkh diff_label2 = diffp++; 2619Sjkh } 2629Sjkh#endif 2639Sjkh diffp[2] = nil; 2649Sjkh 2659Sjkh cov[0] = 0; 2669Sjkh cov[2] = CO; 2679Sjkh cov[3] = "-q"; 2689Sjkh 2699Sjkh /* now handle all filenames */ 2709Sjkh do { 2719Sjkh ffree(); 2729Sjkh 2739Sjkh if (pairfilenames(argc, argv, rcsreadopen, true, false) <= 0) 2749Sjkh continue; 2759Sjkh diagnose("===================================================================\nRCS file: %s\n",RCSfilename); 2769Sjkh if (!rev2) { 2779Sjkh /* Make sure work file is readable, and get its status. */ 2789Sjkh if (!(workptr = Iopen(workfilename,FOPEN_R_WORK,&workstat))) { 2799Sjkh eerror(workfilename); 2809Sjkh continue; 2819Sjkh } 2829Sjkh } 2839Sjkh 2849Sjkh 2859Sjkh gettree(); /* reads in the delta tree */ 2869Sjkh 2879Sjkh if (Head==nil) { 2889Sjkh error("no revisions present"); 2899Sjkh continue; 2909Sjkh } 2919Sjkh if (revnums==0 || !*rev1) 2929Sjkh rev1 = Dbranch ? Dbranch : Head->num; 2939Sjkh 2949Sjkh if (!fexpandsym(rev1, &numericrev, workptr)) continue; 2959Sjkh if (!(target=genrevs(numericrev.string,(char *)nil,(char *)nil,(char *)nil,&gendeltas))) continue; 2969Sjkh xrev1=target->num; 2979Sjkh#if DIFF_L 2989Sjkh if (diff_label1) 2999Sjkh *diff_label1 = setup_label(&labelbuf[0], target->num, target->date); 3009Sjkh#endif 3019Sjkh 3029Sjkh lexpandarg = expandarg; 3039Sjkh if (revnums==2) { 3049Sjkh if (!fexpandsym( 3059Sjkh *rev2 ? rev2 : Dbranch ? Dbranch : Head->num, 3069Sjkh &numericrev, 3079Sjkh workptr 3089Sjkh )) 3099Sjkh continue; 3109Sjkh if (!(target=genrevs(numericrev.string,(char *)nil,(char *)nil,(char *)nil,&gendeltas))) continue; 3119Sjkh xrev2=target->num; 3129Sjkh } else if ( 3139Sjkh target->lockedby 3149Sjkh && !lexpandarg 3159Sjkh && Expand == KEYVAL_EXPAND 3169Sjkh && WORKMODE(RCSstat.st_mode,true) == workstat.st_mode 3179Sjkh ) 3189Sjkh lexpandarg = "-kkvl"; 3199Sjkh Izclose(&workptr); 3209Sjkh#if DIFF_L 3219Sjkh if (diff_label2) 3229Sjkh if (revnums == 2) 3239Sjkh *diff_label2 = setup_label(&labelbuf[1], target->num, target->date); 3249Sjkh else { 3259Sjkh time2date(workstat.st_mtime, date2); 3269Sjkh *diff_label2 = setup_label(&labelbuf[1], workfilename, date2); 3279Sjkh } 3289Sjkh#endif 3299Sjkh 3309Sjkh diagnose("retrieving revision %s\n", xrev1); 3319Sjkh bufscpy(&commarg, "-p"); 3329Sjkh bufscat(&commarg, xrev1); 3339Sjkh 3349Sjkh cov[1] = diffp[0] = maketemp(0); 3359Sjkh pp = &cov[4]; 3369Sjkh *pp++ = commarg.string; 3379Sjkh if (lexpandarg) 3389Sjkh *pp++ = lexpandarg; 3399Sjkh if (versionarg) 3409Sjkh *pp++ = versionarg; 3419Sjkh *pp++ = RCSfilename; 3429Sjkh *pp = 0; 3439Sjkh 3449Sjkh if (runv(cov)) { 3459Sjkh error("co failed"); 3469Sjkh continue; 3479Sjkh } 3489Sjkh if (!rev2) { 3499Sjkh diffp[1] = workfilename; 3509Sjkh if (workfilename[0] == '+') { 3519Sjkh /* Some diffs have options with leading '+'. */ 3529Sjkh char *dp = ftnalloc(char, strlen(workfilename)+3); 3539Sjkh diffp[1] = dp; 3549Sjkh *dp++ = '.'; 3559Sjkh *dp++ = SLASH; 3569Sjkh VOID strcpy(dp, workfilename); 3579Sjkh } 3589Sjkh } else { 3599Sjkh diagnose("retrieving revision %s\n",xrev2); 3609Sjkh bufscpy(&commarg, "-p"); 3619Sjkh bufscat(&commarg, xrev2); 3629Sjkh cov[1] = diffp[1] = maketemp(1); 3639Sjkh cov[4] = commarg.string; 3649Sjkh if (runv(cov)) { 3659Sjkh error("co failed"); 3669Sjkh continue; 3679Sjkh } 3689Sjkh } 3699Sjkh if (!rev2) 3709Sjkh diagnose("diff%s -r%s %s\n", diffvstr, xrev1, workfilename); 3719Sjkh else 3729Sjkh diagnose("diff%s -r%s -r%s\n", diffvstr, xrev1, xrev2); 3739Sjkh 3749Sjkh switch (runv(diffv)) { 3759Sjkh case DIFF_SUCCESS: 3769Sjkh break; 3779Sjkh case DIFF_FAILURE: 3789Sjkh if (exitstatus == DIFF_SUCCESS) 3799Sjkh exitstatus = DIFF_FAILURE; 3809Sjkh break; 3819Sjkh default: 3829Sjkh error("diff failed"); 3839Sjkh } 3849Sjkh } while (cleanup(), 3859Sjkh ++argv, --argc >=1); 3869Sjkh 3879Sjkh 3889Sjkh tempunlink(); 3899Sjkh exitmain(exitstatus); 3909Sjkh} 3919Sjkh 3929Sjkh static void 3939Sjkhcleanup() 3949Sjkh{ 3959Sjkh if (nerror) exitstatus = DIFF_TROUBLE; 3969Sjkh Izclose(&finptr); 3979Sjkh Izclose(&workptr); 3989Sjkh} 3999Sjkh 4009Sjkh#if lint 4019Sjkh# define exiterr rdiffExit 4029Sjkh#endif 4039Sjkh exiting void 4049Sjkhexiterr() 4059Sjkh{ 4069Sjkh tempunlink(); 4079Sjkh _exit(DIFF_TROUBLE); 4089Sjkh} 4099Sjkh 4109Sjkh#if DIFF_L 4119Sjkh static char const * 4129Sjkhsetup_label(b, name, date) 4139Sjkh struct buf *b; 4149Sjkh char const *name; 4159Sjkh char const date[datesize]; 4169Sjkh{ 4179Sjkh char *p; 4189Sjkh size_t l = strlen(name) + 3; 4199Sjkh bufalloc(b, l+datesize); 4209Sjkh p = b->string; 4219Sjkh VOID sprintf(p, "-L%s\t", name); 4229Sjkh VOID date2str(date, p+l); 4239Sjkh return p; 4249Sjkh} 4259Sjkh#endif 426