v24i005: RCS source control system, Part05/12
Rich Salz
rsalz at uunet.uu.net
Fri Feb 22 06:59:00 AEST 1991
Submitted-by: Adam Hammer <hammer at 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 at uunet.uu.net if you want that tool.
# Contents: src/rcsfnms.c src/rcslex.c
# Wrapped by rsalz at 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 at 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 at 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 at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.
More information about the Comp.sources.unix
mailing list