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