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