111894Speter/* Compare working files, ignoring RCS keyword strings.  */
211894Speter
39Sjkh/*****************************************************************************
49Sjkh *                       rcsfcmp()
59Sjkh *                       Testprogram: define FCMPTEST
69Sjkh *****************************************************************************
79Sjkh */
89Sjkh
911894Speter/* Copyright 1982, 1988, 1989 Walter Tichy
1011894Speter   Copyright 1990, 1991, 1992, 1993, 1994, 1995 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
2611894Speteralong with RCS; see the file COPYING.
2711894SpeterIf not, write to the Free Software Foundation,
2811894Speter59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
299Sjkh
309SjkhReport problems and direct all questions to:
319Sjkh
329Sjkh    rcs-bugs@cs.purdue.edu
339Sjkh
349Sjkh*/
359Sjkh
369Sjkh
379Sjkh
389Sjkh
399Sjkh
4011894Speter/*
4111894Speter * Revision 5.14  1995/06/16 06:19:24  eggert
4211894Speter * Update FSF address.
438858Srgrimes *
4411894Speter * Revision 5.13  1995/06/01 16:23:43  eggert
4511894Speter * (rcsfcmp): Add -kb support.
4611894Speter *
4711894Speter * Revision 5.12  1994/03/17 14:05:48  eggert
4811894Speter * Normally calculate the $Log prefix from context, not from RCS file.
4911894Speter * Calculate line numbers correctly even if the $Log prefix contains newlines.
5011894Speter * Remove lint.
5111894Speter *
5211894Speter * Revision 5.11  1993/11/03 17:42:27  eggert
5311894Speter * Fix yet another off-by-one error when comparing Log string expansions.
5411894Speter *
5511894Speter * Revision 5.10  1992/07/28 16:12:44  eggert
5611894Speter * Statement macro names now end in _.
5711894Speter *
589Sjkh * Revision 5.9  1991/10/07  17:32:46  eggert
599Sjkh * Count log lines correctly.
609Sjkh *
619Sjkh * Revision 5.8  1991/08/19  03:13:55  eggert
629Sjkh * Tune.
639Sjkh *
649Sjkh * Revision 5.7  1991/04/21  11:58:22  eggert
659Sjkh * Fix errno bug.  Add MS-DOS support.
669Sjkh *
679Sjkh * Revision 5.6  1991/02/28  19:18:47  eggert
689Sjkh * Open work file at most once.
699Sjkh *
709Sjkh * Revision 5.5  1990/11/27  09:26:05  eggert
719Sjkh * Fix comment leader bug.
729Sjkh *
739Sjkh * Revision 5.4  1990/11/01  05:03:42  eggert
749Sjkh * Permit arbitrary data in logs and comment leaders.
759Sjkh *
769Sjkh * Revision 5.3  1990/09/11  02:41:15  eggert
779Sjkh * Don't ignore differences inside keyword strings if -ko is set.
789Sjkh *
799Sjkh * Revision 5.1  1990/08/29  07:13:58  eggert
809Sjkh * Clean old log messages too.
819Sjkh *
829Sjkh * Revision 5.0  1990/08/22  08:12:49  eggert
839Sjkh * Don't append "checked in with -k by " log to logs,
849Sjkh * so that checking in a program with -k doesn't change it.
859Sjkh * Ansify and Posixate.  Remove lint.
869Sjkh *
879Sjkh * Revision 4.5  89/05/01  15:12:42  narten
889Sjkh * changed copyright header to reflect current distribution rules
898858Srgrimes *
909Sjkh * Revision 4.4  88/08/09  19:12:50  eggert
919Sjkh * Shrink stdio code size.
928858Srgrimes *
939Sjkh * Revision 4.3  87/12/18  11:40:02  narten
949Sjkh * lint cleanups (Guy Harris)
958858Srgrimes *
969Sjkh * Revision 4.2  87/10/18  10:33:06  narten
978858Srgrimes * updting version number. Changes relative to 1.1 actually relative to
989Sjkh * 4.1
998858Srgrimes *
1009Sjkh * Revision 1.2  87/03/27  14:22:19  jenkins
1019Sjkh * Port to suns
1028858Srgrimes *
1039Sjkh * Revision 4.1  83/05/10  16:24:04  wft
1049Sjkh * Marker matching now uses trymatch(). Marker pattern is now
1059Sjkh * checked precisely.
1068858Srgrimes *
1079Sjkh * Revision 3.1  82/12/04  13:21:40  wft
1089Sjkh * Initial revision.
1099Sjkh *
1109Sjkh */
1119Sjkh
1129Sjkh/*
1139Sjkh#define FCMPTEST
1149Sjkh*/
1159Sjkh/* Testprogram; prints out whether two files are identical,
1169Sjkh * except for keywords
1179Sjkh */
1189Sjkh
1199Sjkh#include  "rcsbase.h"
1209Sjkh
12150472SpeterlibId(fcmpId, "$FreeBSD$")
1229Sjkh
12311894Speter	static int discardkeyval P((int,RILE*));
1249Sjkh	static int
1259Sjkhdiscardkeyval(c, f)
1269Sjkh	register int c;
1279Sjkh	register RILE *f;
1289Sjkh{
1299Sjkh	for (;;)
1309Sjkh		switch (c) {
1319Sjkh			case KDELIM:
1329Sjkh			case '\n':
1339Sjkh				return c;
1349Sjkh			default:
13511894Speter				Igeteof_(f, c, return EOF;)
1369Sjkh				break;
1379Sjkh		}
1389Sjkh}
1399Sjkh
1409Sjkh	int
14111894Speterrcsfcmp(xfp, xstatp, uname, delta)
1429Sjkh	register RILE *xfp;
1439Sjkh	struct stat const *xstatp;
14411894Speter	char const *uname;
1459Sjkh	struct hshentry const *delta;
14611894Speter/* Compare the files xfp and uname.  Return zero
14711894Speter * if xfp has the same contents as uname and neither has keywords,
1489Sjkh * otherwise -1 if they are the same ignoring keyword values,
1499Sjkh * and 1 if they differ even ignoring
1509Sjkh * keyword values. For the LOG-keyword, rcsfcmp skips the log message
1519Sjkh * given by the parameter delta in xfp.  Thus, rcsfcmp returns nonpositive
15211894Speter * if xfp contains the same as uname, with the keywords expanded.
1539Sjkh * Implementation: character-by-character comparison until $ is found.
1549Sjkh * If a $ is found, read in the marker keywords; if they are real keywords
1559Sjkh * and identical, read in keyword value. If value is terminated properly,
1569Sjkh * disregard it and optionally skip log message; otherwise, compare value.
1579Sjkh */
1589Sjkh{
1599Sjkh    register int xc, uc;
1609Sjkh    char xkeyword[keylength+2];
1619Sjkh    int eqkeyvals;
1629Sjkh    register RILE *ufp;
1639Sjkh    register int xeof, ueof;
1649Sjkh    register char * tp;
1659Sjkh    register char const *sp;
16611894Speter    register size_t leaderlen;
1679Sjkh    int result;
1689Sjkh    enum markers match1;
1699Sjkh    struct stat ustat;
1709Sjkh
17111894Speter    if (!(ufp = Iopen(uname, FOPEN_R_WORK, &ustat))) {
17211894Speter       efaterror(uname);
1739Sjkh    }
1749Sjkh    xeof = ueof = false;
17511894Speter    if (MIN_UNEXPAND <= Expand) {
1769Sjkh	if (!(result = xstatp->st_size!=ustat.st_size)) {
17711894Speter#	    if large_memory && maps_memory
1789Sjkh		result = !!memcmp(xfp->base,ufp->base,(size_t)xstatp->st_size);
1799Sjkh#	    else
1809Sjkh		for (;;) {
1819Sjkh		    /* get the next characters */
18211894Speter		    Igeteof_(xfp, xc, xeof=true;)
18311894Speter		    Igeteof_(ufp, uc, ueof=true;)
1849Sjkh		    if (xeof | ueof)
1859Sjkh			goto eof;
1869Sjkh		    if (xc != uc)
1879Sjkh			goto return1;
1889Sjkh		}
1899Sjkh#	    endif
1909Sjkh	}
1919Sjkh    } else {
1929Sjkh	xc = 0;
1939Sjkh	uc = 0; /* Keep lint happy.  */
19411894Speter	leaderlen = 0;
1959Sjkh	result = 0;
1969Sjkh
1979Sjkh	for (;;) {
1989Sjkh	  if (xc != KDELIM) {
1999Sjkh	    /* get the next characters */
20011894Speter	    Igeteof_(xfp, xc, xeof=true;)
20111894Speter	    Igeteof_(ufp, uc, ueof=true;)
2029Sjkh	    if (xeof | ueof)
2039Sjkh		goto eof;
2049Sjkh	  } else {
2059Sjkh	    /* try to get both keywords */
2069Sjkh	    tp = xkeyword;
2079Sjkh	    for (;;) {
20811894Speter		Igeteof_(xfp, xc, xeof=true;)
20911894Speter		Igeteof_(ufp, uc, ueof=true;)
2109Sjkh		if (xeof | ueof)
2119Sjkh		    goto eof;
2129Sjkh		if (xc != uc)
2139Sjkh		    break;
2149Sjkh		switch (xc) {
2159Sjkh		    default:
2169Sjkh			if (xkeyword+keylength <= tp)
2179Sjkh			    break;
2189Sjkh			*tp++ = xc;
2199Sjkh			continue;
2209Sjkh		    case '\n': case KDELIM: case VDELIM:
2219Sjkh			break;
2229Sjkh		}
2239Sjkh		break;
2249Sjkh	    }
2259Sjkh	    if (
2269Sjkh		(xc==KDELIM || xc==VDELIM)  &&  (uc==KDELIM || uc==VDELIM)  &&
2279Sjkh		(*tp = xc,  (match1 = trymatch(xkeyword)) != Nomatch)
2289Sjkh	    ) {
2299Sjkh#ifdef FCMPTEST
2309Sjkh	      VOID printf("found common keyword %s\n",xkeyword);
2319Sjkh#endif
2329Sjkh	      result = -1;
2339Sjkh	      for (;;) {
2349Sjkh		  if (xc != uc) {
2359Sjkh		      xc = discardkeyval(xc, xfp);
2369Sjkh		      uc = discardkeyval(uc, ufp);
2379Sjkh		      if ((xeof = xc==EOF)  |  (ueof = uc==EOF))
2389Sjkh			  goto eof;
2399Sjkh		      eqkeyvals = false;
2409Sjkh		      break;
2419Sjkh		  }
2429Sjkh		  switch (xc) {
2439Sjkh		      default:
24411894Speter			  Igeteof_(xfp, xc, xeof=true;)
24511894Speter			  Igeteof_(ufp, uc, ueof=true;)
2469Sjkh			  if (xeof | ueof)
2479Sjkh			      goto eof;
2489Sjkh			  continue;
2499Sjkh
2509Sjkh		      case '\n': case KDELIM:
2519Sjkh			  eqkeyvals = true;
2529Sjkh			  break;
2539Sjkh		  }
2549Sjkh		  break;
2559Sjkh	      }
2569Sjkh	      if (xc != uc)
2579Sjkh		  goto return1;
2589Sjkh	      if (xc==KDELIM) {
2599Sjkh		  /* Skip closing KDELIM.  */
26011894Speter		  Igeteof_(xfp, xc, xeof=true;)
26111894Speter		  Igeteof_(ufp, uc, ueof=true;)
2629Sjkh		  if (xeof | ueof)
2639Sjkh		      goto eof;
2649Sjkh		  /* if the keyword is LOG, also skip the log message in xfp*/
2659Sjkh		  if (match1==Log) {
2669Sjkh		      /* first, compute the number of line feeds in log msg */
26711894Speter		      int lncnt;
2689Sjkh		      size_t ls, ccnt;
2699Sjkh		      sp = delta->log.string;
2709Sjkh		      ls = delta->log.size;
2719Sjkh		      if (ls<sizeof(ciklog)-1 || memcmp(sp,ciklog,sizeof(ciklog)-1)) {
27211894Speter			/*
27311894Speter			* This log message was inserted.  Skip its header.
27411894Speter			* The number of newlines to skip is
27511894Speter			* 1 + (C+1)*(1+L+1), where C is the number of newlines
27611894Speter			* in the comment leader, and L is the number of
27711894Speter			* newlines in the log string.
27811894Speter			*/
27911894Speter			int c1 = 1;
28011894Speter			for (ccnt=Comment.size; ccnt--; )
28111894Speter			    c1 += Comment.string[ccnt] == '\n';
28211894Speter			lncnt = 2*c1 + 1;
28311894Speter			while (ls--) if (*sp++=='\n') lncnt += c1;
2849Sjkh			for (;;) {
2859Sjkh			    if (xc=='\n')
2869Sjkh				if(--lncnt==0) break;
28711894Speter			    Igeteof_(xfp, xc, goto returnresult;)
2889Sjkh			}
2899Sjkh			/* skip last comment leader */
2909Sjkh			/* Can't just skip another line here, because there may be */
2919Sjkh			/* additional characters on the line (after the Log....$)  */
29211894Speter			ccnt = RCSversion<VERSION(5) ? Comment.size : leaderlen;
29311894Speter			do {
29411894Speter			    Igeteof_(xfp, xc, goto returnresult;)
2959Sjkh			    /*
2969Sjkh			     * Read to the end of the comment leader or '\n',
29711894Speter			     * whatever comes first, because the leader's
29811894Speter			     * trailing white space was probably stripped.
2999Sjkh			     */
30011894Speter			} while (ccnt-- && (xc!='\n' || --c1));
3019Sjkh		      }
3029Sjkh		  }
3039Sjkh	      } else {
3049Sjkh		  /* both end in the same character, but not a KDELIM */
3059Sjkh		  /* must compare string values.*/
3069Sjkh#ifdef FCMPTEST
3079Sjkh		  VOID printf("non-terminated keywords %s, potentially different values\n",xkeyword);
3089Sjkh#endif
3099Sjkh		  if (!eqkeyvals)
3109Sjkh		      goto return1;
3119Sjkh	      }
3129Sjkh	    }
3139Sjkh	  }
3149Sjkh	  if (xc != uc)
3159Sjkh	      goto return1;
31611894Speter	  if (xc == '\n')
31711894Speter	      leaderlen = 0;
31811894Speter	  else
31911894Speter	      leaderlen++;
3209Sjkh	}
3219Sjkh    }
3229Sjkh
3239Sjkh  eof:
3249Sjkh    if (xeof==ueof)
3259Sjkh	goto returnresult;
3269Sjkh  return1:
3279Sjkh    result = 1;
3289Sjkh  returnresult:
3299Sjkh    Ifclose(ufp);
3309Sjkh    return result;
3319Sjkh}
3329Sjkh
3339Sjkh
3349Sjkh
3359Sjkh#ifdef FCMPTEST
3369Sjkh
3379Sjkhchar const cmdid[] = "rcsfcmp";
3389Sjkh
3399Sjkhmain(argc, argv)
3409Sjkhint  argc; char  *argv[];
3419Sjkh/* first argument: comment leader; 2nd: log message, 3rd: expanded file,
3429Sjkh * 4th: unexpanded file
3439Sjkh */
3449Sjkh{       struct hshentry delta;
3459Sjkh
3469Sjkh	Comment.string = argv[1];
3479Sjkh	Comment.size = strlen(argv[1]);
3489Sjkh	delta.log.string = argv[2];
3499Sjkh	delta.log.size = strlen(argv[2]);
3509Sjkh	if (rcsfcmp(Iopen(argv[3], FOPEN_R_WORK, (struct stat*)0), argv[4], &delta))
3519Sjkh                VOID printf("files are the same\n");
3529Sjkh        else    VOID printf("files are different\n");
3539Sjkh}
3549Sjkh#endif
355