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