xref: /dragonfly/gnu/usr.bin/rcs/lib/rcsfcmp.c (revision 6b5c5d0d)
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  * $FreeBSD: src/gnu/usr.bin/rcs/lib/rcsfcmp.c,v 1.7 1999/08/27 23:36:45 peter Exp $
42  * $DragonFly: src/gnu/usr.bin/rcs/lib/rcsfcmp.c,v 1.2 2003/06/17 04:25:47 dillon Exp $
43  *
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 
124 libId(fcmpId, "$DragonFly: src/gnu/usr.bin/rcs/lib/rcsfcmp.c,v 1.2 2003/06/17 04:25:47 dillon Exp $")
125 
126 	static int discardkeyval P((int,RILE*));
127 	static int
128 discardkeyval(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
144 rcsfcmp(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 
340 char const cmdid[] = "rcsfcmp";
341 
342 main(argc, argv)
343 int  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