111894Speter/* Compare working files, ignoring RCS keyword strings. */ 211894Speter 39Sjkh/***************************************************************************** 49Sjkh * rcsfcmp() 59Sjkh * Testprogram: define FCMPTEST 69Sjkh ***************************************************************************** 79Sjkh */ 89Sjkh 911894Speter/* Copyright 1982, 1988, 1989 Walter Tichy 1011894Speter Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert 119Sjkh Distributed under license by the Free Software Foundation, Inc. 129Sjkh 139SjkhThis file is part of RCS. 149Sjkh 159SjkhRCS is free software; you can redistribute it and/or modify 169Sjkhit under the terms of the GNU General Public License as published by 179Sjkhthe Free Software Foundation; either version 2, or (at your option) 189Sjkhany later version. 199Sjkh 209SjkhRCS is distributed in the hope that it will be useful, 219Sjkhbut WITHOUT ANY WARRANTY; without even the implied warranty of 229SjkhMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 239SjkhGNU General Public License for more details. 249Sjkh 259SjkhYou should have received a copy of the GNU General Public License 2611894Speteralong with RCS; see the file COPYING. 2711894SpeterIf not, write to the Free Software Foundation, 2811894Speter59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 299Sjkh 309SjkhReport problems and direct all questions to: 319Sjkh 329Sjkh rcs-bugs@cs.purdue.edu 339Sjkh 349Sjkh*/ 359Sjkh 369Sjkh 379Sjkh 389Sjkh 399Sjkh 4011894Speter/* 4111894Speter * Revision 5.14 1995/06/16 06:19:24 eggert 4211894Speter * Update FSF address. 438858Srgrimes * 4411894Speter * Revision 5.13 1995/06/01 16:23:43 eggert 4511894Speter * (rcsfcmp): Add -kb support. 4611894Speter * 4711894Speter * Revision 5.12 1994/03/17 14:05:48 eggert 4811894Speter * Normally calculate the $Log prefix from context, not from RCS file. 4911894Speter * Calculate line numbers correctly even if the $Log prefix contains newlines. 5011894Speter * Remove lint. 5111894Speter * 5211894Speter * Revision 5.11 1993/11/03 17:42:27 eggert 5311894Speter * Fix yet another off-by-one error when comparing Log string expansions. 5411894Speter * 5511894Speter * Revision 5.10 1992/07/28 16:12:44 eggert 5611894Speter * Statement macro names now end in _. 5711894Speter * 589Sjkh * Revision 5.9 1991/10/07 17:32:46 eggert 599Sjkh * Count log lines correctly. 609Sjkh * 619Sjkh * Revision 5.8 1991/08/19 03:13:55 eggert 629Sjkh * Tune. 639Sjkh * 649Sjkh * Revision 5.7 1991/04/21 11:58:22 eggert 659Sjkh * Fix errno bug. Add MS-DOS support. 669Sjkh * 679Sjkh * Revision 5.6 1991/02/28 19:18:47 eggert 689Sjkh * Open work file at most once. 699Sjkh * 709Sjkh * Revision 5.5 1990/11/27 09:26:05 eggert 719Sjkh * Fix comment leader bug. 729Sjkh * 739Sjkh * Revision 5.4 1990/11/01 05:03:42 eggert 749Sjkh * Permit arbitrary data in logs and comment leaders. 759Sjkh * 769Sjkh * Revision 5.3 1990/09/11 02:41:15 eggert 779Sjkh * Don't ignore differences inside keyword strings if -ko is set. 789Sjkh * 799Sjkh * Revision 5.1 1990/08/29 07:13:58 eggert 809Sjkh * Clean old log messages too. 819Sjkh * 829Sjkh * Revision 5.0 1990/08/22 08:12:49 eggert 839Sjkh * Don't append "checked in with -k by " log to logs, 849Sjkh * so that checking in a program with -k doesn't change it. 859Sjkh * Ansify and Posixate. Remove lint. 869Sjkh * 879Sjkh * Revision 4.5 89/05/01 15:12:42 narten 889Sjkh * changed copyright header to reflect current distribution rules 898858Srgrimes * 909Sjkh * Revision 4.4 88/08/09 19:12:50 eggert 919Sjkh * Shrink stdio code size. 928858Srgrimes * 939Sjkh * Revision 4.3 87/12/18 11:40:02 narten 949Sjkh * lint cleanups (Guy Harris) 958858Srgrimes * 969Sjkh * Revision 4.2 87/10/18 10:33:06 narten 978858Srgrimes * updting version number. Changes relative to 1.1 actually relative to 989Sjkh * 4.1 998858Srgrimes * 1009Sjkh * Revision 1.2 87/03/27 14:22:19 jenkins 1019Sjkh * Port to suns 1028858Srgrimes * 1039Sjkh * Revision 4.1 83/05/10 16:24:04 wft 1049Sjkh * Marker matching now uses trymatch(). Marker pattern is now 1059Sjkh * checked precisely. 1068858Srgrimes * 1079Sjkh * Revision 3.1 82/12/04 13:21:40 wft 1089Sjkh * Initial revision. 1099Sjkh * 1109Sjkh */ 1119Sjkh 1129Sjkh/* 1139Sjkh#define FCMPTEST 1149Sjkh*/ 1159Sjkh/* Testprogram; prints out whether two files are identical, 1169Sjkh * except for keywords 1179Sjkh */ 1189Sjkh 1199Sjkh#include "rcsbase.h" 1209Sjkh 12150472SpeterlibId(fcmpId, "$FreeBSD: releng/10.3/gnu/usr.bin/rcs/lib/rcsfcmp.c 50472 1999-08-27 23:37:10Z peter $") 1229Sjkh 12311894Speter static int discardkeyval P((int,RILE*)); 1249Sjkh static int 1259Sjkhdiscardkeyval(c, f) 1269Sjkh register int c; 1279Sjkh register RILE *f; 1289Sjkh{ 1299Sjkh for (;;) 1309Sjkh switch (c) { 1319Sjkh case KDELIM: 1329Sjkh case '\n': 1339Sjkh return c; 1349Sjkh default: 13511894Speter Igeteof_(f, c, return EOF;) 1369Sjkh break; 1379Sjkh } 1389Sjkh} 1399Sjkh 1409Sjkh int 14111894Speterrcsfcmp(xfp, xstatp, uname, delta) 1429Sjkh register RILE *xfp; 1439Sjkh struct stat const *xstatp; 14411894Speter char const *uname; 1459Sjkh struct hshentry const *delta; 14611894Speter/* Compare the files xfp and uname. Return zero 14711894Speter * if xfp has the same contents as uname and neither has keywords, 1489Sjkh * otherwise -1 if they are the same ignoring keyword values, 1499Sjkh * and 1 if they differ even ignoring 1509Sjkh * keyword values. For the LOG-keyword, rcsfcmp skips the log message 1519Sjkh * given by the parameter delta in xfp. Thus, rcsfcmp returns nonpositive 15211894Speter * if xfp contains the same as uname, with the keywords expanded. 1539Sjkh * Implementation: character-by-character comparison until $ is found. 1549Sjkh * If a $ is found, read in the marker keywords; if they are real keywords 1559Sjkh * and identical, read in keyword value. If value is terminated properly, 1569Sjkh * disregard it and optionally skip log message; otherwise, compare value. 1579Sjkh */ 1589Sjkh{ 1599Sjkh register int xc, uc; 1609Sjkh char xkeyword[keylength+2]; 1619Sjkh int eqkeyvals; 1629Sjkh register RILE *ufp; 1639Sjkh register int xeof, ueof; 1649Sjkh register char * tp; 1659Sjkh register char const *sp; 16611894Speter register size_t leaderlen; 1679Sjkh int result; 1689Sjkh enum markers match1; 1699Sjkh struct stat ustat; 1709Sjkh 17111894Speter if (!(ufp = Iopen(uname, FOPEN_R_WORK, &ustat))) { 17211894Speter efaterror(uname); 1739Sjkh } 1749Sjkh xeof = ueof = false; 17511894Speter if (MIN_UNEXPAND <= Expand) { 1769Sjkh if (!(result = xstatp->st_size!=ustat.st_size)) { 17711894Speter# if large_memory && maps_memory 1789Sjkh result = !!memcmp(xfp->base,ufp->base,(size_t)xstatp->st_size); 1799Sjkh# else 1809Sjkh for (;;) { 1819Sjkh /* get the next characters */ 18211894Speter Igeteof_(xfp, xc, xeof=true;) 18311894Speter Igeteof_(ufp, uc, ueof=true;) 1849Sjkh if (xeof | ueof) 1859Sjkh goto eof; 1869Sjkh if (xc != uc) 1879Sjkh goto return1; 1889Sjkh } 1899Sjkh# endif 1909Sjkh } 1919Sjkh } else { 1929Sjkh xc = 0; 1939Sjkh uc = 0; /* Keep lint happy. */ 19411894Speter leaderlen = 0; 1959Sjkh result = 0; 1969Sjkh 1979Sjkh for (;;) { 1989Sjkh if (xc != KDELIM) { 1999Sjkh /* get the next characters */ 20011894Speter Igeteof_(xfp, xc, xeof=true;) 20111894Speter Igeteof_(ufp, uc, ueof=true;) 2029Sjkh if (xeof | ueof) 2039Sjkh goto eof; 2049Sjkh } else { 2059Sjkh /* try to get both keywords */ 2069Sjkh tp = xkeyword; 2079Sjkh for (;;) { 20811894Speter Igeteof_(xfp, xc, xeof=true;) 20911894Speter Igeteof_(ufp, uc, ueof=true;) 2109Sjkh if (xeof | ueof) 2119Sjkh goto eof; 2129Sjkh if (xc != uc) 2139Sjkh break; 2149Sjkh switch (xc) { 2159Sjkh default: 2169Sjkh if (xkeyword+keylength <= tp) 2179Sjkh break; 2189Sjkh *tp++ = xc; 2199Sjkh continue; 2209Sjkh case '\n': case KDELIM: case VDELIM: 2219Sjkh break; 2229Sjkh } 2239Sjkh break; 2249Sjkh } 2259Sjkh if ( 2269Sjkh (xc==KDELIM || xc==VDELIM) && (uc==KDELIM || uc==VDELIM) && 2279Sjkh (*tp = xc, (match1 = trymatch(xkeyword)) != Nomatch) 2289Sjkh ) { 2299Sjkh#ifdef FCMPTEST 2309Sjkh VOID printf("found common keyword %s\n",xkeyword); 2319Sjkh#endif 2329Sjkh result = -1; 2339Sjkh for (;;) { 2349Sjkh if (xc != uc) { 2359Sjkh xc = discardkeyval(xc, xfp); 2369Sjkh uc = discardkeyval(uc, ufp); 2379Sjkh if ((xeof = xc==EOF) | (ueof = uc==EOF)) 2389Sjkh goto eof; 2399Sjkh eqkeyvals = false; 2409Sjkh break; 2419Sjkh } 2429Sjkh switch (xc) { 2439Sjkh default: 24411894Speter Igeteof_(xfp, xc, xeof=true;) 24511894Speter Igeteof_(ufp, uc, ueof=true;) 2469Sjkh if (xeof | ueof) 2479Sjkh goto eof; 2489Sjkh continue; 2499Sjkh 2509Sjkh case '\n': case KDELIM: 2519Sjkh eqkeyvals = true; 2529Sjkh break; 2539Sjkh } 2549Sjkh break; 2559Sjkh } 2569Sjkh if (xc != uc) 2579Sjkh goto return1; 2589Sjkh if (xc==KDELIM) { 2599Sjkh /* Skip closing KDELIM. */ 26011894Speter Igeteof_(xfp, xc, xeof=true;) 26111894Speter Igeteof_(ufp, uc, ueof=true;) 2629Sjkh if (xeof | ueof) 2639Sjkh goto eof; 2649Sjkh /* if the keyword is LOG, also skip the log message in xfp*/ 2659Sjkh if (match1==Log) { 2669Sjkh /* first, compute the number of line feeds in log msg */ 26711894Speter int lncnt; 2689Sjkh size_t ls, ccnt; 2699Sjkh sp = delta->log.string; 2709Sjkh ls = delta->log.size; 2719Sjkh if (ls<sizeof(ciklog)-1 || memcmp(sp,ciklog,sizeof(ciklog)-1)) { 27211894Speter /* 27311894Speter * This log message was inserted. Skip its header. 27411894Speter * The number of newlines to skip is 27511894Speter * 1 + (C+1)*(1+L+1), where C is the number of newlines 27611894Speter * in the comment leader, and L is the number of 27711894Speter * newlines in the log string. 27811894Speter */ 27911894Speter int c1 = 1; 28011894Speter for (ccnt=Comment.size; ccnt--; ) 28111894Speter c1 += Comment.string[ccnt] == '\n'; 28211894Speter lncnt = 2*c1 + 1; 28311894Speter while (ls--) if (*sp++=='\n') lncnt += c1; 2849Sjkh for (;;) { 2859Sjkh if (xc=='\n') 2869Sjkh if(--lncnt==0) break; 28711894Speter Igeteof_(xfp, xc, goto returnresult;) 2889Sjkh } 2899Sjkh /* skip last comment leader */ 2909Sjkh /* Can't just skip another line here, because there may be */ 2919Sjkh /* additional characters on the line (after the Log....$) */ 29211894Speter ccnt = RCSversion<VERSION(5) ? Comment.size : leaderlen; 29311894Speter do { 29411894Speter Igeteof_(xfp, xc, goto returnresult;) 2959Sjkh /* 2969Sjkh * Read to the end of the comment leader or '\n', 29711894Speter * whatever comes first, because the leader's 29811894Speter * trailing white space was probably stripped. 2999Sjkh */ 30011894Speter } while (ccnt-- && (xc!='\n' || --c1)); 3019Sjkh } 3029Sjkh } 3039Sjkh } else { 3049Sjkh /* both end in the same character, but not a KDELIM */ 3059Sjkh /* must compare string values.*/ 3069Sjkh#ifdef FCMPTEST 3079Sjkh VOID printf("non-terminated keywords %s, potentially different values\n",xkeyword); 3089Sjkh#endif 3099Sjkh if (!eqkeyvals) 3109Sjkh goto return1; 3119Sjkh } 3129Sjkh } 3139Sjkh } 3149Sjkh if (xc != uc) 3159Sjkh goto return1; 31611894Speter if (xc == '\n') 31711894Speter leaderlen = 0; 31811894Speter else 31911894Speter leaderlen++; 3209Sjkh } 3219Sjkh } 3229Sjkh 3239Sjkh eof: 3249Sjkh if (xeof==ueof) 3259Sjkh goto returnresult; 3269Sjkh return1: 3279Sjkh result = 1; 3289Sjkh returnresult: 3299Sjkh Ifclose(ufp); 3309Sjkh return result; 3319Sjkh} 3329Sjkh 3339Sjkh 3349Sjkh 3359Sjkh#ifdef FCMPTEST 3369Sjkh 3379Sjkhchar const cmdid[] = "rcsfcmp"; 3389Sjkh 3399Sjkhmain(argc, argv) 3409Sjkhint argc; char *argv[]; 3419Sjkh/* first argument: comment leader; 2nd: log message, 3rd: expanded file, 3429Sjkh * 4th: unexpanded file 3439Sjkh */ 3449Sjkh{ struct hshentry delta; 3459Sjkh 3469Sjkh Comment.string = argv[1]; 3479Sjkh Comment.size = strlen(argv[1]); 3489Sjkh delta.log.string = argv[2]; 3499Sjkh delta.log.size = strlen(argv[2]); 3509Sjkh if (rcsfcmp(Iopen(argv[3], FOPEN_R_WORK, (struct stat*)0), argv[4], &delta)) 3519Sjkh VOID printf("files are the same\n"); 3529Sjkh else VOID printf("files are different\n"); 3539Sjkh} 3549Sjkh#endif 355