/* lexical analysis of RCS files */ /****************************************************************************** * Lexical Analysis. * hashtable, Lexinit, nextlex, getlex, getkey, * getid, getnum, readstring, printstring, savestring, * checkid, fatserror, error, faterror, warn, diagnose * Testprogram: define LEXDB ****************************************************************************** */ /* Copyright 1982, 1988, 1989 Walter Tichy Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert Distributed under license by the Free Software Foundation, Inc. This file is part of RCS. RCS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. RCS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with RCS; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Report problems and direct all questions to: rcs-bugs@cs.purdue.edu */ /* * Revision 5.19 1995/06/16 06:19:24 eggert * Update FSF address. * * Revision 5.18 1995/06/01 16:23:43 eggert * (map_fd_deallocate,mmap_deallocate,read_deallocate,nothing_to_deallocate): * New functions. * (Iclose): If large_memory and maps_memory, use them to deallocate mapping. * (fd2RILE): Use map_fd if available. * If one mapping method fails, try the next instead of giving up; * if they all fail, fall back on ordinary read. * Work around bug: root mmap over NFS succeeds, but accessing dumps core. * Use MAP_FAILED macro for mmap failure, and `char *' instead of caddr_t. * (advise_access): Use madvise only if this instance used mmap. * (Iopen): Use fdSafer to get safer file descriptor. * (aflush): Moved here from rcsedit.c. * * Revision 5.17 1994/03/20 04:52:58 eggert * Don't worry if madvise fails. Add Orewind. Remove lint. * * Revision 5.16 1993/11/09 17:55:29 eggert * Fix `label: }' typo. * * Revision 5.15 1993/11/03 17:42:27 eggert * Improve quality of diagnostics by putting file names in them more often. * Don't discard ignored phrases. * * Revision 5.14 1992/07/28 16:12:44 eggert * Identifiers may now start with a digit and (unless they are symbolic names) * may contain `.'. Avoid `unsigned'. Statement macro names now end in _. * * Revision 5.13 1992/02/17 23:02:27 eggert * Work around NFS mmap SIGBUS problem. * * Revision 5.12 1992/01/06 02:42:34 eggert * Use OPEN_O_BINARY if mode contains 'b'. * * Revision 5.11 1991/11/03 03:30:44 eggert * Fix porting bug to ancient hosts lacking vfprintf. * * Revision 5.10 1991/10/07 17:32:46 eggert * Support piece tables even if !has_mmap. * * Revision 5.9 1991/09/24 00:28:42 eggert * Don't export errsay(). * * Revision 5.8 1991/08/19 03:13:55 eggert * Add eoflex(), mmap support. Tune. * * Revision 5.7 1991/04/21 11:58:26 eggert * Add MS-DOS support. * * Revision 5.6 1991/02/25 07:12:42 eggert * Work around fputs bug. strsave -> str_save (DG/UX name clash) * * Revision 5.5 1990/12/04 05:18:47 eggert * Use -I for prompts and -q for diagnostics. * * Revision 5.4 1990/11/19 20:05:28 hammer * no longer gives warning about unknown keywords if -q is specified * * Revision 5.3 1990/11/01 05:03:48 eggert * When ignoring unknown phrases, copy them to the output RCS file. * * Revision 5.2 1990/09/04 08:02:27 eggert * Count RCS lines better. * * Revision 5.1 1990/08/29 07:14:03 eggert * Work around buggy compilers with defective argument promotion. * * Revision 5.0 1990/08/22 08:12:55 eggert * Remove compile-time limits; use malloc instead. * Report errno-related errors with perror(). * Ansify and Posixate. Add support for ISO 8859. * Use better hash function. * * Revision 4.6 89/05/01 15:13:07 narten * changed copyright header to reflect current distribution rules * * Revision 4.5 88/08/28 15:01:12 eggert * Don't loop when writing error messages to a full filesystem. * Flush stderr/stdout when mixing output. * Yield exit status compatible with diff(1). * Shrink stdio code size; allow cc -R; remove lint. * * Revision 4.4 87/12/18 11:44:47 narten * fixed to use "varargs" in "fprintf"; this is required if it is to * work on a SPARC machine such as a Sun-4 * * Revision 4.3 87/10/18 10:37:18 narten * Updating version numbers. Changes relative to 1.1 actually relative * to version 4.1 * * Revision 1.3 87/09/24 14:00:17 narten * Sources now pass through lint (if you ignore printf/sprintf/fprintf * warnings) * * Revision 1.2 87/03/27 14:22:33 jenkins * Port to suns * * Revision 4.1 83/03/25 18:12:51 wft * Only changed $Header to $Id. * * Revision 3.3 82/12/10 16:22:37 wft * Improved error messages, changed exit status on error to 1. * * Revision 3.2 82/11/28 21:27:10 wft * Renamed ctab to map and included EOFILE; ctab is now a macro in rcsbase.h. * Added fflsbuf(), fputs(), and fprintf(), which abort the RCS operations * properly in case there is an IO-error (e.g., file system full). * * Revision 3.1 82/10/11 19:43:56 wft * removed unused label out:; * made sure all calls to getc() return into an integer, not a char. */ /* #define LEXDB */ /* version LEXDB is for testing the lexical analyzer. The testprogram * reads a stream of lexemes, enters the revision numbers into the * hashtable, and prints the recognized tokens. Keywords are recognized * as identifiers. */ #include "rcsbase.h" libId(lexId, "$FreeBSD$") static char *checkidentifier P((char*,int,int)); static void errsay P((char const*)); static void fatsay P((char const*)); static void lookup P((char const*)); static void startsay P((const char*,const char*)); static void warnsay P((char const*)); static struct hshentry *nexthsh; /*pointer to next hash entry, set by lookup*/ enum tokens nexttok; /*next token, set by nextlex */ int hshenter; /*if true, next suitable lexeme will be entered */ /*into the symbol table. Handle with care. */ int nextc; /*next input character, initialized by Lexinit */ long rcsline; /*current line-number of input */ int nerror; /*counter for errors */ int quietflag; /*indicates quiet mode */ RILE * finptr; /*input file descriptor */ FILE * frewrite; /*file descriptor for echoing input */ FILE * foutptr; /* copy of frewrite, but 0 to suppress echo */ static struct buf tokbuf; /* token buffer */ char const * NextString; /* next token */ /* * Our hash algorithm is h[0] = 0, h[i+1] = 4*h[i] + c, * so hshsize should be odd. * See B J McKenzie, R Harries & T Bell, Selecting a hashing algorithm, * Software--practice & experience 20, 2 (Feb 1990), 209-224. */ #ifndef hshsize # define hshsize 511 #endif static struct hshentry *hshtab[hshsize]; /*hashtable */ static int ignored_phrases; /* have we ignored phrases in this RCS file? */ void warnignore() { if (!ignored_phrases) { ignored_phrases = true; rcswarn("Unknown phrases like `%s ...;' are present.", NextString); } } static void lookup(str) char const *str; /* Function: Looks up the character string pointed to by str in the * hashtable. If the string is not present, a new entry for it is created. * In any case, the address of the corresponding hashtable entry is placed * into nexthsh. */ { register unsigned ihash; /* index into hashtable */ register char const *sp; register struct hshentry *n, **p; /* calculate hash code */ sp = str; ihash = 0; while (*sp) ihash = (ihash<<2) + *sp++; ihash %= hshsize; for (p = &hshtab[ihash]; ; p = &n->nexthsh) if (!(n = *p)) { /* empty slot found */ *p = n = ftalloc(struct hshentry); n->num = fstr_save(str); n->nexthsh = 0; # ifdef LEXDB VOID printf("\nEntered: %s at %u ", str, ihash); # endif break; } else if (strcmp(str, n->num) == 0) /* match found */ break; nexthsh = n; NextString = n->num; } void Lexinit() /* Function: Initialization of lexical analyzer: * initializes the hashtable, * initializes nextc, nexttok if finptr != 0 */ { register int c; for (c = hshsize; 0 <= --c; ) { hshtab[c] = 0; } nerror = 0; if (finptr) { foutptr = 0; hshenter = true; ignored_phrases = false; rcsline = 1; bufrealloc(&tokbuf, 2); Iget_(finptr, nextc) nextlex(); /*initial token*/ } } void nextlex() /* Function: Reads the next token and sets nexttok to the next token code. * Only if hshenter is set, a revision number is entered into the * hashtable and a pointer to it is placed into nexthsh. * This is useful for avoiding that dates are placed into the hashtable. * For ID's and NUM's, NextString is set to the character string. * Assumption: nextc contains the next character. */ { register c; declarecache; register FILE *frew; register char * sp; char const *limit; register enum tokens d; register RILE *fin; fin=finptr; frew=foutptr; setupcache(fin); cache(fin); c = nextc; for (;;) { switch ((d = ctab[c])) { default: fatserror("unknown character `%c'", c); /*NOTREACHED*/ case NEWLN: ++rcsline; # ifdef LEXDB afputc('\n',stdout); # endif /* Note: falls into next case */ case SPACE: GETC_(frew, c) continue; case IDCHAR: case LETTER: case Letter: d = ID; /* fall into */ case DIGIT: case PERIOD: sp = tokbuf.string; limit = sp + tokbuf.size; *sp++ = c; for (;;) { GETC_(frew, c) switch (ctab[c]) { case IDCHAR: case LETTER: case Letter: d = ID; /* fall into */ case DIGIT: case PERIOD: *sp++ = c; if (limit <= sp) sp = bufenlarge(&tokbuf, &limit); continue; default: break; } break; } *sp = 0; if (d == DIGIT || d == PERIOD) { d = NUM; if (hshenter) { lookup(tokbuf.string); break; } } NextString = fstr_save(tokbuf.string); break; case SBEGIN: /* long string */ d = STRING; /* note: only the initial SBEGIN has been read*/ /* read the string, and reset nextc afterwards*/ break; case COLON: case SEMI: GETC_(frew, c) break; } break; } nextc = c; nexttok = d; uncache(fin); } int eoflex() /* * Yield true if we look ahead to the end of the input, false otherwise. * nextc becomes undefined at end of file. */ { register int c; declarecache; register FILE *fout; register RILE *fin; c = nextc; fin = finptr; fout = foutptr; setupcache(fin); cache(fin); for (;;) { switch (ctab[c]) { default: nextc = c; uncache(fin); return false; case NEWLN: ++rcsline; /* fall into */ case SPACE: cachegeteof_(c, {uncache(fin);return true;}) break; } if (fout) aputc_(c, fout) } } int getlex(token) enum tokens token; /* Function: Checks if nexttok is the same as token. If so, * advances the input by calling nextlex and returns true. * otherwise returns false. * Doesn't work for strings and keywords; loses the character string for ids. */ { if (nexttok==token) { nextlex(); return(true); } else return(false); } int getkeyopt(key) char const *key; /* Function: If the current token is a keyword identical to key, * advances the input by calling nextlex and returns true; * otherwise returns false. */ { if (nexttok==ID && strcmp(key,NextString) == 0) { /* match found */ ffree1(NextString); nextlex(); return(true); } return(false); } void getkey(key) char const *key; /* Check that the current input token is a keyword identical to key, * and advance the input by calling nextlex. */ { if (!getkeyopt(key)) fatserror("missing '%s' keyword", key); } void getkeystring(key) char const *key; /* Check that the current input token is a keyword identical to key, * and advance the input by calling nextlex; then look ahead for a string. */ { getkey(key); if (nexttok != STRING) fatserror("missing string after '%s' keyword", key); } char const * getid() /* Function: Checks if nexttok is an identifier. If so, * advances the input by calling nextlex and returns a pointer * to the identifier; otherwise returns 0. * Treats keywords as identifiers. */ { register char const *name; if (nexttok==ID) { name = NextString; nextlex(); return name; } else return 0; } struct hshentry * getnum() /* Function: Checks if nexttok is a number. If so, * advances the input by calling nextlex and returns a pointer * to the hashtable entry. Otherwise returns 0. * Doesn't work if hshenter is false. */ { register struct hshentry * num; if (nexttok==NUM) { num=nexthsh; nextlex(); return num; } else return 0; } struct cbuf getphrases(key) char const *key; /* * Get a series of phrases that do not start with KEY. Yield resulting buffer. * Stop when the next phrase starts with a token that is not an identifier, * or is KEY. Copy input to foutptr if it is set. Unlike ignorephrases(), * this routine assumes nextlex() has already been invoked before we start. */ { declarecache; register int c; register char const *kn; struct cbuf r; register RILE *fin; register FILE *frew; # if large_memory # define savech_(c) ; # else register char *p; char const *limit; struct buf b; # define savech_(c) {if (limit<=p)p=bufenlarge(&b,&limit); *p++ =(c);} # endif if (nexttok!=ID || strcmp(NextString,key) == 0) clear_buf(&r); else { warnignore(); fin = finptr; frew = foutptr; setupcache(fin); cache(fin); # if large_memory r.string = (char const*)cacheptr() - strlen(NextString) - 1; # else bufautobegin(&b); bufscpy(&b, NextString); p = b.string + strlen(b.string); limit = b.string + b.size; # endif ffree1(NextString); c = nextc; for (;;) { for (;;) { savech_(c) switch (ctab[c]) { default: fatserror("unknown character `%c'", c); /*NOTREACHED*/ case NEWLN: ++rcsline; /* fall into */ case COLON: case DIGIT: case LETTER: case Letter: case PERIOD: case SPACE: GETC_(frew, c) continue; case SBEGIN: /* long string */ for (;;) { for (;;) { GETC_(frew, c) savech_(c) switch (c) { case '\n': ++rcsline; /* fall into */ default: continue; case SDELIM: break; } break; } GETC_(frew, c) if (c != SDELIM) break; savech_(c) } continue; case SEMI: cacheget_(c) if (ctab[c] == NEWLN) { if (frew) aputc_(c, frew) ++rcsline; savech_(c) cacheget_(c) } # if large_memory r.size = (char const*)cacheptr() - 1 - r.string; # endif for (;;) { switch (ctab[c]) { case NEWLN: ++rcsline; /* fall into */ case SPACE: cacheget_(c) continue; default: break; } break; } if (frew) aputc_(c, frew) break; } break; } if (ctab[c] == Letter) { for (kn = key; c && *kn==c; kn++) GETC_(frew, c) if (!*kn) switch (ctab[c]) { case DIGIT: case LETTER: case Letter: case IDCHAR: case PERIOD: break; default: nextc = c; NextString = fstr_save(key); nexttok = ID; uncache(fin); goto returnit; } # if !large_memory { register char const *ki; for (ki=key; kistring; limit = tp + target->size; for (;;) { GETC_(frew, c) switch (c) { case '\n': ++rcsline; break; case SDELIM: GETC_(frew, c) if (c != SDELIM) { /* end of string */ nextc=c; r.string = target->string; r.size = tp - r.string; uncache(fin); return r; } break; } if (tp == limit) tp = bufenlarge(target, &limit); *tp++ = c; } } static char * checkidentifier(id, delimiter, dotok) register char *id; int delimiter; register int dotok; /* Function: check whether the string starting at id is an */ /* identifier and return a pointer to the delimiter*/ /* after the identifier. White space, delim and 0 */ /* are legal delimiters. Aborts the program if not*/ /* a legal identifier. Useful for checking commands*/ /* If !delim, the only delimiter is 0. */ /* Allow '.' in identifier only if DOTOK is set. */ { register char *temp; register char c; register char delim = delimiter; int isid = false; temp = id; for (;; id++) { switch (ctab[(unsigned char)(c = *id)]) { case IDCHAR: case LETTER: case Letter: isid = true; continue; case DIGIT: continue; case PERIOD: if (dotok) continue; break; default: break; } break; } if ( ! isid || (c && (!delim || (c!=delim && c!=' ' && c!='\t' && c!='\n'))) ) { /* append \0 to end of id before error message */ while ((c = *id) && c!=' ' && c!='\t' && c!='\n' && c!=delim) id++; *id = '\0'; faterror("invalid %s `%s'", dotok ? "identifier" : "symbol", temp ); } return id; } char * checkid(id, delimiter) char *id; int delimiter; { return checkidentifier(id, delimiter, true); } char * checksym(sym, delimiter) char *sym; int delimiter; { return checkidentifier(sym, delimiter, false); } void checksid(id) char *id; /* Check whether the string ID is an identifier. */ { VOID checkid(id, 0); } void checkssym(sym) char *sym; { VOID checksym(sym, 0); } #if !large_memory # define Iclose(f) fclose(f) #else # if !maps_memory static int Iclose P((RILE *)); static int Iclose(f) register RILE *f; { tfree(f->base); f->base = 0; return fclose(f->stream); } # else static int Iclose P((RILE *)); static int Iclose(f) register RILE *f; { (* f->deallocate) (f); f->base = 0; return close(f->fd); } # if has_map_fd static void map_fd_deallocate P((RILE *)); static void map_fd_deallocate(f) register RILE *f; { if (vm_deallocate( task_self(), (vm_address_t) f->base, (vm_size_t) (f->lim - f->base) ) != KERN_SUCCESS) efaterror("vm_deallocate"); } # endif # if has_mmap static void mmap_deallocate P((RILE *)); static void mmap_deallocate(f) register RILE *f; { if (munmap((char *) f->base, (size_t) (f->lim - f->base)) != 0) efaterror("munmap"); } # endif static void read_deallocate P((RILE *)); static void read_deallocate(f) RILE *f; { tfree(f->base); } static void nothing_to_deallocate P((RILE *)); static void nothing_to_deallocate(f) RILE *f; { } # endif #endif #if large_memory && maps_memory static RILE *fd2_RILE P((int,char const*,struct stat*)); static RILE * fd2_RILE(fd, name, status) #else static RILE *fd2RILE P((int,char const*,char const*,struct stat*)); static RILE * fd2RILE(fd, name, type, status) char const *type; #endif int fd; char const *name; register struct stat *status; { struct stat st; if (!status) status = &st; if (fstat(fd, status) != 0) efaterror(name); if (!S_ISREG(status->st_mode)) { error("`%s' is not a regular file", name); VOID close(fd); errno = EINVAL; return 0; } else { # if !(large_memory && maps_memory) FILE *stream; if (!(stream = fdopen(fd, type))) efaterror(name); # endif # if !large_memory return stream; # else # define RILES 3 { static RILE rilebuf[RILES]; register RILE *f; size_t s = status->st_size; if (s != status->st_size) faterror("%s: too large", name); for (f = rilebuf; f->base; f++) if (f == rilebuf+RILES) faterror("too many RILEs"); # if maps_memory f->deallocate = nothing_to_deallocate; # endif if (!s) { static unsigned char nothing; f->base = ¬hing; /* Any nonzero address will do. */ } else { f->base = 0; # if has_map_fd map_fd( fd, (vm_offset_t)0, (vm_address_t*) &f->base, TRUE, (vm_size_t)s ); f->deallocate = map_fd_deallocate; # endif # if has_mmap if (!f->base) { catchmmapints(); f->base = (unsigned char *) mmap( (char *)0, s, PROT_READ, MAP_SHARED, fd, (off_t)0 ); # ifndef MAP_FAILED # define MAP_FAILED (-1) # endif if (f->base == (unsigned char *) MAP_FAILED) f->base = 0; else { # if has_NFS && mmap_signal /* * On many hosts, the superuser * can mmap an NFS file it can't read. * So access the first page now, and print * a nice message if a bus error occurs. */ readAccessFilenameBuffer(name, f->base); # endif } f->deallocate = mmap_deallocate; } # endif if (!f->base) { f->base = tnalloc(unsigned char, s); # if maps_memory { /* * We can't map the file into memory for some reason. * Read it into main memory all at once; this is * the simplest substitute for memory mapping. */ char *bufptr = (char *) f->base; size_t bufsiz = s; do { ssize_t r = read(fd, bufptr, bufsiz); switch (r) { case -1: efaterror(name); case 0: /* The file must have shrunk! */ status->st_size = s -= bufsiz; bufsiz = 0; break; default: bufptr += r; bufsiz -= r; break; } } while (bufsiz); if (lseek(fd, (off_t)0, SEEK_SET) == -1) efaterror(name); f->deallocate = read_deallocate; } # endif } } f->ptr = f->base; f->lim = f->base + s; f->fd = fd; # if !maps_memory f->readlim = f->base; f->stream = stream; # endif if_advise_access(s, f, MADV_SEQUENTIAL); return f; } # endif } } #if !maps_memory && large_memory int Igetmore(f) register RILE *f; { register fread_type r; register size_t s = f->lim - f->readlim; if (BUFSIZ < s) s = BUFSIZ; if (!(r = Fread(f->readlim, sizeof(*f->readlim), s, f->stream))) { testIerror(f->stream); f->lim = f->readlim; /* The file might have shrunk! */ return 0; } f->readlim += r; return 1; } #endif #if has_madvise && has_mmap && large_memory void advise_access(f, advice) register RILE *f; int advice; { if (f->deallocate == mmap_deallocate) VOID madvise((char *)f->base, (size_t)(f->lim - f->base), advice); /* Don't worry if madvise fails; it's only advisory. */ } #endif RILE * #if large_memory && maps_memory I_open(name, status) #else Iopen(name, type, status) char const *type; #endif char const *name; struct stat *status; /* Open NAME for reading, yield its descriptor, and set *STATUS. */ { int fd = fdSafer(open(name, O_RDONLY # if OPEN_O_BINARY | (strchr(type,'b') ? OPEN_O_BINARY : 0) # endif )); if (fd < 0) return 0; # if large_memory && maps_memory return fd2_RILE(fd, name, status); # else return fd2RILE(fd, name, type, status); # endif } static int Oerrloop; void Oerror() { if (Oerrloop) exiterr(); Oerrloop = true; efaterror("output error"); } void Ieof() { fatserror("unexpected end of file"); } void Ierror() { efaterror("input error"); } void testIerror(f) FILE *f; { if (ferror(f)) Ierror(); } void testOerror(o) FILE *o; { if (ferror(o)) Oerror(); } void Ifclose(f) RILE *f; { if (f && Iclose(f)!=0) Ierror(); } void Ofclose(f) FILE *f; { if (f && fclose(f)!=0) Oerror(); } void Izclose(p) RILE **p; { Ifclose(*p); *p = 0; } void Ozclose(p) FILE **p; { Ofclose(*p); *p = 0; } #if !large_memory void testIeof(f) FILE *f; { testIerror(f); if (feof(f)) Ieof(); } void Irewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Ierror(); } #endif void Orewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Oerror(); } void aflush(f) FILE *f; { if (fflush(f) != 0) Oerror(); } void eflush() { if (fflush(stderr)!=0 && !Oerrloop) Oerror(); } void oflush() { if (fflush(workstdout ? workstdout : stdout) != 0 && !Oerrloop) Oerror(); } void fatcleanup(already_newline) int already_newline; { VOID fprintf(stderr, already_newline+"\n%s aborted\n", cmdid); exiterr(); } static void startsay(s, t) const char *s, *t; { oflush(); if (s) aprintf(stderr, "%s: %s: %s", cmdid, s, t); else aprintf(stderr, "%s: %s", cmdid, t); } static void fatsay(s) char const *s; { startsay(s, ""); } static void errsay(s) char const *s; { fatsay(s); nerror++; } static void warnsay(s) char const *s; { startsay(s, "warning: "); } void eerror(s) char const *s; { enerror(errno,s); } void enerror(e,s) int e; char const *s; { errsay((char const*)0); errno = e; perror(s); eflush(); } void efaterror(s) char const *s; { enfaterror(errno,s); } void enfaterror(e,s) int e; char const *s; { fatsay((char const*)0); errno = e; perror(s); fatcleanup(true); } #if has_prototypes void error(char const *format,...) #else /*VARARGS1*/ void error(format, va_alist) char const *format; va_dcl #endif /* non-fatal error */ { va_list args; errsay((char const*)0); vararg_start(args, format); fvfprintf(stderr, format, args); va_end(args); afputc('\n',stderr); eflush(); } #if has_prototypes void rcserror(char const *format,...) #else /*VARARGS1*/ void rcserror(format, va_alist) char const *format; va_dcl #endif /* non-fatal RCS file error */ { va_list args; errsay(RCSname); vararg_start(args, format); fvfprintf(stderr, format, args); va_end(args); afputc('\n',stderr); eflush(); } #if has_prototypes void workerror(char const *format,...) #else /*VARARGS1*/ void workerror(format, va_alist) char const *format; va_dcl #endif /* non-fatal working file error */ { va_list args; errsay(workname); vararg_start(args, format); fvfprintf(stderr, format, args); va_end(args); afputc('\n',stderr); eflush(); } #if has_prototypes void fatserror(char const *format,...) #else /*VARARGS1*/ void fatserror(format, va_alist) char const *format; va_dcl #endif /* fatal RCS file syntax error */ { va_list args; oflush(); VOID fprintf(stderr, "%s: %s:%ld: ", cmdid, RCSname, rcsline); vararg_start(args, format); fvfprintf(stderr, format, args); va_end(args); fatcleanup(false); } #if has_prototypes void faterror(char const *format,...) #else /*VARARGS1*/ void faterror(format, va_alist) char const *format; va_dcl #endif /* fatal error, terminates program after cleanup */ { va_list args; fatsay((char const*)0); vararg_start(args, format); fvfprintf(stderr, format, args); va_end(args); fatcleanup(false); } #if has_prototypes void rcsfaterror(char const *format,...) #else /*VARARGS1*/ void rcsfaterror(format, va_alist) char const *format; va_dcl #endif /* fatal RCS file error, terminates program after cleanup */ { va_list args; fatsay(RCSname); vararg_start(args, format); fvfprintf(stderr, format, args); va_end(args); fatcleanup(false); } #if has_prototypes void warn(char const *format,...) #else /*VARARGS1*/ void warn(format, va_alist) char const *format; va_dcl #endif /* warning */ { va_list args; if (!quietflag) { warnsay((char *)0); vararg_start(args, format); fvfprintf(stderr, format, args); va_end(args); afputc('\n', stderr); eflush(); } } #if has_prototypes void rcswarn(char const *format,...) #else /*VARARGS1*/ void rcswarn(format, va_alist) char const *format; va_dcl #endif /* RCS file warning */ { va_list args; if (!quietflag) { warnsay(RCSname); vararg_start(args, format); fvfprintf(stderr, format, args); va_end(args); afputc('\n', stderr); eflush(); } } #if has_prototypes void workwarn(char const *format,...) #else /*VARARGS1*/ void workwarn(format, va_alist) char const *format; va_dcl #endif /* working file warning */ { va_list args; if (!quietflag) { warnsay(workname); vararg_start(args, format); fvfprintf(stderr, format, args); va_end(args); afputc('\n', stderr); eflush(); } } void redefined(c) int c; { warn("redefinition of -%c option", c); } #if has_prototypes void diagnose(char const *format,...) #else /*VARARGS1*/ void diagnose(format, va_alist) char const *format; va_dcl #endif /* prints a diagnostic message */ /* Unlike the other routines, it does not append a newline. */ /* This lets some callers suppress the newline, and is faster */ /* in implementations that flush stderr just at the end of each printf. */ { va_list args; if (!quietflag) { oflush(); vararg_start(args, format); fvfprintf(stderr, format, args); va_end(args); eflush(); } } void afputc(c, f) /* afputc(c,f); acts like aputc_(c,f) but is smaller and slower. */ int c; register FILE *f; { aputc_(c,f) } void aputs(s, iop) char const *s; FILE *iop; /* Function: Put string s on file iop, abort on error. */ { #if has_fputs if (fputs(s, iop) < 0) Oerror(); #else awrite(s, strlen(s), iop); #endif } void #if has_prototypes fvfprintf(FILE *stream, char const *format, va_list args) #else fvfprintf(stream,format,args) FILE *stream; char *format; va_list args; #endif /* like vfprintf, except abort program on error */ { #if has_vfprintf if (vfprintf(stream, format, args) < 0) Oerror(); #else # if has__doprintf _doprintf(stream, format, args); # else # if has__doprnt _doprnt(format, args, stream); # else int *a = (int *)args; VOID fprintf(stream, format, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9] ); # endif # endif if (ferror(stream)) Oerror(); #endif } #if has_prototypes void aprintf(FILE *iop, char const *fmt, ...) #else /*VARARGS2*/ void aprintf(iop, fmt, va_alist) FILE *iop; char const *fmt; va_dcl #endif /* Function: formatted output. Same as fprintf in stdio, * but aborts program on error */ { va_list ap; vararg_start(ap, fmt); fvfprintf(iop, fmt, ap); va_end(ap); } #ifdef LEXDB /* test program reading a stream of lexemes and printing the tokens. */ int main(argc,argv) int argc; char * argv[]; { cmdid="lextest"; if (argc<2) { aputs("No input file\n",stderr); exitmain(EXIT_FAILURE); } if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) { faterror("can't open input file %s",argv[1]); } Lexinit(); while (!eoflex()) { switch (nexttok) { case ID: VOID printf("ID: %s",NextString); break; case NUM: if (hshenter) VOID printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab); else VOID printf("NUM, unentered: %s",NextString); hshenter = !hshenter; /*alternate between dates and numbers*/ break; case COLON: VOID printf("COLON"); break; case SEMI: VOID printf("SEMI"); break; case STRING: readstring(); VOID printf("STRING"); break; case UNKN: VOID printf("UNKN"); break; default: VOID printf("DEFAULT"); break; } VOID printf(" | "); nextlex(); } exitmain(EXIT_SUCCESS); } void exiterr() { _exit(EXIT_FAILURE); } #endif