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