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