111894Speter/* RCS file syntactic analysis */ 211894Speter 311894Speter/****************************************************************************** 49Sjkh * Syntax Analysis. 59Sjkh * Keyword table 69Sjkh * Testprogram: define SYNTEST 79Sjkh * Compatibility with Release 2: define COMPAT2=1 811894Speter ****************************************************************************** 99Sjkh */ 109Sjkh 1111894Speter/* Copyright 1982, 1988, 1989 Walter Tichy 1211894Speter Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert 139Sjkh Distributed under license by the Free Software Foundation, Inc. 149Sjkh 159SjkhThis file is part of RCS. 169Sjkh 179SjkhRCS is free software; you can redistribute it and/or modify 189Sjkhit under the terms of the GNU General Public License as published by 199Sjkhthe Free Software Foundation; either version 2, or (at your option) 209Sjkhany later version. 219Sjkh 229SjkhRCS is distributed in the hope that it will be useful, 239Sjkhbut WITHOUT ANY WARRANTY; without even the implied warranty of 249SjkhMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 259SjkhGNU General Public License for more details. 269Sjkh 279SjkhYou should have received a copy of the GNU General Public License 2811894Speteralong with RCS; see the file COPYING. 2911894SpeterIf not, write to the Free Software Foundation, 3011894Speter59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 319Sjkh 329SjkhReport problems and direct all questions to: 339Sjkh 349Sjkh rcs-bugs@cs.purdue.edu 359Sjkh 369Sjkh*/ 379Sjkh 3811894Speter/* 3911894Speter * Revision 5.15 1995/06/16 06:19:24 eggert 4011894Speter * Update FSF address. 418858Srgrimes * 4211894Speter * Revision 5.14 1995/06/01 16:23:43 eggert 4311894Speter * (expand_names): Add "b" for -kb. 4411894Speter * (getdelta): Don't strip leading "19" from MKS RCS dates; see cmpdate. 4511894Speter * 4611894Speter * Revision 5.13 1994/03/20 04:52:58 eggert 4711894Speter * Remove lint. 4811894Speter * 4911894Speter * Revision 5.12 1993/11/03 17:42:27 eggert 5011894Speter * Parse MKS RCS dates; ignore \r in diff control lines. 5111894Speter * Don't discard ignored phrases. Improve quality of diagnostics. 5211894Speter * 5311894Speter * Revision 5.11 1992/07/28 16:12:44 eggert 5411894Speter * Avoid `unsigned'. Statement macro names now end in _. 5511894Speter * 5611894Speter * Revision 5.10 1992/01/24 18:44:19 eggert 5711894Speter * Move put routines to rcsgen.c. 5811894Speter * 5911894Speter * Revision 5.9 1992/01/06 02:42:34 eggert 6011894Speter * ULONG_MAX/10 -> ULONG_MAX_OVER_10 6111894Speter * while (E) ; -> while (E) continue; 6211894Speter * 639Sjkh * Revision 5.8 1991/08/19 03:13:55 eggert 649Sjkh * Tune. 659Sjkh * 669Sjkh * Revision 5.7 1991/04/21 11:58:29 eggert 679Sjkh * Disambiguate names on shortname hosts. 689Sjkh * Fix errno bug. Add MS-DOS support. 699Sjkh * 709Sjkh * Revision 5.6 1991/02/28 19:18:51 eggert 719Sjkh * Fix null termination bug in reporting keyword expansion. 729Sjkh * 739Sjkh * Revision 5.5 1991/02/25 07:12:44 eggert 749Sjkh * Check diff output more carefully; avoid overflow. 759Sjkh * 769Sjkh * Revision 5.4 1990/11/01 05:28:48 eggert 779Sjkh * When ignoring unknown phrases, copy them to the output RCS file. 789Sjkh * Permit arbitrary data in logs and comment leaders. 799Sjkh * Don't check for nontext on initial checkin. 809Sjkh * 819Sjkh * Revision 5.3 1990/09/20 07:58:32 eggert 829Sjkh * Remove the test for non-text bytes; it caused more pain than it cured. 839Sjkh * 849Sjkh * Revision 5.2 1990/09/04 08:02:30 eggert 859Sjkh * Parse RCS files with no revisions. 869Sjkh * Don't strip leading white space from diff commands. Count RCS lines better. 879Sjkh * 889Sjkh * Revision 5.1 1990/08/29 07:14:06 eggert 899Sjkh * Add -kkvl. Clean old log messages too. 909Sjkh * 919Sjkh * Revision 5.0 1990/08/22 08:13:44 eggert 929Sjkh * Try to parse future RCS formats without barfing. 939Sjkh * Add -k. Don't require final newline. 949Sjkh * Remove compile-time limits; use malloc instead. 959Sjkh * Don't output branch keyword if there's no default branch, 969Sjkh * because RCS version 3 doesn't understand it. 979Sjkh * Tune. Remove lint. 989Sjkh * Add support for ISO 8859. Ansify and Posixate. 999Sjkh * Check that a newly checked-in file is acceptable as input to 'diff'. 1009Sjkh * Check diff's output. 1019Sjkh * 1029Sjkh * Revision 4.6 89/05/01 15:13:32 narten 1039Sjkh * changed copyright header to reflect current distribution rules 1048858Srgrimes * 1059Sjkh * Revision 4.5 88/08/09 19:13:21 eggert 1069Sjkh * Allow cc -R; remove lint. 1078858Srgrimes * 1089Sjkh * Revision 4.4 87/12/18 11:46:16 narten 1099Sjkh * more lint cleanups (Guy Harris) 1108858Srgrimes * 1119Sjkh * Revision 4.3 87/10/18 10:39:36 narten 1129Sjkh * Updating version numbers. Changes relative to 1.1 actually relative to 1139Sjkh * 4.1 1148858Srgrimes * 1159Sjkh * Revision 1.3 87/09/24 14:00:49 narten 1168858Srgrimes * Sources now pass through lint (if you ignore printf/sprintf/fprintf 1179Sjkh * warnings) 1188858Srgrimes * 1199Sjkh * Revision 1.2 87/03/27 14:22:40 jenkins 1209Sjkh * Port to suns 1218858Srgrimes * 1229Sjkh * Revision 4.1 83/03/28 11:38:49 wft 1239Sjkh * Added parsing and printing of default branch. 1248858Srgrimes * 1259Sjkh * Revision 3.6 83/01/15 17:46:50 wft 1269Sjkh * Changed readdelta() to initialize selector and log-pointer. 1279Sjkh * Changed puttree to check for selector==DELETE; putdtext() uses DELNUMFORM. 1289Sjkh * 1299Sjkh * Revision 3.5 82/12/08 21:58:58 wft 1309Sjkh * renamed Commentleader to Commleader. 1319Sjkh * 1329Sjkh * Revision 3.4 82/12/04 13:24:40 wft 1339Sjkh * Added routine gettree(), which updates keeplock after reading the 1349Sjkh * delta tree. 1359Sjkh * 1369Sjkh * Revision 3.3 82/11/28 21:30:11 wft 1379Sjkh * Reading and printing of Suffix removed; version COMPAT2 skips the 1389Sjkh * Suffix for files of release 2 format. Fixed problems with printing nil. 1399Sjkh * 1409Sjkh * Revision 3.2 82/10/18 21:18:25 wft 1419Sjkh * renamed putdeltatext to putdtext. 1429Sjkh * 1439Sjkh * Revision 3.1 82/10/11 19:45:11 wft 1449Sjkh * made sure getc() returns into an integer. 1459Sjkh */ 1469Sjkh 1479Sjkh 1489Sjkh 1499Sjkh/* version COMPAT2 reads files of the format of release 2 and 3, but 1509Sjkh * generates files of release 3 format. Need not be defined if no 1519Sjkh * old RCS files generated with release 2 exist. 1529Sjkh */ 1539Sjkh 1549Sjkh#include "rcsbase.h" 1559Sjkh 15650472SpeterlibId(synId, "$FreeBSD: releng/10.3/gnu/usr.bin/rcs/lib/rcssyn.c 50472 1999-08-27 23:37:10Z peter $") 1579Sjkh 1589Sjkhstatic char const *getkeyval P((char const*,enum tokens,int)); 15911894Speterstatic int getdelta P((void)); 1609Sjkhstatic int strn2expmode P((char const*,size_t)); 16111894Speterstatic struct hshentry *getdnum P((void)); 16211894Speterstatic void badDiffOutput P((char const*)) exiting; 16311894Speterstatic void diffLineNumberTooLarge P((char const*)) exiting; 16411894Speterstatic void getsemi P((char const*)); 1659Sjkh 1669Sjkh/* keyword table */ 1679Sjkh 1689Sjkhchar const 1699Sjkh Kaccess[] = "access", 1709Sjkh Kauthor[] = "author", 1719Sjkh Kbranch[] = "branch", 1729Sjkh Kcomment[] = "comment", 1739Sjkh Kdate[] = "date", 17411894Speter Kdesc[] = "desc", 1759Sjkh Kexpand[] = "expand", 1769Sjkh Khead[] = "head", 1779Sjkh Klocks[] = "locks", 17811894Speter Klog[] = "log", 1799Sjkh Knext[] = "next", 1809Sjkh Kstate[] = "state", 1819Sjkh Kstrict[] = "strict", 18211894Speter Ksymbols[] = "symbols", 18311894Speter Ktext[] = "text"; 18411894Speter 18511894Speterstatic char const 1869Sjkh#if COMPAT2 1879Sjkh Ksuffix[] = "suffix", 1889Sjkh#endif 18911894Speter K_branches[]= "branches"; 1909Sjkh 1919Sjkhstatic struct buf Commleader; 1929Sjkhstruct cbuf Comment; 19311894Speterstruct cbuf Ignored; 1949Sjkhstruct access * AccessList; 1959Sjkhstruct assoc * Symbols; 19611894Speterstruct rcslock *Locks; 1979Sjkhint Expand; 1989Sjkhint StrictLocks; 1999Sjkhstruct hshentry * Head; 2009Sjkhchar const * Dbranch; 20111894Speterint TotalDeltas; 2029Sjkh 2039Sjkh 2049Sjkh static void 2059Sjkhgetsemi(key) 2069Sjkh char const *key; 2079Sjkh/* Get a semicolon to finish off a phrase started by KEY. */ 2089Sjkh{ 2099Sjkh if (!getlex(SEMI)) 2109Sjkh fatserror("missing ';' after '%s'", key); 2119Sjkh} 2129Sjkh 2139Sjkh static struct hshentry * 2149Sjkhgetdnum() 2159Sjkh/* Get a delta number. */ 2169Sjkh{ 2179Sjkh register struct hshentry *delta = getnum(); 2189Sjkh if (delta && countnumflds(delta->num)&1) 2199Sjkh fatserror("%s isn't a delta number", delta->num); 2209Sjkh return delta; 2219Sjkh} 2229Sjkh 2239Sjkh 2249Sjkh void 2259Sjkhgetadmin() 2269Sjkh/* Read an <admin> and initialize the appropriate global variables. */ 2279Sjkh{ 2289Sjkh register char const *id; 2299Sjkh struct access * newaccess; 2309Sjkh struct assoc * newassoc; 23111894Speter struct rcslock *newlock; 2329Sjkh struct hshentry * delta; 2339Sjkh struct access **LastAccess; 2349Sjkh struct assoc **LastSymbol; 23511894Speter struct rcslock **LastLock; 2369Sjkh struct buf b; 2379Sjkh struct cbuf cb; 2389Sjkh 2399Sjkh TotalDeltas=0; 2409Sjkh 2419Sjkh getkey(Khead); 2429Sjkh Head = getdnum(); 2439Sjkh getsemi(Khead); 2449Sjkh 24511894Speter Dbranch = 0; 2469Sjkh if (getkeyopt(Kbranch)) { 2479Sjkh if ((delta = getnum())) 2489Sjkh Dbranch = delta->num; 2499Sjkh getsemi(Kbranch); 2509Sjkh } 2519Sjkh 2529Sjkh 2539Sjkh#if COMPAT2 2549Sjkh /* read suffix. Only in release 2 format */ 2559Sjkh if (getkeyopt(Ksuffix)) { 2569Sjkh if (nexttok==STRING) { 2579Sjkh readstring(); nextlex(); /* Throw away the suffix. */ 2589Sjkh } else if (nexttok==ID) { 2599Sjkh nextlex(); 2609Sjkh } 2619Sjkh getsemi(Ksuffix); 2629Sjkh } 2639Sjkh#endif 2649Sjkh 2659Sjkh getkey(Kaccess); 2669Sjkh LastAccess = &AccessList; 26711894Speter while ((id = getid())) { 2689Sjkh newaccess = ftalloc(struct access); 2699Sjkh newaccess->login = id; 2709Sjkh *LastAccess = newaccess; 2719Sjkh LastAccess = &newaccess->nextaccess; 2729Sjkh } 27311894Speter *LastAccess = 0; 2749Sjkh getsemi(Kaccess); 2759Sjkh 2769Sjkh getkey(Ksymbols); 2779Sjkh LastSymbol = &Symbols; 27811894Speter while ((id = getid())) { 2799Sjkh if (!getlex(COLON)) 2809Sjkh fatserror("missing ':' in symbolic name definition"); 2819Sjkh if (!(delta=getnum())) { 2829Sjkh fatserror("missing number in symbolic name definition"); 2839Sjkh } else { /*add new pair to association list*/ 2849Sjkh newassoc = ftalloc(struct assoc); 2859Sjkh newassoc->symbol=id; 2869Sjkh newassoc->num = delta->num; 2879Sjkh *LastSymbol = newassoc; 2889Sjkh LastSymbol = &newassoc->nextassoc; 2899Sjkh } 2909Sjkh } 29111894Speter *LastSymbol = 0; 2929Sjkh getsemi(Ksymbols); 2939Sjkh 2949Sjkh getkey(Klocks); 2959Sjkh LastLock = &Locks; 29611894Speter while ((id = getid())) { 2979Sjkh if (!getlex(COLON)) 2989Sjkh fatserror("missing ':' in lock"); 2999Sjkh if (!(delta=getdnum())) { 3009Sjkh fatserror("missing number in lock"); 3019Sjkh } else { /*add new pair to lock list*/ 30211894Speter newlock = ftalloc(struct rcslock); 3039Sjkh newlock->login=id; 3049Sjkh newlock->delta=delta; 3059Sjkh *LastLock = newlock; 3069Sjkh LastLock = &newlock->nextlock; 3079Sjkh } 3089Sjkh } 30911894Speter *LastLock = 0; 3109Sjkh getsemi(Klocks); 3119Sjkh 3129Sjkh if ((StrictLocks = getkeyopt(Kstrict))) 3139Sjkh getsemi(Kstrict); 3149Sjkh 31511894Speter clear_buf(&Comment); 3169Sjkh if (getkeyopt(Kcomment)) { 3179Sjkh if (nexttok==STRING) { 3189Sjkh Comment = savestring(&Commleader); 3199Sjkh nextlex(); 3209Sjkh } 3219Sjkh getsemi(Kcomment); 3229Sjkh } 3239Sjkh 3249Sjkh Expand = KEYVAL_EXPAND; 3259Sjkh if (getkeyopt(Kexpand)) { 3269Sjkh if (nexttok==STRING) { 3279Sjkh bufautobegin(&b); 3289Sjkh cb = savestring(&b); 3299Sjkh if ((Expand = strn2expmode(cb.string,cb.size)) < 0) 3309Sjkh fatserror("unknown expand mode %.*s", 3319Sjkh (int)cb.size, cb.string 3329Sjkh ); 3339Sjkh bufautoend(&b); 3349Sjkh nextlex(); 3359Sjkh } 3369Sjkh getsemi(Kexpand); 3379Sjkh } 3389Sjkh Ignored = getphrases(Kdesc); 3399Sjkh} 3409Sjkh 3419Sjkhchar const *const expand_names[] = { 3429Sjkh /* These must agree with *_EXPAND in rcsbase.h. */ 34311894Speter "kv", "kvl", "k", "v", "o", "b", 3449Sjkh 0 3459Sjkh}; 3469Sjkh 3479Sjkh int 3489Sjkhstr2expmode(s) 3499Sjkh char const *s; 3509Sjkh/* Yield expand mode corresponding to S, or -1 if bad. */ 3519Sjkh{ 3529Sjkh return strn2expmode(s, strlen(s)); 3539Sjkh} 3549Sjkh 3559Sjkh static int 3569Sjkhstrn2expmode(s, n) 3579Sjkh char const *s; 3589Sjkh size_t n; 3599Sjkh{ 3609Sjkh char const *const *p; 3619Sjkh 3629Sjkh for (p = expand_names; *p; ++p) 3639Sjkh if (memcmp(*p,s,n) == 0 && !(*p)[n]) 3649Sjkh return p - expand_names; 3659Sjkh return -1; 3669Sjkh} 3679Sjkh 3689Sjkh 3699Sjkh void 37011894Speterignorephrases(key) 37111894Speter const char *key; 37211894Speter/* 37311894Speter* Ignore a series of phrases that do not start with KEY. 37411894Speter* Stop when the next phrase starts with a token that is not an identifier, 37511894Speter* or is KEY. 37611894Speter*/ 3779Sjkh{ 3789Sjkh for (;;) { 37911894Speter nextlex(); 38011894Speter if (nexttok != ID || strcmp(NextString,key) == 0) 38111894Speter break; 38211894Speter warnignore(); 38311894Speter hshenter=false; 38411894Speter for (;; nextlex()) { 38511894Speter switch (nexttok) { 38611894Speter case SEMI: hshenter=true; break; 38711894Speter case ID: 38811894Speter case NUM: ffree1(NextString); continue; 38911894Speter case STRING: readstring(); continue; 39011894Speter default: continue; 39111894Speter } 39211894Speter break; 39311894Speter } 3949Sjkh } 3959Sjkh} 3969Sjkh 3979Sjkh 3989Sjkh static int 3999Sjkhgetdelta() 4009Sjkh/* Function: reads a delta block. 4019Sjkh * returns false if the current block does not start with a number. 4029Sjkh */ 4039Sjkh{ 4049Sjkh register struct hshentry * Delta, * num; 4059Sjkh struct branchhead **LastBranch, *NewBranch; 4069Sjkh 4079Sjkh if (!(Delta = getdnum())) 4089Sjkh return false; 4099Sjkh 4109Sjkh hshenter = false; /*Don't enter dates into hashtable*/ 41111894Speter Delta->date = getkeyval(Kdate, NUM, false); 4129Sjkh hshenter=true; /*reset hshenter for revision numbers.*/ 4139Sjkh 4149Sjkh Delta->author = getkeyval(Kauthor, ID, false); 4159Sjkh 4169Sjkh Delta->state = getkeyval(Kstate, ID, true); 4179Sjkh 4189Sjkh getkey(K_branches); 4199Sjkh LastBranch = &Delta->branches; 4209Sjkh while ((num = getdnum())) { 4219Sjkh NewBranch = ftalloc(struct branchhead); 4229Sjkh NewBranch->hsh = num; 4239Sjkh *LastBranch = NewBranch; 4249Sjkh LastBranch = &NewBranch->nextbranch; 4259Sjkh } 42611894Speter *LastBranch = 0; 4279Sjkh getsemi(K_branches); 4289Sjkh 4299Sjkh getkey(Knext); 4309Sjkh Delta->next = num = getdnum(); 4319Sjkh getsemi(Knext); 43211894Speter Delta->lockedby = 0; 4339Sjkh Delta->log.string = 0; 4349Sjkh Delta->selector = true; 4359Sjkh Delta->ig = getphrases(Kdesc); 4369Sjkh TotalDeltas++; 4379Sjkh return (true); 4389Sjkh} 4399Sjkh 4409Sjkh 4419Sjkh void 4429Sjkhgettree() 4439Sjkh/* Function: Reads in the delta tree with getdelta(), then 4449Sjkh * updates the lockedby fields. 4459Sjkh */ 4469Sjkh{ 44711894Speter struct rcslock const *currlock; 4489Sjkh 44911894Speter while (getdelta()) 45011894Speter continue; 4519Sjkh currlock=Locks; 4529Sjkh while (currlock) { 4539Sjkh currlock->delta->lockedby = currlock->login; 4549Sjkh currlock = currlock->nextlock; 4559Sjkh } 4569Sjkh} 4579Sjkh 4589Sjkh 4599Sjkh void 4609Sjkhgetdesc(prdesc) 4619Sjkhint prdesc; 4629Sjkh/* Function: read in descriptive text 4639Sjkh * nexttok is not advanced afterwards. 4649Sjkh * If prdesc is set, the text is printed to stdout. 4659Sjkh */ 4669Sjkh{ 4679Sjkh 4689Sjkh getkeystring(Kdesc); 4699Sjkh if (prdesc) 4709Sjkh printstring(); /*echo string*/ 4719Sjkh else readstring(); /*skip string*/ 4729Sjkh} 4739Sjkh 4749Sjkh 4759Sjkh 4769Sjkh 4779Sjkh 4789Sjkh 4799Sjkh static char const * 4809Sjkhgetkeyval(keyword, token, optional) 4819Sjkh char const *keyword; 4829Sjkh enum tokens token; 4839Sjkh int optional; 4849Sjkh/* reads a pair of the form 4859Sjkh * <keyword> <token> ; 4869Sjkh * where token is one of <id> or <num>. optional indicates whether 4879Sjkh * <token> is optional. A pointer to 4889Sjkh * the actual character string of <id> or <num> is returned. 4899Sjkh */ 4909Sjkh{ 49111894Speter register char const *val = 0; 4929Sjkh 4939Sjkh getkey(keyword); 4949Sjkh if (nexttok==token) { 4959Sjkh val = NextString; 4969Sjkh nextlex(); 4979Sjkh } else { 4989Sjkh if (!optional) 4999Sjkh fatserror("missing %s", keyword); 5009Sjkh } 5019Sjkh getsemi(keyword); 5029Sjkh return(val); 5039Sjkh} 5049Sjkh 5059Sjkh 5069Sjkh void 5079Sjkhunexpected_EOF() 5089Sjkh{ 50911894Speter rcsfaterror("unexpected EOF in diff output"); 5109Sjkh} 5119Sjkh 5129Sjkh void 5139Sjkhinitdiffcmd(dc) 5149Sjkh register struct diffcmd *dc; 5159Sjkh/* Initialize *dc suitably for getdiffcmd(). */ 5169Sjkh{ 5179Sjkh dc->adprev = 0; 5189Sjkh dc->dafter = 0; 5199Sjkh} 5209Sjkh 52111894Speter static void 5229SjkhbadDiffOutput(buf) 5239Sjkh char const *buf; 5249Sjkh{ 52511894Speter rcsfaterror("bad diff output line: %s", buf); 5269Sjkh} 5279Sjkh 52811894Speter static void 5299SjkhdiffLineNumberTooLarge(buf) 5309Sjkh char const *buf; 5319Sjkh{ 53211894Speter rcsfaterror("diff line number too large: %s", buf); 5339Sjkh} 5349Sjkh 5359Sjkh int 5369Sjkhgetdiffcmd(finfile, delimiter, foutfile, dc) 5379Sjkh RILE *finfile; 5389Sjkh FILE *foutfile; 5399Sjkh int delimiter; 5409Sjkh struct diffcmd *dc; 5419Sjkh/* Get a editing command output by 'diff -n' from fin. 5429Sjkh * The input is delimited by SDELIM if delimiter is set, EOF otherwise. 5439Sjkh * Copy a clean version of the command to fout (if nonnull). 5449Sjkh * Yield 0 for 'd', 1 for 'a', and -1 for EOF. 5459Sjkh * Store the command's line number and length into dc->line1 and dc->nlines. 5469Sjkh * Keep dc->adprev and dc->dafter up to date. 5479Sjkh */ 5489Sjkh{ 5499Sjkh register int c; 5509Sjkh declarecache; 5519Sjkh register FILE *fout; 5529Sjkh register char *p; 5539Sjkh register RILE *fin; 55411894Speter long line1, nlines, t; 5559Sjkh char buf[BUFSIZ]; 5569Sjkh 5579Sjkh fin = finfile; 5589Sjkh fout = foutfile; 5599Sjkh setupcache(fin); cache(fin); 56011894Speter cachegeteof_(c, { if (delimiter) unexpected_EOF(); return -1; } ) 5619Sjkh if (delimiter) { 5629Sjkh if (c==SDELIM) { 56311894Speter cacheget_(c) 5649Sjkh if (c==SDELIM) { 5659Sjkh buf[0] = c; 5669Sjkh buf[1] = 0; 5679Sjkh badDiffOutput(buf); 5689Sjkh } 5699Sjkh uncache(fin); 5709Sjkh nextc = c; 5719Sjkh if (fout) 5729Sjkh aprintf(fout, "%c%c", SDELIM, c); 5739Sjkh return -1; 5749Sjkh } 5759Sjkh } 5769Sjkh p = buf; 5779Sjkh do { 5789Sjkh if (buf+BUFSIZ-2 <= p) { 57911894Speter rcsfaterror("diff output command line too long"); 5809Sjkh } 5819Sjkh *p++ = c; 58211894Speter cachegeteof_(c, unexpected_EOF();) 5839Sjkh } while (c != '\n'); 5849Sjkh uncache(fin); 5859Sjkh if (delimiter) 5869Sjkh ++rcsline; 5879Sjkh *p = '\0'; 5889Sjkh for (p = buf+1; (c = *p++) == ' '; ) 58911894Speter continue; 5909Sjkh line1 = 0; 5919Sjkh while (isdigit(c)) { 5929Sjkh if ( 59311894Speter LONG_MAX/10 < line1 || 59411894Speter (t = line1 * 10, (line1 = t + (c - '0')) < t) 5959Sjkh ) 5969Sjkh diffLineNumberTooLarge(buf); 5979Sjkh c = *p++; 5989Sjkh } 5999Sjkh while (c == ' ') 6009Sjkh c = *p++; 6019Sjkh nlines = 0; 6029Sjkh while (isdigit(c)) { 6039Sjkh if ( 60411894Speter LONG_MAX/10 < nlines || 60511894Speter (t = nlines * 10, (nlines = t + (c - '0')) < t) 6069Sjkh ) 6079Sjkh diffLineNumberTooLarge(buf); 6089Sjkh c = *p++; 6099Sjkh } 61011894Speter if (c == '\r') 61111894Speter c = *p++; 6129Sjkh if (c || !nlines) { 6139Sjkh badDiffOutput(buf); 6149Sjkh } 6159Sjkh if (line1+nlines < line1) 6169Sjkh diffLineNumberTooLarge(buf); 6179Sjkh switch (buf[0]) { 6189Sjkh case 'a': 6199Sjkh if (line1 < dc->adprev) { 62011894Speter rcsfaterror("backward insertion in diff output: %s", buf); 6219Sjkh } 6229Sjkh dc->adprev = line1 + 1; 6239Sjkh break; 6249Sjkh case 'd': 6259Sjkh if (line1 < dc->adprev || line1 < dc->dafter) { 62611894Speter rcsfaterror("backward deletion in diff output: %s", buf); 6279Sjkh } 6289Sjkh dc->adprev = line1; 6299Sjkh dc->dafter = line1 + nlines; 6309Sjkh break; 6319Sjkh default: 6329Sjkh badDiffOutput(buf); 6339Sjkh } 6349Sjkh if (fout) { 6359Sjkh aprintf(fout, "%s\n", buf); 6369Sjkh } 6379Sjkh dc->line1 = line1; 6389Sjkh dc->nlines = nlines; 6399Sjkh return buf[0] == 'a'; 6409Sjkh} 6419Sjkh 6429Sjkh 6439Sjkh 6449Sjkh#ifdef SYNTEST 6459Sjkh 64611894Speter/* Input an RCS file and print its internal data structures. */ 64711894Speter 6489Sjkhchar const cmdid[] = "syntest"; 6499Sjkh 6509Sjkh int 6519Sjkhmain(argc,argv) 6529Sjkhint argc; char * argv[]; 6539Sjkh{ 6549Sjkh 6559Sjkh if (argc<2) { 6569Sjkh aputs("No input file\n",stderr); 6579Sjkh exitmain(EXIT_FAILURE); 6589Sjkh } 6599Sjkh if (!(finptr = Iopen(argv[1], FOPEN_R, (struct stat*)0))) { 6609Sjkh faterror("can't open input file %s", argv[1]); 6619Sjkh } 6629Sjkh Lexinit(); 6639Sjkh getadmin(); 66411894Speter fdlock = STDOUT_FILENO; 66511894Speter putadmin(); 6669Sjkh 6679Sjkh gettree(); 6689Sjkh 6699Sjkh getdesc(true); 6709Sjkh 6719Sjkh nextlex(); 6729Sjkh 6739Sjkh if (!eoflex()) { 6749Sjkh fatserror("expecting EOF"); 6759Sjkh } 6769Sjkh exitmain(EXIT_SUCCESS); 6779Sjkh} 6789Sjkh 67911894Spetervoid exiterr() { _exit(EXIT_FAILURE); } 6809Sjkh 6819Sjkh#endif 682