1/* lexical analysis of RCS files */ 2 3/****************************************************************************** 4 * Lexical Analysis. 5 * hashtable, Lexinit, nextlex, getlex, getkey, 6 * getid, getnum, readstring, printstring, savestring, 7 * checkid, fatserror, error, faterror, warn, diagnose 8 * Testprogram: define LEXDB 9 ****************************************************************************** 10 */ 11 12/* Copyright 1982, 1988, 1989 Walter Tichy 13 Copyright 1990, 1991, 1992, 1993, 1994, 1995 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. 30If not, write to the Free Software Foundation, 3159 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 32 33Report problems and direct all questions to: 34 35 rcs-bugs@cs.purdue.edu 36 37*/ 38 39 40 41/* 42 * $Log: rcslex.c,v $ 43 * Revision 1.1 2003/06/11 15:56:10 darkwyrm 44 * Added rcs, gzip, sed, and associated utilities. 45 * 46 * Revision 5.19 1995/06/16 06:19:24 eggert 47 * Update FSF address. 48 * 49 * Revision 5.18 1995/06/01 16:23:43 eggert 50 * (map_fd_deallocate,mmap_deallocate,read_deallocate,nothing_to_deallocate): 51 * New functions. 52 * (Iclose): If large_memory and maps_memory, use them to deallocate mapping. 53 * (fd2RILE): Use map_fd if available. 54 * If one mapping method fails, try the next instead of giving up; 55 * if they all fail, fall back on ordinary read. 56 * Work around bug: root mmap over NFS succeeds, but accessing dumps core. 57 * Use MAP_FAILED macro for mmap failure, and `char *' instead of caddr_t. 58 * (advise_access): Use madvise only if this instance used mmap. 59 * (Iopen): Use fdSafer to get safer file descriptor. 60 * (aflush): Moved here from rcsedit.c. 61 * 62 * Revision 5.17 1994/03/20 04:52:58 eggert 63 * Don't worry if madvise fails. Add Orewind. Remove lint. 64 * 65 * Revision 5.16 1993/11/09 17:55:29 eggert 66 * Fix `label: }' typo. 67 * 68 * Revision 5.15 1993/11/03 17:42:27 eggert 69 * Improve quality of diagnostics by putting file names in them more often. 70 * Don't discard ignored phrases. 71 * 72 * Revision 5.14 1992/07/28 16:12:44 eggert 73 * Identifiers may now start with a digit and (unless they are symbolic names) 74 * may contain `.'. Avoid `unsigned'. Statement macro names now end in _. 75 * 76 * Revision 5.13 1992/02/17 23:02:27 eggert 77 * Work around NFS mmap SIGBUS problem. 78 * 79 * Revision 5.12 1992/01/06 02:42:34 eggert 80 * Use OPEN_O_BINARY if mode contains 'b'. 81 * 82 * Revision 5.11 1991/11/03 03:30:44 eggert 83 * Fix porting bug to ancient hosts lacking vfprintf. 84 * 85 * Revision 5.10 1991/10/07 17:32:46 eggert 86 * Support piece tables even if !has_mmap. 87 * 88 * Revision 5.9 1991/09/24 00:28:42 eggert 89 * Don't export errsay(). 90 * 91 * Revision 5.8 1991/08/19 03:13:55 eggert 92 * Add eoflex(), mmap support. Tune. 93 * 94 * Revision 5.7 1991/04/21 11:58:26 eggert 95 * Add MS-DOS support. 96 * 97 * Revision 5.6 1991/02/25 07:12:42 eggert 98 * Work around fputs bug. strsave -> str_save (DG/UX name clash) 99 * 100 * Revision 5.5 1990/12/04 05:18:47 eggert 101 * Use -I for prompts and -q for diagnostics. 102 * 103 * Revision 5.4 1990/11/19 20:05:28 hammer 104 * no longer gives warning about unknown keywords if -q is specified 105 * 106 * Revision 5.3 1990/11/01 05:03:48 eggert 107 * When ignoring unknown phrases, copy them to the output RCS file. 108 * 109 * Revision 5.2 1990/09/04 08:02:27 eggert 110 * Count RCS lines better. 111 * 112 * Revision 5.1 1990/08/29 07:14:03 eggert 113 * Work around buggy compilers with defective argument promotion. 114 * 115 * Revision 5.0 1990/08/22 08:12:55 eggert 116 * Remove compile-time limits; use malloc instead. 117 * Report errno-related errors with perror(). 118 * Ansify and Posixate. Add support for ISO 8859. 119 * Use better hash function. 120 * 121 * Revision 4.6 89/05/01 15:13:07 narten 122 * changed copyright header to reflect current distribution rules 123 * 124 * Revision 4.5 88/08/28 15:01:12 eggert 125 * Don't loop when writing error messages to a full filesystem. 126 * Flush stderr/stdout when mixing output. 127 * Yield exit status compatible with diff(1). 128 * Shrink stdio code size; allow cc -R; remove lint. 129 * 130 * Revision 4.4 87/12/18 11:44:47 narten 131 * fixed to use "varargs" in "fprintf"; this is required if it is to 132 * work on a SPARC machine such as a Sun-4 133 * 134 * Revision 4.3 87/10/18 10:37:18 narten 135 * Updating version numbers. Changes relative to 1.1 actually relative 136 * to version 4.1 137 * 138 * Revision 1.3 87/09/24 14:00:17 narten 139 * Sources now pass through lint (if you ignore printf/sprintf/fprintf 140 * warnings) 141 * 142 * Revision 1.2 87/03/27 14:22:33 jenkins 143 * Port to suns 144 * 145 * Revision 4.1 83/03/25 18:12:51 wft 146 * Only changed $Header to $Id. 147 * 148 * Revision 3.3 82/12/10 16:22:37 wft 149 * Improved error messages, changed exit status on error to 1. 150 * 151 * Revision 3.2 82/11/28 21:27:10 wft 152 * Renamed ctab to map and included EOFILE; ctab is now a macro in rcsbase.h. 153 * Added fflsbuf(), fputs(), and fprintf(), which abort the RCS operations 154 * properly in case there is an IO-error (e.g., file system full). 155 * 156 * Revision 3.1 82/10/11 19:43:56 wft 157 * removed unused label out:; 158 * made sure all calls to getc() return into an integer, not a char. 159 */ 160 161 162/* 163#define LEXDB 164*/ 165/* version LEXDB is for testing the lexical analyzer. The testprogram 166 * reads a stream of lexemes, enters the revision numbers into the 167 * hashtable, and prints the recognized tokens. Keywords are recognized 168 * as identifiers. 169 */ 170 171 172 173#include "rcsbase.h" 174 175libId(lexId, "$Id: rcslex.c 3476 2003-06-11 15:56:10Z darkwyrm $") 176 177static char *checkidentifier P((char*,int,int)); 178static void errsay P((char const*)); 179static void fatsay P((char const*)); 180static void lookup P((char const*)); 181static void startsay P((const char*,const char*)); 182static void warnsay P((char const*)); 183 184static struct hshentry *nexthsh; /*pointer to next hash entry, set by lookup*/ 185 186enum tokens nexttok; /*next token, set by nextlex */ 187 188int hshenter; /*if true, next suitable lexeme will be entered */ 189 /*into the symbol table. Handle with care. */ 190int nextc; /*next input character, initialized by Lexinit */ 191 192long rcsline; /*current line-number of input */ 193int nerror; /*counter for errors */ 194int quietflag; /*indicates quiet mode */ 195RILE * finptr; /*input file descriptor */ 196 197FILE * frewrite; /*file descriptor for echoing input */ 198 199FILE * foutptr; /* copy of frewrite, but 0 to suppress echo */ 200 201static struct buf tokbuf; /* token buffer */ 202 203char const * NextString; /* next token */ 204 205/* 206 * Our hash algorithm is h[0] = 0, h[i+1] = 4*h[i] + c, 207 * so hshsize should be odd. 208 * See B J McKenzie, R Harries & T Bell, Selecting a hashing algorithm, 209 * Software--practice & experience 20, 2 (Feb 1990), 209-224. 210 */ 211#ifndef hshsize 212# define hshsize 511 213#endif 214 215static struct hshentry *hshtab[hshsize]; /*hashtable */ 216 217static int ignored_phrases; /* have we ignored phrases in this RCS file? */ 218 219 void 220warnignore() 221{ 222 if (!ignored_phrases) { 223 ignored_phrases = true; 224 rcswarn("Unknown phrases like `%s ...;' are present.", NextString); 225 } 226} 227 228 229 230 static void 231lookup(str) 232 char const *str; 233/* Function: Looks up the character string pointed to by str in the 234 * hashtable. If the string is not present, a new entry for it is created. 235 * In any case, the address of the corresponding hashtable entry is placed 236 * into nexthsh. 237 */ 238{ 239 register unsigned ihash; /* index into hashtable */ 240 register char const *sp; 241 register struct hshentry *n, **p; 242 243 /* calculate hash code */ 244 sp = str; 245 ihash = 0; 246 while (*sp) 247 ihash = (ihash<<2) + *sp++; 248 ihash %= hshsize; 249 250 for (p = &hshtab[ihash]; ; p = &n->nexthsh) 251 if (!(n = *p)) { 252 /* empty slot found */ 253 *p = n = ftalloc(struct hshentry); 254 n->num = fstr_save(str); 255 n->nexthsh = 0; 256# ifdef LEXDB 257 VOID printf("\nEntered: %s at %u ", str, ihash); 258# endif 259 break; 260 } else if (strcmp(str, n->num) == 0) 261 /* match found */ 262 break; 263 nexthsh = n; 264 NextString = n->num; 265} 266 267 268 269 270 271 272 void 273Lexinit() 274/* Function: Initialization of lexical analyzer: 275 * initializes the hashtable, 276 * initializes nextc, nexttok if finptr != 0 277 */ 278{ register int c; 279 280 for (c = hshsize; 0 <= --c; ) { 281 hshtab[c] = 0; 282 } 283 284 nerror = 0; 285 if (finptr) { 286 foutptr = 0; 287 hshenter = true; 288 ignored_phrases = false; 289 rcsline = 1; 290 bufrealloc(&tokbuf, 2); 291 Iget_(finptr, nextc) 292 nextlex(); /*initial token*/ 293 } 294} 295 296 297 298 299 300 301 302 void 303nextlex() 304 305/* Function: Reads the next token and sets nexttok to the next token code. 306 * Only if hshenter is set, a revision number is entered into the 307 * hashtable and a pointer to it is placed into nexthsh. 308 * This is useful for avoiding that dates are placed into the hashtable. 309 * For ID's and NUM's, NextString is set to the character string. 310 * Assumption: nextc contains the next character. 311 */ 312{ register c; 313 declarecache; 314 register FILE *frew; 315 register char * sp; 316 char const *limit; 317 register enum tokens d; 318 register RILE *fin; 319 320 fin=finptr; frew=foutptr; 321 setupcache(fin); cache(fin); 322 c = nextc; 323 324 for (;;) { switch ((d = ctab[c])) { 325 326 default: 327 fatserror("unknown character `%c'", c); 328 /*NOTREACHED*/ 329 330 case NEWLN: 331 ++rcsline; 332# ifdef LEXDB 333 afputc('\n',stdout); 334# endif 335 /* Note: falls into next case */ 336 337 case SPACE: 338 GETC_(frew, c) 339 continue; 340 341 case IDCHAR: 342 case LETTER: 343 case Letter: 344 d = ID; 345 /* fall into */ 346 case DIGIT: 347 case PERIOD: 348 sp = tokbuf.string; 349 limit = sp + tokbuf.size; 350 *sp++ = c; 351 for (;;) { 352 GETC_(frew, c) 353 switch (ctab[c]) { 354 case IDCHAR: 355 case LETTER: 356 case Letter: 357 d = ID; 358 /* fall into */ 359 case DIGIT: 360 case PERIOD: 361 *sp++ = c; 362 if (limit <= sp) 363 sp = bufenlarge(&tokbuf, &limit); 364 continue; 365 366 default: 367 break; 368 } 369 break; 370 } 371 *sp = 0; 372 if (d == DIGIT || d == PERIOD) { 373 d = NUM; 374 if (hshenter) { 375 lookup(tokbuf.string); 376 break; 377 } 378 } 379 NextString = fstr_save(tokbuf.string); 380 break; 381 382 case SBEGIN: /* long string */ 383 d = STRING; 384 /* note: only the initial SBEGIN has been read*/ 385 /* read the string, and reset nextc afterwards*/ 386 break; 387 388 case COLON: 389 case SEMI: 390 GETC_(frew, c) 391 break; 392 } break; } 393 nextc = c; 394 nexttok = d; 395 uncache(fin); 396} 397 398 int 399eoflex() 400/* 401 * Yield true if we look ahead to the end of the input, false otherwise. 402 * nextc becomes undefined at end of file. 403 */ 404{ 405 register int c; 406 declarecache; 407 register FILE *fout; 408 register RILE *fin; 409 410 c = nextc; 411 fin = finptr; 412 fout = foutptr; 413 setupcache(fin); cache(fin); 414 415 for (;;) { 416 switch (ctab[c]) { 417 default: 418 nextc = c; 419 uncache(fin); 420 return false; 421 422 case NEWLN: 423 ++rcsline; 424 /* fall into */ 425 case SPACE: 426 cachegeteof_(c, {uncache(fin);return true;}) 427 break; 428 } 429 if (fout) 430 aputc_(c, fout) 431 } 432} 433 434 435int getlex(token) 436enum tokens token; 437/* Function: Checks if nexttok is the same as token. If so, 438 * advances the input by calling nextlex and returns true. 439 * otherwise returns false. 440 * Doesn't work for strings and keywords; loses the character string for ids. 441 */ 442{ 443 if (nexttok==token) { 444 nextlex(); 445 return(true); 446 } else return(false); 447} 448 449 int 450getkeyopt(key) 451 char const *key; 452/* Function: If the current token is a keyword identical to key, 453 * advances the input by calling nextlex and returns true; 454 * otherwise returns false. 455 */ 456{ 457 if (nexttok==ID && strcmp(key,NextString) == 0) { 458 /* match found */ 459 ffree1(NextString); 460 nextlex(); 461 return(true); 462 } 463 return(false); 464} 465 466 void 467getkey(key) 468 char const *key; 469/* Check that the current input token is a keyword identical to key, 470 * and advance the input by calling nextlex. 471 */ 472{ 473 if (!getkeyopt(key)) 474 fatserror("missing '%s' keyword", key); 475} 476 477 void 478getkeystring(key) 479 char const *key; 480/* Check that the current input token is a keyword identical to key, 481 * and advance the input by calling nextlex; then look ahead for a string. 482 */ 483{ 484 getkey(key); 485 if (nexttok != STRING) 486 fatserror("missing string after '%s' keyword", key); 487} 488 489 490 char const * 491getid() 492/* Function: Checks if nexttok is an identifier. If so, 493 * advances the input by calling nextlex and returns a pointer 494 * to the identifier; otherwise returns 0. 495 * Treats keywords as identifiers. 496 */ 497{ 498 register char const *name; 499 if (nexttok==ID) { 500 name = NextString; 501 nextlex(); 502 return name; 503 } else 504 return 0; 505} 506 507 508struct hshentry * getnum() 509/* Function: Checks if nexttok is a number. If so, 510 * advances the input by calling nextlex and returns a pointer 511 * to the hashtable entry. Otherwise returns 0. 512 * Doesn't work if hshenter is false. 513 */ 514{ 515 register struct hshentry * num; 516 if (nexttok==NUM) { 517 num=nexthsh; 518 nextlex(); 519 return num; 520 } else 521 return 0; 522} 523 524 struct cbuf 525getphrases(key) 526 char const *key; 527/* 528* Get a series of phrases that do not start with KEY. Yield resulting buffer. 529* Stop when the next phrase starts with a token that is not an identifier, 530* or is KEY. Copy input to foutptr if it is set. Unlike ignorephrases(), 531* this routine assumes nextlex() has already been invoked before we start. 532*/ 533{ 534 declarecache; 535 register int c; 536 register char const *kn; 537 struct cbuf r; 538 register RILE *fin; 539 register FILE *frew; 540# if large_memory 541# define savech_(c) ; 542# else 543 register char *p; 544 char const *limit; 545 struct buf b; 546# define savech_(c) {if (limit<=p)p=bufenlarge(&b,&limit); *p++ =(c);} 547# endif 548 549 if (nexttok!=ID || strcmp(NextString,key) == 0) 550 clear_buf(&r); 551 else { 552 warnignore(); 553 fin = finptr; 554 frew = foutptr; 555 setupcache(fin); cache(fin); 556# if large_memory 557 r.string = (char const*)cacheptr() - strlen(NextString) - 1; 558# else 559 bufautobegin(&b); 560 bufscpy(&b, NextString); 561 p = b.string + strlen(b.string); 562 limit = b.string + b.size; 563# endif 564 ffree1(NextString); 565 c = nextc; 566 for (;;) { 567 for (;;) { 568 savech_(c) 569 switch (ctab[c]) { 570 default: 571 fatserror("unknown character `%c'", c); 572 /*NOTREACHED*/ 573 case NEWLN: 574 ++rcsline; 575 /* fall into */ 576 case COLON: case DIGIT: case LETTER: case Letter: 577 case PERIOD: case SPACE: 578 GETC_(frew, c) 579 continue; 580 case SBEGIN: /* long string */ 581 for (;;) { 582 for (;;) { 583 GETC_(frew, c) 584 savech_(c) 585 switch (c) { 586 case '\n': 587 ++rcsline; 588 /* fall into */ 589 default: 590 continue; 591 592 case SDELIM: 593 break; 594 } 595 break; 596 } 597 GETC_(frew, c) 598 if (c != SDELIM) 599 break; 600 savech_(c) 601 } 602 continue; 603 case SEMI: 604 cacheget_(c) 605 if (ctab[c] == NEWLN) { 606 if (frew) 607 aputc_(c, frew) 608 ++rcsline; 609 savech_(c) 610 cacheget_(c) 611 } 612# if large_memory 613 r.size = (char const*)cacheptr() - 1 - r.string; 614# endif 615 for (;;) { 616 switch (ctab[c]) { 617 case NEWLN: 618 ++rcsline; 619 /* fall into */ 620 case SPACE: 621 cacheget_(c) 622 continue; 623 624 default: break; 625 } 626 break; 627 } 628 if (frew) 629 aputc_(c, frew) 630 break; 631 } 632 break; 633 } 634 if (ctab[c] == Letter) { 635 for (kn = key; c && *kn==c; kn++) 636 GETC_(frew, c) 637 if (!*kn) 638 switch (ctab[c]) { 639 case DIGIT: case LETTER: case Letter: 640 case IDCHAR: case PERIOD: 641 break; 642 default: 643 nextc = c; 644 NextString = fstr_save(key); 645 nexttok = ID; 646 uncache(fin); 647 goto returnit; 648 } 649# if !large_memory 650 { 651 register char const *ki; 652 for (ki=key; ki<kn; ) 653 savech_(*ki++) 654 } 655# endif 656 } else { 657 nextc = c; 658 uncache(fin); 659 nextlex(); 660 break; 661 } 662 } 663 returnit:; 664# if !large_memory 665 return bufremember(&b, (size_t)(p - b.string)); 666# endif 667 } 668 return r; 669} 670 671 672 void 673readstring() 674/* skip over characters until terminating single SDELIM */ 675/* If foutptr is set, copy every character read to foutptr. */ 676/* Does not advance nextlex at the end. */ 677{ register c; 678 declarecache; 679 register FILE *frew; 680 register RILE *fin; 681 fin=finptr; frew=foutptr; 682 setupcache(fin); cache(fin); 683 for (;;) { 684 GETC_(frew, c) 685 switch (c) { 686 case '\n': 687 ++rcsline; 688 break; 689 690 case SDELIM: 691 GETC_(frew, c) 692 if (c != SDELIM) { 693 /* end of string */ 694 nextc = c; 695 uncache(fin); 696 return; 697 } 698 break; 699 } 700 } 701} 702 703 704 void 705printstring() 706/* Function: copy a string to stdout, until terminated with a single SDELIM. 707 * Does not advance nextlex at the end. 708 */ 709{ 710 register c; 711 declarecache; 712 register FILE *fout; 713 register RILE *fin; 714 fin=finptr; 715 fout = stdout; 716 setupcache(fin); cache(fin); 717 for (;;) { 718 cacheget_(c) 719 switch (c) { 720 case '\n': 721 ++rcsline; 722 break; 723 case SDELIM: 724 cacheget_(c) 725 if (c != SDELIM) { 726 nextc=c; 727 uncache(fin); 728 return; 729 } 730 break; 731 } 732 aputc_(c,fout) 733 } 734} 735 736 737 738 struct cbuf 739savestring(target) 740 struct buf *target; 741/* Copies a string terminated with SDELIM from file finptr to buffer target. 742 * Double SDELIM is replaced with SDELIM. 743 * If foutptr is set, the string is also copied unchanged to foutptr. 744 * Does not advance nextlex at the end. 745 * Yield a copy of *TARGET, except with exact length. 746 */ 747{ 748 register c; 749 declarecache; 750 register FILE *frew; 751 register char *tp; 752 register RILE *fin; 753 char const *limit; 754 struct cbuf r; 755 756 fin=finptr; frew=foutptr; 757 setupcache(fin); cache(fin); 758 tp = target->string; limit = tp + target->size; 759 for (;;) { 760 GETC_(frew, c) 761 switch (c) { 762 case '\n': 763 ++rcsline; 764 break; 765 case SDELIM: 766 GETC_(frew, c) 767 if (c != SDELIM) { 768 /* end of string */ 769 nextc=c; 770 r.string = target->string; 771 r.size = tp - r.string; 772 uncache(fin); 773 return r; 774 } 775 break; 776 } 777 if (tp == limit) 778 tp = bufenlarge(target, &limit); 779 *tp++ = c; 780 } 781} 782 783 784 static char * 785checkidentifier(id, delimiter, dotok) 786 register char *id; 787 int delimiter; 788 register int dotok; 789/* Function: check whether the string starting at id is an */ 790/* identifier and return a pointer to the delimiter*/ 791/* after the identifier. White space, delim and 0 */ 792/* are legal delimiters. Aborts the program if not*/ 793/* a legal identifier. Useful for checking commands*/ 794/* If !delim, the only delimiter is 0. */ 795/* Allow '.' in identifier only if DOTOK is set. */ 796{ 797 register char *temp; 798 register char c; 799 register char delim = delimiter; 800 int isid = false; 801 802 temp = id; 803 for (;; id++) { 804 switch (ctab[(unsigned char)(c = *id)]) { 805 case IDCHAR: 806 case LETTER: 807 case Letter: 808 isid = true; 809 continue; 810 811 case DIGIT: 812 continue; 813 814 case PERIOD: 815 if (dotok) 816 continue; 817 break; 818 819 default: 820 break; 821 } 822 break; 823 } 824 if ( ! isid 825 || (c && (!delim || (c!=delim && c!=' ' && c!='\t' && c!='\n'))) 826 ) { 827 /* append \0 to end of id before error message */ 828 while ((c = *id) && c!=' ' && c!='\t' && c!='\n' && c!=delim) 829 id++; 830 *id = '\0'; 831 faterror("invalid %s `%s'", 832 dotok ? "identifier" : "symbol", temp 833 ); 834 } 835 return id; 836} 837 838 char * 839checkid(id, delimiter) 840 char *id; 841 int delimiter; 842{ 843 return checkidentifier(id, delimiter, true); 844} 845 846 char * 847checksym(sym, delimiter) 848 char *sym; 849 int delimiter; 850{ 851 return checkidentifier(sym, delimiter, false); 852} 853 854 void 855checksid(id) 856 char *id; 857/* Check whether the string ID is an identifier. */ 858{ 859 VOID checkid(id, 0); 860} 861 862 void 863checkssym(sym) 864 char *sym; 865{ 866 VOID checksym(sym, 0); 867} 868 869 870#if !large_memory 871# define Iclose(f) fclose(f) 872#else 873# if !maps_memory 874 static int Iclose P((RILE *)); 875 static int 876 Iclose(f) 877 register RILE *f; 878 { 879 tfree(f->base); 880 f->base = 0; 881 return fclose(f->stream); 882 } 883# else 884 static int Iclose P((RILE *)); 885 static int 886 Iclose(f) 887 register RILE *f; 888 { 889 (* f->deallocate) (f); 890 f->base = 0; 891 return close(f->fd); 892 } 893 894# if has_map_fd 895 static void map_fd_deallocate P((RILE *)); 896 static void 897 map_fd_deallocate(f) 898 register RILE *f; 899 { 900 if (vm_deallocate( 901 task_self(), 902 (vm_address_t) f->base, 903 (vm_size_t) (f->lim - f->base) 904 ) != KERN_SUCCESS) 905 efaterror("vm_deallocate"); 906 } 907# endif 908# if has_mmap 909 static void mmap_deallocate P((RILE *)); 910 static void 911 mmap_deallocate(f) 912 register RILE *f; 913 { 914 if (munmap((char *) f->base, (size_t) (f->lim - f->base)) != 0) 915 efaterror("munmap"); 916 } 917# endif 918 static void read_deallocate P((RILE *)); 919 static void 920 read_deallocate(f) 921 RILE *f; 922 { 923 tfree(f->base); 924 } 925 926 static void nothing_to_deallocate P((RILE *)); 927 static void 928 nothing_to_deallocate(f) 929 RILE *f; 930 { 931 } 932# endif 933#endif 934 935 936#if large_memory && maps_memory 937 static RILE *fd2_RILE P((int,char const*,struct stat*)); 938 static RILE * 939fd2_RILE(fd, name, status) 940#else 941 static RILE *fd2RILE P((int,char const*,char const*,struct stat*)); 942 static RILE * 943fd2RILE(fd, name, type, status) 944 char const *type; 945#endif 946 int fd; 947 char const *name; 948 register struct stat *status; 949{ 950 struct stat st; 951 952 if (!status) 953 status = &st; 954 if (fstat(fd, status) != 0) 955 efaterror(name); 956 if (!S_ISREG(status->st_mode)) { 957 error("`%s' is not a regular file", name); 958 VOID close(fd); 959 errno = EINVAL; 960 return 0; 961 } else { 962 963# if !(large_memory && maps_memory) 964 FILE *stream; 965 if (!(stream = fdopen(fd, type))) 966 efaterror(name); 967# endif 968 969# if !large_memory 970 return stream; 971# else 972# define RILES 3 973 { 974 static RILE rilebuf[RILES]; 975 976 register RILE *f; 977 size_t s = status->st_size; 978 979 if (s != status->st_size) 980 faterror("%s: too large", name); 981 for (f = rilebuf; f->base; f++) 982 if (f == rilebuf+RILES) 983 faterror("too many RILEs"); 984# if maps_memory 985 f->deallocate = nothing_to_deallocate; 986# endif 987 if (!s) { 988# if maps_memory 989 static unsigned char nothing; 990 f->base = ¬hing; /* Any nonzero address will do. */ 991# else 992 f->base = tnalloc(unsigned char, 1); /* We need something free()able in here */ 993# endif 994 } else { 995 f->base = 0; 996# if has_map_fd 997 map_fd( 998 fd, (vm_offset_t)0, (vm_address_t*) &f->base, 999 TRUE, (vm_size_t)s 1000 ); 1001 f->deallocate = map_fd_deallocate; 1002# endif 1003# if has_mmap 1004 if (!f->base) { 1005 catchmmapints(); 1006 f->base = (unsigned char *) mmap( 1007 (char *)0, s, PROT_READ, MAP_SHARED, 1008 fd, (off_t)0 1009 ); 1010# ifndef MAP_FAILED 1011# define MAP_FAILED (-1) 1012# endif 1013 if (f->base == (unsigned char *) MAP_FAILED) 1014 f->base = 0; 1015 else { 1016# if has_NFS && mmap_signal 1017 /* 1018 * On many hosts, the superuser 1019 * can mmap an NFS file it can't read. 1020 * So access the first page now, and print 1021 * a nice message if a bus error occurs. 1022 */ 1023 readAccessFilenameBuffer(name, f->base); 1024# endif 1025 } 1026 f->deallocate = mmap_deallocate; 1027 } 1028# endif 1029 if (!f->base) { 1030 f->base = tnalloc(unsigned char, s); 1031# if maps_memory 1032 { 1033 /* 1034 * We can't map the file into memory for some reason. 1035 * Read it into main memory all at once; this is 1036 * the simplest substitute for memory mapping. 1037 */ 1038 char *bufptr = (char *) f->base; 1039 size_t bufsiz = s; 1040 do { 1041 ssize_t r = read(fd, bufptr, bufsiz); 1042 switch (r) { 1043 case -1: 1044 efaterror(name); 1045 1046 case 0: 1047 /* The file must have shrunk! */ 1048 status->st_size = s -= bufsiz; 1049 bufsiz = 0; 1050 break; 1051 1052 default: 1053 bufptr += r; 1054 bufsiz -= r; 1055 break; 1056 } 1057 } while (bufsiz); 1058 if (lseek(fd, (off_t)0, SEEK_SET) == -1) 1059 efaterror(name); 1060 f->deallocate = read_deallocate; 1061 } 1062# endif 1063 } 1064 } 1065 f->ptr = f->base; 1066 f->lim = f->base + s; 1067 f->fd = fd; 1068# if !maps_memory 1069 f->readlim = f->base; 1070 f->stream = stream; 1071# endif 1072 if_advise_access(s, f, MADV_SEQUENTIAL); 1073 return f; 1074 } 1075# endif 1076 } 1077} 1078 1079#if !maps_memory && large_memory 1080 int 1081Igetmore(f) 1082 register RILE *f; 1083{ 1084 register fread_type r; 1085 register size_t s = f->lim - f->readlim; 1086 1087 if (BUFSIZ < s) 1088 s = BUFSIZ; 1089 if (!(r = Fread(f->readlim, sizeof(*f->readlim), s, f->stream))) { 1090 testIerror(f->stream); 1091 f->lim = f->readlim; /* The file might have shrunk! */ 1092 return 0; 1093 } 1094 f->readlim += r; 1095 return 1; 1096} 1097#endif 1098 1099#if has_madvise && has_mmap && large_memory 1100 void 1101advise_access(f, advice) 1102 register RILE *f; 1103 int advice; 1104{ 1105 if (f->deallocate == mmap_deallocate) 1106 VOID madvise((char *)f->base, (size_t)(f->lim - f->base), advice); 1107 /* Don't worry if madvise fails; it's only advisory. */ 1108} 1109#endif 1110 1111 RILE * 1112#if large_memory && maps_memory 1113I_open(name, status) 1114#else 1115Iopen(name, type, status) 1116 char const *type; 1117#endif 1118 char const *name; 1119 struct stat *status; 1120/* Open NAME for reading, yield its descriptor, and set *STATUS. */ 1121{ 1122 int fd = fdSafer(open(name, O_RDONLY 1123# if OPEN_O_BINARY 1124 | (strchr(type,'b') ? OPEN_O_BINARY : 0) 1125# endif 1126 )); 1127 1128 if (fd < 0) 1129 return 0; 1130# if large_memory && maps_memory 1131 return fd2_RILE(fd, name, status); 1132# else 1133 return fd2RILE(fd, name, type, status); 1134# endif 1135} 1136 1137 1138static int Oerrloop; 1139 1140 void 1141Oerror() 1142{ 1143 if (Oerrloop) 1144 exiterr(); 1145 Oerrloop = true; 1146 efaterror("output error"); 1147} 1148 1149void Ieof() { fatserror("unexpected end of file"); } 1150void Ierror() { efaterror("input error"); } 1151void testIerror(f) FILE *f; { if (ferror(f)) Ierror(); } 1152void testOerror(o) FILE *o; { if (ferror(o)) Oerror(); } 1153 1154void Ifclose(f) RILE *f; { if (f && Iclose(f)!=0) Ierror(); } 1155void Ofclose(f) FILE *f; { if (f && fclose(f)!=0) Oerror(); } 1156void Izclose(p) RILE **p; { Ifclose(*p); *p = 0; } 1157void Ozclose(p) FILE **p; { Ofclose(*p); *p = 0; } 1158 1159#if !large_memory 1160 void 1161testIeof(f) 1162 FILE *f; 1163{ 1164 testIerror(f); 1165 if (feof(f)) 1166 Ieof(); 1167} 1168void Irewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Ierror(); } 1169#endif 1170 1171void Orewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Oerror(); } 1172 1173void aflush(f) FILE *f; { if (fflush(f) != 0) Oerror(); } 1174void eflush() { if (fflush(stderr)!=0 && !Oerrloop) Oerror(); } 1175void oflush() 1176{ 1177 if (fflush(workstdout ? workstdout : stdout) != 0 && !Oerrloop) 1178 Oerror(); 1179} 1180 1181 void 1182fatcleanup(already_newline) 1183 int already_newline; 1184{ 1185 VOID fprintf(stderr, already_newline+"\n%s aborted\n", cmdid); 1186 exiterr(); 1187} 1188 1189 static void 1190startsay(s, t) 1191 const char *s, *t; 1192{ 1193 oflush(); 1194 if (s) 1195 aprintf(stderr, "%s: %s: %s", cmdid, s, t); 1196 else 1197 aprintf(stderr, "%s: %s", cmdid, t); 1198} 1199 1200 static void 1201fatsay(s) 1202 char const *s; 1203{ 1204 startsay(s, ""); 1205} 1206 1207 static void 1208errsay(s) 1209 char const *s; 1210{ 1211 fatsay(s); 1212 nerror++; 1213} 1214 1215 static void 1216warnsay(s) 1217 char const *s; 1218{ 1219 startsay(s, "warning: "); 1220} 1221 1222void eerror(s) char const *s; { enerror(errno,s); } 1223 1224 void 1225enerror(e,s) 1226 int e; 1227 char const *s; 1228{ 1229 errsay((char const*)0); 1230 errno = e; 1231 perror(s); 1232 eflush(); 1233} 1234 1235void efaterror(s) char const *s; { enfaterror(errno,s); } 1236 1237 void 1238enfaterror(e,s) 1239 int e; 1240 char const *s; 1241{ 1242 fatsay((char const*)0); 1243 errno = e; 1244 perror(s); 1245 fatcleanup(true); 1246} 1247 1248#if has_prototypes 1249 void 1250error(char const *format,...) 1251#else 1252 /*VARARGS1*/ void error(format, va_alist) char const *format; va_dcl 1253#endif 1254/* non-fatal error */ 1255{ 1256 va_list args; 1257 errsay((char const*)0); 1258 vararg_start(args, format); 1259 fvfprintf(stderr, format, args); 1260 va_end(args); 1261 afputc('\n',stderr); 1262 eflush(); 1263} 1264 1265#if has_prototypes 1266 void 1267rcserror(char const *format,...) 1268#else 1269 /*VARARGS1*/ void rcserror(format, va_alist) char const *format; va_dcl 1270#endif 1271/* non-fatal RCS file error */ 1272{ 1273 va_list args; 1274 errsay(RCSname); 1275 vararg_start(args, format); 1276 fvfprintf(stderr, format, args); 1277 va_end(args); 1278 afputc('\n',stderr); 1279 eflush(); 1280} 1281 1282#if has_prototypes 1283 void 1284workerror(char const *format,...) 1285#else 1286 /*VARARGS1*/ void workerror(format, va_alist) char const *format; va_dcl 1287#endif 1288/* non-fatal working file error */ 1289{ 1290 va_list args; 1291 errsay(workname); 1292 vararg_start(args, format); 1293 fvfprintf(stderr, format, args); 1294 va_end(args); 1295 afputc('\n',stderr); 1296 eflush(); 1297} 1298 1299#if has_prototypes 1300 void 1301fatserror(char const *format,...) 1302#else 1303 /*VARARGS1*/ void 1304 fatserror(format, va_alist) char const *format; va_dcl 1305#endif 1306/* fatal RCS file syntax error */ 1307{ 1308 va_list args; 1309 oflush(); 1310 VOID fprintf(stderr, "%s: %s:%ld: ", cmdid, RCSname, rcsline); 1311 vararg_start(args, format); 1312 fvfprintf(stderr, format, args); 1313 va_end(args); 1314 fatcleanup(false); 1315} 1316 1317#if has_prototypes 1318 void 1319faterror(char const *format,...) 1320#else 1321 /*VARARGS1*/ void faterror(format, va_alist) 1322 char const *format; va_dcl 1323#endif 1324/* fatal error, terminates program after cleanup */ 1325{ 1326 va_list args; 1327 fatsay((char const*)0); 1328 vararg_start(args, format); 1329 fvfprintf(stderr, format, args); 1330 va_end(args); 1331 fatcleanup(false); 1332} 1333 1334#if has_prototypes 1335 void 1336rcsfaterror(char const *format,...) 1337#else 1338 /*VARARGS1*/ void rcsfaterror(format, va_alist) 1339 char const *format; va_dcl 1340#endif 1341/* fatal RCS file error, terminates program after cleanup */ 1342{ 1343 va_list args; 1344 fatsay(RCSname); 1345 vararg_start(args, format); 1346 fvfprintf(stderr, format, args); 1347 va_end(args); 1348 fatcleanup(false); 1349} 1350 1351#if has_prototypes 1352 void 1353warn(char const *format,...) 1354#else 1355 /*VARARGS1*/ void warn(format, va_alist) char const *format; va_dcl 1356#endif 1357/* warning */ 1358{ 1359 va_list args; 1360 if (!quietflag) { 1361 warnsay((char *)0); 1362 vararg_start(args, format); 1363 fvfprintf(stderr, format, args); 1364 va_end(args); 1365 afputc('\n', stderr); 1366 eflush(); 1367 } 1368} 1369 1370#if has_prototypes 1371 void 1372rcswarn(char const *format,...) 1373#else 1374 /*VARARGS1*/ void rcswarn(format, va_alist) char const *format; va_dcl 1375#endif 1376/* RCS file warning */ 1377{ 1378 va_list args; 1379 if (!quietflag) { 1380 warnsay(RCSname); 1381 vararg_start(args, format); 1382 fvfprintf(stderr, format, args); 1383 va_end(args); 1384 afputc('\n', stderr); 1385 eflush(); 1386 } 1387} 1388 1389#if has_prototypes 1390 void 1391workwarn(char const *format,...) 1392#else 1393 /*VARARGS1*/ void workwarn(format, va_alist) char const *format; va_dcl 1394#endif 1395/* working file warning */ 1396{ 1397 va_list args; 1398 if (!quietflag) { 1399 warnsay(workname); 1400 vararg_start(args, format); 1401 fvfprintf(stderr, format, args); 1402 va_end(args); 1403 afputc('\n', stderr); 1404 eflush(); 1405 } 1406} 1407 1408 void 1409redefined(c) 1410 int c; 1411{ 1412 warn("redefinition of -%c option", c); 1413} 1414 1415#if has_prototypes 1416 void 1417diagnose(char const *format,...) 1418#else 1419 /*VARARGS1*/ void diagnose(format, va_alist) char const *format; va_dcl 1420#endif 1421/* prints a diagnostic message */ 1422/* Unlike the other routines, it does not append a newline. */ 1423/* This lets some callers suppress the newline, and is faster */ 1424/* in implementations that flush stderr just at the end of each printf. */ 1425{ 1426 va_list args; 1427 if (!quietflag) { 1428 oflush(); 1429 vararg_start(args, format); 1430 fvfprintf(stderr, format, args); 1431 va_end(args); 1432 eflush(); 1433 } 1434} 1435 1436 1437 1438 void 1439afputc(c, f) 1440/* afputc(c,f); acts like aputc_(c,f) but is smaller and slower. */ 1441 int c; 1442 register FILE *f; 1443{ 1444 aputc_(c,f) 1445} 1446 1447 1448 void 1449aputs(s, iop) 1450 char const *s; 1451 FILE *iop; 1452/* Function: Put string s on file iop, abort on error. 1453 */ 1454{ 1455#if has_fputs 1456 if (fputs(s, iop) < 0) 1457 Oerror(); 1458#else 1459 awrite(s, strlen(s), iop); 1460#endif 1461} 1462 1463 1464 1465 void 1466#if has_prototypes 1467fvfprintf(FILE *stream, char const *format, va_list args) 1468#else 1469 fvfprintf(stream,format,args) FILE *stream; char *format; va_list args; 1470#endif 1471/* like vfprintf, except abort program on error */ 1472{ 1473#if has_vfprintf 1474 if (vfprintf(stream, format, args) < 0) 1475 Oerror(); 1476#else 1477# if has__doprintf 1478 _doprintf(stream, format, args); 1479# else 1480# if has__doprnt 1481 _doprnt(format, args, stream); 1482# else 1483 int *a = (int *)args; 1484 VOID fprintf(stream, format, 1485 a[0], a[1], a[2], a[3], a[4], 1486 a[5], a[6], a[7], a[8], a[9] 1487 ); 1488# endif 1489# endif 1490 if (ferror(stream)) 1491 Oerror(); 1492#endif 1493} 1494 1495#if has_prototypes 1496 void 1497aprintf(FILE *iop, char const *fmt, ...) 1498#else 1499 /*VARARGS2*/ void 1500aprintf(iop, fmt, va_alist) 1501FILE *iop; 1502char const *fmt; 1503va_dcl 1504#endif 1505/* Function: formatted output. Same as fprintf in stdio, 1506 * but aborts program on error 1507 */ 1508{ 1509 va_list ap; 1510 vararg_start(ap, fmt); 1511 fvfprintf(iop, fmt, ap); 1512 va_end(ap); 1513} 1514 1515 1516 1517#ifdef LEXDB 1518/* test program reading a stream of lexemes and printing the tokens. 1519 */ 1520 1521 1522 1523 int 1524main(argc,argv) 1525int argc; char * argv[]; 1526{ 1527 cmdid="lextest"; 1528 if (argc<2) { 1529 aputs("No input file\n",stderr); 1530 exitmain(EXIT_FAILURE); 1531 } 1532 if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) { 1533 faterror("can't open input file %s",argv[1]); 1534 } 1535 Lexinit(); 1536 while (!eoflex()) { 1537 switch (nexttok) { 1538 1539 case ID: 1540 VOID printf("ID: %s",NextString); 1541 break; 1542 1543 case NUM: 1544 if (hshenter) 1545 VOID printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab); 1546 else 1547 VOID printf("NUM, unentered: %s",NextString); 1548 hshenter = !hshenter; /*alternate between dates and numbers*/ 1549 break; 1550 1551 case COLON: 1552 VOID printf("COLON"); break; 1553 1554 case SEMI: 1555 VOID printf("SEMI"); break; 1556 1557 case STRING: 1558 readstring(); 1559 VOID printf("STRING"); break; 1560 1561 case UNKN: 1562 VOID printf("UNKN"); break; 1563 1564 default: 1565 VOID printf("DEFAULT"); break; 1566 } 1567 VOID printf(" | "); 1568 nextlex(); 1569 } 1570 exitmain(EXIT_SUCCESS); 1571} 1572 1573void exiterr() { _exit(EXIT_FAILURE); } 1574 1575 1576#endif 1577