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