rcsfcmp.c revision 11894
1/* Compare working files, ignoring RCS keyword strings.  */
2
3/*****************************************************************************
4 *                       rcsfcmp()
5 *                       Testprogram: define FCMPTEST
6 *****************************************************************************
7 */
8
9/* Copyright 1982, 1988, 1989 Walter Tichy
10   Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
11   Distributed under license by the Free Software Foundation, Inc.
12
13This file is part of RCS.
14
15RCS is free software; you can redistribute it and/or modify
16it under the terms of the GNU General Public License as published by
17the Free Software Foundation; either version 2, or (at your option)
18any later version.
19
20RCS is distributed in the hope that it will be useful,
21but WITHOUT ANY WARRANTY; without even the implied warranty of
22MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23GNU General Public License for more details.
24
25You should have received a copy of the GNU General Public License
26along with RCS; see the file COPYING.
27If not, write to the Free Software Foundation,
2859 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29
30Report problems and direct all questions to:
31
32    rcs-bugs@cs.purdue.edu
33
34*/
35
36
37
38
39
40/*
41 * $Log: rcsfcmp.c,v $
42 * Revision 5.14  1995/06/16 06:19:24  eggert
43 * Update FSF address.
44 *
45 * Revision 5.13  1995/06/01 16:23:43  eggert
46 * (rcsfcmp): Add -kb support.
47 *
48 * Revision 5.12  1994/03/17 14:05:48  eggert
49 * Normally calculate the $Log prefix from context, not from RCS file.
50 * Calculate line numbers correctly even if the $Log prefix contains newlines.
51 * Remove lint.
52 *
53 * Revision 5.11  1993/11/03 17:42:27  eggert
54 * Fix yet another off-by-one error when comparing Log string expansions.
55 *
56 * Revision 5.10  1992/07/28 16:12:44  eggert
57 * Statement macro names now end in _.
58 *
59 * Revision 5.9  1991/10/07  17:32:46  eggert
60 * Count log lines correctly.
61 *
62 * Revision 5.8  1991/08/19  03:13:55  eggert
63 * Tune.
64 *
65 * Revision 5.7  1991/04/21  11:58:22  eggert
66 * Fix errno bug.  Add MS-DOS support.
67 *
68 * Revision 5.6  1991/02/28  19:18:47  eggert
69 * Open work file at most once.
70 *
71 * Revision 5.5  1990/11/27  09:26:05  eggert
72 * Fix comment leader bug.
73 *
74 * Revision 5.4  1990/11/01  05:03:42  eggert
75 * Permit arbitrary data in logs and comment leaders.
76 *
77 * Revision 5.3  1990/09/11  02:41:15  eggert
78 * Don't ignore differences inside keyword strings if -ko is set.
79 *
80 * Revision 5.1  1990/08/29  07:13:58  eggert
81 * Clean old log messages too.
82 *
83 * Revision 5.0  1990/08/22  08:12:49  eggert
84 * Don't append "checked in with -k by " log to logs,
85 * so that checking in a program with -k doesn't change it.
86 * Ansify and Posixate.  Remove lint.
87 *
88 * Revision 4.5  89/05/01  15:12:42  narten
89 * changed copyright header to reflect current distribution rules
90 *
91 * Revision 4.4  88/08/09  19:12:50  eggert
92 * Shrink stdio code size.
93 *
94 * Revision 4.3  87/12/18  11:40:02  narten
95 * lint cleanups (Guy Harris)
96 *
97 * Revision 4.2  87/10/18  10:33:06  narten
98 * updting version number. Changes relative to 1.1 actually relative to
99 * 4.1
100 *
101 * Revision 1.2  87/03/27  14:22:19  jenkins
102 * Port to suns
103 *
104 * Revision 4.1  83/05/10  16:24:04  wft
105 * Marker matching now uses trymatch(). Marker pattern is now
106 * checked precisely.
107 *
108 * Revision 3.1  82/12/04  13:21:40  wft
109 * Initial revision.
110 *
111 */
112
113/*
114#define FCMPTEST
115*/
116/* Testprogram; prints out whether two files are identical,
117 * except for keywords
118 */
119
120#include  "rcsbase.h"
121
122libId(fcmpId, "$Id: rcsfcmp.c,v 5.14 1995/06/16 06:19:24 eggert Exp $")
123
124	static int discardkeyval P((int,RILE*));
125	static int
126discardkeyval(c, f)
127	register int c;
128	register RILE *f;
129{
130	for (;;)
131		switch (c) {
132			case KDELIM:
133			case '\n':
134				return c;
135			default:
136				Igeteof_(f, c, return EOF;)
137				break;
138		}
139}
140
141	int
142rcsfcmp(xfp, xstatp, uname, delta)
143	register RILE *xfp;
144	struct stat const *xstatp;
145	char const *uname;
146	struct hshentry const *delta;
147/* Compare the files xfp and uname.  Return zero
148 * if xfp has the same contents as uname and neither has keywords,
149 * otherwise -1 if they are the same ignoring keyword values,
150 * and 1 if they differ even ignoring
151 * keyword values. For the LOG-keyword, rcsfcmp skips the log message
152 * given by the parameter delta in xfp.  Thus, rcsfcmp returns nonpositive
153 * if xfp contains the same as uname, with the keywords expanded.
154 * Implementation: character-by-character comparison until $ is found.
155 * If a $ is found, read in the marker keywords; if they are real keywords
156 * and identical, read in keyword value. If value is terminated properly,
157 * disregard it and optionally skip log message; otherwise, compare value.
158 */
159{
160    register int xc, uc;
161    char xkeyword[keylength+2];
162    int eqkeyvals;
163    register RILE *ufp;
164    register int xeof, ueof;
165    register char * tp;
166    register char const *sp;
167    register size_t leaderlen;
168    int result;
169    enum markers match1;
170    struct stat ustat;
171
172    if (!(ufp = Iopen(uname, FOPEN_R_WORK, &ustat))) {
173       efaterror(uname);
174    }
175    xeof = ueof = false;
176    if (MIN_UNEXPAND <= Expand) {
177	if (!(result = xstatp->st_size!=ustat.st_size)) {
178#	    if large_memory && maps_memory
179		result = !!memcmp(xfp->base,ufp->base,(size_t)xstatp->st_size);
180#	    else
181		for (;;) {
182		    /* get the next characters */
183		    Igeteof_(xfp, xc, xeof=true;)
184		    Igeteof_(ufp, uc, ueof=true;)
185		    if (xeof | ueof)
186			goto eof;
187		    if (xc != uc)
188			goto return1;
189		}
190#	    endif
191	}
192    } else {
193	xc = 0;
194	uc = 0; /* Keep lint happy.  */
195	leaderlen = 0;
196	result = 0;
197
198	for (;;) {
199	  if (xc != KDELIM) {
200	    /* get the next characters */
201	    Igeteof_(xfp, xc, xeof=true;)
202	    Igeteof_(ufp, uc, ueof=true;)
203	    if (xeof | ueof)
204		goto eof;
205	  } else {
206	    /* try to get both keywords */
207	    tp = xkeyword;
208	    for (;;) {
209		Igeteof_(xfp, xc, xeof=true;)
210		Igeteof_(ufp, uc, ueof=true;)
211		if (xeof | ueof)
212		    goto eof;
213		if (xc != uc)
214		    break;
215		switch (xc) {
216		    default:
217			if (xkeyword+keylength <= tp)
218			    break;
219			*tp++ = xc;
220			continue;
221		    case '\n': case KDELIM: case VDELIM:
222			break;
223		}
224		break;
225	    }
226	    if (
227		(xc==KDELIM || xc==VDELIM)  &&  (uc==KDELIM || uc==VDELIM)  &&
228		(*tp = xc,  (match1 = trymatch(xkeyword)) != Nomatch)
229	    ) {
230#ifdef FCMPTEST
231	      VOID printf("found common keyword %s\n",xkeyword);
232#endif
233	      result = -1;
234	      for (;;) {
235		  if (xc != uc) {
236		      xc = discardkeyval(xc, xfp);
237		      uc = discardkeyval(uc, ufp);
238		      if ((xeof = xc==EOF)  |  (ueof = uc==EOF))
239			  goto eof;
240		      eqkeyvals = false;
241		      break;
242		  }
243		  switch (xc) {
244		      default:
245			  Igeteof_(xfp, xc, xeof=true;)
246			  Igeteof_(ufp, uc, ueof=true;)
247			  if (xeof | ueof)
248			      goto eof;
249			  continue;
250
251		      case '\n': case KDELIM:
252			  eqkeyvals = true;
253			  break;
254		  }
255		  break;
256	      }
257	      if (xc != uc)
258		  goto return1;
259	      if (xc==KDELIM) {
260		  /* Skip closing KDELIM.  */
261		  Igeteof_(xfp, xc, xeof=true;)
262		  Igeteof_(ufp, uc, ueof=true;)
263		  if (xeof | ueof)
264		      goto eof;
265		  /* if the keyword is LOG, also skip the log message in xfp*/
266		  if (match1==Log) {
267		      /* first, compute the number of line feeds in log msg */
268		      int lncnt;
269		      size_t ls, ccnt;
270		      sp = delta->log.string;
271		      ls = delta->log.size;
272		      if (ls<sizeof(ciklog)-1 || memcmp(sp,ciklog,sizeof(ciklog)-1)) {
273			/*
274			* This log message was inserted.  Skip its header.
275			* The number of newlines to skip is
276			* 1 + (C+1)*(1+L+1), where C is the number of newlines
277			* in the comment leader, and L is the number of
278			* newlines in the log string.
279			*/
280			int c1 = 1;
281			for (ccnt=Comment.size; ccnt--; )
282			    c1 += Comment.string[ccnt] == '\n';
283			lncnt = 2*c1 + 1;
284			while (ls--) if (*sp++=='\n') lncnt += c1;
285			for (;;) {
286			    if (xc=='\n')
287				if(--lncnt==0) break;
288			    Igeteof_(xfp, xc, goto returnresult;)
289			}
290			/* skip last comment leader */
291			/* Can't just skip another line here, because there may be */
292			/* additional characters on the line (after the Log....$)  */
293			ccnt = RCSversion<VERSION(5) ? Comment.size : leaderlen;
294			do {
295			    Igeteof_(xfp, xc, goto returnresult;)
296			    /*
297			     * Read to the end of the comment leader or '\n',
298			     * whatever comes first, because the leader's
299			     * trailing white space was probably stripped.
300			     */
301			} while (ccnt-- && (xc!='\n' || --c1));
302		      }
303		  }
304	      } else {
305		  /* both end in the same character, but not a KDELIM */
306		  /* must compare string values.*/
307#ifdef FCMPTEST
308		  VOID printf("non-terminated keywords %s, potentially different values\n",xkeyword);
309#endif
310		  if (!eqkeyvals)
311		      goto return1;
312	      }
313	    }
314	  }
315	  if (xc != uc)
316	      goto return1;
317	  if (xc == '\n')
318	      leaderlen = 0;
319	  else
320	      leaderlen++;
321	}
322    }
323
324  eof:
325    if (xeof==ueof)
326	goto returnresult;
327  return1:
328    result = 1;
329  returnresult:
330    Ifclose(ufp);
331    return result;
332}
333
334
335
336#ifdef FCMPTEST
337
338char const cmdid[] = "rcsfcmp";
339
340main(argc, argv)
341int  argc; char  *argv[];
342/* first argument: comment leader; 2nd: log message, 3rd: expanded file,
343 * 4th: unexpanded file
344 */
345{       struct hshentry delta;
346
347	Comment.string = argv[1];
348	Comment.size = strlen(argv[1]);
349	delta.log.string = argv[2];
350	delta.log.size = strlen(argv[2]);
351	if (rcsfcmp(Iopen(argv[3], FOPEN_R_WORK, (struct stat*)0), argv[4], &delta))
352                VOID printf("files are the same\n");
353        else    VOID printf("files are different\n");
354}
355#endif
356