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