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