rcsfcmp.c revision 11894
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 13This file is part of RCS. 14 15RCS is free software; you can redistribute it and/or modify 16it under the terms of the GNU General Public License as published by 17the Free Software Foundation; either version 2, or (at your option) 18any later version. 19 20RCS is distributed in the hope that it will be useful, 21but WITHOUT ANY WARRANTY; without even the implied warranty of 22MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23GNU General Public License for more details. 24 25You should have received a copy of the GNU General Public License 26along with RCS; see the file COPYING. 27If not, write to the Free Software Foundation, 2859 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 29 30Report 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 122libId(fcmpId, "$Id: rcsfcmp.c,v 5.14 1995/06/16 06:19:24 eggert Exp $") 123 124 static int discardkeyval P((int,RILE*)); 125 static int 126discardkeyval(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 142rcsfcmp(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# if large_memory && maps_memory 179 result = !!memcmp(xfp->base,ufp->base,(size_t)xstatp->st_size); 180# else 181 for (;;) { 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 if (xc != uc) 188 goto return1; 189 } 190# endif 191 } 192 } else { 193 xc = 0; 194 uc = 0; /* Keep lint happy. */ 195 leaderlen = 0; 196 result = 0; 197 198 for (;;) { 199 if (xc != KDELIM) { 200 /* get the next characters */ 201 Igeteof_(xfp, xc, xeof=true;) 202 Igeteof_(ufp, uc, ueof=true;) 203 if (xeof | ueof) 204 goto eof; 205 } else { 206 /* try to get both keywords */ 207 tp = xkeyword; 208 for (;;) { 209 Igeteof_(xfp, xc, xeof=true;) 210 Igeteof_(ufp, uc, ueof=true;) 211 if (xeof | ueof) 212 goto eof; 213 if (xc != uc) 214 break; 215 switch (xc) { 216 default: 217 if (xkeyword+keylength <= tp) 218 break; 219 *tp++ = xc; 220 continue; 221 case '\n': case KDELIM: case VDELIM: 222 break; 223 } 224 break; 225 } 226 if ( 227 (xc==KDELIM || xc==VDELIM) && (uc==KDELIM || uc==VDELIM) && 228 (*tp = xc, (match1 = trymatch(xkeyword)) != Nomatch) 229 ) { 230#ifdef FCMPTEST 231 VOID printf("found common keyword %s\n",xkeyword); 232#endif 233 result = -1; 234 for (;;) { 235 if (xc != uc) { 236 xc = discardkeyval(xc, xfp); 237 uc = discardkeyval(uc, ufp); 238 if ((xeof = xc==EOF) | (ueof = uc==EOF)) 239 goto eof; 240 eqkeyvals = false; 241 break; 242 } 243 switch (xc) { 244 default: 245 Igeteof_(xfp, xc, xeof=true;) 246 Igeteof_(ufp, uc, ueof=true;) 247 if (xeof | ueof) 248 goto eof; 249 continue; 250 251 case '\n': case KDELIM: 252 eqkeyvals = true; 253 break; 254 } 255 break; 256 } 257 if (xc != uc) 258 goto return1; 259 if (xc==KDELIM) { 260 /* Skip closing KDELIM. */ 261 Igeteof_(xfp, xc, xeof=true;) 262 Igeteof_(ufp, uc, ueof=true;) 263 if (xeof | ueof) 264 goto eof; 265 /* if the keyword is LOG, also skip the log message in xfp*/ 266 if (match1==Log) { 267 /* first, compute the number of line feeds in log msg */ 268 int lncnt; 269 size_t ls, ccnt; 270 sp = delta->log.string; 271 ls = delta->log.size; 272 if (ls<sizeof(ciklog)-1 || memcmp(sp,ciklog,sizeof(ciklog)-1)) { 273 /* 274 * This log message was inserted. Skip its header. 275 * The number of newlines to skip is 276 * 1 + (C+1)*(1+L+1), where C is the number of newlines 277 * in the comment leader, and L is the number of 278 * newlines in the log string. 279 */ 280 int c1 = 1; 281 for (ccnt=Comment.size; ccnt--; ) 282 c1 += Comment.string[ccnt] == '\n'; 283 lncnt = 2*c1 + 1; 284 while (ls--) if (*sp++=='\n') lncnt += c1; 285 for (;;) { 286 if (xc=='\n') 287 if(--lncnt==0) break; 288 Igeteof_(xfp, xc, goto returnresult;) 289 } 290 /* skip last comment leader */ 291 /* Can't just skip another line here, because there may be */ 292 /* additional characters on the line (after the Log....$) */ 293 ccnt = RCSversion<VERSION(5) ? Comment.size : leaderlen; 294 do { 295 Igeteof_(xfp, xc, goto returnresult;) 296 /* 297 * Read to the end of the comment leader or '\n', 298 * whatever comes first, because the leader's 299 * trailing white space was probably stripped. 300 */ 301 } while (ccnt-- && (xc!='\n' || --c1)); 302 } 303 } 304 } else { 305 /* both end in the same character, but not a KDELIM */ 306 /* must compare string values.*/ 307#ifdef FCMPTEST 308 VOID printf("non-terminated keywords %s, potentially different values\n",xkeyword); 309#endif 310 if (!eqkeyvals) 311 goto return1; 312 } 313 } 314 } 315 if (xc != uc) 316 goto return1; 317 if (xc == '\n') 318 leaderlen = 0; 319 else 320 leaderlen++; 321 } 322 } 323 324 eof: 325 if (xeof==ueof) 326 goto returnresult; 327 return1: 328 result = 1; 329 returnresult: 330 Ifclose(ufp); 331 return result; 332} 333 334 335 336#ifdef FCMPTEST 337 338char const cmdid[] = "rcsfcmp"; 339 340main(argc, argv) 341int argc; char *argv[]; 342/* first argument: comment leader; 2nd: log message, 3rd: expanded file, 343 * 4th: unexpanded file 344 */ 345{ struct hshentry delta; 346 347 Comment.string = argv[1]; 348 Comment.size = strlen(argv[1]); 349 delta.log.string = argv[2]; 350 delta.log.size = strlen(argv[2]); 351 if (rcsfcmp(Iopen(argv[3], FOPEN_R_WORK, (struct stat*)0), argv[4], &delta)) 352 VOID printf("files are the same\n"); 353 else VOID printf("files are different\n"); 354} 355#endif 356