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