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.4 2007/01/21 17:58:42 pavalos 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.4 2007/01/21 17:58:42 pavalos 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 Delta->lockedby = 0; 437 Delta->log.string = 0; 438 Delta->selector = true; 439 440 if (getkeyopt(Kcommitid)) { 441 Delta->commitid = NextString; 442 nextlex(); 443 getsemi(Kcommitid); 444 } else { 445 Delta->commitid = NULL; 446 } 447 448 Delta->ig = getphrases(Kdesc); 449 TotalDeltas++; 450 return (true); 451 } 452 453 454 void 455 gettree() 456 /* Function: Reads in the delta tree with getdelta(), then 457 * updates the lockedby fields. 458 */ 459 { 460 struct rcslock const *currlock; 461 462 while (getdelta()) 463 continue; 464 currlock=Locks; 465 while (currlock) { 466 currlock->delta->lockedby = currlock->login; 467 currlock = currlock->nextlock; 468 } 469 } 470 471 472 void 473 getdesc(prdesc) 474 int prdesc; 475 /* Function: read in descriptive text 476 * nexttok is not advanced afterwards. 477 * If prdesc is set, the text is printed to stdout. 478 */ 479 { 480 481 getkeystring(Kdesc); 482 if (prdesc) 483 printstring(); /*echo string*/ 484 else readstring(); /*skip string*/ 485 } 486 487 488 489 490 491 492 static char const * 493 getkeyval(keyword, token, optional) 494 char const *keyword; 495 enum tokens token; 496 int optional; 497 /* reads a pair of the form 498 * <keyword> <token> ; 499 * where token is one of <id> or <num>. optional indicates whether 500 * <token> is optional. A pointer to 501 * the actual character string of <id> or <num> is returned. 502 */ 503 { 504 register char const *val = 0; 505 506 getkey(keyword); 507 if (nexttok==token) { 508 val = NextString; 509 nextlex(); 510 } else { 511 if (!optional) 512 fatserror("missing %s", keyword); 513 } 514 getsemi(keyword); 515 return(val); 516 } 517 518 519 void 520 unexpected_EOF() 521 { 522 rcsfaterror("unexpected EOF in diff output"); 523 } 524 525 void 526 initdiffcmd(dc) 527 register struct diffcmd *dc; 528 /* Initialize *dc suitably for getdiffcmd(). */ 529 { 530 dc->adprev = 0; 531 dc->dafter = 0; 532 } 533 534 static void 535 badDiffOutput(buf) 536 char const *buf; 537 { 538 rcsfaterror("bad diff output line: %s", buf); 539 } 540 541 static void 542 diffLineNumberTooLarge(buf) 543 char const *buf; 544 { 545 rcsfaterror("diff line number too large: %s", buf); 546 } 547 548 int 549 getdiffcmd(finfile, delimiter, foutfile, dc) 550 RILE *finfile; 551 FILE *foutfile; 552 int delimiter; 553 struct diffcmd *dc; 554 /* Get a editing command output by 'diff -n' from fin. 555 * The input is delimited by SDELIM if delimiter is set, EOF otherwise. 556 * Copy a clean version of the command to fout (if nonnull). 557 * Yield 0 for 'd', 1 for 'a', and -1 for EOF. 558 * Store the command's line number and length into dc->line1 and dc->nlines. 559 * Keep dc->adprev and dc->dafter up to date. 560 */ 561 { 562 register int c; 563 declarecache; 564 register FILE *fout; 565 register char *p; 566 register RILE *fin; 567 long line1, nlines, t; 568 char buf[BUFSIZ]; 569 570 fin = finfile; 571 fout = foutfile; 572 setupcache(fin); cache(fin); 573 cachegeteof_(c, { if (delimiter) unexpected_EOF(); return -1; } ) 574 if (delimiter) { 575 if (c==SDELIM) { 576 cacheget_(c) 577 if (c==SDELIM) { 578 buf[0] = c; 579 buf[1] = 0; 580 badDiffOutput(buf); 581 } 582 uncache(fin); 583 nextc = c; 584 if (fout) 585 aprintf(fout, "%c%c", SDELIM, c); 586 return -1; 587 } 588 } 589 p = buf; 590 do { 591 if (buf+BUFSIZ-2 <= p) { 592 rcsfaterror("diff output command line too long"); 593 } 594 *p++ = c; 595 cachegeteof_(c, unexpected_EOF();) 596 } while (c != '\n'); 597 uncache(fin); 598 if (delimiter) 599 ++rcsline; 600 *p = '\0'; 601 for (p = buf+1; (c = *p++) == ' '; ) 602 continue; 603 line1 = 0; 604 while (isdigit(c)) { 605 if ( 606 LONG_MAX/10 < line1 || 607 (t = line1 * 10, (line1 = t + (c - '0')) < t) 608 ) 609 diffLineNumberTooLarge(buf); 610 c = *p++; 611 } 612 while (c == ' ') 613 c = *p++; 614 nlines = 0; 615 while (isdigit(c)) { 616 if ( 617 LONG_MAX/10 < nlines || 618 (t = nlines * 10, (nlines = t + (c - '0')) < t) 619 ) 620 diffLineNumberTooLarge(buf); 621 c = *p++; 622 } 623 if (c == '\r') 624 c = *p++; 625 if (c || !nlines) { 626 badDiffOutput(buf); 627 } 628 if (line1+nlines < line1) 629 diffLineNumberTooLarge(buf); 630 switch (buf[0]) { 631 case 'a': 632 if (line1 < dc->adprev) { 633 rcsfaterror("backward insertion in diff output: %s", buf); 634 } 635 dc->adprev = line1 + 1; 636 break; 637 case 'd': 638 if (line1 < dc->adprev || line1 < dc->dafter) { 639 rcsfaterror("backward deletion in diff output: %s", buf); 640 } 641 dc->adprev = line1; 642 dc->dafter = line1 + nlines; 643 break; 644 default: 645 badDiffOutput(buf); 646 } 647 if (fout) { 648 aprintf(fout, "%s\n", buf); 649 } 650 dc->line1 = line1; 651 dc->nlines = nlines; 652 return buf[0] == 'a'; 653 } 654 655 656 657 #ifdef SYNTEST 658 659 /* Input an RCS file and print its internal data structures. */ 660 661 char const cmdid[] = "syntest"; 662 663 int 664 main(argc,argv) 665 int argc; char * argv[]; 666 { 667 668 if (argc<2) { 669 aputs("No input file\n",stderr); 670 exitmain(EXIT_FAILURE); 671 } 672 if (!(finptr = Iopen(argv[1], FOPEN_R, (struct stat*)0))) { 673 faterror("can't open input file %s", argv[1]); 674 } 675 Lexinit(); 676 getadmin(); 677 fdlock = STDOUT_FILENO; 678 putadmin(); 679 680 gettree(); 681 682 getdesc(true); 683 684 nextlex(); 685 686 if (!eoflex()) { 687 fatserror("expecting EOF"); 688 } 689 exitmain(EXIT_SUCCESS); 690 } 691 692 void exiterr() { _exit(EXIT_FAILURE); } 693 694 #endif 695