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