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