home *** CD-ROM | disk | FTP | other *** search
- Subject: v24i005: RCS source control system, Part05/12
- Newsgroups: comp.sources.unix
- Approved: rsalz@uunet.UU.NET
- X-Checksum-Snefru: acf53e5d 10754ffb 88973713 807e4109
-
- 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...
-