rsalz@uunet.uu.net (Rich Salz) (02/22/91)
Submitted-by: Adam Hammer <hammer@cs.purdue.edu> Posting-number: Volume 24, Issue 5 Archive-name: rcs/part05 #! /bin/sh # This is a shell archive. Remove anything before this line, then feed it # into a shell via "sh file" or similar. To overwrite existing files, # type "sh file -c". # The tool that generated this appeared in the comp.sources.unix newsgroup; # send mail to comp-sources-unix@uunet.uu.net if you want that tool. # Contents: src/rcsfnms.c src/rcslex.c # Wrapped by rsalz@litchi.bbn.com on Thu Feb 21 14:36:58 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH echo If this archive is complete, you will see the following message: echo ' "shar: End of archive 5 (of 12)."' if test -f 'src/rcsfnms.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'src/rcsfnms.c'\" else echo shar: Extracting \"'src/rcsfnms.c'\" \(26159 characters\) sed "s/^X//" >'src/rcsfnms.c' <<'END_OF_FILE' X/* X * RCS file name handling X */ X/**************************************************************************** X * creation and deletion of /tmp temporaries X * pairing of RCS file names and working file names. X * Testprogram: define PAIRTEST X **************************************************************************** X */ X X/* Copyright (C) 1982, 1988, 1989 Walter Tichy X Copyright 1990 by Paul Eggert X Distributed under license by the Free Software Foundation, Inc. X XThis file is part of RCS. X XRCS is free software; you can redistribute it and/or modify Xit under the terms of the GNU General Public License as published by Xthe Free Software Foundation; either version 1, or (at your option) Xany later version. X XRCS is distributed in the hope that it will be useful, Xbut WITHOUT ANY WARRANTY; without even the implied warranty of XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the XGNU General Public License for more details. X XYou should have received a copy of the GNU General Public License Xalong with RCS; see the file COPYING. If not, write to Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. X XReport problems and direct all questions to: X X rcs-bugs@cs.purdue.edu X X*/ X X X X X/* $Log: rcsfnms.c,v $ X * Revision 5.4 1990/11/01 05:03:43 eggert X * Permit arbitrary data in comment leaders. X * X * Revision 5.3 1990/09/14 22:56:16 hammer X * added more filename extensions and their comment leaders X * X * Revision 5.2 1990/09/04 08:02:23 eggert X * Fix typo when !RCSSEP. X * X * Revision 5.1 1990/08/29 07:13:59 eggert X * Work around buggy compilers with defective argument promotion. X * X * Revision 5.0 1990/08/22 08:12:50 eggert X * Ignore signals when manipulating the semaphore file. X * Modernize list of file name extensions. X * Permit paths of arbitrary length. Beware file names beginning with "-". X * Remove compile-time limits; use malloc instead. X * Permit dates past 1999/12/31. Make lock and temp files faster and safer. X * Ansify and Posixate. X * Don't use access(). Fix test for non-regular files. Tune. X * X * Revision 4.8 89/05/01 15:09:41 narten X * changed getwd to not stat empty directories. X * X * Revision 4.7 88/08/09 19:12:53 eggert X * Fix troff macro comment leader bug; add Prolog; allow cc -R; remove lint. X * X * Revision 4.6 87/12/18 11:40:23 narten X * additional file types added from 4.3 BSD version, and SPARC assembler X * comment character added. Also, more lint cleanups. (Guy Harris) X * X * Revision 4.5 87/10/18 10:34:16 narten X * Updating version numbers. Changes relative to 1.1 actually relative X * to verion 4.3 X * X * Revision 1.3 87/03/27 14:22:21 jenkins X * Port to suns X * X * Revision 1.2 85/06/26 07:34:28 svb X * Comment leader '% ' for '*.tex' files added. X * X * Revision 4.3 83/12/15 12:26:48 wft X * Added check for KDELIM in file names to pairfilenames(). X * X * Revision 4.2 83/12/02 22:47:45 wft X * Added csh, red, and sl file name suffixes. X * X * Revision 4.1 83/05/11 16:23:39 wft X * Added initialization of Dbranch to InitAdmin(). Canged pairfilenames(): X * 1. added copying of path from workfile to RCS file, if RCS file is omitted; X * 2. added getting the file status of RCS and working files; X * 3. added ignoring of directories. X * X * Revision 3.7 83/05/11 15:01:58 wft X * Added comtable[] which pairs file name suffixes with comment leaders; X * updated InitAdmin() accordingly. X * X * Revision 3.6 83/04/05 14:47:36 wft X * fixed Suffix in InitAdmin(). X * X * Revision 3.5 83/01/17 18:01:04 wft X * Added getwd() and rename(); these can be removed by defining X * V4_2BSD, since they are not needed in 4.2 bsd. X * Changed sys/param.h to sys/types.h. X * X * Revision 3.4 82/12/08 21:55:20 wft X * removed unused variable. X * X * Revision 3.3 82/11/28 20:31:37 wft X * Changed mktempfile() to store the generated file names. X * Changed getfullRCSname() to store the file and pathname, and to X * delete leading "../" and "./". X * X * Revision 3.2 82/11/12 14:29:40 wft X * changed pairfilenames() to handle file.sfx,v; also deleted checkpathnosfx(), X * checksuffix(), checkfullpath(). Semaphore name generation updated. X * mktempfile() now checks for nil path; freefilename initialized properly. X * Added Suffix .h to InitAdmin. Added testprogram PAIRTEST. X * Moved rmsema, trysema, trydiraccess, getfullRCSname from rcsutil.c to here. X * X * Revision 3.1 82/10/18 14:51:28 wft X * InitAdmin() now initializes StrictLocks=STRICT_LOCKING (def. in rcsbase.h). X * renamed checkpath() to checkfullpath(). X */ X X X#include "rcsbase.h" X XlibId(fnmsId, "$Id: rcsfnms.c,v 5.4 1990/11/01 05:03:43 eggert Exp $") X Xconst char *RCSfilename; Xchar *workfilename; Xstruct stat RCSstat, workstat; /* file status for RCS file and working file */ Xint haveworkstat; X Xstatic const char rcsdir[] = RCSDIR; X X X#define TEMPNAMES 4 /* must be at least DIRTEMPNAMES (see rcsedit.c) */ Xstatic char tfnames[TEMPNAMES][L_tmpnam]; /* unlink these when done */ Xstatic volatile int tfmade[TEMPNAMES]; /* if these flags are set */ X X Xstruct compair { X const char *suffix, *comlead; X}; X Xstatic const struct compair comtable[] = { X/* comtable pairs each filename suffix with a comment leader. The comment */ X/* leader is placed before each line generated by the $Log keyword. This */ X/* table is used to guess the proper comment leader from the working file's */ X/* suffix during initial ci (see InitAdmin()). Comment leaders are needed */ X/* for languages without multiline comments; for others they are optional. */ X "a", "-- ", /* Ada */ X "c", " * ", /* C */ X "C", "// ", /* C++ in all its infinite guises */ X "CC", "// ", X "c++", "// ", X "cc", "// ", X "cxx", "// ", X "cl", ";;; ", /* Common Lisp */ X "cmf", "C ", /* CM FORTRAN */ X "cs", " * ", /* C* */ X "el", "; ", /* Emacs Lisp */ X "f", "c ", /* Fortran */ X "for", "c ", X "h", " * ", /* C-header */ X "l", " * ", /* lex NOTE: conflict between lex and franzlisp */ X "lisp",";;; ", /* Lucid Lisp */ X "mac", "; ", /* macro vms or dec-20 or pdp-11 macro */ X "me", ".\\\" ",/* me-macros t/nroff*/ X "ml", "; ", /* mocklisp */ X "mm", ".\\\" ",/* mm-macros t/nroff*/ X "ms", ".\\\" ",/* ms-macros t/nroff*/ X "p", " * ", /* Pascal */ X "pl", "% ", /* Prolog */ X "tex", "% ", /* TeX */ X "y", " * ", /* yacc */ X nil, "# " /* default for unknown suffix; must always be last */ X}; X X X void Xffclose(fptr) XFILE * fptr; X/* Function: checks ferror(fptr) and aborts the program if there were X * errors; otherwise closes fptr. X */ X{ if (ferror(fptr) || fclose(fptr)==EOF) X IOerror(); X} X X X X char * Xmaketemp(n) X int n; X/* Create a unique filename using n and the process id and store it X * into the nth slot in tfnames. X * Because of storage in tfnames, tempunlink() can unlink the file later. X * Returns a pointer to the filename created. X */ X{ X char *p = tfnames[n]; X X if (!tfmade[n]) { X#if has_tmpnam X if (!tmpnam(p)) X#else X VOID sprintf(p, "%sRCS%cXXXXXX", tmp(), 'A'+n); X if (!mktemp(p)) X#endif X faterror("can't make temporary file name"); X } X tfmade[n] = true; X return p; X} X X void Xtempunlink() X/* Clean up maketemp() files. May be invoked by signal handler. X */ X{ X register int i; X X for (i = TEMPNAMES; 0 <= --i; ) X if (tfmade[i]) { X VOID unlink(tfnames[i]); X tfmade[i] = 0; X } X} X X X const char * Xbindex(sp,ch) X register const char *sp; X int ch; X/* Function: Finds the last occurrence of character c in string sp X * and returns a pointer to the character just beyond it. If the X * character doesn't occur in the string, sp is returned. X */ X{ X register const char c=ch, *r; X r = sp; X while (*sp) { X if (*sp++ == c) r=sp; X } X return r; X} X X X X X X static void XInitAdmin() X/* function: initializes an admin node */ X{ X register const char *Suffix; X register int i; X X Head=nil; Dbranch=nil; AccessList=nil; Symbols=nil; Locks=nil; X StrictLocks=STRICT_LOCKING; X X /* guess the comment leader from the suffix*/ X Suffix=bindex(workfilename, '.'); X if (Suffix==workfilename) Suffix= ""; /* empty suffix; will get default*/ X for (i=0; comtable[i].suffix && strcmp(Suffix,comtable[i].suffix); i++) X ; X Comment.string = comtable[i].comlead; X Comment.size = strlen(comtable[i].comlead); X Lexinit(); /* Note: if finptr==NULL, reads nothing; only initializes*/ X} X X X#if !RCSSEP X static int XisRCSfilename(f, p) X const char *f, *p; X/* Yield true iff F (with pure file name P) is an RCS file name. */ X{ X return X p-f <= sizeof(rcsdir)-1 && X ((p -= sizeof(rcsdir)-1) == f || p[-1] == SLASH) && X strncmp(p, rcsdir, sizeof(rcsdir)-1) == 0; X} X#endif X X#if RCSSEP X# define findpair(c,v,f,m) findpairfile(c,v,f) X#else X# define findpair(c,v,f,m) findpairfile(c,v,f,m) X#endif X X static char * X#if RCSSEP Xfindpairfile(argc, argv, fname) X#else Xfindpairfile(argc, argv, fname, rcsmatch) Xint rcsmatch; /* *ARGV must be an RCS file name iff this is set. */ X#endif Xint argc; char * argv[], *fname; X/* Peek ahead in an ARGC-ARGV argument vector for a pathname ending in FNAME. X * Yield it if found, and set the corresponding pointer in ARGV to nil. X * Yield FNAME otherwise. X */ X{ X register char *arg; X#if !RCSSEP X register char *b; X#endif X if ( X 0 < argc X#if RCSSEP X && strcmp(bindex(arg = *argv,SLASH), fname) == 0 X#else X && strcmp(b = bindex(arg = *argv,SLASH), fname) == 0 X && isRCSfilename(arg, b) == rcsmatch X#endif X ) { X *argv = nil; X return arg; X } X return fname; X} X X X static int Xhandleworkstat(s) X int s; X{ X if (s==0 && !S_ISREG(workstat.st_mode)) { X error("%s isn't a regular file", workfilename); X return false; X } X haveworkstat = errno; X return true; X} X Xint getworkstat() X/* Function: get status of workfilename. */ X{ X errno = 0; X return handleworkstat(stat(workfilename, &workstat)); X} X X int Xgetfworkstat(f) X int f; X/* Get status of file descriptor f. */ X{ X errno = 0; X return handleworkstat(fstat(f, &workstat)); X} X X X#if defined(_POSIX_NO_TRUNC) & _POSIX_NO_TRUNC!=-1 X# define LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED 0 X#else X# define LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED 1 X#endif X X#if LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED X#ifdef NAME_MAX X# define filenametoolong(path) (NAME_MAX < strlen(bindex(path,SLASH))) X#else X static int Xfilenametoolong(path) X char *path; X/* Yield true if the last file name in PATH is too long. */ X{ X static unsigned long dot_namemax; X X register size_t namelen; X register char *lastslash; X register unsigned long namemax; X X lastslash = strrchr(path, SLASH); X namelen = strlen(lastslash ? lastslash+1 : path); X if (namelen <= _POSIX_NAME_MAX) /* fast check for shorties */ X return false; X if (lastslash) { X *lastslash = 0; X namemax = pathconf(path, _PC_NAME_MAX); X *lastslash = SLASH; X } else { X /* Cache the results for the working directory, for speed. */ X if (!dot_namemax) X dot_namemax = pathconf(".", _PC_NAME_MAX); X namemax = dot_namemax; X } X /* If pathconf() yielded -1, namemax is now ULONG_MAX. */ X return namemax<namelen; X} X#endif X#endif X X void Xbufalloc(b, size) X register struct buf *b; X size_t size; X/* Ensure *B is a name buffer of at least SIZE bytes. X * *B's old contents can be freed; *B's new contents are undefined. X */ X{ X if (b->size < size) { X if (b->size) X tfree(b->string); X else X b->size = sizeof(malloc_type); X while (b->size < size) X b->size <<= 1; X b->string = tnalloc(char, b->size); X } X} X X void Xbufrealloc(b, size) X register struct buf *b; X size_t size; X/* like bufalloc, except *B's old contents, if any, are preserved */ X{ X if (b->size < size) { X if (!b->size) X bufalloc(b, size); X else { X while ((b->size <<= 1) < size) X ; X b->string = (char *)testrealloc((malloc_type)b->string, b->size); X } X } X} X X void Xbufautoend(b) X struct buf *b; X/* Free an auto buffer at block exit. */ X{ X if (b->size) X tfree(b->string); X} X X char * Xbufenlarge(b, alim) X register struct buf *b; X const char **alim; X/* Make *B larger. Set *ALIM to its new limit, and yield the relocated value X * of its old limit. X */ X{ X size_t s = b->size; X bufrealloc(b, s + 1); X *alim = b->string + b->size; X return b->string + s; X} X X void Xbufscat(b, s) X struct buf *b; X const char *s; X/* Concatenate S to B's end. */ X{ X size_t blen = b->string ? strlen(b->string) : 0; X bufrealloc(b, blen+strlen(s)+1); X VOID strcpy(b->string+blen, s); X} X X void Xbufscpy(b, s) X struct buf *b; X const char *s; X/* Copy S into B. */ X{ X bufalloc(b, strlen(s)+1); X VOID strcpy(b->string, s); X} X X X FILE * Xrcsreadopen(RCSname) X const char *RCSname; X/* Open RCSNAME for reading and yield its FILE* descriptor. X * Pass this routine to pairfilenames() for read-only access to the file. */ X{ X FILE *f; X seteid(); X f = fopen(RCSname, "r"); X setrid(); X return f; X} X X int Xpairfilenames(argc, argv, rcsopen, mustread, tostdout) X int argc; X char **argv; X FILE *(*rcsopen)P((const char*)); X int mustread, tostdout; X/* Function: Pairs the filenames pointed to by argv; argc indicates X * how many there are. X * Places a pointer to the RCS filename into RCSfilename, X * and a pointer to the name of the working file into workfilename. X * If both the workfilename and the RCS filename are given, and tostdout X * is true, a warning is printed. X * X * If the RCS file exists, places its status into RCSstat. X * X * If the RCS file exists, it is RCSOPENed for reading, the file pointer X * is placed into finptr, and the admin-node is read in; returns 1. X * If the RCS file does not exist and mustread is set, an error is printed X * and 0 returned. X * If the RCS file does not exist and !mustread, the admin node X * is initialized and -1 returned. X * X * 0 is returned on all errors, e.g. files that are not regular files. X */ X{ X static struct buf RCSbuf, tempbuf; X X register char *p, *arg, *tempfilename, *RCS1; X const char *purefname, *pureRCSname; X FILE *lock1; X X if (!(arg = *argv)) return 0; /* already paired filename */ X if (*arg == '-') { X error("%s option is ignored after file names", arg); X return 0; X } X X /* Allocate buffer temporary to hold the default paired file name. */ X for (purefname = p = arg; *p; ) X switch (*p++) { X case SLASH: X purefname = p; X break; X /* Beware characters that cause havoc with ci -k. */ X case KDELIM: X error("RCS file name `%s' contains %c", arg, KDELIM); X return 0; X case ' ': case '\n': case '\t': X error("RCS file name `%s' contains white space", arg); X return 0; X } X bufalloc(&tempbuf, p - purefname + 3); X tempfilename = tempbuf.string; X X /* first check suffix to see whether it is an RCS file or not */ X#if RCSSEP X if (purefname<(p-=2) && p[0]==RCSSEP && p[1]==RCSSUF) X#else X if (isRCSfilename(arg, purefname)) X#endif X { X /* RCS file name given*/ X RCS1 = arg; X pureRCSname = purefname; X /* derive workfilename*/ X VOID strcpy(tempfilename, purefname); X tempfilename[p - purefname] = 0; X /* try to find workfile name among arguments */ X workfilename = findpair(argc-1,argv+1,tempfilename,false); X } else { X /* working file given; now try to find RCS file */ X workfilename = arg; X /* derive RCS file name*/ X VOID sprintf(tempfilename,"%s%c%c", purefname, RCSSEP, RCSSUF); X /* Try to find RCS file name among arguments*/ X RCS1 = findpair(argc-1,argv+1,tempfilename,true); X pureRCSname=bindex(RCS1, SLASH); X } X /* now we have a (tentative) RCS filename in RCS1 and workfilename */ X /* Second, try to find the right RCS file */ X if (pureRCSname!=RCS1) { X /* a path for RCSfile is given; single RCS file to look for */ X errno = 0; X RCSfilename = p = RCS1; X finptr = (*rcsopen)(RCSfilename = p = RCS1); X } else { X /* no path for RCS file name. Prefix it with path of work */ X /* file if RCS file omitted. Try RCSDIR subdirectory 1st.*/ X bufalloc(&RCSbuf, strlen(workfilename)+sizeof(rcsdir)+2); X RCSfilename = p = RCSbuf.string; X if (RCS1==tempfilename) { X /* RCS file name not given; prepend work path */ X VOID strncpy(p, arg, purefname-arg); X p += purefname-arg; X } X VOID strcpy(p, rcsdir); X VOID strcpy(p+sizeof(rcsdir)-1, RCS1); X X /* Try D/RCS/file,v. */ X errno = 0; X if (!(finptr = (*rcsopen)(RCSfilename)) X && (errno==ENOTDIR || errno==ENOENT) X /* X * Many (broken) systems yield ENOENT, not ENOTDIR, X * when the problem is a missing RCS subdirectory. X */ X ) { X lock1 = frewrite; X X /* Try D/file,v. */ X VOID strcpy(p, RCS1); X errno = 0; X if (!(finptr=(*rcsopen)(RCSfilename)) && errno==ENOENT) { X /* X * Neither file exists; determine the default. X * Prefer D/RCS/file,v to D/file,v. X */ X if (mustread || lock1) { X /* Switch back to D/RCS/file,v. */ X VOID strcpy(p, rcsdir); X VOID strcpy(p+sizeof(rcsdir)-1, RCS1); X } X } X } X p = RCSbuf.string; X } X if (finptr) { X if (fstat(fileno(finptr), &RCSstat) < 0) X efaterror(p); X if (!S_ISREG(RCSstat.st_mode)) { X error("%s isn't a regular file -- ignored", p); X return 0; X } X Lexinit(); getadmin(); X } else { X if (errno!=ENOENT || mustread || !frewrite) { X if (errno == EEXIST) X error("RCS file %s is in use", p); X else X eerror(p); X return 0; X } X InitAdmin(); X }; X# if LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED X if (filenametoolong(p)) { X error("RCS file name %s is too long", p); X return 0; X } X# ifndef NAME_MAX X /* X * Check workfilename, even though it is shorter, X * because it may reside on a different filesystem. X */ X if (filenametoolong(workfilename)) { X error("working file name %s is too long", workfilename); X return 0; X } X# endif X# endif X X if (tostdout&& X !(RCS1==tempfilename||workfilename==tempfilename)) X /*The last term determines whether a pair of */ X /* file names was given in the argument list */ X warn("Option -p is set; ignoring output file %s",workfilename); X X return finptr ? 1 : -1; X} X X X const char * XgetfullRCSname() X/* Function: returns a pointer to the full path name of the RCS file. X * Gets the working directory's name at most once. X * Removes leading "../" and "./". X */ X{ X static const char *wd; X static struct buf rcsbuf, wdbuf; X static size_t pathlength; X X register const char *realname; X register size_t parentdirlength; X register unsigned dotdotcounter; X register char *d; X X if (ROOTPATH(RCSfilename)) { X return(RCSfilename); X } else { X if (!wd) { /* Get working directory for the first time. */ X if (!(d = cgetenv("PWD"))) { X bufalloc(&wdbuf, 1 + X# ifdef PATH_MAX X PATH_MAX X# else X _POSIX_PATH_MAX X# endif X ); X errno = 0; X# if !has_getcwd X d = getwd(wdbuf.string); X# else X while ( X !(d = getcwd(wdbuf.string,(int)wdbuf.size)) X && errno==ERANGE X ) X bufalloc(&wdbuf, wdbuf.size<<1); X# endif X if (!d) X efaterror("working directory"); X } X pathlength = strlen(d); X while (pathlength && d[pathlength-1]==SLASH) { X d[--pathlength] = 0; X /* Check needed because some getwd implementations */ X /* generate "/" for the root. */ X } X wd = d; X } X /*the following must be redone since RCSfilename may change*/ X /* Find how many `../'s to remove from RCSfilename. */ X dotdotcounter =0; X realname = RCSfilename; X while( realname[0]=='.' && X (realname[1]==SLASH||(realname[1]=='.'&&realname[2]==SLASH))){ X if (realname[1]==SLASH) { X /* drop leading ./ */ X realname += 2; X } else { X /* drop leading ../ and remember */ X dotdotcounter++; X realname += 3; X } X } X /* Now remove dotdotcounter trailing directories from wd. */ X parentdirlength = pathlength; X while (dotdotcounter && parentdirlength) { X /* move pointer backwards over trailing directory */ X if (wd[--parentdirlength] == SLASH) { X dotdotcounter--; X } X } X if (dotdotcounter) { X error("can't generate full path name for RCS file"); X return RCSfilename; X } else { X /* build full path name */ X bufalloc(&rcsbuf, parentdirlength+strlen(realname)+2); X VOID strncpy(rcsbuf.string, wd, parentdirlength); X rcsbuf.string[parentdirlength++] = SLASH; X VOID strcpy(rcsbuf.string+parentdirlength, realname); X return rcsbuf.string; X } X } X} X X const char * Xtmp() X/* Yield the name of the tmp directory, with a trailing SLASH. */ X{ X static const char *s; X if (!s) X if (!(s = getenv("TMP"))) X s = TMPDIR; X else { X size_t l = strlen(s); X int extra = l && s[l-1]!=SLASH; X char *p = ftnalloc(char, l + extra + 1); X VOID strcpy(p, s); X if (extra) { X p[l] = SLASH; X p[l+1] = 0; X } X s = p; X } X return s; X} X X X#if !has_rename | bad_rename X X int Xre_name(from, to) X const char *from, *to; X/* Function: renames a file with the name given by from to the name given by to. X * unlinks the to-file if it already exists. returns -1 on error, 0 otherwise. X */ X{ VOID unlink(to); /* no need to check return code; will be caught by link*/ X /* no harm done if file "to" does not exist */ X#if has_rename X return rename(from,to); X#else X if (link(from,to)<0) return -1; X return(unlink(from)); X#endif X} X X#endif X X X#if !has_getcwd & !has_getwd X X#if !MAKEDEPEND X#include <sys/dir.h> X#endif X X X#define dot "." X#define dotdot ".." X X X Xchar * getwd(name) Xchar * name; X/* Function: places full pathname of current working directory into name and X * returns name on success, NULL on failure. X * getwd is an adaptation of pwd. May not return to the current directory on X * failure. X */ X{ X FILE *file; X struct stat d, dd; X char buf[2]; /* to NUL-terminate dir.d_name */ X struct direct dir; X X int rdev, rino; X int off; X register i,j; X X off = 0; X name[0] = SLASH; X name[1] = '\0'; X buf[0] = '\0'; X if (stat(name, &d)<0) return NULL; X rdev = d.st_dev; X rino = d.st_ino; X for (;;) { X if (stat(dot, &d)<0) return NULL; X if (d.st_ino==rino && d.st_dev==rdev) { X if (name[off] == SLASH) X name[off] = '\0'; X VOID chdir(name); /*change back to current directory*/ X return name; X } X if ((file = fopen(dotdot,"r")) == NULL) return NULL; X if (fstat(fileno(file), &dd)<0) goto fail; X VOID chdir(dotdot); X if(d.st_dev == dd.st_dev) { X if(d.st_ino == dd.st_ino) { X if (name[off] == SLASH) X name[off] = 0; X VOID chdir(name); /*change back to current directory*/ X ffclose(file); X return name; X } X do { X if (fread((char *)&dir, sizeof(dir), 1, file) !=1) X goto fail; X } while (dir.d_ino != d.st_ino); X } X else do { X if(fread((char *)&dir, sizeof(dir), 1, file) != 1) { X goto fail; X } X if (dir.d_ino == 0) X dd.st_ino = d.st_ino + 1; X else if (stat(dir.d_name, &dd) < 0) X goto fail; X } while(dd.st_ino != d.st_ino || dd.st_dev != d.st_dev); X ffclose(file); X X /* concatenate file name */ X i = -1; X while (dir.d_name[++i] != 0); X for(j=off+1; j>0; --j) X name[j+i+1] = name[j]; X off=i+off+1; X name[i+1] = SLASH; X for(--i; i>=0; --i) X name[i+1] = dir.d_name[i]; X } /* end for */ X Xfail: ffclose(file); X return NULL; X} X X X#endif X X X#ifdef PAIRTEST X/* test program for pairfilenames() and getfullRCSname() */ X Xconst char cmdid[] = "pair"; X Xmain(argc, argv) Xint argc; char *argv[]; X{ X int result; X int initflag,tostdout; X quietflag=tostdout=initflag=false; X X while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) { X switch ((*argv)[1]) { X X case 'p': tostdout=true; X break; X case 'i': initflag=true; X break; X case 'q': quietflag=true; X break; X default: error("unknown option: %s", *argv); X break; X } X } X X do { X RCSfilename=workfilename=nil; X result = pairfilenames(argc,argv,rcsreadopen,!initflag,tostdout); X if (result!=0) { X diagnose("RCS file: %s; working file: %s\nFull RCS file name: %s\n", X RCSfilename,workfilename,getfullRCSname() X ); X } X switch (result) { X case 0: continue; /* already paired file */ X X case 1: if (initflag) { X error("RCS file %s exists already",RCSfilename); X } else { X diagnose("RCS file %s exists\n",RCSfilename); X } X ffclose(finptr); X break; X X case -1:diagnose("RCS file doesn't exist\n"); X break; X } X X } while (++argv, --argc>=1); X X} X#endif END_OF_FILE if test 26159 -ne `wc -c <'src/rcsfnms.c'`; then echo shar: \"'src/rcsfnms.c'\" unpacked with wrong size! fi # end of 'src/rcsfnms.c' fi if test -f 'src/rcslex.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'src/rcslex.c'\" else echo shar: Extracting \"'src/rcslex.c'\" \(24012 characters\) sed "s/^X//" >'src/rcslex.c' <<'END_OF_FILE' X/* X * RCS file input X */ X/********************************************************************************* X * Lexical Analysis. X * hashtable, Lexinit, nextlex, getlex, getkey, X * getid, getnum, readstring, printstring, savestring, X * checkid, fatserror, error, faterror, warn, diagnose X * Testprogram: define LEXDB X ********************************************************************************* X */ X X/* Copyright (C) 1982, 1988, 1989 Walter Tichy X Copyright 1990 by Paul Eggert X Distributed under license by the Free Software Foundation, Inc. X XThis file is part of RCS. X XRCS is free software; you can redistribute it and/or modify Xit under the terms of the GNU General Public License as published by Xthe Free Software Foundation; either version 1, or (at your option) Xany later version. X XRCS is distributed in the hope that it will be useful, Xbut WITHOUT ANY WARRANTY; without even the implied warranty of XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the XGNU General Public License for more details. X XYou should have received a copy of the GNU General Public License Xalong with RCS; see the file COPYING. If not, write to Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. X XReport problems and direct all questions to: X X rcs-bugs@cs.purdue.edu X X*/ X X X X/* $Log: rcslex.c,v $ X * Revision 5.5 1990/12/04 05:18:47 eggert X * Use -I for prompts and -q for diagnostics. X * X * Revision 5.4 1990/11/19 20:05:28 hammer X * no longer gives warning about unknown keywords if -q is specified X * X * Revision 5.3 1990/11/01 05:03:48 eggert X * When ignoring unknown phrases, copy them to the output RCS file. X * X * Revision 5.2 1990/09/04 08:02:27 eggert X * Count RCS lines better. X * X * Revision 5.1 1990/08/29 07:14:03 eggert X * Work around buggy compilers with defective argument promotion. X * X * Revision 5.0 1990/08/22 08:12:55 eggert X * Remove compile-time limits; use malloc instead. X * Report errno-related errors with perror(). X * Ansify and Posixate. Add support for ISO 8859. X * Use better hash function. X * X * Revision 4.6 89/05/01 15:13:07 narten X * changed copyright header to reflect current distribution rules X * X * Revision 4.5 88/08/28 15:01:12 eggert X * Don't loop when writing error messages to a full filesystem. X * Flush stderr/stdout when mixing output. X * Yield exit status compatible with diff(1). X * Shrink stdio code size; allow cc -R; remove lint. X * X * Revision 4.4 87/12/18 11:44:47 narten X * fixed to use "varargs" in "fprintf"; this is required if it is to X * work on a SPARC machine such as a Sun-4 X * X * Revision 4.3 87/10/18 10:37:18 narten X * Updating version numbers. Changes relative to 1.1 actually relative X * to version 4.1 X * X * Revision 1.3 87/09/24 14:00:17 narten X * Sources now pass through lint (if you ignore printf/sprintf/fprintf X * warnings) X * X * Revision 1.2 87/03/27 14:22:33 jenkins X * Port to suns X * X * Revision 4.1 83/03/25 18:12:51 wft X * Only changed $Header to $Id. X * X * Revision 3.3 82/12/10 16:22:37 wft X * Improved error messages, changed exit status on error to 1. X * X * Revision 3.2 82/11/28 21:27:10 wft X * Renamed ctab to map and included EOFILE; ctab is now a macro in rcsbase.h. X * Added fflsbuf(), fputs(), and fprintf(), which abort the RCS operations X * properly in case there is an IO-error (e.g., file system full). X * X * Revision 3.1 82/10/11 19:43:56 wft X * removed unused label out:; X * made sure all calls to getc() return into an integer, not a char. X */ X X X/* X#define LEXDB X*/ X/* version LEXDB is for testing the lexical analyzer. The testprogram X * reads a stream of lexemes, enters the revision numbers into the X * hashtable, and prints the recognized tokens. Keywords are recognized X * as identifiers. X */ X X X X#include "rcsbase.h" X XlibId(lexId, "$Id: rcslex.c,v 5.5 1990/12/04 05:18:47 eggert Exp $") X Xstatic struct hshentry *nexthsh; /*pointer to next hash entry, set by lookup*/ X Xenum tokens nexttok; /*next token, set by nextlex */ X Xint hshenter; /*if true, next suitable lexeme will be entered */ X /*into the symbol table. Handle with care. */ Xint nextc; /*next input character, initialized by Lexinit */ X Xunsigned long rcsline; /*current line-number of input */ Xint nerror; /*counter for errors */ Xint quietflag; /*indicates quiet mode */ XFILE * finptr; /*input file descriptor */ X XFILE * frewrite; /*file descriptor for echoing input */ X XFILE * foutptr; /* copy of frewrite, but NULL to suppress echo */ X Xstatic struct buf tokbuf; /* token buffer */ X Xconst char * NextString; /* next token */ X X/* X * Our hash algorithm is h[0] = 0, h[i+1] = 4*h[i] + c, X * so hshsize should be odd. X * See B J McKenzie, R Harries & T Bell, Selecting a hashing algorithm, X * Software--practice & experience 20, 2 (Feb 1990), 209-224. X */ X#ifndef hshsize X# define hshsize 511 X#endif X Xstatic struct hshentry *hshtab[hshsize]; /*hashtable */ X Xstatic int ignored_phrases; /* have we ignored phrases in this RCS file? */ X X void Xwarnignore() X{ X if (! (ignored_phrases|quietflag)) { X ignored_phrases = true; X warn("Unknown phrases like `%s ...;' are in the RCS file.", NextString); X } X} X X X X static void Xlookup(str) X const char *str; X/* Function: Looks up the character string pointed to by str in the X * hashtable. If the string is not present, a new entry for it is created. X * In any case, the address of the corresponding hashtable entry is placed X * into nexthsh. X */ X{ X register unsigned ihash; /* index into hashtable */ X register const char *sp; X register struct hshentry *n, **p; X X /* calculate hash code */ X sp = str; X ihash = 0; X while (*sp) X ihash = (ihash<<2) + *sp++; X ihash %= hshsize; X X for (p = &hshtab[ihash]; ; p = &n->nexthsh) X if (!(n = *p)) { X /* empty slot found */ X *p = n = ftalloc(struct hshentry); X n->num = fstrsave(str); X n->nexthsh = nil; X# ifdef LEXDB X VOID printf("\nEntered: %s at %u ", str, ihash); X# endif X break; X } else if (strcmp(str, n->num) == 0) X /* match found */ X break; X nexthsh = n; X NextString = n->num; X} X X X X X X X void XLexinit() X/* Function: Initialization of lexical analyzer: X * initializes the hashtable, X * initializes nextc, nexttok if finptr != NULL X */ X{ register int c; X X for (c = hshsize; 0 <= --c; ) { X hshtab[c] = nil; X } X X hshenter=true; rcsline=1; nerror=0; X ignored_phrases = false; X bufrealloc(&tokbuf, 2); X if (finptr) { X GETC(finptr,foutptr,c); X nextc = c; /*initial character*/ X nexttok = DELIM; /* anything but EOFILE */ X nextlex(); /*initial token*/ X } else { X nextc = '\0'; X nexttok=EOFILE; X } X} X X X static exiting void XunexpectedEOF() X{ X fatserror("unexpected EOF"); X} X X X X X X X X void Xnextlex() X X/* Function: Reads the next token and sets nexttok to the next token code. X * Only if hshenter is set, a revision number is entered into the X * hashtable and a pointer to it is placed into nexthsh. X * This is useful for avoiding that dates are placed into the hashtable. X * For ID's and NUM's, NextString is set to the character string. X * Assumption: nextc contains the next character. X */ X{ register c; X register FILE * fin, * frew; X register char * sp; X const char *lim; X register enum tokens d; X X if (nexttok == EOFILE) X unexpectedEOF(); X fin=finptr; frew=foutptr; X X for (;;) switch ((nexttok=ctab[nextc])) { X X default: X fatserror("unknown character `%c'", nextc); X /*NOTREACHED*/ X X case NEWLN: X ++rcsline; X# ifdef LEXDB X afputc('\n',stdout); X# endif X /* Note: falls into next case */ X X case SPACE: X GETC(fin,frew,c); X nextc = c; X continue; X X case EOFILE: X return; X X case DIGIT: X sp = tokbuf.string; X lim = sp + tokbuf.size; X *sp++ = nextc; X for (;;) { X GETC(fin,frew,c); X if ((d=ctab[c])!=DIGIT && d!=PERIOD) X break; X *sp++ = c; /* 1.2. and 1.2 are different */ X if (lim <= sp) X sp = bufenlarge(&tokbuf, &lim); X } X *sp = 0; X nextc = c; X if (hshenter) X lookup(tokbuf.string); X else X NextString = fstrsave(tokbuf.string); X nexttok = NUM; X return; X X X case LETTER: X case Letter: X sp = tokbuf.string; X lim = sp + tokbuf.size; X *sp++ = nextc; X for (;;) { X GETC(fin,frew,c); X if ((d=ctab[c])!=LETTER && d!=Letter && d!=DIGIT && d!=IDCHAR) X break; X *sp++ = c; X if (lim <= sp) X sp = bufenlarge(&tokbuf, &lim); X } X *sp = 0; X nextc = c; X NextString = fstrsave(tokbuf.string); X nexttok = ID; /* may be ID or keyword */ X return; X X case SBEGIN: /* long string */ X nexttok = STRING; X /* note: only the initial SBEGIN has been read*/ X /* read the string, and reset nextc afterwards*/ X return; X X case COLON: X case SEMI: X GETC(fin,frew,c); X nextc = c; X return; X } X} X X Xint getlex(token) Xenum tokens token; X/* Function: Checks if nexttok is the same as token. If so, X * advances the input by calling nextlex and returns true. X * otherwise returns false. X * Doesn't work for strings and keywords; loses the character string for ids. X */ X{ X if (nexttok==token) { X nextlex(); X return(true); X } else return(false); X} X X int Xgetkeyopt(key) X const char *key; X/* Function: If the current token is a keyword identical to key, X * advances the input by calling nextlex and returns true; X * otherwise returns false. X */ X{ X if (nexttok==ID && strcmp(key,NextString) == 0) { X /* match found */ X ffree1(NextString); X nextlex(); X return(true); X } X return(false); X} X X void Xgetkey(key) X const char *key; X/* Check that the current input token is a keyword identical to key, X * and advance the input by calling nextlex. X */ X{ X if (!getkeyopt(key)) X fatserror("missing '%s' keyword", key); X} X X void Xgetkeystring(key) X const char *key; X/* Check that the current input token is a keyword identical to key, X * and advance the input by calling nextlex; then look ahead for a string. X */ X{ X getkey(key); X if (nexttok != STRING) X fatserror("missing string after '%s' keyword", key); X} X X X const char * Xgetid() X/* Function: Checks if nexttok is an identifier. If so, X * advances the input by calling nextlex and returns a pointer X * to the identifier; otherwise returns nil. X * Treats keywords as identifiers. X */ X{ X register const char *name; X if (nexttok==ID) { X name = NextString; X nextlex(); X return name; X } else return nil; X} X X Xstruct hshentry * getnum() X/* Function: Checks if nexttok is a number. If so, X * advances the input by calling nextlex and returns a pointer X * to the hashtable entry. Otherwise returns nil. X * Doesn't work if hshenter is false. X */ X{ X register struct hshentry * num; X if (nexttok==NUM) { X num=nexthsh; X nextlex(); X return num; X } else return nil; X} X X struct cbuf Xgetphrases(key) X const char *key; X/* Get a series of phrases that do not start with KEY, yield resulting buffer. X * Stop when the next phrase starts with a token that is not an identifier, X * or is KEY. X * Assume foutptr == NULL. X */ X{ X register FILE *fin; X register int c; X register char *p; X const char *lim; X register const char *ki, *kn; X struct cbuf r; X struct buf b; X X if (nexttok!=ID || strcmp(NextString,key) == 0) { X r.string = 0; X r.size = 0; X return r; X } else { X warnignore(); X fin = finptr; X bufautobegin(&b); X bufscpy(&b, NextString); X ffree1(NextString); X p = b.string + strlen(b.string); X lim = b.string + b.size; X c = nextc; X for (;;) { X for (;;) { X if (lim <= p) X p = bufenlarge(&b, &lim); X *p++ = c; X switch (ctab[c]) { X default: X fatserror("unknown character `%c'", c); X /*NOTREACHED*/ X case EOFILE: X unexpectedEOF(); X /*NOTREACHED*/ X case NEWLN: X ++rcsline; X /* fall into */ X case COLON: case DIGIT: case LETTER: case Letter: X case PERIOD: case SPACE: X c = getc(fin); X continue; X case SBEGIN: /* long string */ X for (;;) { X for (;;) { X c = getc(fin); X if (lim <= p) X p = bufenlarge(&b, &lim); X *p++ = c; X switch (c) { X case EOF: X unexpectedEOF(); X /*NOTREACHED*/ X case '\n': X ++rcsline; X /* fall into */ X default: X continue; X case SDELIM: X break; X } X break; X } X c = getc(fin); X if (c != SDELIM) X break; X if (lim <= p) X p = bufenlarge(&b, &lim); X *p++ = c; X } X continue; X case SEMI: X c = getc(fin); X if (ctab[c] == NEWLN) { X ++rcsline; X if (lim <= p) X p = bufenlarge(&b, &lim); X *p++ = c; X c = getc(fin); X } X for (;; c = getc(fin)) { X switch (ctab[c]) { X case NEWLN: ++rcsline; continue; X case SPACE: continue; X default: break; X } X break; X } X break; X } X break; X } X switch (ctab[c]) { X case LETTER: X case Letter: X for (kn = key; c && *kn==c; kn++) X if ((c = getc(fin)) == EOF) X unexpectedEOF(); X if (!*kn) X switch (ctab[c]) { X case DIGIT: case LETTER: case Letter: X break; X default: X nextc = c; X NextString = fstrsave(key); X nexttok = ID; X goto returnit; X } X for (ki=key; ki<kn; ) { X if (lim <= p) X p = bufenlarge(&b, &lim); X *p++ = *ki++; X } X break; X default: X nextc = c; X nextlex(); X goto returnit; X } X } X X returnit: X /* X * Do the following instead of bufautoend(&b), X * because the buffer must survive until we are done with the file. X */ X r.size = p - b.string; X r.string = (char*)fremember(testrealloc((malloc_type)b.string, r.size)); X return r; X } X} X X X void Xreadstring() X/* skip over characters until terminating single SDELIM */ X/* If foutptr is set, copy every character read to foutptr. */ X/* Does not advance nextlex at the end. */ X{ register c; X register FILE * fin, * frew; X fin=finptr; frew=foutptr; X if (frew) { X /* Copy string verbatim to foutptr. */ X while ((c=getc(fin)) != EOF) { X aputc(c,frew); X switch (c) { X case '\n': X ++rcsline; X break; X case SDELIM: X if ((c=getc(fin)) == EOF) { X nextc=c; X return; X } X aputc(c,frew); X if (c != SDELIM) { X /* end of string */ X nextc=c; X return; X } X break; X } X } X } else { X /* skip string */ X while ((c=getc(fin)) != EOF) { X switch (c) { X case '\n': X ++rcsline; X break; X case SDELIM: X if ((c=getc(fin)) != SDELIM) { X /* end of string */ X nextc=c; X return; X } X break; X } X } X } X unterminatedString(); X} X X X void Xprintstring() X/* Function: copy a string to stdout, until terminated with a single SDELIM. X * Does not advance nextlex at the end. X */ X{ X register c; X register FILE *fin, *fout; X fin=finptr; X fout = stdout; X while ((c=getc(fin)) != EOF) { X switch (c) { X case '\n': X ++rcsline; X break; X case SDELIM: X if ((c=getc(fin)) != SDELIM) { X /* end of string */ X nextc=c; X return; X } X break; X } X aputc(c,fout); X } X unterminatedString(); X} X X X X struct cbuf Xsavestring(target) X struct buf *target; X/* Copies a string terminated with SDELIM from file finptr to buffer target. X * Double SDELIM is replaced with SDELIM. X * If foutptr is set, the string is also copied unchanged to foutptr. X * Does not advance nextlex at the end. X * Yield a copy of *TARGET, except with exact length. X */ X{ X register c; X register FILE * fin, * frew; X register char *tp; X const char *lim; X struct cbuf r; X X fin=finptr; frew=foutptr; X tp = target->string; lim = tp + target->size; X for (;;) { X GETC(fin,frew,c); X switch (c) { X case '\n': X ++rcsline; X break; X case SDELIM: X GETC(fin,frew,c); X if (c != SDELIM) { X /* end of string */ X nextc=c; X r.string = target->string; X r.size = tp - r.string; X return r; X } X break; X case EOF: X unterminatedString(); X } X if (tp == lim) X tp = bufenlarge(target, &lim); X *tp++ = c; X } X} X X X char * Xcheckid(id, delimiter) X register char *id; X int delimiter; X/* Function: check whether the string starting at id is an */ X/* identifier and return a pointer to the delimiter*/ X/* after the identifier. White space, delim and 0 */ X/* are legal delimiters. Aborts the program if not*/ X/* a legal identifier. Useful for checking commands*/ X/* If !delim, the only delimiter is 0. */ X{ X register enum tokens d; X register char *temp; X register char c,tc; X register char delim = delimiter; X X temp = id; X if ((d = ctab[(unsigned char)(c = *id)])==LETTER || d==Letter) { X while ((d = ctab[(unsigned char)(c = *++id)])==LETTER X || d==Letter || d==DIGIT || d==IDCHAR X ) X ; X if (c && (!delim || c!=delim && c!=' ' && c!='\t' && c!='\n')) { X /* append \0 to end of id before error message */ X tc = c; X while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ; X *id = '\0'; X faterror("invalid character %c in identifier `%s'",tc,temp); X } X } else { X /* append \0 to end of id before error message */ X while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ; X *id = '\0'; X faterror("identifier `%s' doesn't start with letter", temp); X } X return id; X} X X void Xchecksid(id) X register char *id; X/* Check whether the string ID is an identifier. */ X{ X VOID checkid(id, 0); X} X X X exiting void XIOerror() X{ X static looping; X if (looping) X exiterr(); X looping = true; X faterror("input/output error; is the file system full?"); X} X Xvoid eflush() { if (fflush(stderr) == EOF) IOerror(); } Xvoid oflush() { if (fflush(stdout) == EOF) IOerror(); } X Xexiting void unterminatedString() { fatserror("unterminated string"); } X X static exiting void Xfatcleanup(already_newline) X int already_newline; X{ X VOID fprintf(stderr, already_newline+"\n%s aborted\n", cmdid); X exiterr(); X} X Xstatic void errsay() { oflush(); aprintf(stderr,"%s error: ",cmdid); nerror++; } Xstatic void fatsay() { oflush(); VOID fprintf(stderr,"%s error: ",cmdid); } X Xvoid eerror(n) const char *n; { errsay(); perror(n); eflush(); } Xexiting void efaterror(n) const char *n; { fatsay(); perror(n); fatcleanup(true); } X X#if has_prototypes X void Xerror(const char *format,...) X#else X /*VARARGS1*/ void error(format, va_alist) const char *format; va_dcl X#endif X/* non-fatal error */ X{ X va_list args; X errsay(); X vararg_start(args, format); X fvfprintf(stderr, format, args); X va_end(args); X afputc('\n',stderr); X eflush(); X} X X#if has_prototypes X exiting void Xfatserror(const char *format,...) X#else X /*VARARGS1*/ exiting void X fatserror(format, va_alist) const char *format; va_dcl X#endif X/* fatal syntax error */ X{ X va_list args; X oflush(); X VOID fprintf(stderr, "%s: %s:%lu: ", cmdid, RCSfilename, rcsline); X vararg_start(args, format); X fvfprintf(stderr, format, args); X va_end(args); X fatcleanup(false); X} X X#if has_prototypes X exiting void Xfaterror(const char *format,...) X#else X /*VARARGS1*/ exiting void faterror(format, va_alist) X const char *format; va_dcl X#endif X/* fatal error, terminates program after cleanup */ X{ X va_list args; X fatsay(); X vararg_start(args, format); X fvfprintf(stderr, format, args); X va_end(args); X fatcleanup(false); X} X X#if has_prototypes X void Xwarn(const char *format,...) X#else X /*VARARGS1*/ void warn(format, va_alist) const char *format; va_dcl X#endif X/* prints a warning message */ X{ X va_list args; X oflush(); X aprintf(stderr,"%s warning: ",cmdid); X vararg_start(args, format); X fvfprintf(stderr, format, args); X va_end(args); X afputc('\n',stderr); X eflush(); X} X X void Xredefined(c) X int c; X{ X warn("redefinition of -%c option", c); X} X X#if has_prototypes X void Xdiagnose(const char *format,...) X#else X /*VARARGS1*/ void diagnose(format, va_alist) const char *format; va_dcl X#endif X/* prints a diagnostic message */ X/* Unlike the other routines, it does not append a newline. */ X/* This lets some callers suppress the newline, and is faster */ X/* in implementations that flush stderr just at the end of each printf. */ X{ X va_list args; X if (!quietflag) { X oflush(); X vararg_start(args, format); X fvfprintf(stderr, format, args); X va_end(args); X eflush(); X } X} X X X X void Xafputc(c, f) X/* Function: afputc(c,f) acts like aputc(c,f), but is smaller and slower. X */ X int c; X register FILE *f; X{ X aputc(c,f); X} X X X void Xaputs(s, iop) X const char *s; X FILE *iop; X/* Function: Put string s on file iop, abort on error. X */ X{ X if (fputs(s, iop) == EOF) X IOerror(); X} X X X X void X#if has_prototypes Xfvfprintf(FILE *stream, const char *format, va_list args) X#else X fvfprintf(stream,format,args) FILE *stream; char *format; va_list args; X#endif X/* like vfprintf, except abort program on error */ X{ X#if has_vfprintf X if (vfprintf(stream, format, args) == EOF) X#else X _doprnt(format, args, stream); X if (ferror(stream)) X#endif X IOerror(); X} X X#if has_prototypes X void Xaprintf(FILE *iop, const char *fmt, ...) X#else X /*VARARGS2*/ void Xaprintf(iop, fmt, va_alist) XFILE *iop; Xconst char *fmt; Xva_dcl X#endif X/* Function: formatted output. Same as fprintf in stdio, X * but aborts program on error X */ X{ X va_list ap; X vararg_start(ap, fmt); X fvfprintf(iop, fmt, ap); X va_end(ap); X} X X X X#ifdef LEXDB X/* test program reading a stream of lexemes and printing the tokens. X */ X X X X int Xmain(argc,argv) Xint argc; char * argv[]; X{ X cmdid="lextest"; X if (argc<2) { X aputs("No input file\n",stderr); X exitmain(EXIT_FAILURE); X } X if ((finptr=fopen(argv[1], "r")) == NULL) { X faterror("can't open input file %s",argv[1]); X } X Lexinit(); X while (nexttok != EOFILE) { X switch (nexttok) { X X case ID: X VOID printf("ID: %s",NextString); X break; X X case NUM: X if (hshenter) X VOID printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab); X else X VOID printf("NUM, unentered: %s",NextString); X hshenter = !hshenter; /*alternate between dates and numbers*/ X break; X X case COLON: X VOID printf("COLON"); break; X X case SEMI: X VOID printf("SEMI"); break; X X case STRING: X readstring(); X VOID printf("STRING"); break; X X case UNKN: X VOID printf("UNKN"); break; X X default: X VOID printf("DEFAULT"); break; X } X VOID printf(" | "); X nextlex(); X } X VOID printf("\nEnd of lexical analyzer test\n"); X exitmain(EXIT_SUCCESS); X} X Xexiting void exiterr() { _exit(EXIT_FAILURE); } X X X#endif END_OF_FILE if test 24012 -ne `wc -c <'src/rcslex.c'`; then echo shar: \"'src/rcslex.c'\" unpacked with wrong size! fi # end of 'src/rcslex.c' fi echo shar: End of archive 5 \(of 12\). cp /dev/null ark5isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 12 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 12 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still must unpack the following archives: echo " " ${MISSING} fi exit 0 exit 0 # Just in case... -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net. Use a domain-based address or give alternate paths, or you may lose out.