rcsfcmp.c revision 8858
1192830Sed/* 2192830Sed * RCS file comparison 3192830Sed */ 4192830Sed/***************************************************************************** 5192830Sed * rcsfcmp() 6192830Sed * Testprogram: define FCMPTEST 7192830Sed ***************************************************************************** 8192830Sed */ 9192830Sed 10192830Sed/* Copyright (C) 1982, 1988, 1989 Walter Tichy 11192830Sed Copyright 1990, 1991 by Paul Eggert 12192830Sed Distributed under license by the Free Software Foundation, Inc. 13192830Sed 14192830SedThis file is part of RCS. 15192830Sed 16192830SedRCS is free software; you can redistribute it and/or modify 17192830Sedit under the terms of the GNU General Public License as published by 18192830Sedthe Free Software Foundation; either version 2, or (at your option) 19192830Sedany later version. 20192830Sed 21192830SedRCS is distributed in the hope that it will be useful, 22192830Sedbut WITHOUT ANY WARRANTY; without even the implied warranty of 23192830SedMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24192830SedGNU General Public License for more details. 25192830Sed 26192830SedYou should have received a copy of the GNU General Public License 27192830Sedalong with RCS; see the file COPYING. If not, write to 28192830Sedthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 29192830Sed 30192830SedReport problems and direct all questions to: 31192830Sed 32192830Sed rcs-bugs@cs.purdue.edu 33192830Sed 34192830Sed*/ 35192830Sed 36192830Sed 37192830Sed 38192830Sed 39192830Sed 40192830Sed/* $Log: rcsfcmp.c,v $ 41192830Sed * Revision 1.1.1.1 1993/06/18 04:22:13 jkh 42192830Sed * Updated GNU utilities 43192830Sed * 44192830Sed * Revision 5.9 1991/10/07 17:32:46 eggert 45192830Sed * Count log lines correctly. 46192830Sed * 47192830Sed * Revision 5.8 1991/08/19 03:13:55 eggert 48192830Sed * Tune. 49192830Sed * 50192830Sed * Revision 5.7 1991/04/21 11:58:22 eggert 51192830Sed * Fix errno bug. Add MS-DOS support. 52192830Sed * 53192830Sed * Revision 5.6 1991/02/28 19:18:47 eggert 54192830Sed * Open work file at most once. 55192830Sed * 56192830Sed * Revision 5.5 1990/11/27 09:26:05 eggert 57192830Sed * Fix comment leader bug. 58192830Sed * 59192830Sed * Revision 5.4 1990/11/01 05:03:42 eggert 60192830Sed * Permit arbitrary data in logs and comment leaders. 61192830Sed * 62192830Sed * Revision 5.3 1990/09/11 02:41:15 eggert 63192830Sed * Don't ignore differences inside keyword strings if -ko is set. 64192830Sed * 65192830Sed * Revision 5.1 1990/08/29 07:13:58 eggert 66192830Sed * Clean old log messages too. 67192830Sed * 68192830Sed * Revision 5.0 1990/08/22 08:12:49 eggert 69192830Sed * Don't append "checked in with -k by " log to logs, 70192830Sed * so that checking in a program with -k doesn't change it. 71192830Sed * Ansify and Posixate. Remove lint. 72192830Sed * 73192830Sed * Revision 4.5 89/05/01 15:12:42 narten 74192830Sed * changed copyright header to reflect current distribution rules 75192830Sed * 76192830Sed * Revision 4.4 88/08/09 19:12:50 eggert 77192830Sed * Shrink stdio code size. 78192830Sed * 79192830Sed * Revision 4.3 87/12/18 11:40:02 narten 80192830Sed * lint cleanups (Guy Harris) 81192830Sed * 82192830Sed * Revision 4.2 87/10/18 10:33:06 narten 83192830Sed * updting version number. Changes relative to 1.1 actually relative to 84192830Sed * 4.1 85192830Sed * 86192830Sed * Revision 1.2 87/03/27 14:22:19 jenkins 87192830Sed * Port to suns 88192830Sed * 89192830Sed * Revision 4.1 83/05/10 16:24:04 wft 90192830Sed * Marker matching now uses trymatch(). Marker pattern is now 91192830Sed * checked precisely. 92192830Sed * 93192830Sed * Revision 3.1 82/12/04 13:21:40 wft 94192830Sed * Initial revision. 95192830Sed * 96192830Sed */ 97192830Sed 98192830Sed/* 99192830Sed#define FCMPTEST 100192830Sed*/ 101192830Sed/* Testprogram; prints out whether two files are identical, 102192830Sed * except for keywords 103192830Sed */ 104192830Sed 105192830Sed#include "rcsbase.h" 106192830Sed 107192830SedlibId(fcmpId, "$Id: rcsfcmp.c,v 1.1.1.1 1993/06/18 04:22:13 jkh Exp $") 108192830Sed 109192830Sed static int 110192830Seddiscardkeyval(c, f) 111192830Sed register int c; 112192830Sed register RILE *f; 113192830Sed{ 114192830Sed for (;;) 115192830Sed switch (c) { 116192830Sed case KDELIM: 117192830Sed case '\n': 118192830Sed return c; 119192830Sed default: 120192830Sed Igeteof(f, c, return EOF;); 121192830Sed break; 122192830Sed } 123192830Sed} 124192830Sed 125192830Sed int 126192830Sedrcsfcmp(xfp, xstatp, ufname, delta) 127192830Sed register RILE *xfp; 128192830Sed struct stat const *xstatp; 129192830Sed char const *ufname; 130192830Sed struct hshentry const *delta; 131192830Sed/* Compare the files xfp and ufname. Return zero 132192830Sed * if xfp has the same contents as ufname and neither has keywords, 133192830Sed * otherwise -1 if they are the same ignoring keyword values, 134192830Sed * and 1 if they differ even ignoring 135192830Sed * keyword values. For the LOG-keyword, rcsfcmp skips the log message 136192830Sed * given by the parameter delta in xfp. Thus, rcsfcmp returns nonpositive 137192830Sed * if xfp contains the same as ufname, with the keywords expanded. 138192830Sed * Implementation: character-by-character comparison until $ is found. 139192830Sed * If a $ is found, read in the marker keywords; if they are real keywords 140192830Sed * and identical, read in keyword value. If value is terminated properly, 141192830Sed * disregard it and optionally skip log message; otherwise, compare value. 142192830Sed */ 143192830Sed{ 144192830Sed register int xc, uc; 145192830Sed char xkeyword[keylength+2]; 146192830Sed int eqkeyvals; 147192830Sed register RILE *ufp; 148192830Sed register int xeof, ueof; 149192830Sed register char * tp; 150192830Sed register char const *sp; 151192830Sed int result; 152192830Sed enum markers match1; 153192830Sed struct stat ustat; 154192830Sed 155192830Sed if (!(ufp = Iopen(ufname, FOPEN_R_WORK, &ustat))) { 156192830Sed efaterror(ufname); 157192830Sed } 158192830Sed xeof = ueof = false; 159192830Sed if (Expand==OLD_EXPAND) { 160192830Sed if (!(result = xstatp->st_size!=ustat.st_size)) { 161192830Sed# if has_mmap && large_memory 162192830Sed result = !!memcmp(xfp->base,ufp->base,(size_t)xstatp->st_size); 163192830Sed# else 164192830Sed for (;;) { 165192830Sed /* get the next characters */ 166192830Sed Igeteof(xfp, xc, xeof=true;); 167192830Sed Igeteof(ufp, uc, ueof=true;); 168192830Sed if (xeof | ueof) 169192830Sed goto eof; 170192830Sed if (xc != uc) 171192830Sed goto return1; 172192830Sed } 173192830Sed# endif 174192830Sed } 175192830Sed } else { 176192830Sed xc = 0; 177192830Sed uc = 0; /* Keep lint happy. */ 178192830Sed result = 0; 179192830Sed 180192830Sed for (;;) { 181192830Sed if (xc != KDELIM) { 182192830Sed /* get the next characters */ 183192830Sed Igeteof(xfp, xc, xeof=true;); 184192830Sed Igeteof(ufp, uc, ueof=true;); 185192830Sed if (xeof | ueof) 186192830Sed goto eof; 187192830Sed } else { 188192830Sed /* try to get both keywords */ 189192830Sed tp = xkeyword; 190192830Sed for (;;) { 191192830Sed Igeteof(xfp, xc, xeof=true;); 192192830Sed Igeteof(ufp, uc, ueof=true;); 193192830Sed if (xeof | ueof) 194192830Sed goto eof; 195192830Sed if (xc != uc) 196192830Sed break; 197192830Sed switch (xc) { 198192830Sed default: 199192830Sed if (xkeyword+keylength <= tp) 200192830Sed break; 201192830Sed *tp++ = xc; 202192830Sed continue; 203192830Sed case '\n': case KDELIM: case VDELIM: 204192830Sed break; 205192830Sed } 206192830Sed break; 207192830Sed } 208192830Sed if ( 209192830Sed (xc==KDELIM || xc==VDELIM) && (uc==KDELIM || uc==VDELIM) && 210192830Sed (*tp = xc, (match1 = trymatch(xkeyword)) != Nomatch) 211192830Sed ) { 212192830Sed#ifdef FCMPTEST 213192830Sed VOID printf("found common keyword %s\n",xkeyword); 214192830Sed#endif 215192830Sed result = -1; 216192830Sed for (;;) { 217192830Sed if (xc != uc) { 218192830Sed xc = discardkeyval(xc, xfp); 219192830Sed uc = discardkeyval(uc, ufp); 220192830Sed if ((xeof = xc==EOF) | (ueof = uc==EOF)) 221192830Sed goto eof; 222192830Sed eqkeyvals = false; 223192830Sed break; 224192830Sed } 225192830Sed switch (xc) { 226192830Sed default: 227192830Sed Igeteof(xfp, xc, xeof=true;); 228192830Sed Igeteof(ufp, uc, ueof=true;); 229192830Sed if (xeof | ueof) 230192830Sed goto eof; 231192830Sed continue; 232192830Sed 233192830Sed case '\n': case KDELIM: 234192830Sed eqkeyvals = true; 235192830Sed break; 236192830Sed } 237192830Sed break; 238192830Sed } 239192830Sed if (xc != uc) 240192830Sed goto return1; 241192830Sed if (xc==KDELIM) { 242192830Sed /* Skip closing KDELIM. */ 243192830Sed Igeteof(xfp, xc, xeof=true;); 244192830Sed Igeteof(ufp, uc, ueof=true;); 245192830Sed if (xeof | ueof) 246192830Sed goto eof; 247192830Sed /* if the keyword is LOG, also skip the log message in xfp*/ 248192830Sed if (match1==Log) { 249192830Sed /* first, compute the number of line feeds in log msg */ 250192830Sed unsigned lncnt; 251192830Sed size_t ls, ccnt; 252192830Sed sp = delta->log.string; 253192830Sed ls = delta->log.size; 254192830Sed if (ls<sizeof(ciklog)-1 || memcmp(sp,ciklog,sizeof(ciklog)-1)) { 255192830Sed /* This log message was inserted. */ 256192830Sed lncnt = 3; 257192830Sed while (ls--) if (*sp++=='\n') lncnt++; 258192830Sed for (;;) { 259192830Sed if (xc=='\n') 260192830Sed if(--lncnt==0) break; 261192830Sed Igeteof(xfp, xc, goto returnresult;); 262192830Sed } 263192830Sed /* skip last comment leader */ 264192830Sed /* Can't just skip another line here, because there may be */ 265192830Sed /* additional characters on the line (after the Log....$) */ 266192830Sed for (ccnt=Comment.size; ccnt--; ) { 267192830Sed Igeteof(xfp, xc, goto returnresult;); 268192830Sed if(xc=='\n') break; 269192830Sed /* 270192830Sed * Read to the end of the comment leader or '\n', 271192830Sed * whatever comes first. Some editors strip 272192830Sed * trailing white space from a leader like " * ". 273192830Sed */ 274192830Sed } 275192830Sed } 276192830Sed } 277192830Sed } else { 278192830Sed /* both end in the same character, but not a KDELIM */ 279192830Sed /* must compare string values.*/ 280192830Sed#ifdef FCMPTEST 281192830Sed VOID printf("non-terminated keywords %s, potentially different values\n",xkeyword); 282192830Sed#endif 283192830Sed if (!eqkeyvals) 284192830Sed goto return1; 285192830Sed } 286192830Sed } 287192830Sed } 288192830Sed if (xc != uc) 289192830Sed goto return1; 290192830Sed } 291192830Sed } 292192830Sed 293192830Sed eof: 294192830Sed if (xeof==ueof) 295192830Sed goto returnresult; 296192830Sed return1: 297192830Sed result = 1; 298192830Sed returnresult: 299192830Sed Ifclose(ufp); 300192830Sed return result; 301192830Sed} 302192830Sed 303192830Sed 304192830Sed 305192830Sed#ifdef FCMPTEST 306192830Sed 307192830Sedchar const cmdid[] = "rcsfcmp"; 308192830Sed 309192830Sedmain(argc, argv) 310192830Sedint argc; char *argv[]; 311192830Sed/* first argument: comment leader; 2nd: log message, 3rd: expanded file, 312192830Sed * 4th: unexpanded file 313192830Sed */ 314192830Sed{ struct hshentry delta; 315192830Sed 316192830Sed Comment.string = argv[1]; 317192830Sed Comment.size = strlen(argv[1]); 318192830Sed delta.log.string = argv[2]; 319192830Sed delta.log.size = strlen(argv[2]); 320192830Sed if (rcsfcmp(Iopen(argv[3], FOPEN_R_WORK, (struct stat*)0), argv[4], &delta)) 321192830Sed VOID printf("files are the same\n"); 322192830Sed else VOID printf("files are different\n"); 323192830Sed} 324192830Sed#endif 325192830Sed