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