1 /* $NetBSD: rcslex.c,v 1.2 2016/01/14 04:22:39 christos 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 libId(lexId, "Id: rcslex.c,v 5.19 1995/06/16 06:19:24 eggert Exp ")
175
176 static char *checkidentifier P((char*,int,int));
177 static void errsay P((char const*));
178 static void fatsay P((char const*));
179 static void lookup P((char const*));
180 static void startsay P((const char*,const char*));
181 static void warnsay P((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 VOID 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 { 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 # if large_memory
540 # define savech_(c) ;
541 # else
542 register char *p;
543 char const *limit;
544 struct buf b;
545 # define savech_(c) {if (limit<=p)p=bufenlarge(&b,&limit); *p++ =(c);}
546 # endif
547
548 if (nexttok!=ID || strcmp(NextString,key) == 0)
549 clear_buf(&r);
550 else {
551 warnignore();
552 fin = finptr;
553 frew = foutptr;
554 setupcache(fin); cache(fin);
555 # if large_memory
556 r.string = (char const*)cacheptr() - strlen(NextString) - 1;
557 # else
558 bufautobegin(&b);
559 bufscpy(&b, NextString);
560 p = b.string + strlen(b.string);
561 limit = b.string + b.size;
562 # endif
563 ffree1(NextString);
564 c = nextc;
565 for (;;) {
566 for (;;) {
567 savech_(c)
568 switch (ctab[c]) {
569 default:
570 fatserror("unknown character `%c'", c);
571 /*NOTREACHED*/
572 case NEWLN:
573 ++rcsline;
574 /* fall into */
575 case COLON: case DIGIT: case LETTER: case Letter:
576 case PERIOD: case SPACE:
577 GETC_(frew, c)
578 continue;
579 case SBEGIN: /* long string */
580 for (;;) {
581 for (;;) {
582 GETC_(frew, c)
583 savech_(c)
584 switch (c) {
585 case '\n':
586 ++rcsline;
587 /* fall into */
588 default:
589 continue;
590
591 case SDELIM:
592 break;
593 }
594 break;
595 }
596 GETC_(frew, c)
597 if (c != SDELIM)
598 break;
599 savech_(c)
600 }
601 continue;
602 case SEMI:
603 cacheget_(c)
604 if (ctab[c] == NEWLN) {
605 if (frew)
606 aputc_(c, frew)
607 ++rcsline;
608 savech_(c)
609 cacheget_(c)
610 }
611 # if large_memory
612 r.size = (char const*)cacheptr() - 1 - r.string;
613 # endif
614 for (;;) {
615 switch (ctab[c]) {
616 case NEWLN:
617 ++rcsline;
618 /* fall into */
619 case SPACE:
620 cacheget_(c)
621 continue;
622
623 default: break;
624 }
625 break;
626 }
627 if (frew)
628 aputc_(c, frew)
629 break;
630 }
631 break;
632 }
633 if (ctab[c] == Letter) {
634 for (kn = key; c && *kn==c; kn++)
635 GETC_(frew, c)
636 if (!*kn)
637 switch (ctab[c]) {
638 case DIGIT: case LETTER: case Letter:
639 case IDCHAR: case PERIOD:
640 break;
641 default:
642 nextc = c;
643 NextString = fstr_save(key);
644 nexttok = ID;
645 uncache(fin);
646 goto returnit;
647 }
648 # if !large_memory
649 {
650 register char const *ki;
651 for (ki=key; ki<kn; )
652 savech_(*ki++)
653 }
654 # endif
655 } else {
656 nextc = c;
657 uncache(fin);
658 nextlex();
659 break;
660 }
661 }
662 returnit:;
663 # if !large_memory
664 return bufremember(&b, (size_t)(p - b.string));
665 # endif
666 }
667 return r;
668 }
669
670
671 void
readstring()672 readstring()
673 /* skip over characters until terminating single SDELIM */
674 /* If foutptr is set, copy every character read to foutptr. */
675 /* Does not advance nextlex at the end. */
676 { int c;
677 declarecache;
678 register FILE *frew;
679 register RILE *fin;
680 fin=finptr; frew=foutptr;
681 setupcache(fin); cache(fin);
682 for (;;) {
683 GETC_(frew, c)
684 switch (c) {
685 case '\n':
686 ++rcsline;
687 break;
688
689 case SDELIM:
690 GETC_(frew, c)
691 if (c != SDELIM) {
692 /* end of string */
693 nextc = c;
694 uncache(fin);
695 return;
696 }
697 break;
698 }
699 }
700 }
701
702
703 void
printstring()704 printstring()
705 /* Function: copy a string to stdout, until terminated with a single SDELIM.
706 * Does not advance nextlex at the end.
707 */
708 {
709 int c;
710 declarecache;
711 register FILE *fout;
712 register RILE *fin;
713 fin=finptr;
714 fout = stdout;
715 setupcache(fin); cache(fin);
716 for (;;) {
717 cacheget_(c)
718 switch (c) {
719 case '\n':
720 ++rcsline;
721 break;
722 case SDELIM:
723 cacheget_(c)
724 if (c != SDELIM) {
725 nextc=c;
726 uncache(fin);
727 return;
728 }
729 break;
730 }
731 aputc_(c,fout)
732 }
733 }
734
735
736
737 struct cbuf
savestring(target)738 savestring(target)
739 struct buf *target;
740 /* Copies a string terminated with SDELIM from file finptr to buffer target.
741 * Double SDELIM is replaced with SDELIM.
742 * If foutptr is set, the string is also copied unchanged to foutptr.
743 * Does not advance nextlex at the end.
744 * Yield a copy of *TARGET, except with exact length.
745 */
746 {
747 int c;
748 declarecache;
749 register FILE *frew;
750 register char *tp;
751 register RILE *fin;
752 char const *limit;
753 struct cbuf r;
754
755 fin=finptr; frew=foutptr;
756 setupcache(fin); cache(fin);
757 tp = target->string; limit = tp + target->size;
758 for (;;) {
759 GETC_(frew, c)
760 switch (c) {
761 case '\n':
762 ++rcsline;
763 break;
764 case SDELIM:
765 GETC_(frew, c)
766 if (c != SDELIM) {
767 /* end of string */
768 nextc=c;
769 r.string = target->string;
770 r.size = tp - r.string;
771 uncache(fin);
772 return r;
773 }
774 break;
775 }
776 if (tp == limit)
777 tp = bufenlarge(target, &limit);
778 *tp++ = c;
779 }
780 }
781
782
783 static char *
checkidentifier(id,delimiter,dotok)784 checkidentifier(id, delimiter, dotok)
785 register char *id;
786 int delimiter;
787 register int dotok;
788 /* Function: check whether the string starting at id is an */
789 /* identifier and return a pointer to the delimiter*/
790 /* after the identifier. White space, delim and 0 */
791 /* are legal delimiters. Aborts the program if not*/
792 /* a legal identifier. Useful for checking commands*/
793 /* If !delim, the only delimiter is 0. */
794 /* Allow '.' in identifier only if DOTOK is set. */
795 {
796 register char *temp;
797 register char c;
798 register char delim = delimiter;
799 int isid = false;
800
801 temp = id;
802 for (;; id++) {
803 switch (ctab[(unsigned char)(c = *id)]) {
804 case IDCHAR:
805 case LETTER:
806 case Letter:
807 isid = true;
808 continue;
809
810 case DIGIT:
811 continue;
812
813 case PERIOD:
814 if (dotok)
815 continue;
816 break;
817
818 default:
819 break;
820 }
821 break;
822 }
823 if ( ! isid
824 || (c && (!delim || (c!=delim && c!=' ' && c!='\t' && c!='\n')))
825 ) {
826 /* append \0 to end of id before error message */
827 while ((c = *id) && c!=' ' && c!='\t' && c!='\n' && c!=delim)
828 id++;
829 *id = '\0';
830 faterror("invalid %s `%s'",
831 dotok ? "identifier" : "symbol", temp
832 );
833 }
834 return id;
835 }
836
837 char *
checkid(id,delimiter)838 checkid(id, delimiter)
839 char *id;
840 int delimiter;
841 {
842 return checkidentifier(id, delimiter, true);
843 }
844
845 char *
checksym(sym,delimiter)846 checksym(sym, delimiter)
847 char *sym;
848 int delimiter;
849 {
850 return checkidentifier(sym, delimiter, false);
851 }
852
853 void
checksid(id)854 checksid(id)
855 char *id;
856 /* Check whether the string ID is an identifier. */
857 {
858 VOID checkid(id, 0);
859 }
860
861 void
checkssym(sym)862 checkssym(sym)
863 char *sym;
864 {
865 VOID checksym(sym, 0);
866 }
867
868
869 #if !large_memory
870 # define Iclose(f) fclose(f)
871 #else
872 # if !maps_memory
873 static int Iclose P((RILE *));
874 static int
Iclose(f)875 Iclose(f)
876 register RILE *f;
877 {
878 tfree(f->base);
879 f->base = 0;
880 return fclose(f->stream);
881 }
882 # else
883 static int Iclose P((RILE *));
884 static int
Iclose(f)885 Iclose(f)
886 register RILE *f;
887 {
888 (* f->deallocate) (f);
889 f->base = 0;
890 return close(f->fd);
891 }
892
893 # if has_map_fd
894 static void map_fd_deallocate P((RILE *));
895 static void
map_fd_deallocate(f)896 map_fd_deallocate(f)
897 register RILE *f;
898 {
899 if (vm_deallocate(
900 task_self(),
901 (vm_address_t) f->base,
902 (vm_size_t) (f->lim - f->base)
903 ) != KERN_SUCCESS)
904 efaterror("vm_deallocate");
905 }
906 # endif
907 # if has_mmap
908 static void mmap_deallocate P((RILE *));
909 static void
mmap_deallocate(f)910 mmap_deallocate(f)
911 register RILE *f;
912 {
913 if (munmap((char *) f->base, (size_t) (f->lim - f->base)) != 0)
914 efaterror("munmap");
915 }
916 # endif
917 static void read_deallocate P((RILE *));
918 static void
read_deallocate(f)919 read_deallocate(f)
920 RILE *f;
921 {
922 tfree(f->base);
923 }
924
925 static void nothing_to_deallocate P((RILE *));
926 static void
nothing_to_deallocate(f)927 nothing_to_deallocate(f)
928 RILE *f;
929 {
930 }
931 # endif
932 #endif
933
934
935 #if large_memory && maps_memory
936 static RILE *fd2_RILE P((int,char const*,struct stat*));
937 static RILE *
fd2_RILE(fd,name,status)938 fd2_RILE(fd, name, status)
939 #else
940 static RILE *fd2RILE P((int,char const*,char const*,struct stat*));
941 static RILE *
942 fd2RILE(fd, name, type, status)
943 char const *type;
944 #endif
945 int fd;
946 char const *name;
947 register struct stat *status;
948 {
949 struct stat st;
950
951 if (!status)
952 status = &st;
953 if (fstat(fd, status) != 0)
954 efaterror(name);
955 if (!S_ISREG(status->st_mode)) {
956 error("`%s' is not a regular file", name);
957 VOID close(fd);
958 errno = EINVAL;
959 return 0;
960 } else {
961
962 # if !(large_memory && maps_memory)
963 FILE *stream;
964 if (!(stream = fdopen(fd, type)))
965 efaterror(name);
966 # endif
967
968 # if !large_memory
969 return stream;
970 # else
971 # define RILES 3
972 {
973 static RILE rilebuf[RILES];
974
975 register RILE *f;
976 size_t s = status->st_size;
977
978 if (s != status->st_size)
979 faterror("%s: too large", name);
980 for (f = rilebuf; f->base; f++)
981 if (f == rilebuf+RILES)
982 faterror("too many RILEs");
983 # if maps_memory
984 f->deallocate = nothing_to_deallocate;
985 # endif
986 if (!s) {
987 static unsigned char nothing;
988 f->base = ¬hing; /* Any nonzero address will do. */
989 } else {
990 f->base = 0;
991 # if has_map_fd
992 map_fd(
993 fd, (vm_offset_t)0, (vm_address_t*) &f->base,
994 TRUE, (vm_size_t)s
995 );
996 f->deallocate = map_fd_deallocate;
997 # endif
998 # if has_mmap
999 if (!f->base) {
1000 catchmmapints();
1001 f->base = (unsigned char *) mmap(
1002 (char *)0, s, PROT_READ, MAP_FILE|MAP_SHARED,
1003 fd, (off_t)0
1004 );
1005 # ifndef MAP_FAILED
1006 # define MAP_FAILED (-1)
1007 # endif
1008 if (f->base == (unsigned char *) MAP_FAILED)
1009 f->base = 0;
1010 else {
1011 # if has_NFS && mmap_signal
1012 /*
1013 * On many hosts, the superuser
1014 * can mmap an NFS file it can't read.
1015 * So access the first page now, and print
1016 * a nice message if a bus error occurs.
1017 */
1018 readAccessFilenameBuffer(name, f->base);
1019 # endif
1020 }
1021 f->deallocate = mmap_deallocate;
1022 }
1023 # endif
1024 if (!f->base) {
1025 f->base = tnalloc(unsigned char, s);
1026 # if maps_memory
1027 {
1028 /*
1029 * We can't map the file into memory for some reason.
1030 * Read it into main memory all at once; this is
1031 * the simplest substitute for memory mapping.
1032 */
1033 char *bufptr = (char *) f->base;
1034 size_t bufsiz = s;
1035 do {
1036 ssize_t r = read(fd, bufptr, bufsiz);
1037 switch (r) {
1038 case -1:
1039 efaterror(name);
1040
1041 case 0:
1042 /* The file must have shrunk! */
1043 status->st_size = s -= bufsiz;
1044 bufsiz = 0;
1045 break;
1046
1047 default:
1048 bufptr += r;
1049 bufsiz -= r;
1050 break;
1051 }
1052 } while (bufsiz);
1053 if (lseek(fd, (off_t)0, SEEK_SET) == -1)
1054 efaterror(name);
1055 f->deallocate = read_deallocate;
1056 }
1057 # endif
1058 }
1059 }
1060 f->ptr = f->base;
1061 f->lim = f->base + s;
1062 f->fd = fd;
1063 # if !maps_memory
1064 f->readlim = f->base;
1065 f->stream = stream;
1066 # endif
1067 if_advise_access(s, f, MADV_SEQUENTIAL);
1068 return f;
1069 }
1070 # endif
1071 }
1072 }
1073
1074 #if !maps_memory && large_memory
1075 int
Igetmore(f)1076 Igetmore(f)
1077 register RILE *f;
1078 {
1079 register fread_type r;
1080 register size_t s = f->lim - f->readlim;
1081
1082 if (BUFSIZ < s)
1083 s = BUFSIZ;
1084 if (!(r = Fread(f->readlim, sizeof(*f->readlim), s, f->stream))) {
1085 testIerror(f->stream);
1086 f->lim = f->readlim; /* The file might have shrunk! */
1087 return 0;
1088 }
1089 f->readlim += r;
1090 return 1;
1091 }
1092 #endif
1093
1094 #if has_madvise && has_mmap && large_memory
1095 void
advise_access(f,advice)1096 advise_access(f, advice)
1097 register RILE *f;
1098 int advice;
1099 {
1100 if (f->deallocate == mmap_deallocate)
1101 VOID madvise((char *)f->base, (size_t)(f->lim - f->base), advice);
1102 /* Don't worry if madvise fails; it's only advisory. */
1103 }
1104 #endif
1105
1106 RILE *
1107 #if large_memory && maps_memory
I_open(name,status)1108 I_open(name, status)
1109 #else
1110 Iopen(name, type, status)
1111 char const *type;
1112 #endif
1113 char const *name;
1114 struct stat *status;
1115 /* Open NAME for reading, yield its descriptor, and set *STATUS. */
1116 {
1117 int fd = fdSafer(open(name, O_RDONLY
1118 # if OPEN_O_BINARY
1119 | (strchr(type,'b') ? OPEN_O_BINARY : 0)
1120 # endif
1121 ));
1122
1123 if (fd < 0)
1124 return 0;
1125 # if large_memory && maps_memory
1126 return fd2_RILE(fd, name, status);
1127 # else
1128 return fd2RILE(fd, name, type, status);
1129 # endif
1130 }
1131
1132
1133 static int Oerrloop;
1134
1135 void
Oerror()1136 Oerror()
1137 {
1138 if (Oerrloop)
1139 exiterr();
1140 Oerrloop = true;
1141 efaterror("output error");
1142 }
1143
Ieof()1144 void Ieof() { fatserror("unexpected end of file"); }
Ierror()1145 void Ierror() { efaterror("input error"); }
testIerror(f)1146 void testIerror(f) FILE *f; { if (ferror(f)) Ierror(); }
testOerror(o)1147 void testOerror(o) FILE *o; { if (ferror(o)) Oerror(); }
1148
Ifclose(f)1149 void Ifclose(f) RILE *f; { if (f && Iclose(f)!=0) Ierror(); }
Ofclose(f)1150 void Ofclose(f) FILE *f; { if (f && fclose(f)!=0) Oerror(); }
Izclose(p)1151 void Izclose(p) RILE **p; { Ifclose(*p); *p = 0; }
Ozclose(p)1152 void Ozclose(p) FILE **p; { Ofclose(*p); *p = 0; }
1153
1154 #if !large_memory
1155 void
testIeof(f)1156 testIeof(f)
1157 FILE *f;
1158 {
1159 testIerror(f);
1160 if (feof(f))
1161 Ieof();
1162 }
Irewind(f)1163 void Irewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Ierror(); }
1164 #endif
1165
Orewind(f)1166 void Orewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Oerror(); }
1167
aflush(f)1168 void aflush(f) FILE *f; { if (fflush(f) != 0) Oerror(); }
eflush()1169 void eflush() { if (fflush(stderr)!=0 && !Oerrloop) Oerror(); }
oflush()1170 void oflush()
1171 {
1172 if (fflush(workstdout ? workstdout : stdout) != 0 && !Oerrloop)
1173 Oerror();
1174 }
1175
1176 void
fatcleanup(already_newline)1177 fatcleanup(already_newline)
1178 int already_newline;
1179 {
1180 VOID fprintf(stderr, already_newline+"\n%s aborted\n", cmdid);
1181 exiterr();
1182 }
1183
1184 static void
startsay(s,t)1185 startsay(s, t)
1186 const char *s, *t;
1187 {
1188 oflush();
1189 if (s)
1190 aprintf(stderr, "%s: %s: %s", cmdid, s, t);
1191 else
1192 aprintf(stderr, "%s: %s", cmdid, t);
1193 }
1194
1195 static void
fatsay(s)1196 fatsay(s)
1197 char const *s;
1198 {
1199 startsay(s, "");
1200 }
1201
1202 static void
errsay(s)1203 errsay(s)
1204 char const *s;
1205 {
1206 fatsay(s);
1207 nerror++;
1208 }
1209
1210 static void
warnsay(s)1211 warnsay(s)
1212 char const *s;
1213 {
1214 startsay(s, "warning: ");
1215 }
1216
eerror(s)1217 void eerror(s) char const *s; { enerror(errno,s); }
1218
1219 void
enerror(e,s)1220 enerror(e,s)
1221 int e;
1222 char const *s;
1223 {
1224 errsay((char const*)0);
1225 errno = e;
1226 perror(s);
1227 eflush();
1228 }
1229
efaterror(s)1230 void efaterror(s) char const *s; { enfaterror(errno,s); }
1231
1232 void
enfaterror(e,s)1233 enfaterror(e,s)
1234 int e;
1235 char const *s;
1236 {
1237 fatsay((char const*)0);
1238 errno = e;
1239 perror(s);
1240 fatcleanup(true);
1241 }
1242
1243 #if has_prototypes
1244 void
error(char const * format,...)1245 error(char const *format,...)
1246 #else
1247 /*VARARGS1*/ void error(format, va_alist) char const *format; va_dcl
1248 #endif
1249 /* non-fatal error */
1250 {
1251 va_list args;
1252 errsay((char const*)0);
1253 vararg_start(args, format);
1254 fvfprintf(stderr, format, args);
1255 va_end(args);
1256 afputc('\n',stderr);
1257 eflush();
1258 }
1259
1260 #if has_prototypes
1261 void
rcserror(char const * format,...)1262 rcserror(char const *format,...)
1263 #else
1264 /*VARARGS1*/ void rcserror(format, va_alist) char const *format; va_dcl
1265 #endif
1266 /* non-fatal RCS file error */
1267 {
1268 va_list args;
1269 errsay(RCSname);
1270 vararg_start(args, format);
1271 fvfprintf(stderr, format, args);
1272 va_end(args);
1273 afputc('\n',stderr);
1274 eflush();
1275 }
1276
1277 #if has_prototypes
1278 void
workerror(char const * format,...)1279 workerror(char const *format,...)
1280 #else
1281 /*VARARGS1*/ void workerror(format, va_alist) char const *format; va_dcl
1282 #endif
1283 /* non-fatal working file error */
1284 {
1285 va_list args;
1286 errsay(workname);
1287 vararg_start(args, format);
1288 fvfprintf(stderr, format, args);
1289 va_end(args);
1290 afputc('\n',stderr);
1291 eflush();
1292 }
1293
1294 #if has_prototypes
1295 void
fatserror(char const * format,...)1296 fatserror(char const *format,...)
1297 #else
1298 /*VARARGS1*/ void
1299 fatserror(format, va_alist) char const *format; va_dcl
1300 #endif
1301 /* fatal RCS file syntax error */
1302 {
1303 va_list args;
1304 oflush();
1305 VOID fprintf(stderr, "%s: %s:%ld: ", cmdid, RCSname, rcsline);
1306 vararg_start(args, format);
1307 fvfprintf(stderr, format, args);
1308 va_end(args);
1309 fatcleanup(false);
1310 }
1311
1312 #if has_prototypes
1313 void
faterror(char const * format,...)1314 faterror(char const *format,...)
1315 #else
1316 /*VARARGS1*/ void faterror(format, va_alist)
1317 char const *format; va_dcl
1318 #endif
1319 /* fatal error, terminates program after cleanup */
1320 {
1321 va_list args;
1322 fatsay((char const*)0);
1323 vararg_start(args, format);
1324 fvfprintf(stderr, format, args);
1325 va_end(args);
1326 fatcleanup(false);
1327 }
1328
1329 #if has_prototypes
1330 void
rcsfaterror(char const * format,...)1331 rcsfaterror(char const *format,...)
1332 #else
1333 /*VARARGS1*/ void rcsfaterror(format, va_alist)
1334 char const *format; va_dcl
1335 #endif
1336 /* fatal RCS file error, terminates program after cleanup */
1337 {
1338 va_list args;
1339 fatsay(RCSname);
1340 vararg_start(args, format);
1341 fvfprintf(stderr, format, args);
1342 va_end(args);
1343 fatcleanup(false);
1344 }
1345
1346 #if has_prototypes
1347 void
warn(char const * format,...)1348 warn(char const *format,...)
1349 #else
1350 /*VARARGS1*/ void warn(format, va_alist) char const *format; va_dcl
1351 #endif
1352 /* warning */
1353 {
1354 va_list args;
1355 if (!quietflag) {
1356 warnsay((char *)0);
1357 vararg_start(args, format);
1358 fvfprintf(stderr, format, args);
1359 va_end(args);
1360 afputc('\n', stderr);
1361 eflush();
1362 }
1363 }
1364
1365 #if has_prototypes
1366 void
rcswarn(char const * format,...)1367 rcswarn(char const *format,...)
1368 #else
1369 /*VARARGS1*/ void rcswarn(format, va_alist) char const *format; va_dcl
1370 #endif
1371 /* RCS file warning */
1372 {
1373 va_list args;
1374 if (!quietflag) {
1375 warnsay(RCSname);
1376 vararg_start(args, format);
1377 fvfprintf(stderr, format, args);
1378 va_end(args);
1379 afputc('\n', stderr);
1380 eflush();
1381 }
1382 }
1383
1384 #if has_prototypes
1385 void
workwarn(char const * format,...)1386 workwarn(char const *format,...)
1387 #else
1388 /*VARARGS1*/ void workwarn(format, va_alist) char const *format; va_dcl
1389 #endif
1390 /* working file warning */
1391 {
1392 va_list args;
1393 if (!quietflag) {
1394 warnsay(workname);
1395 vararg_start(args, format);
1396 fvfprintf(stderr, format, args);
1397 va_end(args);
1398 afputc('\n', stderr);
1399 eflush();
1400 }
1401 }
1402
1403 void
redefined(c)1404 redefined(c)
1405 int c;
1406 {
1407 warn("redefinition of -%c option", c);
1408 }
1409
1410 #if has_prototypes
1411 void
diagnose(char const * format,...)1412 diagnose(char const *format,...)
1413 #else
1414 /*VARARGS1*/ void diagnose(format, va_alist) char const *format; va_dcl
1415 #endif
1416 /* prints a diagnostic message */
1417 /* Unlike the other routines, it does not append a newline. */
1418 /* This lets some callers suppress the newline, and is faster */
1419 /* in implementations that flush stderr just at the end of each printf. */
1420 {
1421 va_list args;
1422 if (!quietflag) {
1423 oflush();
1424 vararg_start(args, format);
1425 fvfprintf(stderr, format, args);
1426 va_end(args);
1427 eflush();
1428 }
1429 }
1430
1431
1432
1433 void
afputc(c,f)1434 afputc(c, f)
1435 /* afputc(c,f); acts like aputc_(c,f) but is smaller and slower. */
1436 int c;
1437 register FILE *f;
1438 {
1439 aputc_(c,f)
1440 }
1441
1442
1443 void
aputs(s,iop)1444 aputs(s, iop)
1445 char const *s;
1446 FILE *iop;
1447 /* Function: Put string s on file iop, abort on error.
1448 */
1449 {
1450 #if has_fputs
1451 if (fputs(s, iop) < 0)
1452 Oerror();
1453 #else
1454 awrite(s, strlen(s), iop);
1455 #endif
1456 }
1457
1458
1459
1460 void
1461 #if has_prototypes
fvfprintf(FILE * stream,char const * format,va_list args)1462 fvfprintf(FILE *stream, char const *format, va_list args)
1463 #else
1464 fvfprintf(stream,format,args) FILE *stream; char *format; va_list args;
1465 #endif
1466 /* like vfprintf, except abort program on error */
1467 {
1468 #if has_vfprintf
1469 if (vfprintf(stream, format, args) < 0)
1470 Oerror();
1471 #else
1472 # if has__doprintf
1473 _doprintf(stream, format, args);
1474 # else
1475 # if has__doprnt
1476 _doprnt(format, args, stream);
1477 # else
1478 int *a = (int *)args;
1479 VOID fprintf(stream, format,
1480 a[0], a[1], a[2], a[3], a[4],
1481 a[5], a[6], a[7], a[8], a[9]
1482 );
1483 # endif
1484 # endif
1485 if (ferror(stream))
1486 Oerror();
1487 #endif
1488 }
1489
1490 #if has_prototypes
1491 void
aprintf(FILE * iop,char const * fmt,...)1492 aprintf(FILE *iop, char const *fmt, ...)
1493 #else
1494 /*VARARGS2*/ void
1495 aprintf(iop, fmt, va_alist)
1496 FILE *iop;
1497 char const *fmt;
1498 va_dcl
1499 #endif
1500 /* Function: formatted output. Same as fprintf in stdio,
1501 * but aborts program on error
1502 */
1503 {
1504 va_list ap;
1505 vararg_start(ap, fmt);
1506 fvfprintf(iop, fmt, ap);
1507 va_end(ap);
1508 }
1509
1510
1511
1512 #ifdef LEXDB
1513 /* test program reading a stream of lexemes and printing the tokens.
1514 */
1515
1516
1517
1518 int
main(argc,argv)1519 main(argc,argv)
1520 int argc; char * argv[];
1521 {
1522 cmdid="lextest";
1523 if (argc<2) {
1524 aputs("No input file\n",stderr);
1525 exitmain(EXIT_FAILURE);
1526 }
1527 if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
1528 faterror("can't open input file %s",argv[1]);
1529 }
1530 Lexinit();
1531 while (!eoflex()) {
1532 switch (nexttok) {
1533
1534 case ID:
1535 VOID printf("ID: %s",NextString);
1536 break;
1537
1538 case NUM:
1539 if (hshenter)
1540 VOID printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab);
1541 else
1542 VOID printf("NUM, unentered: %s",NextString);
1543 hshenter = !hshenter; /*alternate between dates and numbers*/
1544 break;
1545
1546 case COLON:
1547 VOID printf("COLON"); break;
1548
1549 case SEMI:
1550 VOID printf("SEMI"); break;
1551
1552 case STRING:
1553 readstring();
1554 VOID printf("STRING"); break;
1555
1556 case UNKN:
1557 VOID printf("UNKN"); break;
1558
1559 default:
1560 VOID printf("DEFAULT"); break;
1561 }
1562 VOID printf(" | ");
1563 nextlex();
1564 }
1565 exitmain(EXIT_SUCCESS);
1566 }
1567
exiterr()1568 void exiterr() { _exit(EXIT_FAILURE); }
1569
1570
1571 #endif
1572