rcsfcmp.c revision 256198
1117395Skan/* Compare working files, ignoring RCS keyword strings.  */
2132718Skan
3117395Skan/*****************************************************************************
4117395Skan *                       rcsfcmp()
5117395Skan *                       Testprogram: define FCMPTEST
6117395Skan *****************************************************************************
7117395Skan */
8117395Skan
9117395Skan/* Copyright 1982, 1988, 1989 Walter Tichy
10117395Skan   Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
11117395Skan   Distributed under license by the Free Software Foundation, Inc.
12117395Skan
13117395SkanThis file is part of RCS.
14117395Skan
15117395SkanRCS is free software; you can redistribute it and/or modify
16117395Skanit under the terms of the GNU General Public License as published by
17117395Skanthe Free Software Foundation; either version 2, or (at your option)
18117395Skanany later version.
19117395Skan
20117395SkanRCS is distributed in the hope that it will be useful,
21117395Skanbut WITHOUT ANY WARRANTY; without even the implied warranty of
22117395SkanMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23117395SkanGNU General Public License for more details.
24117395Skan
25117395SkanYou should have received a copy of the GNU General Public License
26117395Skanalong with RCS; see the file COPYING.
27117395SkanIf not, write to the Free Software Foundation,
28117395Skan59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29117395Skan
30132718SkanReport problems and direct all questions to:
31132718Skan
32132718Skan    rcs-bugs@cs.purdue.edu
33132718Skan
34132718Skan*/
35132718Skan
36132718Skan
37117395Skan
38117395Skan
39132718Skan
40117395Skan/*
41117395Skan * Revision 5.14  1995/06/16 06:19:24  eggert
42132718Skan * Update FSF address.
43117395Skan *
44132718Skan * Revision 5.13  1995/06/01 16:23:43  eggert
45132718Skan * (rcsfcmp): Add -kb support.
46132718Skan *
47132718Skan * Revision 5.12  1994/03/17 14:05:48  eggert
48132718Skan * Normally calculate the $Log prefix from context, not from RCS file.
49132718Skan * Calculate line numbers correctly even if the $Log prefix contains newlines.
50132718Skan * Remove lint.
51132718Skan *
52132718Skan * Revision 5.11  1993/11/03 17:42:27  eggert
53132718Skan * Fix yet another off-by-one error when comparing Log string expansions.
54132718Skan *
55117395Skan * Revision 5.10  1992/07/28 16:12:44  eggert
56117395Skan * Statement macro names now end in _.
57132718Skan *
58117395Skan * Revision 5.9  1991/10/07  17:32:46  eggert
59117395Skan * Count log lines correctly.
60117395Skan *
61117395Skan * Revision 5.8  1991/08/19  03:13:55  eggert
62132718Skan * Tune.
63132718Skan *
64132718Skan * Revision 5.7  1991/04/21  11:58:22  eggert
65132718Skan * Fix errno bug.  Add MS-DOS support.
66117395Skan *
67117395Skan * Revision 5.6  1991/02/28  19:18:47  eggert
68117395Skan * Open work file at most once.
69132718Skan *
70132718Skan * Revision 5.5  1990/11/27  09:26:05  eggert
71132718Skan * Fix comment leader bug.
72117395Skan *
73132718Skan * Revision 5.4  1990/11/01  05:03:42  eggert
74132718Skan * Permit arbitrary data in logs and comment leaders.
75117395Skan *
76132718Skan * Revision 5.3  1990/09/11  02:41:15  eggert
77132718Skan * Don't ignore differences inside keyword strings if -ko is set.
78132718Skan *
79132718Skan * Revision 5.1  1990/08/29  07:13:58  eggert
80117395Skan * Clean old log messages too.
81117395Skan *
82117395Skan * Revision 5.0  1990/08/22  08:12:49  eggert
83132718Skan * Don't append "checked in with -k by " log to logs,
84117395Skan * so that checking in a program with -k doesn't change it.
85117395Skan * Ansify and Posixate.  Remove lint.
86117395Skan *
87117395Skan * Revision 4.5  89/05/01  15:12:42  narten
88117395Skan * changed copyright header to reflect current distribution rules
89117395Skan *
90132718Skan * Revision 4.4  88/08/09  19:12:50  eggert
91117395Skan * Shrink stdio code size.
92117395Skan *
93132718Skan * Revision 4.3  87/12/18  11:40:02  narten
94132718Skan * lint cleanups (Guy Harris)
95132718Skan *
96132718Skan * Revision 4.2  87/10/18  10:33:06  narten
97132718Skan * updting version number. Changes relative to 1.1 actually relative to
98117395Skan * 4.1
99117395Skan *
100117395Skan * Revision 1.2  87/03/27  14:22:19  jenkins
101117395Skan * Port to suns
102117395Skan *
103132718Skan * Revision 4.1  83/05/10  16:24:04  wft
104117395Skan * Marker matching now uses trymatch(). Marker pattern is now
105132718Skan * checked precisely.
106132718Skan *
107132718Skan * Revision 3.1  82/12/04  13:21:40  wft
108132718Skan * Initial revision.
109132718Skan *
110117395Skan */
111132718Skan
112117395Skan/*
113132718Skan#define FCMPTEST
114132718Skan*/
115132718Skan/* Testprogram; prints out whether two files are identical,
116132718Skan * except for keywords
117132718Skan */
118132718Skan
119132718Skan#include  "rcsbase.h"
120117395Skan
121132718SkanlibId(fcmpId, "$FreeBSD: head/gnu/usr.bin/rcs/lib/rcsfcmp.c 50472 1999-08-27 23:37:10Z peter $")
122132718Skan
123132718Skan	static int discardkeyval P((int,RILE*));
124132718Skan	static int
125132718Skandiscardkeyval(c, f)
126132718Skan	register int c;
127132718Skan	register RILE *f;
128117395Skan{
129117395Skan	for (;;)
130132718Skan		switch (c) {
131117395Skan			case KDELIM:
132132718Skan			case '\n':
133132718Skan				return c;
134117395Skan			default:
135132718Skan				Igeteof_(f, c, return EOF;)
136117395Skan				break;
137132718Skan		}
138117395Skan}
139132718Skan
140117395Skan	int
141132718Skanrcsfcmp(xfp, xstatp, uname, delta)
142117395Skan	register RILE *xfp;
143132718Skan	struct stat const *xstatp;
144117395Skan	char const *uname;
145132718Skan	struct hshentry const *delta;
146117395Skan/* Compare the files xfp and uname.  Return zero
147132718Skan * if xfp has the same contents as uname and neither has keywords,
148132718Skan * otherwise -1 if they are the same ignoring keyword values,
149132718Skan * and 1 if they differ even ignoring
150117395Skan * keyword values. For the LOG-keyword, rcsfcmp skips the log message
151117395Skan * given by the parameter delta in xfp.  Thus, rcsfcmp returns nonpositive
152117395Skan * if xfp contains the same as uname, with the keywords expanded.
153132718Skan * Implementation: character-by-character comparison until $ is found.
154117395Skan * If a $ is found, read in the marker keywords; if they are real keywords
155117395Skan * and identical, read in keyword value. If value is terminated properly,
156132718Skan * disregard it and optionally skip log message; otherwise, compare value.
157132718Skan */
158132718Skan{
159132718Skan    register int xc, uc;
160132718Skan    char xkeyword[keylength+2];
161132718Skan    int eqkeyvals;
162132718Skan    register RILE *ufp;
163132718Skan    register int xeof, ueof;
164132718Skan    register char * tp;
165132718Skan    register char const *sp;
166132718Skan    register size_t leaderlen;
167117395Skan    int result;
168117395Skan    enum markers match1;
169132718Skan    struct stat ustat;
170132718Skan
171132718Skan    if (!(ufp = Iopen(uname, FOPEN_R_WORK, &ustat))) {
172132718Skan       efaterror(uname);
173132718Skan    }
174132718Skan    xeof = ueof = false;
175132718Skan    if (MIN_UNEXPAND <= Expand) {
176132718Skan	if (!(result = xstatp->st_size!=ustat.st_size)) {
177132718Skan#	    if large_memory && maps_memory
178132718Skan		result = !!memcmp(xfp->base,ufp->base,(size_t)xstatp->st_size);
179132718Skan#	    else
180132718Skan		for (;;) {
181132718Skan		    /* get the next characters */
182132718Skan		    Igeteof_(xfp, xc, xeof=true;)
183117395Skan		    Igeteof_(ufp, uc, ueof=true;)
184132718Skan		    if (xeof | ueof)
185117395Skan			goto eof;
186132718Skan		    if (xc != uc)
187132718Skan			goto return1;
188132718Skan		}
189132718Skan#	    endif
190132718Skan	}
191132718Skan    } else {
192132718Skan	xc = 0;
193132718Skan	uc = 0; /* Keep lint happy.  */
194132718Skan	leaderlen = 0;
195132718Skan	result = 0;
196132718Skan
197132718Skan	for (;;) {
198132718Skan	  if (xc != KDELIM) {
199117395Skan	    /* get the next characters */
200117395Skan	    Igeteof_(xfp, xc, xeof=true;)
201	    Igeteof_(ufp, uc, ueof=true;)
202	    if (xeof | ueof)
203		goto eof;
204	  } else {
205	    /* try to get both keywords */
206	    tp = xkeyword;
207	    for (;;) {
208		Igeteof_(xfp, xc, xeof=true;)
209		Igeteof_(ufp, uc, ueof=true;)
210		if (xeof | ueof)
211		    goto eof;
212		if (xc != uc)
213		    break;
214		switch (xc) {
215		    default:
216			if (xkeyword+keylength <= tp)
217			    break;
218			*tp++ = xc;
219			continue;
220		    case '\n': case KDELIM: case VDELIM:
221			break;
222		}
223		break;
224	    }
225	    if (
226		(xc==KDELIM || xc==VDELIM)  &&  (uc==KDELIM || uc==VDELIM)  &&
227		(*tp = xc,  (match1 = trymatch(xkeyword)) != Nomatch)
228	    ) {
229#ifdef FCMPTEST
230	      VOID printf("found common keyword %s\n",xkeyword);
231#endif
232	      result = -1;
233	      for (;;) {
234		  if (xc != uc) {
235		      xc = discardkeyval(xc, xfp);
236		      uc = discardkeyval(uc, ufp);
237		      if ((xeof = xc==EOF)  |  (ueof = uc==EOF))
238			  goto eof;
239		      eqkeyvals = false;
240		      break;
241		  }
242		  switch (xc) {
243		      default:
244			  Igeteof_(xfp, xc, xeof=true;)
245			  Igeteof_(ufp, uc, ueof=true;)
246			  if (xeof | ueof)
247			      goto eof;
248			  continue;
249
250		      case '\n': case KDELIM:
251			  eqkeyvals = true;
252			  break;
253		  }
254		  break;
255	      }
256	      if (xc != uc)
257		  goto return1;
258	      if (xc==KDELIM) {
259		  /* Skip closing KDELIM.  */
260		  Igeteof_(xfp, xc, xeof=true;)
261		  Igeteof_(ufp, uc, ueof=true;)
262		  if (xeof | ueof)
263		      goto eof;
264		  /* if the keyword is LOG, also skip the log message in xfp*/
265		  if (match1==Log) {
266		      /* first, compute the number of line feeds in log msg */
267		      int lncnt;
268		      size_t ls, ccnt;
269		      sp = delta->log.string;
270		      ls = delta->log.size;
271		      if (ls<sizeof(ciklog)-1 || memcmp(sp,ciklog,sizeof(ciklog)-1)) {
272			/*
273			* This log message was inserted.  Skip its header.
274			* The number of newlines to skip is
275			* 1 + (C+1)*(1+L+1), where C is the number of newlines
276			* in the comment leader, and L is the number of
277			* newlines in the log string.
278			*/
279			int c1 = 1;
280			for (ccnt=Comment.size; ccnt--; )
281			    c1 += Comment.string[ccnt] == '\n';
282			lncnt = 2*c1 + 1;
283			while (ls--) if (*sp++=='\n') lncnt += c1;
284			for (;;) {
285			    if (xc=='\n')
286				if(--lncnt==0) break;
287			    Igeteof_(xfp, xc, goto returnresult;)
288			}
289			/* skip last comment leader */
290			/* Can't just skip another line here, because there may be */
291			/* additional characters on the line (after the Log....$)  */
292			ccnt = RCSversion<VERSION(5) ? Comment.size : leaderlen;
293			do {
294			    Igeteof_(xfp, xc, goto returnresult;)
295			    /*
296			     * Read to the end of the comment leader or '\n',
297			     * whatever comes first, because the leader's
298			     * trailing white space was probably stripped.
299			     */
300			} while (ccnt-- && (xc!='\n' || --c1));
301		      }
302		  }
303	      } else {
304		  /* both end in the same character, but not a KDELIM */
305		  /* must compare string values.*/
306#ifdef FCMPTEST
307		  VOID printf("non-terminated keywords %s, potentially different values\n",xkeyword);
308#endif
309		  if (!eqkeyvals)
310		      goto return1;
311	      }
312	    }
313	  }
314	  if (xc != uc)
315	      goto return1;
316	  if (xc == '\n')
317	      leaderlen = 0;
318	  else
319	      leaderlen++;
320	}
321    }
322
323  eof:
324    if (xeof==ueof)
325	goto returnresult;
326  return1:
327    result = 1;
328  returnresult:
329    Ifclose(ufp);
330    return result;
331}
332
333
334
335#ifdef FCMPTEST
336
337char const cmdid[] = "rcsfcmp";
338
339main(argc, argv)
340int  argc; char  *argv[];
341/* first argument: comment leader; 2nd: log message, 3rd: expanded file,
342 * 4th: unexpanded file
343 */
344{       struct hshentry delta;
345
346	Comment.string = argv[1];
347	Comment.size = strlen(argv[1]);
348	delta.log.string = argv[2];
349	delta.log.size = strlen(argv[2]);
350	if (rcsfcmp(Iopen(argv[3], FOPEN_R_WORK, (struct stat*)0), argv[4], &delta))
351                VOID printf("files are the same\n");
352        else    VOID printf("files are different\n");
353}
354#endif
355