rcsfcmp.c revision 8858
1192830Sed/*
2192830Sed *                     RCS file comparison
3192830Sed */
4192830Sed/*****************************************************************************
5192830Sed *                       rcsfcmp()
6192830Sed *                       Testprogram: define FCMPTEST
7192830Sed *****************************************************************************
8192830Sed */
9192830Sed
10192830Sed/* Copyright (C) 1982, 1988, 1989 Walter Tichy
11192830Sed   Copyright 1990, 1991 by Paul Eggert
12192830Sed   Distributed under license by the Free Software Foundation, Inc.
13192830Sed
14192830SedThis file is part of RCS.
15192830Sed
16192830SedRCS is free software; you can redistribute it and/or modify
17192830Sedit under the terms of the GNU General Public License as published by
18192830Sedthe Free Software Foundation; either version 2, or (at your option)
19192830Sedany later version.
20192830Sed
21192830SedRCS is distributed in the hope that it will be useful,
22192830Sedbut WITHOUT ANY WARRANTY; without even the implied warranty of
23192830SedMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24192830SedGNU General Public License for more details.
25192830Sed
26192830SedYou should have received a copy of the GNU General Public License
27192830Sedalong with RCS; see the file COPYING.  If not, write to
28192830Sedthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
29192830Sed
30192830SedReport problems and direct all questions to:
31192830Sed
32192830Sed    rcs-bugs@cs.purdue.edu
33192830Sed
34192830Sed*/
35192830Sed
36192830Sed
37192830Sed
38192830Sed
39192830Sed
40192830Sed/* $Log: rcsfcmp.c,v $
41192830Sed * Revision 1.1.1.1  1993/06/18  04:22:13  jkh
42192830Sed * Updated GNU utilities
43192830Sed *
44192830Sed * Revision 5.9  1991/10/07  17:32:46  eggert
45192830Sed * Count log lines correctly.
46192830Sed *
47192830Sed * Revision 5.8  1991/08/19  03:13:55  eggert
48192830Sed * Tune.
49192830Sed *
50192830Sed * Revision 5.7  1991/04/21  11:58:22  eggert
51192830Sed * Fix errno bug.  Add MS-DOS support.
52192830Sed *
53192830Sed * Revision 5.6  1991/02/28  19:18:47  eggert
54192830Sed * Open work file at most once.
55192830Sed *
56192830Sed * Revision 5.5  1990/11/27  09:26:05  eggert
57192830Sed * Fix comment leader bug.
58192830Sed *
59192830Sed * Revision 5.4  1990/11/01  05:03:42  eggert
60192830Sed * Permit arbitrary data in logs and comment leaders.
61192830Sed *
62192830Sed * Revision 5.3  1990/09/11  02:41:15  eggert
63192830Sed * Don't ignore differences inside keyword strings if -ko is set.
64192830Sed *
65192830Sed * Revision 5.1  1990/08/29  07:13:58  eggert
66192830Sed * Clean old log messages too.
67192830Sed *
68192830Sed * Revision 5.0  1990/08/22  08:12:49  eggert
69192830Sed * Don't append "checked in with -k by " log to logs,
70192830Sed * so that checking in a program with -k doesn't change it.
71192830Sed * Ansify and Posixate.  Remove lint.
72192830Sed *
73192830Sed * Revision 4.5  89/05/01  15:12:42  narten
74192830Sed * changed copyright header to reflect current distribution rules
75192830Sed *
76192830Sed * Revision 4.4  88/08/09  19:12:50  eggert
77192830Sed * Shrink stdio code size.
78192830Sed *
79192830Sed * Revision 4.3  87/12/18  11:40:02  narten
80192830Sed * lint cleanups (Guy Harris)
81192830Sed *
82192830Sed * Revision 4.2  87/10/18  10:33:06  narten
83192830Sed * updting version number. Changes relative to 1.1 actually relative to
84192830Sed * 4.1
85192830Sed *
86192830Sed * Revision 1.2  87/03/27  14:22:19  jenkins
87192830Sed * Port to suns
88192830Sed *
89192830Sed * Revision 4.1  83/05/10  16:24:04  wft
90192830Sed * Marker matching now uses trymatch(). Marker pattern is now
91192830Sed * checked precisely.
92192830Sed *
93192830Sed * Revision 3.1  82/12/04  13:21:40  wft
94192830Sed * Initial revision.
95192830Sed *
96192830Sed */
97192830Sed
98192830Sed/*
99192830Sed#define FCMPTEST
100192830Sed*/
101192830Sed/* Testprogram; prints out whether two files are identical,
102192830Sed * except for keywords
103192830Sed */
104192830Sed
105192830Sed#include  "rcsbase.h"
106192830Sed
107192830SedlibId(fcmpId, "$Id: rcsfcmp.c,v 1.1.1.1 1993/06/18 04:22:13 jkh Exp $")
108192830Sed
109192830Sed	static int
110192830Seddiscardkeyval(c, f)
111192830Sed	register int c;
112192830Sed	register RILE *f;
113192830Sed{
114192830Sed	for (;;)
115192830Sed		switch (c) {
116192830Sed			case KDELIM:
117192830Sed			case '\n':
118192830Sed				return c;
119192830Sed			default:
120192830Sed				Igeteof(f, c, return EOF;);
121192830Sed				break;
122192830Sed		}
123192830Sed}
124192830Sed
125192830Sed	int
126192830Sedrcsfcmp(xfp, xstatp, ufname, delta)
127192830Sed	register RILE *xfp;
128192830Sed	struct stat const *xstatp;
129192830Sed	char const *ufname;
130192830Sed	struct hshentry const *delta;
131192830Sed/* Compare the files xfp and ufname.  Return zero
132192830Sed * if xfp has the same contents as ufname and neither has keywords,
133192830Sed * otherwise -1 if they are the same ignoring keyword values,
134192830Sed * and 1 if they differ even ignoring
135192830Sed * keyword values. For the LOG-keyword, rcsfcmp skips the log message
136192830Sed * given by the parameter delta in xfp.  Thus, rcsfcmp returns nonpositive
137192830Sed * if xfp contains the same as ufname, with the keywords expanded.
138192830Sed * Implementation: character-by-character comparison until $ is found.
139192830Sed * If a $ is found, read in the marker keywords; if they are real keywords
140192830Sed * and identical, read in keyword value. If value is terminated properly,
141192830Sed * disregard it and optionally skip log message; otherwise, compare value.
142192830Sed */
143192830Sed{
144192830Sed    register int xc, uc;
145192830Sed    char xkeyword[keylength+2];
146192830Sed    int eqkeyvals;
147192830Sed    register RILE *ufp;
148192830Sed    register int xeof, ueof;
149192830Sed    register char * tp;
150192830Sed    register char const *sp;
151192830Sed    int result;
152192830Sed    enum markers match1;
153192830Sed    struct stat ustat;
154192830Sed
155192830Sed    if (!(ufp = Iopen(ufname, FOPEN_R_WORK, &ustat))) {
156192830Sed       efaterror(ufname);
157192830Sed    }
158192830Sed    xeof = ueof = false;
159192830Sed    if (Expand==OLD_EXPAND) {
160192830Sed	if (!(result = xstatp->st_size!=ustat.st_size)) {
161192830Sed#	    if has_mmap && large_memory
162192830Sed		result = !!memcmp(xfp->base,ufp->base,(size_t)xstatp->st_size);
163192830Sed#	    else
164192830Sed		for (;;) {
165192830Sed		    /* get the next characters */
166192830Sed		    Igeteof(xfp, xc, xeof=true;);
167192830Sed		    Igeteof(ufp, uc, ueof=true;);
168192830Sed		    if (xeof | ueof)
169192830Sed			goto eof;
170192830Sed		    if (xc != uc)
171192830Sed			goto return1;
172192830Sed		}
173192830Sed#	    endif
174192830Sed	}
175192830Sed    } else {
176192830Sed	xc = 0;
177192830Sed	uc = 0; /* Keep lint happy.  */
178192830Sed	result = 0;
179192830Sed
180192830Sed	for (;;) {
181192830Sed	  if (xc != KDELIM) {
182192830Sed	    /* get the next characters */
183192830Sed	    Igeteof(xfp, xc, xeof=true;);
184192830Sed	    Igeteof(ufp, uc, ueof=true;);
185192830Sed	    if (xeof | ueof)
186192830Sed		goto eof;
187192830Sed	  } else {
188192830Sed	    /* try to get both keywords */
189192830Sed	    tp = xkeyword;
190192830Sed	    for (;;) {
191192830Sed		Igeteof(xfp, xc, xeof=true;);
192192830Sed		Igeteof(ufp, uc, ueof=true;);
193192830Sed		if (xeof | ueof)
194192830Sed		    goto eof;
195192830Sed		if (xc != uc)
196192830Sed		    break;
197192830Sed		switch (xc) {
198192830Sed		    default:
199192830Sed			if (xkeyword+keylength <= tp)
200192830Sed			    break;
201192830Sed			*tp++ = xc;
202192830Sed			continue;
203192830Sed		    case '\n': case KDELIM: case VDELIM:
204192830Sed			break;
205192830Sed		}
206192830Sed		break;
207192830Sed	    }
208192830Sed	    if (
209192830Sed		(xc==KDELIM || xc==VDELIM)  &&  (uc==KDELIM || uc==VDELIM)  &&
210192830Sed		(*tp = xc,  (match1 = trymatch(xkeyword)) != Nomatch)
211192830Sed	    ) {
212192830Sed#ifdef FCMPTEST
213192830Sed	      VOID printf("found common keyword %s\n",xkeyword);
214192830Sed#endif
215192830Sed	      result = -1;
216192830Sed	      for (;;) {
217192830Sed		  if (xc != uc) {
218192830Sed		      xc = discardkeyval(xc, xfp);
219192830Sed		      uc = discardkeyval(uc, ufp);
220192830Sed		      if ((xeof = xc==EOF)  |  (ueof = uc==EOF))
221192830Sed			  goto eof;
222192830Sed		      eqkeyvals = false;
223192830Sed		      break;
224192830Sed		  }
225192830Sed		  switch (xc) {
226192830Sed		      default:
227192830Sed			  Igeteof(xfp, xc, xeof=true;);
228192830Sed			  Igeteof(ufp, uc, ueof=true;);
229192830Sed			  if (xeof | ueof)
230192830Sed			      goto eof;
231192830Sed			  continue;
232192830Sed
233192830Sed		      case '\n': case KDELIM:
234192830Sed			  eqkeyvals = true;
235192830Sed			  break;
236192830Sed		  }
237192830Sed		  break;
238192830Sed	      }
239192830Sed	      if (xc != uc)
240192830Sed		  goto return1;
241192830Sed	      if (xc==KDELIM) {
242192830Sed		  /* Skip closing KDELIM.  */
243192830Sed		  Igeteof(xfp, xc, xeof=true;);
244192830Sed		  Igeteof(ufp, uc, ueof=true;);
245192830Sed		  if (xeof | ueof)
246192830Sed		      goto eof;
247192830Sed		  /* if the keyword is LOG, also skip the log message in xfp*/
248192830Sed		  if (match1==Log) {
249192830Sed		      /* first, compute the number of line feeds in log msg */
250192830Sed		      unsigned lncnt;
251192830Sed		      size_t ls, ccnt;
252192830Sed		      sp = delta->log.string;
253192830Sed		      ls = delta->log.size;
254192830Sed		      if (ls<sizeof(ciklog)-1 || memcmp(sp,ciklog,sizeof(ciklog)-1)) {
255192830Sed			/* This log message was inserted.  */
256192830Sed			lncnt = 3;
257192830Sed			while (ls--) if (*sp++=='\n') lncnt++;
258192830Sed			for (;;) {
259192830Sed			    if (xc=='\n')
260192830Sed				if(--lncnt==0) break;
261192830Sed			    Igeteof(xfp, xc, goto returnresult;);
262192830Sed			}
263192830Sed			/* skip last comment leader */
264192830Sed			/* Can't just skip another line here, because there may be */
265192830Sed			/* additional characters on the line (after the Log....$)  */
266192830Sed			for (ccnt=Comment.size; ccnt--; ) {
267192830Sed			    Igeteof(xfp, xc, goto returnresult;);
268192830Sed			    if(xc=='\n') break;
269192830Sed			    /*
270192830Sed			     * Read to the end of the comment leader or '\n',
271192830Sed			     * whatever comes first.  Some editors strip
272192830Sed			     * trailing white space from a leader like " * ".
273192830Sed			     */
274192830Sed			}
275192830Sed		      }
276192830Sed		  }
277192830Sed	      } else {
278192830Sed		  /* both end in the same character, but not a KDELIM */
279192830Sed		  /* must compare string values.*/
280192830Sed#ifdef FCMPTEST
281192830Sed		  VOID printf("non-terminated keywords %s, potentially different values\n",xkeyword);
282192830Sed#endif
283192830Sed		  if (!eqkeyvals)
284192830Sed		      goto return1;
285192830Sed	      }
286192830Sed	    }
287192830Sed	  }
288192830Sed	  if (xc != uc)
289192830Sed	      goto return1;
290192830Sed	}
291192830Sed    }
292192830Sed
293192830Sed  eof:
294192830Sed    if (xeof==ueof)
295192830Sed	goto returnresult;
296192830Sed  return1:
297192830Sed    result = 1;
298192830Sed  returnresult:
299192830Sed    Ifclose(ufp);
300192830Sed    return result;
301192830Sed}
302192830Sed
303192830Sed
304192830Sed
305192830Sed#ifdef FCMPTEST
306192830Sed
307192830Sedchar const cmdid[] = "rcsfcmp";
308192830Sed
309192830Sedmain(argc, argv)
310192830Sedint  argc; char  *argv[];
311192830Sed/* first argument: comment leader; 2nd: log message, 3rd: expanded file,
312192830Sed * 4th: unexpanded file
313192830Sed */
314192830Sed{       struct hshentry delta;
315192830Sed
316192830Sed	Comment.string = argv[1];
317192830Sed	Comment.size = strlen(argv[1]);
318192830Sed	delta.log.string = argv[2];
319192830Sed	delta.log.size = strlen(argv[2]);
320192830Sed	if (rcsfcmp(Iopen(argv[3], FOPEN_R_WORK, (struct stat*)0), argv[4], &delta))
321192830Sed                VOID printf("files are the same\n");
322192830Sed        else    VOID printf("files are different\n");
323192830Sed}
324192830Sed#endif
325192830Sed