1 /* RCS file syntactic analysis */ 2 3 /****************************************************************************** 4 * Syntax Analysis. 5 * Keyword table 6 * Testprogram: define SYNTEST 7 * Compatibility with Release 2: define COMPAT2=1 8 ****************************************************************************** 9 */ 10 11 /* Copyright 1982, 1988, 1989 Walter Tichy 12 Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert 13 Distributed under license by the Free Software Foundation, Inc. 14 15 This file is part of RCS. 16 17 RCS is free software; you can redistribute it and/or modify 18 it under the terms of the GNU General Public License as published by 19 the Free Software Foundation; either version 2, or (at your option) 20 any later version. 21 22 RCS is distributed in the hope that it will be useful, 23 but WITHOUT ANY WARRANTY; without even the implied warranty of 24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 GNU General Public License for more details. 26 27 You should have received a copy of the GNU General Public License 28 along with RCS; see the file COPYING. 29 If not, write to the Free Software Foundation, 30 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 31 32 Report problems and direct all questions to: 33 34 rcs-bugs@cs.purdue.edu 35 36 */ 37 38 /* 39 * $FreeBSD: src/gnu/usr.bin/rcs/lib/rcssyn.c,v 1.7 1999/08/27 23:36:48 peter Exp $ 40 * $DragonFly: src/gnu/usr.bin/rcs/lib/rcssyn.c,v 1.2 2003/06/17 04:25:47 dillon Exp $ 41 * 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, "$DragonFly: src/gnu/usr.bin/rcs/lib/rcssyn.c,v 1.2 2003/06/17 04:25:47 dillon 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 Kdate[] = "date", 177 Kdesc[] = "desc", 178 Kexpand[] = "expand", 179 Khead[] = "head", 180 Klocks[] = "locks", 181 Klog[] = "log", 182 Knext[] = "next", 183 Kstate[] = "state", 184 Kstrict[] = "strict", 185 Ksymbols[] = "symbols", 186 Ktext[] = "text"; 187 188 static char const 189 #if COMPAT2 190 Ksuffix[] = "suffix", 191 #endif 192 K_branches[]= "branches"; 193 194 static struct buf Commleader; 195 struct cbuf Comment; 196 struct cbuf Ignored; 197 struct access * AccessList; 198 struct assoc * Symbols; 199 struct rcslock *Locks; 200 int Expand; 201 int StrictLocks; 202 struct hshentry * Head; 203 char const * Dbranch; 204 int TotalDeltas; 205 206 207 static void 208 getsemi(key) 209 char const *key; 210 /* Get a semicolon to finish off a phrase started by KEY. */ 211 { 212 if (!getlex(SEMI)) 213 fatserror("missing ';' after '%s'", key); 214 } 215 216 static struct hshentry * 217 getdnum() 218 /* Get a delta number. */ 219 { 220 register struct hshentry *delta = getnum(); 221 if (delta && countnumflds(delta->num)&1) 222 fatserror("%s isn't a delta number", delta->num); 223 return delta; 224 } 225 226 227 void 228 getadmin() 229 /* Read an <admin> and initialize the appropriate global variables. */ 230 { 231 register char const *id; 232 struct access * newaccess; 233 struct assoc * newassoc; 234 struct rcslock *newlock; 235 struct hshentry * delta; 236 struct access **LastAccess; 237 struct assoc **LastSymbol; 238 struct rcslock **LastLock; 239 struct buf b; 240 struct cbuf cb; 241 242 TotalDeltas=0; 243 244 getkey(Khead); 245 Head = getdnum(); 246 getsemi(Khead); 247 248 Dbranch = 0; 249 if (getkeyopt(Kbranch)) { 250 if ((delta = getnum())) 251 Dbranch = delta->num; 252 getsemi(Kbranch); 253 } 254 255 256 #if COMPAT2 257 /* read suffix. Only in release 2 format */ 258 if (getkeyopt(Ksuffix)) { 259 if (nexttok==STRING) { 260 readstring(); nextlex(); /* Throw away the suffix. */ 261 } else if (nexttok==ID) { 262 nextlex(); 263 } 264 getsemi(Ksuffix); 265 } 266 #endif 267 268 getkey(Kaccess); 269 LastAccess = &AccessList; 270 while ((id = getid())) { 271 newaccess = ftalloc(struct access); 272 newaccess->login = id; 273 *LastAccess = newaccess; 274 LastAccess = &newaccess->nextaccess; 275 } 276 *LastAccess = 0; 277 getsemi(Kaccess); 278 279 getkey(Ksymbols); 280 LastSymbol = &Symbols; 281 while ((id = getid())) { 282 if (!getlex(COLON)) 283 fatserror("missing ':' in symbolic name definition"); 284 if (!(delta=getnum())) { 285 fatserror("missing number in symbolic name definition"); 286 } else { /*add new pair to association list*/ 287 newassoc = ftalloc(struct assoc); 288 newassoc->symbol=id; 289 newassoc->num = delta->num; 290 *LastSymbol = newassoc; 291 LastSymbol = &newassoc->nextassoc; 292 } 293 } 294 *LastSymbol = 0; 295 getsemi(Ksymbols); 296 297 getkey(Klocks); 298 LastLock = &Locks; 299 while ((id = getid())) { 300 if (!getlex(COLON)) 301 fatserror("missing ':' in lock"); 302 if (!(delta=getdnum())) { 303 fatserror("missing number in lock"); 304 } else { /*add new pair to lock list*/ 305 newlock = ftalloc(struct rcslock); 306 newlock->login=id; 307 newlock->delta=delta; 308 *LastLock = newlock; 309 LastLock = &newlock->nextlock; 310 } 311 } 312 *LastLock = 0; 313 getsemi(Klocks); 314 315 if ((StrictLocks = getkeyopt(Kstrict))) 316 getsemi(Kstrict); 317 318 clear_buf(&Comment); 319 if (getkeyopt(Kcomment)) { 320 if (nexttok==STRING) { 321 Comment = savestring(&Commleader); 322 nextlex(); 323 } 324 getsemi(Kcomment); 325 } 326 327 Expand = KEYVAL_EXPAND; 328 if (getkeyopt(Kexpand)) { 329 if (nexttok==STRING) { 330 bufautobegin(&b); 331 cb = savestring(&b); 332 if ((Expand = strn2expmode(cb.string,cb.size)) < 0) 333 fatserror("unknown expand mode %.*s", 334 (int)cb.size, cb.string 335 ); 336 bufautoend(&b); 337 nextlex(); 338 } 339 getsemi(Kexpand); 340 } 341 Ignored = getphrases(Kdesc); 342 } 343 344 char const *const expand_names[] = { 345 /* These must agree with *_EXPAND in rcsbase.h. */ 346 "kv", "kvl", "k", "v", "o", "b", 347 0 348 }; 349 350 int 351 str2expmode(s) 352 char const *s; 353 /* Yield expand mode corresponding to S, or -1 if bad. */ 354 { 355 return strn2expmode(s, strlen(s)); 356 } 357 358 static int 359 strn2expmode(s, n) 360 char const *s; 361 size_t n; 362 { 363 char const *const *p; 364 365 for (p = expand_names; *p; ++p) 366 if (memcmp(*p,s,n) == 0 && !(*p)[n]) 367 return p - expand_names; 368 return -1; 369 } 370 371 372 void 373 ignorephrases(key) 374 const char *key; 375 /* 376 * Ignore a series of phrases that do not start with KEY. 377 * Stop when the next phrase starts with a token that is not an identifier, 378 * or is KEY. 379 */ 380 { 381 for (;;) { 382 nextlex(); 383 if (nexttok != ID || strcmp(NextString,key) == 0) 384 break; 385 warnignore(); 386 hshenter=false; 387 for (;; nextlex()) { 388 switch (nexttok) { 389 case SEMI: hshenter=true; break; 390 case ID: 391 case NUM: ffree1(NextString); continue; 392 case STRING: readstring(); continue; 393 default: continue; 394 } 395 break; 396 } 397 } 398 } 399 400 401 static int 402 getdelta() 403 /* Function: reads a delta block. 404 * returns false if the current block does not start with a number. 405 */ 406 { 407 register struct hshentry * Delta, * num; 408 struct branchhead **LastBranch, *NewBranch; 409 410 if (!(Delta = getdnum())) 411 return false; 412 413 hshenter = false; /*Don't enter dates into hashtable*/ 414 Delta->date = getkeyval(Kdate, NUM, false); 415 hshenter=true; /*reset hshenter for revision numbers.*/ 416 417 Delta->author = getkeyval(Kauthor, ID, false); 418 419 Delta->state = getkeyval(Kstate, ID, true); 420 421 getkey(K_branches); 422 LastBranch = &Delta->branches; 423 while ((num = getdnum())) { 424 NewBranch = ftalloc(struct branchhead); 425 NewBranch->hsh = num; 426 *LastBranch = NewBranch; 427 LastBranch = &NewBranch->nextbranch; 428 } 429 *LastBranch = 0; 430 getsemi(K_branches); 431 432 getkey(Knext); 433 Delta->next = num = getdnum(); 434 getsemi(Knext); 435 Delta->lockedby = 0; 436 Delta->log.string = 0; 437 Delta->selector = true; 438 Delta->ig = getphrases(Kdesc); 439 TotalDeltas++; 440 return (true); 441 } 442 443 444 void 445 gettree() 446 /* Function: Reads in the delta tree with getdelta(), then 447 * updates the lockedby fields. 448 */ 449 { 450 struct rcslock const *currlock; 451 452 while (getdelta()) 453 continue; 454 currlock=Locks; 455 while (currlock) { 456 currlock->delta->lockedby = currlock->login; 457 currlock = currlock->nextlock; 458 } 459 } 460 461 462 void 463 getdesc(prdesc) 464 int prdesc; 465 /* Function: read in descriptive text 466 * nexttok is not advanced afterwards. 467 * If prdesc is set, the text is printed to stdout. 468 */ 469 { 470 471 getkeystring(Kdesc); 472 if (prdesc) 473 printstring(); /*echo string*/ 474 else readstring(); /*skip string*/ 475 } 476 477 478 479 480 481 482 static char const * 483 getkeyval(keyword, token, optional) 484 char const *keyword; 485 enum tokens token; 486 int optional; 487 /* reads a pair of the form 488 * <keyword> <token> ; 489 * where token is one of <id> or <num>. optional indicates whether 490 * <token> is optional. A pointer to 491 * the actual character string of <id> or <num> is returned. 492 */ 493 { 494 register char const *val = 0; 495 496 getkey(keyword); 497 if (nexttok==token) { 498 val = NextString; 499 nextlex(); 500 } else { 501 if (!optional) 502 fatserror("missing %s", keyword); 503 } 504 getsemi(keyword); 505 return(val); 506 } 507 508 509 void 510 unexpected_EOF() 511 { 512 rcsfaterror("unexpected EOF in diff output"); 513 } 514 515 void 516 initdiffcmd(dc) 517 register struct diffcmd *dc; 518 /* Initialize *dc suitably for getdiffcmd(). */ 519 { 520 dc->adprev = 0; 521 dc->dafter = 0; 522 } 523 524 static void 525 badDiffOutput(buf) 526 char const *buf; 527 { 528 rcsfaterror("bad diff output line: %s", buf); 529 } 530 531 static void 532 diffLineNumberTooLarge(buf) 533 char const *buf; 534 { 535 rcsfaterror("diff line number too large: %s", buf); 536 } 537 538 int 539 getdiffcmd(finfile, delimiter, foutfile, dc) 540 RILE *finfile; 541 FILE *foutfile; 542 int delimiter; 543 struct diffcmd *dc; 544 /* Get a editing command output by 'diff -n' from fin. 545 * The input is delimited by SDELIM if delimiter is set, EOF otherwise. 546 * Copy a clean version of the command to fout (if nonnull). 547 * Yield 0 for 'd', 1 for 'a', and -1 for EOF. 548 * Store the command's line number and length into dc->line1 and dc->nlines. 549 * Keep dc->adprev and dc->dafter up to date. 550 */ 551 { 552 register int c; 553 declarecache; 554 register FILE *fout; 555 register char *p; 556 register RILE *fin; 557 long line1, nlines, t; 558 char buf[BUFSIZ]; 559 560 fin = finfile; 561 fout = foutfile; 562 setupcache(fin); cache(fin); 563 cachegeteof_(c, { if (delimiter) unexpected_EOF(); return -1; } ) 564 if (delimiter) { 565 if (c==SDELIM) { 566 cacheget_(c) 567 if (c==SDELIM) { 568 buf[0] = c; 569 buf[1] = 0; 570 badDiffOutput(buf); 571 } 572 uncache(fin); 573 nextc = c; 574 if (fout) 575 aprintf(fout, "%c%c", SDELIM, c); 576 return -1; 577 } 578 } 579 p = buf; 580 do { 581 if (buf+BUFSIZ-2 <= p) { 582 rcsfaterror("diff output command line too long"); 583 } 584 *p++ = c; 585 cachegeteof_(c, unexpected_EOF();) 586 } while (c != '\n'); 587 uncache(fin); 588 if (delimiter) 589 ++rcsline; 590 *p = '\0'; 591 for (p = buf+1; (c = *p++) == ' '; ) 592 continue; 593 line1 = 0; 594 while (isdigit(c)) { 595 if ( 596 LONG_MAX/10 < line1 || 597 (t = line1 * 10, (line1 = t + (c - '0')) < t) 598 ) 599 diffLineNumberTooLarge(buf); 600 c = *p++; 601 } 602 while (c == ' ') 603 c = *p++; 604 nlines = 0; 605 while (isdigit(c)) { 606 if ( 607 LONG_MAX/10 < nlines || 608 (t = nlines * 10, (nlines = t + (c - '0')) < t) 609 ) 610 diffLineNumberTooLarge(buf); 611 c = *p++; 612 } 613 if (c == '\r') 614 c = *p++; 615 if (c || !nlines) { 616 badDiffOutput(buf); 617 } 618 if (line1+nlines < line1) 619 diffLineNumberTooLarge(buf); 620 switch (buf[0]) { 621 case 'a': 622 if (line1 < dc->adprev) { 623 rcsfaterror("backward insertion in diff output: %s", buf); 624 } 625 dc->adprev = line1 + 1; 626 break; 627 case 'd': 628 if (line1 < dc->adprev || line1 < dc->dafter) { 629 rcsfaterror("backward deletion in diff output: %s", buf); 630 } 631 dc->adprev = line1; 632 dc->dafter = line1 + nlines; 633 break; 634 default: 635 badDiffOutput(buf); 636 } 637 if (fout) { 638 aprintf(fout, "%s\n", buf); 639 } 640 dc->line1 = line1; 641 dc->nlines = nlines; 642 return buf[0] == 'a'; 643 } 644 645 646 647 #ifdef SYNTEST 648 649 /* Input an RCS file and print its internal data structures. */ 650 651 char const cmdid[] = "syntest"; 652 653 int 654 main(argc,argv) 655 int argc; char * argv[]; 656 { 657 658 if (argc<2) { 659 aputs("No input file\n",stderr); 660 exitmain(EXIT_FAILURE); 661 } 662 if (!(finptr = Iopen(argv[1], FOPEN_R, (struct stat*)0))) { 663 faterror("can't open input file %s", argv[1]); 664 } 665 Lexinit(); 666 getadmin(); 667 fdlock = STDOUT_FILENO; 668 putadmin(); 669 670 gettree(); 671 672 getdesc(true); 673 674 nextlex(); 675 676 if (!eoflex()) { 677 fatserror("expecting EOF"); 678 } 679 exitmain(EXIT_SUCCESS); 680 } 681 682 void exiterr() { _exit(EXIT_FAILURE); } 683 684 #endif 685