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