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 * Revision 5.14  1995/06/16 06:19:24  eggert
42 * Update FSF address.
43 *
44 * Revision 5.13  1995/06/01 16:23:43  eggert
45 * (rcsfcmp): Add -kb support.
46 *
47 * Revision 5.12  1994/03/17 14:05:48  eggert
48 * Normally calculate the $Log prefix from context, not from RCS file.
49 * Calculate line numbers correctly even if the $Log prefix contains newlines.
50 * Remove lint.
51 *
52 * Revision 5.11  1993/11/03 17:42:27  eggert
53 * Fix yet another off-by-one error when comparing Log string expansions.
54 *
55 * Revision 5.10  1992/07/28 16:12:44  eggert
56 * Statement macro names now end in _.
57 *
58 * Revision 5.9  1991/10/07  17:32:46  eggert
59 * Count log lines correctly.
60 *
61 * Revision 5.8  1991/08/19  03:13:55  eggert
62 * Tune.
63 *
64 * Revision 5.7  1991/04/21  11:58:22  eggert
65 * Fix errno bug.  Add MS-DOS support.
66 *
67 * Revision 5.6  1991/02/28  19:18:47  eggert
68 * Open work file at most once.
69 *
70 * Revision 5.5  1990/11/27  09:26:05  eggert
71 * Fix comment leader bug.
72 *
73 * Revision 5.4  1990/11/01  05:03:42  eggert
74 * Permit arbitrary data in logs and comment leaders.
75 *
76 * Revision 5.3  1990/09/11  02:41:15  eggert
77 * Don't ignore differences inside keyword strings if -ko is set.
78 *
79 * Revision 5.1  1990/08/29  07:13:58  eggert
80 * Clean old log messages too.
81 *
82 * Revision 5.0  1990/08/22  08:12:49  eggert
83 * Don't append "checked in with -k by " log to logs,
84 * so that checking in a program with -k doesn't change it.
85 * Ansify and Posixate.  Remove lint.
86 *
87 * Revision 4.5  89/05/01  15:12:42  narten
88 * changed copyright header to reflect current distribution rules
89 *
90 * Revision 4.4  88/08/09  19:12:50  eggert
91 * Shrink stdio code size.
92 *
93 * Revision 4.3  87/12/18  11:40:02  narten
94 * lint cleanups (Guy Harris)
95 *
96 * Revision 4.2  87/10/18  10:33:06  narten
97 * updting version number. Changes relative to 1.1 actually relative to
98 * 4.1
99 *
100 * Revision 1.2  87/03/27  14:22:19  jenkins
101 * Port to suns
102 *
103 * Revision 4.1  83/05/10  16:24:04  wft
104 * Marker matching now uses trymatch(). Marker pattern is now
105 * checked precisely.
106 *
107 * Revision 3.1  82/12/04  13:21:40  wft
108 * Initial revision.
109 *
110 */
111
112/*
113#define FCMPTEST
114*/
115/* Testprogram; prints out whether two files are identical,
116 * except for keywords
117 */
118
119#include  "rcsbase.h"
120
121libId(fcmpId, "$FreeBSD$")
122
123	static int discardkeyval P((int,RILE*));
124	static int
125discardkeyval(c, f)
126	register int c;
127	register RILE *f;
128{
129	for (;;)
130		switch (c) {
131			case KDELIM:
132			case '\n':
133				return c;
134			default:
135				Igeteof_(f, c, return EOF;)
136				break;
137		}
138}
139
140	int
141rcsfcmp(xfp, xstatp, uname, delta)
142	register RILE *xfp;
143	struct stat const *xstatp;
144	char const *uname;
145	struct hshentry const *delta;
146/* Compare the files xfp and uname.  Return zero
147 * if xfp has the same contents as uname and neither has keywords,
148 * otherwise -1 if they are the same ignoring keyword values,
149 * and 1 if they differ even ignoring
150 * keyword values. For the LOG-keyword, rcsfcmp skips the log message
151 * given by the parameter delta in xfp.  Thus, rcsfcmp returns nonpositive
152 * if xfp contains the same as uname, with the keywords expanded.
153 * Implementation: character-by-character comparison until $ is found.
154 * If a $ is found, read in the marker keywords; if they are real keywords
155 * and identical, read in keyword value. If value is terminated properly,
156 * disregard it and optionally skip log message; otherwise, compare value.
157 */
158{
159    register int xc, uc;
160    char xkeyword[keylength+2];
161    int eqkeyvals;
162    register RILE *ufp;
163    register int xeof, ueof;
164    register char * tp;
165    register char const *sp;
166    register size_t leaderlen;
167    int result;
168    enum markers match1;
169    struct stat ustat;
170
171    if (!(ufp = Iopen(uname, FOPEN_R_WORK, &ustat))) {
172       efaterror(uname);
173    }
174    xeof = ueof = false;
175    if (MIN_UNEXPAND <= Expand) {
176	if (!(result = xstatp->st_size!=ustat.st_size)) {
177#	    if large_memory && maps_memory
178		result = !!memcmp(xfp->base,ufp->base,(size_t)xstatp->st_size);
179#	    else
180		for (;;) {
181		    /* get the next characters */
182		    Igeteof_(xfp, xc, xeof=true;)
183		    Igeteof_(ufp, uc, ueof=true;)
184		    if (xeof | ueof)
185			goto eof;
186		    if (xc != uc)
187			goto return1;
188		}
189#	    endif
190	}
191    } else {
192	xc = 0;
193	uc = 0; /* Keep lint happy.  */
194	leaderlen = 0;
195	result = 0;
196
197	for (;;) {
198	  if (xc != KDELIM) {
199	    /* get the next characters */
200	    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