Home | History | Annotate | Line # | Download | only in src
      1 /*	$NetBSD: rcssyn.c,v 1.2 2016/01/14 04:22:39 christos Exp $	*/
      2 
      3 /* RCS file syntactic analysis */
      4 
      5 /******************************************************************************
      6  *                       Syntax Analysis.
      7  *                       Keyword table
      8  *                       Testprogram: define SYNTEST
      9  *                       Compatibility with Release 2: define COMPAT2=1
     10  ******************************************************************************
     11  */
     12 
     13 /* Copyright 1982, 1988, 1989 Walter Tichy
     14    Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
     15    Distributed under license by the Free Software Foundation, Inc.
     16 
     17 This file is part of RCS.
     18 
     19 RCS is free software; you can redistribute it and/or modify
     20 it under the terms of the GNU General Public License as published by
     21 the Free Software Foundation; either version 2, or (at your option)
     22 any later version.
     23 
     24 RCS is distributed in the hope that it will be useful,
     25 but WITHOUT ANY WARRANTY; without even the implied warranty of
     26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     27 GNU General Public License for more details.
     28 
     29 You should have received a copy of the GNU General Public License
     30 along with RCS; see the file COPYING.
     31 If not, write to the Free Software Foundation,
     32 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     33 
     34 Report problems and direct all questions to:
     35 
     36     rcs-bugs (at) cs.purdue.edu
     37 
     38 */
     39 
     40 /*
     41  * Log: rcssyn.c,v
     42  * Revision 5.15  1995/06/16 06:19:24  eggert
     43  * Update FSF address.
     44  *
     45  * Revision 5.14  1995/06/01 16:23:43  eggert
     46  * (expand_names): Add "b" for -kb.
     47  * (getdelta): Don't strip leading "19" from MKS RCS dates; see cmpdate.
     48  *
     49  * Revision 5.13  1994/03/20 04:52:58  eggert
     50  * Remove lint.
     51  *
     52  * Revision 5.12  1993/11/03 17:42:27  eggert
     53  * Parse MKS RCS dates; ignore \r in diff control lines.
     54  * Don't discard ignored phrases.  Improve quality of diagnostics.
     55  *
     56  * Revision 5.11  1992/07/28  16:12:44  eggert
     57  * Avoid `unsigned'.  Statement macro names now end in _.
     58  *
     59  * Revision 5.10  1992/01/24  18:44:19  eggert
     60  * Move put routines to rcsgen.c.
     61  *
     62  * Revision 5.9  1992/01/06  02:42:34  eggert
     63  * ULONG_MAX/10 -> ULONG_MAX_OVER_10
     64  * while (E) ; -> while (E) continue;
     65  *
     66  * Revision 5.8  1991/08/19  03:13:55  eggert
     67  * Tune.
     68  *
     69  * Revision 5.7  1991/04/21  11:58:29  eggert
     70  * Disambiguate names on shortname hosts.
     71  * Fix errno bug.  Add MS-DOS support.
     72  *
     73  * Revision 5.6  1991/02/28  19:18:51  eggert
     74  * Fix null termination bug in reporting keyword expansion.
     75  *
     76  * Revision 5.5  1991/02/25  07:12:44  eggert
     77  * Check diff output more carefully; avoid overflow.
     78  *
     79  * Revision 5.4  1990/11/01  05:28:48  eggert
     80  * When ignoring unknown phrases, copy them to the output RCS file.
     81  * Permit arbitrary data in logs and comment leaders.
     82  * Don't check for nontext on initial checkin.
     83  *
     84  * Revision 5.3  1990/09/20  07:58:32  eggert
     85  * Remove the test for non-text bytes; it caused more pain than it cured.
     86  *
     87  * Revision 5.2  1990/09/04  08:02:30  eggert
     88  * Parse RCS files with no revisions.
     89  * Don't strip leading white space from diff commands.  Count RCS lines better.
     90  *
     91  * Revision 5.1  1990/08/29  07:14:06  eggert
     92  * Add -kkvl.  Clean old log messages too.
     93  *
     94  * Revision 5.0  1990/08/22  08:13:44  eggert
     95  * Try to parse future RCS formats without barfing.
     96  * Add -k.  Don't require final newline.
     97  * Remove compile-time limits; use malloc instead.
     98  * Don't output branch keyword if there's no default branch,
     99  * because RCS version 3 doesn't understand it.
    100  * Tune.  Remove lint.
    101  * Add support for ISO 8859.  Ansify and Posixate.
    102  * Check that a newly checked-in file is acceptable as input to 'diff'.
    103  * Check diff's output.
    104  *
    105  * Revision 4.6  89/05/01  15:13:32  narten
    106  * changed copyright header to reflect current distribution rules
    107  *
    108  * Revision 4.5  88/08/09  19:13:21  eggert
    109  * Allow cc -R; remove lint.
    110  *
    111  * Revision 4.4  87/12/18  11:46:16  narten
    112  * more lint cleanups (Guy Harris)
    113  *
    114  * Revision 4.3  87/10/18  10:39:36  narten
    115  * Updating version numbers. Changes relative to 1.1 actually relative to
    116  * 4.1
    117  *
    118  * Revision 1.3  87/09/24  14:00:49  narten
    119  * Sources now pass through lint (if you ignore printf/sprintf/fprintf
    120  * warnings)
    121  *
    122  * Revision 1.2  87/03/27  14:22:40  jenkins
    123  * Port to suns
    124  *
    125  * Revision 4.1  83/03/28  11:38:49  wft
    126  * Added parsing and printing of default branch.
    127  *
    128  * Revision 3.6  83/01/15  17:46:50  wft
    129  * Changed readdelta() to initialize selector and log-pointer.
    130  * Changed puttree to check for selector==DELETE; putdtext() uses DELNUMFORM.
    131  *
    132  * Revision 3.5  82/12/08  21:58:58  wft
    133  * renamed Commentleader to Commleader.
    134  *
    135  * Revision 3.4  82/12/04  13:24:40  wft
    136  * Added routine gettree(), which updates keeplock after reading the
    137  * delta tree.
    138  *
    139  * Revision 3.3  82/11/28  21:30:11  wft
    140  * Reading and printing of Suffix removed; version COMPAT2 skips the
    141  * Suffix for files of release 2 format. Fixed problems with printing nil.
    142  *
    143  * Revision 3.2  82/10/18  21:18:25  wft
    144  * renamed putdeltatext to putdtext.
    145  *
    146  * Revision 3.1  82/10/11  19:45:11  wft
    147  * made sure getc() returns into an integer.
    148  */
    149 
    150 
    151 
    152 /* version COMPAT2 reads files of the format of release 2 and 3, but
    153  * generates files of release 3 format. Need not be defined if no
    154  * old RCS files generated with release 2 exist.
    155  */
    156 
    157 #include "rcsbase.h"
    158 
    159 libId(synId, "Id: rcssyn.c,v 5.15 1995/06/16 06:19:24 eggert Exp ")
    160 
    161 static char const *getkeyval P((char const*,enum tokens,int));
    162 static int getdelta P((void));
    163 static int strn2expmode P((char const*,size_t));
    164 static struct hshentry *getdnum P((void));
    165 static void badDiffOutput P((char const*)) exiting;
    166 static void diffLineNumberTooLarge P((char const*)) exiting;
    167 static void getsemi P((char const*));
    168 
    169 /* keyword table */
    170 
    171 char const
    172 	Kaccess[]   = "access",
    173 	Kauthor[]   = "author",
    174 	Kbranch[]   = "branch",
    175 	Kcomment[]  = "comment",
    176 	Kcommitid[] = "commitid",
    177 	Kdate[]     = "date",
    178 	Kdesc[]     = "desc",
    179 	Kexpand[]   = "expand",
    180 	Khead[]     = "head",
    181 	Klocks[]    = "locks",
    182 	Klog[]      = "log",
    183 	Knext[]     = "next",
    184 	Kstate[]    = "state",
    185 	Kstrict[]   = "strict",
    186 	Ksymbols[]  = "symbols",
    187 	Ktext[]     = "text";
    188 
    189 static char const
    190 #if COMPAT2
    191 	Ksuffix[]   = "suffix",
    192 #endif
    193 	K_branches[]= "branches";
    194 
    195 static struct buf Commleader;
    196 struct cbuf Comment;
    197 struct cbuf Ignored;
    198 struct access   * AccessList;
    199 struct assoc    * Symbols;
    200 struct rcslock *Locks;
    201 int		  Expand;
    202 int               StrictLocks;
    203 struct hshentry * Head;
    204 char const      * Dbranch;
    205 int TotalDeltas;
    206 
    207 
    208 	static void
    209 getsemi(key)
    210 	char const *key;
    211 /* Get a semicolon to finish off a phrase started by KEY.  */
    212 {
    213 	if (!getlex(SEMI))
    214 		fatserror("missing ';' after '%s'", key);
    215 }
    216 
    217 	static struct hshentry *
    218 getdnum()
    219 /* Get a delta number.  */
    220 {
    221 	register struct hshentry *delta = getnum();
    222 	if (delta && countnumflds(delta->num)&1)
    223 		fatserror("%s isn't a delta number", delta->num);
    224 	return delta;
    225 }
    226 
    227 
    228 	void
    229 getadmin()
    230 /* Read an <admin> and initialize the appropriate global variables.  */
    231 {
    232 	register char const *id;
    233         struct access   * newaccess;
    234         struct assoc    * newassoc;
    235 	struct rcslock *newlock;
    236         struct hshentry * delta;
    237 	struct access **LastAccess;
    238 	struct assoc **LastSymbol;
    239 	struct rcslock **LastLock;
    240 	struct buf b;
    241 	struct cbuf cb;
    242 
    243         TotalDeltas=0;
    244 
    245 	getkey(Khead);
    246 	Head = getdnum();
    247 	getsemi(Khead);
    248 
    249 	Dbranch = 0;
    250 	if (getkeyopt(Kbranch)) {
    251 		if ((delta = getnum()))
    252 			Dbranch = delta->num;
    253 		getsemi(Kbranch);
    254         }
    255 
    256 
    257 #if COMPAT2
    258         /* read suffix. Only in release 2 format */
    259 	if (getkeyopt(Ksuffix)) {
    260                 if (nexttok==STRING) {
    261 			readstring(); nextlex(); /* Throw away the suffix.  */
    262 		} else if (nexttok==ID) {
    263                         nextlex();
    264                 }
    265 		getsemi(Ksuffix);
    266         }
    267 #endif
    268 
    269 	getkey(Kaccess);
    270 	LastAccess = &AccessList;
    271 	while ((id = getid())) {
    272 		newaccess = ftalloc(struct access);
    273                 newaccess->login = id;
    274 		*LastAccess = newaccess;
    275 		LastAccess = &newaccess->nextaccess;
    276         }
    277 	*LastAccess = 0;
    278 	getsemi(Kaccess);
    279 
    280 	getkey(Ksymbols);
    281 	LastSymbol = &Symbols;
    282         while ((id = getid())) {
    283                 if (!getlex(COLON))
    284 			fatserror("missing ':' in symbolic name definition");
    285                 if (!(delta=getnum())) {
    286 			fatserror("missing number in symbolic name definition");
    287                 } else { /*add new pair to association list*/
    288 			newassoc = ftalloc(struct assoc);
    289                         newassoc->symbol=id;
    290 			newassoc->num = delta->num;
    291 			*LastSymbol = newassoc;
    292 			LastSymbol = &newassoc->nextassoc;
    293                 }
    294         }
    295 	*LastSymbol = 0;
    296 	getsemi(Ksymbols);
    297 
    298 	getkey(Klocks);
    299 	LastLock = &Locks;
    300         while ((id = getid())) {
    301                 if (!getlex(COLON))
    302 			fatserror("missing ':' in lock");
    303 		if (!(delta=getdnum())) {
    304 			fatserror("missing number in lock");
    305                 } else { /*add new pair to lock list*/
    306 			newlock = ftalloc(struct rcslock);
    307                         newlock->login=id;
    308                         newlock->delta=delta;
    309 			*LastLock = newlock;
    310 			LastLock = &newlock->nextlock;
    311                 }
    312         }
    313 	*LastLock = 0;
    314 	getsemi(Klocks);
    315 
    316 	if ((StrictLocks = getkeyopt(Kstrict)))
    317 		getsemi(Kstrict);
    318 
    319 	clear_buf(&Comment);
    320 	if (getkeyopt(Kcomment)) {
    321 		if (nexttok==STRING) {
    322 			Comment = savestring(&Commleader);
    323 			nextlex();
    324 		}
    325 		getsemi(Kcomment);
    326         }
    327 
    328 	Expand = KEYVAL_EXPAND;
    329 	if (getkeyopt(Kexpand)) {
    330 		if (nexttok==STRING) {
    331 			bufautobegin(&b);
    332 			cb = savestring(&b);
    333 			if ((Expand = strn2expmode(cb.string,cb.size)) < 0)
    334 			    fatserror("unknown expand mode %.*s",
    335 				(int)cb.size, cb.string
    336 			    );
    337 			bufautoend(&b);
    338 			nextlex();
    339 		}
    340 		getsemi(Kexpand);
    341         }
    342 	Ignored = getphrases(Kdesc);
    343 }
    344 
    345 char const *const expand_names[] = {
    346 	/* These must agree with *_EXPAND in rcsbase.h.  */
    347 	"kv", "kvl", "k", "v", "o", "b",
    348 	0
    349 };
    350 
    351 	int
    352 str2expmode(s)
    353 	char const *s;
    354 /* Yield expand mode corresponding to S, or -1 if bad.  */
    355 {
    356 	return strn2expmode(s, strlen(s));
    357 }
    358 
    359 	static int
    360 strn2expmode(s, n)
    361 	char const *s;
    362 	size_t n;
    363 {
    364 	char const *const *p;
    365 
    366 	for (p = expand_names;  *p;  ++p)
    367 		if (memcmp(*p,s,n) == 0  &&  !(*p)[n])
    368 			return p - expand_names;
    369 	return -1;
    370 }
    371 
    372 
    373 	void
    374 ignorephrases(key)
    375 	const char *key;
    376 /*
    377 * Ignore a series of phrases that do not start with KEY.
    378 * Stop when the next phrase starts with a token that is not an identifier,
    379 * or is KEY.
    380 */
    381 {
    382 	for (;;) {
    383 		nextlex();
    384 		if (nexttok != ID  ||  strcmp(NextString,key) == 0)
    385 			break;
    386 		warnignore();
    387 		hshenter=false;
    388 		for (;; nextlex()) {
    389 			switch (nexttok) {
    390 				case SEMI: hshenter=true; break;
    391 				case ID:
    392 				case NUM: ffree1(NextString); continue;
    393 				case STRING: readstring(); continue;
    394 				default: continue;
    395 			}
    396 			break;
    397 		}
    398 	}
    399 }
    400 
    401 
    402 	static int
    403 getdelta()
    404 /* Function: reads a delta block.
    405  * returns false if the current block does not start with a number.
    406  */
    407 {
    408         register struct hshentry * Delta, * num;
    409 	struct branchhead **LastBranch, *NewBranch;
    410 
    411 	if (!(Delta = getdnum()))
    412 		return false;
    413 
    414         hshenter = false; /*Don't enter dates into hashtable*/
    415 	Delta->date = getkeyval(Kdate, NUM, false);
    416         hshenter=true;    /*reset hshenter for revision numbers.*/
    417 
    418         Delta->author = getkeyval(Kauthor, ID, false);
    419 
    420         Delta->state = getkeyval(Kstate, ID, true);
    421 
    422 	getkey(K_branches);
    423 	LastBranch = &Delta->branches;
    424 	while ((num = getdnum())) {
    425 		NewBranch = ftalloc(struct branchhead);
    426                 NewBranch->hsh = num;
    427 		*LastBranch = NewBranch;
    428 		LastBranch = &NewBranch->nextbranch;
    429         }
    430 	*LastBranch = 0;
    431 	getsemi(K_branches);
    432 
    433 	getkey(Knext);
    434 	Delta->next = num = getdnum();
    435 	getsemi(Knext);
    436 	if (getkeyopt(Kcommitid)) {
    437 	    if (nexttok == ID) {
    438 		Delta->commitid = NextString;
    439 		nextlex();
    440 	    } else {
    441 		fatserror("missing %s", Kcommitid);
    442 		Delta->commitid = NULL;
    443 	    }
    444 	    getsemi(Kcommitid);
    445 	} else
    446 	    Delta->commitid = NULL;
    447 	Delta->lockedby = 0;
    448 	Delta->log.string = 0;
    449 	Delta->selector = true;
    450 	Delta->ig = getphrases(Kdesc);
    451         TotalDeltas++;
    452         return (true);
    453 }
    454 
    455 
    456 	void
    457 gettree()
    458 /* Function: Reads in the delta tree with getdelta(), then
    459  * updates the lockedby fields.
    460  */
    461 {
    462 	struct rcslock const *currlock;
    463 
    464 	while (getdelta())
    465 		continue;
    466         currlock=Locks;
    467         while (currlock) {
    468                 currlock->delta->lockedby = currlock->login;
    469                 currlock = currlock->nextlock;
    470         }
    471 }
    472 
    473 
    474 	void
    475 getdesc(prdesc)
    476 int  prdesc;
    477 /* Function: read in descriptive text
    478  * nexttok is not advanced afterwards.
    479  * If prdesc is set, the text is printed to stdout.
    480  */
    481 {
    482 
    483 	getkeystring(Kdesc);
    484         if (prdesc)
    485                 printstring();  /*echo string*/
    486         else    readstring();   /*skip string*/
    487 }
    488 
    489 
    490 
    491 
    492 
    493 
    494 	static char const *
    495 getkeyval(keyword, token, optional)
    496 	char const *keyword;
    497 	enum tokens token;
    498 	int optional;
    499 /* reads a pair of the form
    500  * <keyword> <token> ;
    501  * where token is one of <id> or <num>. optional indicates whether
    502  * <token> is optional. A pointer to
    503  * the actual character string of <id> or <num> is returned.
    504  */
    505 {
    506 	register char const *val = 0;
    507 
    508 	getkey(keyword);
    509         if (nexttok==token) {
    510                 val = NextString;
    511                 nextlex();
    512         } else {
    513 		if (!optional)
    514 			fatserror("missing %s", keyword);
    515         }
    516 	getsemi(keyword);
    517         return(val);
    518 }
    519 
    520 
    521 	void
    522 unexpected_EOF()
    523 {
    524 	rcsfaterror("unexpected EOF in diff output");
    525 }
    526 
    527 	void
    528 initdiffcmd(dc)
    529 	register struct diffcmd *dc;
    530 /* Initialize *dc suitably for getdiffcmd(). */
    531 {
    532 	dc->adprev = 0;
    533 	dc->dafter = 0;
    534 }
    535 
    536 	static void
    537 badDiffOutput(buf)
    538 	char const *buf;
    539 {
    540 	rcsfaterror("bad diff output line: %s", buf);
    541 }
    542 
    543 	static void
    544 diffLineNumberTooLarge(buf)
    545 	char const *buf;
    546 {
    547 	rcsfaterror("diff line number too large: %s", buf);
    548 }
    549 
    550 	int
    551 getdiffcmd(finfile, delimiter, foutfile, dc)
    552 	RILE *finfile;
    553 	FILE *foutfile;
    554 	int delimiter;
    555 	struct diffcmd *dc;
    556 /* Get a editing command output by 'diff -n' from fin.
    557  * The input is delimited by SDELIM if delimiter is set, EOF otherwise.
    558  * Copy a clean version of the command to fout (if nonnull).
    559  * Yield 0 for 'd', 1 for 'a', and -1 for EOF.
    560  * Store the command's line number and length into dc->line1 and dc->nlines.
    561  * Keep dc->adprev and dc->dafter up to date.
    562  */
    563 {
    564 	register int c;
    565 	declarecache;
    566 	register FILE *fout;
    567 	register char *p;
    568 	register RILE *fin;
    569 	long line1, nlines, t;
    570 	char buf[BUFSIZ];
    571 
    572 	fin = finfile;
    573 	fout = foutfile;
    574 	setupcache(fin); cache(fin);
    575 	cachegeteof_(c, { if (delimiter) unexpected_EOF(); return -1; } )
    576 	if (delimiter) {
    577 		if (c==SDELIM) {
    578 			cacheget_(c)
    579 			if (c==SDELIM) {
    580 				buf[0] = c;
    581 				buf[1] = 0;
    582 				badDiffOutput(buf);
    583 			}
    584 			uncache(fin);
    585 			nextc = c;
    586 			if (fout)
    587 				aprintf(fout, "%c%c", SDELIM, c);
    588 			return -1;
    589 		}
    590 	}
    591 	p = buf;
    592 	do {
    593 		if (buf+BUFSIZ-2 <= p) {
    594 			rcsfaterror("diff output command line too long");
    595 		}
    596 		*p++ = c;
    597 		cachegeteof_(c, unexpected_EOF();)
    598 	} while (c != '\n');
    599 	uncache(fin);
    600 	if (delimiter)
    601 		++rcsline;
    602 	*p = '\0';
    603 	for (p = buf+1;  (c = *p++) == ' ';  )
    604 		continue;
    605 	line1 = 0;
    606 	while (isdigit(c)) {
    607 		if (
    608 			LONG_MAX/10 < line1  ||
    609 			(t = line1 * 10,   (line1 = t + (c - '0'))  <  t)
    610 		)
    611 			diffLineNumberTooLarge(buf);
    612 		c = *p++;
    613 	}
    614 	while (c == ' ')
    615 		c = *p++;
    616 	nlines = 0;
    617 	while (isdigit(c)) {
    618 		if (
    619 			LONG_MAX/10 < nlines  ||
    620 			(t = nlines * 10,   (nlines = t + (c - '0'))  <  t)
    621 		)
    622 			diffLineNumberTooLarge(buf);
    623 		c = *p++;
    624 	}
    625 	if (c == '\r')
    626 		c = *p++;
    627 	if (c || !nlines) {
    628 		badDiffOutput(buf);
    629 	}
    630 	if (line1+nlines < line1)
    631 		diffLineNumberTooLarge(buf);
    632 	switch (buf[0]) {
    633 	    case 'a':
    634 		if (line1 < dc->adprev) {
    635 		    rcsfaterror("backward insertion in diff output: %s", buf);
    636 		}
    637 		dc->adprev = line1 + 1;
    638 		break;
    639 	    case 'd':
    640 		if (line1 < dc->adprev  ||  line1 < dc->dafter) {
    641 		    rcsfaterror("backward deletion in diff output: %s", buf);
    642 		}
    643 		dc->adprev = line1;
    644 		dc->dafter = line1 + nlines;
    645 		break;
    646 	    default:
    647 		badDiffOutput(buf);
    648 	}
    649 	if (fout) {
    650 		aprintf(fout, "%s\n", buf);
    651 	}
    652 	dc->line1 = line1;
    653 	dc->nlines = nlines;
    654 	return buf[0] == 'a';
    655 }
    656 
    657 
    658 
    659 #ifdef SYNTEST
    660 
    661 /* Input an RCS file and print its internal data structures.  */
    662 
    663 char const cmdid[] = "syntest";
    664 
    665 	int
    666 main(argc,argv)
    667 int argc; char * argv[];
    668 {
    669 
    670         if (argc<2) {
    671 		aputs("No input file\n",stderr);
    672 		exitmain(EXIT_FAILURE);
    673         }
    674 	if (!(finptr = Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
    675 		faterror("can't open input file %s", argv[1]);
    676         }
    677         Lexinit();
    678         getadmin();
    679 	fdlock = STDOUT_FILENO;
    680 	putadmin();
    681 
    682         gettree();
    683 
    684         getdesc(true);
    685 
    686 	nextlex();
    687 
    688 	if (!eoflex()) {
    689 		fatserror("expecting EOF");
    690         }
    691 	exitmain(EXIT_SUCCESS);
    692 }
    693 
    694 void exiterr() { _exit(EXIT_FAILURE); }
    695 
    696 #endif
    697