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