rcslex.c revision 8858
1/* 2 * RCS file input 3 */ 4/********************************************************************************* 5 * Lexical Analysis. 6 * hashtable, Lexinit, nextlex, getlex, getkey, 7 * getid, getnum, readstring, printstring, savestring, 8 * checkid, fatserror, error, faterror, warn, diagnose 9 * Testprogram: define LEXDB 10 ********************************************************************************* 11 */ 12 13/* Copyright (C) 1982, 1988, 1989 Walter Tichy 14 Copyright 1990, 1991 by Paul Eggert 15 Distributed under license by the Free Software Foundation, Inc. 16 17This file is part of RCS. 18 19RCS is free software; you can redistribute it and/or modify 20it under the terms of the GNU General Public License as published by 21the Free Software Foundation; either version 2, or (at your option) 22any later version. 23 24RCS is distributed in the hope that it will be useful, 25but WITHOUT ANY WARRANTY; without even the implied warranty of 26MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27GNU General Public License for more details. 28 29You should have received a copy of the GNU General Public License 30along with RCS; see the file COPYING. If not, write to 31the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 32 33Report problems and direct all questions to: 34 35 rcs-bugs@cs.purdue.edu 36 37*/ 38 39 40 41/* $Log: rcslex.c,v $ 42 * Revision 1.2 1993/06/28 19:13:10 nate 43 * Added Chris Demetriou's FSYNC_ALL option which causes all writes to be 44 * flushed immediately. (In case of a crash in the middle of CVS/RCS commits 45 * 46 * Revision 1.1.1.1 1993/06/18 04:22:12 jkh 47 * Updated GNU utilities 48 * 49 * Revision 5.11 1991/11/03 03:30:44 eggert 50 * Fix porting bug to ancient hosts lacking vfprintf. 51 * 52 * Revision 5.10 1991/10/07 17:32:46 eggert 53 * Support piece tables even if !has_mmap. 54 * 55 * Revision 5.9 1991/09/24 00:28:42 eggert 56 * Don't export errsay(). 57 * 58 * Revision 5.8 1991/08/19 03:13:55 eggert 59 * Add eoflex(), mmap support. Tune. 60 * 61 * Revision 5.7 1991/04/21 11:58:26 eggert 62 * Add MS-DOS support. 63 * 64 * Revision 5.6 1991/02/25 07:12:42 eggert 65 * Work around fputs bug. strsave -> str_save (DG/UX name clash) 66 * 67 * Revision 5.5 1990/12/04 05:18:47 eggert 68 * Use -I for prompts and -q for diagnostics. 69 * 70 * Revision 5.4 1990/11/19 20:05:28 hammer 71 * no longer gives warning about unknown keywords if -q is specified 72 * 73 * Revision 5.3 1990/11/01 05:03:48 eggert 74 * When ignoring unknown phrases, copy them to the output RCS file. 75 * 76 * Revision 5.2 1990/09/04 08:02:27 eggert 77 * Count RCS lines better. 78 * 79 * Revision 5.1 1990/08/29 07:14:03 eggert 80 * Work around buggy compilers with defective argument promotion. 81 * 82 * Revision 5.0 1990/08/22 08:12:55 eggert 83 * Remove compile-time limits; use malloc instead. 84 * Report errno-related errors with perror(). 85 * Ansify and Posixate. Add support for ISO 8859. 86 * Use better hash function. 87 * 88 * Revision 4.6 89/05/01 15:13:07 narten 89 * changed copyright header to reflect current distribution rules 90 * 91 * Revision 4.5 88/08/28 15:01:12 eggert 92 * Don't loop when writing error messages to a full filesystem. 93 * Flush stderr/stdout when mixing output. 94 * Yield exit status compatible with diff(1). 95 * Shrink stdio code size; allow cc -R; remove lint. 96 * 97 * Revision 4.4 87/12/18 11:44:47 narten 98 * fixed to use "varargs" in "fprintf"; this is required if it is to 99 * work on a SPARC machine such as a Sun-4 100 * 101 * Revision 4.3 87/10/18 10:37:18 narten 102 * Updating version numbers. Changes relative to 1.1 actually relative 103 * to version 4.1 104 * 105 * Revision 1.3 87/09/24 14:00:17 narten 106 * Sources now pass through lint (if you ignore printf/sprintf/fprintf 107 * warnings) 108 * 109 * Revision 1.2 87/03/27 14:22:33 jenkins 110 * Port to suns 111 * 112 * Revision 4.1 83/03/25 18:12:51 wft 113 * Only changed $Header to $Id. 114 * 115 * Revision 3.3 82/12/10 16:22:37 wft 116 * Improved error messages, changed exit status on error to 1. 117 * 118 * Revision 3.2 82/11/28 21:27:10 wft 119 * Renamed ctab to map and included EOFILE; ctab is now a macro in rcsbase.h. 120 * Added fflsbuf(), fputs(), and fprintf(), which abort the RCS operations 121 * properly in case there is an IO-error (e.g., file system full). 122 * 123 * Revision 3.1 82/10/11 19:43:56 wft 124 * removed unused label out:; 125 * made sure all calls to getc() return into an integer, not a char. 126 */ 127 128 129/* 130#define LEXDB 131*/ 132/* version LEXDB is for testing the lexical analyzer. The testprogram 133 * reads a stream of lexemes, enters the revision numbers into the 134 * hashtable, and prints the recognized tokens. Keywords are recognized 135 * as identifiers. 136 */ 137 138 139 140#include "rcsbase.h" 141 142libId(lexId, "$Id: rcslex.c,v 1.2 1993/06/28 19:13:10 nate Exp $") 143 144static struct hshentry *nexthsh; /*pointer to next hash entry, set by lookup*/ 145 146enum tokens nexttok; /*next token, set by nextlex */ 147 148int hshenter; /*if true, next suitable lexeme will be entered */ 149 /*into the symbol table. Handle with care. */ 150int nextc; /*next input character, initialized by Lexinit */ 151 152unsigned long rcsline; /*current line-number of input */ 153int nerror; /*counter for errors */ 154int quietflag; /*indicates quiet mode */ 155RILE * finptr; /*input file descriptor */ 156 157FILE * frewrite; /*file descriptor for echoing input */ 158 159FILE * foutptr; /* copy of frewrite, but 0 to suppress echo */ 160 161static struct buf tokbuf; /* token buffer */ 162 163char const * NextString; /* next token */ 164 165/* 166 * Our hash algorithm is h[0] = 0, h[i+1] = 4*h[i] + c, 167 * so hshsize should be odd. 168 * See B J McKenzie, R Harries & T Bell, Selecting a hashing algorithm, 169 * Software--practice & experience 20, 2 (Feb 1990), 209-224. 170 */ 171#ifndef hshsize 172# define hshsize 511 173#endif 174 175static struct hshentry *hshtab[hshsize]; /*hashtable */ 176 177static int ignored_phrases; /* have we ignored phrases in this RCS file? */ 178 179 void 180warnignore() 181{ 182 if (! (ignored_phrases|quietflag)) { 183 ignored_phrases = true; 184 warn("Unknown phrases like `%s ...;' are in the RCS file.", NextString); 185 } 186} 187 188 189 190 static void 191lookup(str) 192 char const *str; 193/* Function: Looks up the character string pointed to by str in the 194 * hashtable. If the string is not present, a new entry for it is created. 195 * In any case, the address of the corresponding hashtable entry is placed 196 * into nexthsh. 197 */ 198{ 199 register unsigned ihash; /* index into hashtable */ 200 register char const *sp; 201 register struct hshentry *n, **p; 202 203 /* calculate hash code */ 204 sp = str; 205 ihash = 0; 206 while (*sp) 207 ihash = (ihash<<2) + *sp++; 208 ihash %= hshsize; 209 210 for (p = &hshtab[ihash]; ; p = &n->nexthsh) 211 if (!(n = *p)) { 212 /* empty slot found */ 213 *p = n = ftalloc(struct hshentry); 214 n->num = fstr_save(str); 215 n->nexthsh = nil; 216# ifdef LEXDB 217 VOID printf("\nEntered: %s at %u ", str, ihash); 218# endif 219 break; 220 } else if (strcmp(str, n->num) == 0) 221 /* match found */ 222 break; 223 nexthsh = n; 224 NextString = n->num; 225} 226 227 228 229 230 231 232 void 233Lexinit() 234/* Function: Initialization of lexical analyzer: 235 * initializes the hashtable, 236 * initializes nextc, nexttok if finptr != 0 237 */ 238{ register int c; 239 240 for (c = hshsize; 0 <= --c; ) { 241 hshtab[c] = nil; 242 } 243 244 nerror = 0; 245 if (finptr) { 246 foutptr = 0; 247 hshenter = true; 248 ignored_phrases = false; 249 rcsline = 1; 250 bufrealloc(&tokbuf, 2); 251 Iget(finptr, nextc); 252 nextlex(); /*initial token*/ 253 } 254} 255 256 257 258 259 260 261 262 void 263nextlex() 264 265/* Function: Reads the next token and sets nexttok to the next token code. 266 * Only if hshenter is set, a revision number is entered into the 267 * hashtable and a pointer to it is placed into nexthsh. 268 * This is useful for avoiding that dates are placed into the hashtable. 269 * For ID's and NUM's, NextString is set to the character string. 270 * Assumption: nextc contains the next character. 271 */ 272{ register c; 273 declarecache; 274 register FILE *frew; 275 register char * sp; 276 char const *limit; 277 register enum tokens d; 278 register RILE *fin; 279 280 fin=finptr; frew=foutptr; 281 setupcache(fin); cache(fin); 282 c = nextc; 283 284 for (;;) { switch ((d = ctab[c])) { 285 286 default: 287 fatserror("unknown character `%c'", c); 288 /*NOTREACHED*/ 289 290 case NEWLN: 291 ++rcsline; 292# ifdef LEXDB 293 afputc('\n',stdout); 294# endif 295 /* Note: falls into next case */ 296 297 case SPACE: 298 GETC(frew, c); 299 continue; 300 301 case DIGIT: 302 sp = tokbuf.string; 303 limit = sp + tokbuf.size; 304 *sp++ = c; 305 for (;;) { 306 GETC(frew, c); 307 if ((d=ctab[c])!=DIGIT && d!=PERIOD) 308 break; 309 *sp++ = c; /* 1.2. and 1.2 are different */ 310 if (limit <= sp) 311 sp = bufenlarge(&tokbuf, &limit); 312 } 313 *sp = 0; 314 if (hshenter) 315 lookup(tokbuf.string); 316 else 317 NextString = fstr_save(tokbuf.string); 318 d = NUM; 319 break; 320 321 322 case LETTER: 323 case Letter: 324 sp = tokbuf.string; 325 limit = sp + tokbuf.size; 326 *sp++ = c; 327 for (;;) { 328 GETC(frew, c); 329 if ((d=ctab[c])!=LETTER && d!=Letter && d!=DIGIT && d!=IDCHAR) 330 break; 331 *sp++ = c; 332 if (limit <= sp) 333 sp = bufenlarge(&tokbuf, &limit); 334 } 335 *sp = 0; 336 NextString = fstr_save(tokbuf.string); 337 d = ID; /* may be ID or keyword */ 338 break; 339 340 case SBEGIN: /* long string */ 341 d = STRING; 342 /* note: only the initial SBEGIN has been read*/ 343 /* read the string, and reset nextc afterwards*/ 344 break; 345 346 case COLON: 347 case SEMI: 348 GETC(frew, c); 349 break; 350 } break; } 351 nextc = c; 352 nexttok = d; 353 uncache(fin); 354} 355 356 int 357eoflex() 358/* 359 * Yield true if we look ahead to the end of the input, false otherwise. 360 * nextc becomes undefined at end of file. 361 */ 362{ 363 register int c; 364 declarecache; 365 register FILE *fout; 366 register RILE *fin; 367 368 c = nextc; 369 fin = finptr; 370 fout = foutptr; 371 setupcache(fin); cache(fin); 372 373 for (;;) { 374 switch (ctab[c]) { 375 default: 376 nextc = c; 377 uncache(fin); 378 return false; 379 380 case NEWLN: 381 ++rcsline; 382 /* fall into */ 383 case SPACE: 384 cachegeteof(c, {uncache(fin);return true;}); 385 break; 386 } 387 if (fout) 388 aputc(c, fout); 389 } 390} 391 392 393int getlex(token) 394enum tokens token; 395/* Function: Checks if nexttok is the same as token. If so, 396 * advances the input by calling nextlex and returns true. 397 * otherwise returns false. 398 * Doesn't work for strings and keywords; loses the character string for ids. 399 */ 400{ 401 if (nexttok==token) { 402 nextlex(); 403 return(true); 404 } else return(false); 405} 406 407 int 408getkeyopt(key) 409 char const *key; 410/* Function: If the current token is a keyword identical to key, 411 * advances the input by calling nextlex and returns true; 412 * otherwise returns false. 413 */ 414{ 415 if (nexttok==ID && strcmp(key,NextString) == 0) { 416 /* match found */ 417 ffree1(NextString); 418 nextlex(); 419 return(true); 420 } 421 return(false); 422} 423 424 void 425getkey(key) 426 char const *key; 427/* Check that the current input token is a keyword identical to key, 428 * and advance the input by calling nextlex. 429 */ 430{ 431 if (!getkeyopt(key)) 432 fatserror("missing '%s' keyword", key); 433} 434 435 void 436getkeystring(key) 437 char const *key; 438/* Check that the current input token is a keyword identical to key, 439 * and advance the input by calling nextlex; then look ahead for a string. 440 */ 441{ 442 getkey(key); 443 if (nexttok != STRING) 444 fatserror("missing string after '%s' keyword", key); 445} 446 447 448 char const * 449getid() 450/* Function: Checks if nexttok is an identifier. If so, 451 * advances the input by calling nextlex and returns a pointer 452 * to the identifier; otherwise returns nil. 453 * Treats keywords as identifiers. 454 */ 455{ 456 register char const *name; 457 if (nexttok==ID) { 458 name = NextString; 459 nextlex(); 460 return name; 461 } else return nil; 462} 463 464 465struct hshentry * getnum() 466/* Function: Checks if nexttok is a number. If so, 467 * advances the input by calling nextlex and returns a pointer 468 * to the hashtable entry. Otherwise returns nil. 469 * Doesn't work if hshenter is false. 470 */ 471{ 472 register struct hshentry * num; 473 if (nexttok==NUM) { 474 num=nexthsh; 475 nextlex(); 476 return num; 477 } else return nil; 478} 479 480 struct cbuf 481getphrases(key) 482 char const *key; 483/* Get a series of phrases that do not start with KEY, yield resulting buffer. 484 * Stop when the next phrase starts with a token that is not an identifier, 485 * or is KEY. 486 * Assume !foutptr. 487 */ 488{ 489 declarecache; 490 register int c; 491 register char *p; 492 char const *limit; 493 register char const *ki, *kn; 494 struct cbuf r; 495 struct buf b; 496 register RILE *fin; 497 498 if (nexttok!=ID || strcmp(NextString,key) == 0) { 499 r.string = 0; 500 r.size = 0; 501 return r; 502 } else { 503 warnignore(); 504 fin = finptr; 505 setupcache(fin); cache(fin); 506 bufautobegin(&b); 507 bufscpy(&b, NextString); 508 ffree1(NextString); 509 p = b.string + strlen(b.string); 510 limit = b.string + b.size; 511 c = nextc; 512 for (;;) { 513 for (;;) { 514 if (limit <= p) 515 p = bufenlarge(&b, &limit); 516 *p++ = c; 517 switch (ctab[c]) { 518 default: 519 fatserror("unknown character `%c'", c); 520 /*NOTREACHED*/ 521 case NEWLN: 522 ++rcsline; 523 /* fall into */ 524 case COLON: case DIGIT: case LETTER: case Letter: 525 case PERIOD: case SPACE: 526 cacheget(c); 527 continue; 528 case SBEGIN: /* long string */ 529 for (;;) { 530 for (;;) { 531 if (limit <= p) 532 p = bufenlarge(&b, &limit); 533 cacheget(c); 534 *p++ = c; 535 switch (c) { 536 case '\n': 537 ++rcsline; 538 /* fall into */ 539 default: 540 continue; 541 542 case SDELIM: 543 break; 544 } 545 break; 546 } 547 cacheget(c); 548 if (c != SDELIM) 549 break; 550 if (limit <= p) 551 p = bufenlarge(&b, &limit); 552 *p++ = c; 553 } 554 continue; 555 case SEMI: 556 cacheget(c); 557 if (ctab[c] == NEWLN) { 558 ++rcsline; 559 if (limit <= p) 560 p = bufenlarge(&b, &limit); 561 *p++ = c; 562 cacheget(c); 563 } 564 for (;;) { 565 switch (ctab[c]) { 566 case NEWLN: 567 ++rcsline; 568 /* fall into */ 569 case SPACE: 570 cacheget(c); 571 continue; 572 573 default: break; 574 } 575 break; 576 } 577 break; 578 } 579 break; 580 } 581 switch (ctab[c]) { 582 case LETTER: 583 case Letter: 584 for (kn = key; c && *kn==c; kn++) 585 cacheget(c); 586 if (!*kn) 587 switch (ctab[c]) { 588 case DIGIT: case LETTER: case Letter: 589 break; 590 default: 591 nextc = c; 592 NextString = fstr_save(key); 593 nexttok = ID; 594 uncache(fin); 595 goto returnit; 596 } 597 for (ki=key; ki<kn; ) { 598 if (limit <= p) 599 p = bufenlarge(&b, &limit); 600 *p++ = *ki++; 601 } 602 break; 603 604 default: 605 nextc = c; 606 uncache(fin); 607 nextlex(); 608 goto returnit; 609 } 610 } 611 returnit: 612 return bufremember(&b, (size_t)(p - b.string)); 613 } 614} 615 616 617 void 618readstring() 619/* skip over characters until terminating single SDELIM */ 620/* If foutptr is set, copy every character read to foutptr. */ 621/* Does not advance nextlex at the end. */ 622{ register c; 623 declarecache; 624 register FILE *frew; 625 register RILE *fin; 626 fin=finptr; frew=foutptr; 627 setupcache(fin); cache(fin); 628 for (;;) { 629 GETC(frew, c); 630 switch (c) { 631 case '\n': 632 ++rcsline; 633 break; 634 635 case SDELIM: 636 GETC(frew, c); 637 if (c != SDELIM) { 638 /* end of string */ 639 nextc = c; 640 uncache(fin); 641 return; 642 } 643 break; 644 } 645 } 646} 647 648 649 void 650printstring() 651/* Function: copy a string to stdout, until terminated with a single SDELIM. 652 * Does not advance nextlex at the end. 653 */ 654{ 655 register c; 656 declarecache; 657 register FILE *fout; 658 register RILE *fin; 659 fin=finptr; 660 fout = stdout; 661 setupcache(fin); cache(fin); 662 for (;;) { 663 cacheget(c); 664 switch (c) { 665 case '\n': 666 ++rcsline; 667 break; 668 case SDELIM: 669 cacheget(c); 670 if (c != SDELIM) { 671 nextc=c; 672 uncache(fin); 673 return; 674 } 675 break; 676 } 677 aputc(c,fout); 678 } 679} 680 681 682 683 struct cbuf 684savestring(target) 685 struct buf *target; 686/* Copies a string terminated with SDELIM from file finptr to buffer target. 687 * Double SDELIM is replaced with SDELIM. 688 * If foutptr is set, the string is also copied unchanged to foutptr. 689 * Does not advance nextlex at the end. 690 * Yield a copy of *TARGET, except with exact length. 691 */ 692{ 693 register c; 694 declarecache; 695 register FILE *frew; 696 register char *tp; 697 register RILE *fin; 698 char const *limit; 699 struct cbuf r; 700 701 fin=finptr; frew=foutptr; 702 setupcache(fin); cache(fin); 703 tp = target->string; limit = tp + target->size; 704 for (;;) { 705 GETC(frew, c); 706 switch (c) { 707 case '\n': 708 ++rcsline; 709 break; 710 case SDELIM: 711 GETC(frew, c); 712 if (c != SDELIM) { 713 /* end of string */ 714 nextc=c; 715 r.string = target->string; 716 r.size = tp - r.string; 717 uncache(fin); 718 return r; 719 } 720 break; 721 } 722 if (tp == limit) 723 tp = bufenlarge(target, &limit); 724 *tp++ = c; 725 } 726} 727 728 729 char * 730checkid(id, delimiter) 731 register char *id; 732 int delimiter; 733/* Function: check whether the string starting at id is an */ 734/* identifier and return a pointer to the delimiter*/ 735/* after the identifier. White space, delim and 0 */ 736/* are legal delimiters. Aborts the program if not*/ 737/* a legal identifier. Useful for checking commands*/ 738/* If !delim, the only delimiter is 0. */ 739{ 740 register enum tokens d; 741 register char *temp; 742 register char c,tc; 743 register char delim = delimiter; 744 745 temp = id; 746 if ((d = ctab[(unsigned char)(c = *id)])==LETTER || d==Letter) { 747 while ((d = ctab[(unsigned char)(c = *++id)])==LETTER 748 || d==Letter || d==DIGIT || d==IDCHAR 749 ) 750 ; 751 if (c && (!delim || c!=delim && c!=' ' && c!='\t' && c!='\n')) { 752 /* append \0 to end of id before error message */ 753 tc = c; 754 while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ; 755 *id = '\0'; 756 faterror("invalid character %c in identifier `%s'",tc,temp); 757 } 758 } else { 759 /* append \0 to end of id before error message */ 760 while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ; 761 *id = '\0'; 762 faterror("identifier `%s' doesn't start with letter", temp); 763 } 764 return id; 765} 766 767 void 768checksid(id) 769 char *id; 770/* Check whether the string ID is an identifier. */ 771{ 772 VOID checkid(id, 0); 773} 774 775 776 static RILE * 777#if has_mmap && large_memory 778fd2_RILE(fd, filename, status) 779#else 780fd2RILE(fd, filename, mode, status) 781 char const *mode; 782#endif 783 int fd; 784 char const *filename; 785 register struct stat *status; 786{ 787 struct stat st; 788 789 if (!status) 790 status = &st; 791 if (fstat(fd, status) != 0) 792 efaterror(filename); 793 if (!S_ISREG(status->st_mode)) { 794 error("`%s' is not a regular file", filename); 795 VOID close(fd); 796 errno = EINVAL; 797 return 0; 798 } else { 799 800# if ! (has_mmap && large_memory) 801 FILE *stream; 802 if (!(stream = fdopen(fd, mode))) 803 efaterror(filename); 804# endif 805 806# if !large_memory 807 return stream; 808# else 809# define RILES 3 810 { 811 static RILE rilebuf[RILES]; 812 813 register RILE *f; 814 size_t s = status->st_size; 815 816 if (s != status->st_size) 817 faterror("`%s' is enormous", filename); 818 for (f = rilebuf; f->base; f++) 819 if (f == rilebuf+RILES) 820 faterror("too many RILEs"); 821 if (!s) { 822 static unsigned char dummy; 823 f->base = &dummy; 824 } else { 825# if has_mmap 826 if ( 827 (f->base = (unsigned char *)mmap( 828 (caddr_t)0, s, PROT_READ, MAP_SHARED, 829 fd, (off_t)0 830 )) == (unsigned char *)-1 831 ) 832 efaterror("mmap"); 833# else 834 f->base = tnalloc(unsigned char, s); 835# endif 836 } 837 f->ptr = f->base; 838 f->lim = f->base + s; 839# if has_mmap 840 f->fd = fd; 841# else 842 f->readlim = f->base; 843 f->stream = stream; 844# endif 845 if_advise_access(s, f, MADV_SEQUENTIAL); 846 return f; 847 } 848# endif 849 } 850} 851 852#if !has_mmap && large_memory 853 int 854Igetmore(f) 855 register RILE *f; 856{ 857 register fread_type r; 858 register size_t s = f->lim - f->readlim; 859 860 if (BUFSIZ < s) 861 s = BUFSIZ; 862 if (!(r = Fread(f->readlim, sizeof(*f->readlim), s, f->stream))) { 863 testIerror(f->stream); 864 f->lim = f->readlim; /* The file might have shrunk! */ 865 return 0; 866 } 867 f->readlim += r; 868 return 1; 869} 870#endif 871 872#if has_madvise && has_mmap && large_memory 873 void 874advise_access(f, advice) 875 register RILE *f; 876 int advice; 877{ 878 if (madvise((caddr_t)f->base, (size_t)(f->lim - f->base), advice) != 0) 879 efaterror("madvise"); 880} 881#endif 882 883 RILE * 884#if has_mmap && large_memory 885I_open(filename, status) 886#else 887Iopen(filename, mode, status) 888 char const *mode; 889#endif 890 char const *filename; 891 struct stat *status; 892/* Open FILENAME for reading, yield its descriptor, and set *STATUS. */ 893{ 894 int fd; 895 896 if ((fd = open(filename,O_RDONLY|O_BINARY)) < 0) 897 return 0; 898# if has_mmap && large_memory 899 return fd2_RILE(fd, filename, status); 900# else 901 return fd2RILE(fd, filename, mode, status); 902# endif 903} 904 905 906#if !large_memory 907# define Iclose(f) fclose(f) 908#else 909 static int 910 Iclose(f) 911 register RILE *f; 912 { 913# if has_mmap 914 size_t s = f->lim - f->base; 915 if (s && munmap((caddr_t)f->base, s) != 0) 916 return -1; 917 f->base = 0; 918 return close(f->fd); 919# else 920 tfree(f->base); 921 f->base = 0; 922 return fclose(f->stream); 923# endif 924 } 925#endif 926 927 928static int Oerrloop; 929 930 exiting void 931Oerror() 932{ 933 if (Oerrloop) 934 exiterr(); 935 Oerrloop = true; 936 efaterror("output error"); 937} 938 939exiting void Ieof() { fatserror("unexpected end of file"); } 940exiting void Ierror() { efaterror("input error"); } 941void testIerror(f) FILE *f; { if (ferror(f)) Ierror(); } 942void testOerror(o) FILE *o; { if (ferror(o)) Oerror(); } 943 944void Ifclose(f) RILE *f; { if (f && Iclose(f)!=0) Ierror(); } 945#ifndef FSYNC_ALL 946void Ofclose(f) FILE *f; { if (f && fclose(f)!=0) Oerror(); } 947#else 948void Ofclose(f) FILE *f; { if (f && (fflush(f)!=0 || 949 fsync(fileno(f))!=0 || 950 fclose(f)!=0)) Oerror(); } 951#endif 952void Izclose(p) RILE **p; { Ifclose(*p); *p = 0; } 953void Ozclose(p) FILE **p; { Ofclose(*p); *p = 0; } 954 955#if !large_memory 956 void 957testIeof(f) 958 FILE *f; 959{ 960 testIerror(f); 961 if (feof(f)) 962 Ieof(); 963} 964void Irewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Ierror(); } 965#endif 966 967void eflush() 968{ 969 if (fflush(stderr) != 0 && !Oerrloop) 970 Oerror(); 971} 972 973void oflush() 974{ 975 if (fflush(workstdout ? workstdout : stdout) != 0 && !Oerrloop) 976 Oerror(); 977} 978 979 static exiting void 980fatcleanup(already_newline) 981 int already_newline; 982{ 983 VOID fprintf(stderr, already_newline+"\n%s aborted\n", cmdid); 984 exiterr(); 985} 986 987static void errsay() { oflush(); aprintf(stderr,"%s error: ",cmdid); nerror++; } 988static void fatsay() { oflush(); VOID fprintf(stderr,"%s error: ",cmdid); } 989 990void eerror(s) char const *s; { enerror(errno,s); } 991 992 void 993enerror(e,s) 994 int e; 995 char const *s; 996{ 997 errsay(); 998 errno = e; 999 perror(s); 1000 eflush(); 1001} 1002 1003exiting void efaterror(s) char const *s; { enfaterror(errno,s); } 1004 1005 exiting void 1006enfaterror(e,s) 1007 int e; 1008 char const *s; 1009{ 1010 fatsay(); 1011 errno = e; 1012 perror(s); 1013 fatcleanup(true); 1014} 1015 1016#if has_prototypes 1017 void 1018error(char const *format,...) 1019#else 1020 /*VARARGS1*/ void error(format, va_alist) char const *format; va_dcl 1021#endif 1022/* non-fatal error */ 1023{ 1024 va_list args; 1025 errsay(); 1026 vararg_start(args, format); 1027 fvfprintf(stderr, format, args); 1028 va_end(args); 1029 afputc('\n',stderr); 1030 eflush(); 1031} 1032 1033#if has_prototypes 1034 exiting void 1035fatserror(char const *format,...) 1036#else 1037 /*VARARGS1*/ exiting void 1038 fatserror(format, va_alist) char const *format; va_dcl 1039#endif 1040/* fatal syntax error */ 1041{ 1042 va_list args; 1043 oflush(); 1044 VOID fprintf(stderr, "%s: %s:%lu: ", cmdid, RCSfilename, rcsline); 1045 vararg_start(args, format); 1046 fvfprintf(stderr, format, args); 1047 va_end(args); 1048 fatcleanup(false); 1049} 1050 1051#if has_prototypes 1052 exiting void 1053faterror(char const *format,...) 1054#else 1055 /*VARARGS1*/ exiting void faterror(format, va_alist) 1056 char const *format; va_dcl 1057#endif 1058/* fatal error, terminates program after cleanup */ 1059{ 1060 va_list args; 1061 fatsay(); 1062 vararg_start(args, format); 1063 fvfprintf(stderr, format, args); 1064 va_end(args); 1065 fatcleanup(false); 1066} 1067 1068#if has_prototypes 1069 void 1070warn(char const *format,...) 1071#else 1072 /*VARARGS1*/ void warn(format, va_alist) char const *format; va_dcl 1073#endif 1074/* prints a warning message */ 1075{ 1076 va_list args; 1077 oflush(); 1078 aprintf(stderr,"%s warning: ",cmdid); 1079 vararg_start(args, format); 1080 fvfprintf(stderr, format, args); 1081 va_end(args); 1082 afputc('\n',stderr); 1083 eflush(); 1084} 1085 1086 void 1087redefined(c) 1088 int c; 1089{ 1090 warn("redefinition of -%c option", c); 1091} 1092 1093#if has_prototypes 1094 void 1095diagnose(char const *format,...) 1096#else 1097 /*VARARGS1*/ void diagnose(format, va_alist) char const *format; va_dcl 1098#endif 1099/* prints a diagnostic message */ 1100/* Unlike the other routines, it does not append a newline. */ 1101/* This lets some callers suppress the newline, and is faster */ 1102/* in implementations that flush stderr just at the end of each printf. */ 1103{ 1104 va_list args; 1105 if (!quietflag) { 1106 oflush(); 1107 vararg_start(args, format); 1108 fvfprintf(stderr, format, args); 1109 va_end(args); 1110 eflush(); 1111 } 1112} 1113 1114 1115 1116 void 1117afputc(c, f) 1118/* Function: afputc(c,f) acts like aputc(c,f), but is smaller and slower. 1119 */ 1120 int c; 1121 register FILE *f; 1122{ 1123 aputc(c,f); 1124} 1125 1126 1127 void 1128aputs(s, iop) 1129 char const *s; 1130 FILE *iop; 1131/* Function: Put string s on file iop, abort on error. 1132 */ 1133{ 1134#if has_fputs 1135 if (fputs(s, iop) < 0) 1136 Oerror(); 1137#else 1138 awrite(s, strlen(s), iop); 1139#endif 1140} 1141 1142 1143 1144 void 1145#if has_prototypes 1146fvfprintf(FILE *stream, char const *format, va_list args) 1147#else 1148 fvfprintf(stream,format,args) FILE *stream; char *format; va_list args; 1149#endif 1150/* like vfprintf, except abort program on error */ 1151{ 1152#if has_vfprintf 1153 if (vfprintf(stream, format, args) < 0) 1154#else 1155# if has__doprintf 1156 _doprintf(stream, format, args); 1157# else 1158# if has__doprnt 1159 _doprnt(format, args, stream); 1160# else 1161 int *a = (int *)args; 1162 VOID fprintf(stream, format, 1163 a[0], a[1], a[2], a[3], a[4], 1164 a[5], a[6], a[7], a[8], a[9] 1165 ); 1166# endif 1167# endif 1168 if (ferror(stream)) 1169#endif 1170 Oerror(); 1171} 1172 1173#if has_prototypes 1174 void 1175aprintf(FILE *iop, char const *fmt, ...) 1176#else 1177 /*VARARGS2*/ void 1178aprintf(iop, fmt, va_alist) 1179FILE *iop; 1180char const *fmt; 1181va_dcl 1182#endif 1183/* Function: formatted output. Same as fprintf in stdio, 1184 * but aborts program on error 1185 */ 1186{ 1187 va_list ap; 1188 vararg_start(ap, fmt); 1189 fvfprintf(iop, fmt, ap); 1190 va_end(ap); 1191} 1192 1193 1194 1195#ifdef LEXDB 1196/* test program reading a stream of lexemes and printing the tokens. 1197 */ 1198 1199 1200 1201 int 1202main(argc,argv) 1203int argc; char * argv[]; 1204{ 1205 cmdid="lextest"; 1206 if (argc<2) { 1207 aputs("No input file\n",stderr); 1208 exitmain(EXIT_FAILURE); 1209 } 1210 if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) { 1211 faterror("can't open input file %s",argv[1]); 1212 } 1213 Lexinit(); 1214 while (!eoflex()) { 1215 switch (nexttok) { 1216 1217 case ID: 1218 VOID printf("ID: %s",NextString); 1219 break; 1220 1221 case NUM: 1222 if (hshenter) 1223 VOID printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab); 1224 else 1225 VOID printf("NUM, unentered: %s",NextString); 1226 hshenter = !hshenter; /*alternate between dates and numbers*/ 1227 break; 1228 1229 case COLON: 1230 VOID printf("COLON"); break; 1231 1232 case SEMI: 1233 VOID printf("SEMI"); break; 1234 1235 case STRING: 1236 readstring(); 1237 VOID printf("STRING"); break; 1238 1239 case UNKN: 1240 VOID printf("UNKN"); break; 1241 1242 default: 1243 VOID printf("DEFAULT"); break; 1244 } 1245 VOID printf(" | "); 1246 nextlex(); 1247 } 1248 exitmain(EXIT_SUCCESS); 1249} 1250 1251exiting void exiterr() { _exit(EXIT_FAILURE); } 1252 1253 1254#endif 1255