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