home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-12-19 | 91.0 KB | 3,600 lines |
- Newsgroups: comp.sources.unix
- From: hammer@cs.purdue.edu (Adam Hammer)
- Subject: v25i082: rcs-5.6 - Revision Control System, V5.6, Part06/11
- Sender: sources-moderator@pa.dec.com
- Approved: vixie@pa.dec.com
-
- Submitted-By: hammer@cs.purdue.edu (Adam Hammer)
- Posting-Number: Volume 25, Issue 82
- Archive-Name: rcs-5.6/part06
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of archive 6 (of 11)."
- # Contents: src/rcsfnms.c src/rcslex.c src/rlog.c
- # Wrapped by vixie@cognition.pa.dec.com on Fri Dec 20 16:23:41 1991
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- 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'\" \(27744 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, 1991 by Paul Eggert
- X Distributed under license by the Free Software Foundation, Inc.
- X
- This file is part of RCS.
- X
- RCS is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
- X
- RCS is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- X
- You should have received a copy of the GNU General Public License
- along with RCS; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- X
- Report 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.8 1991/09/24 00:28:40 eggert
- X * Don't export bindex().
- X *
- X * Revision 5.7 1991/08/19 03:13:55 eggert
- X * Fix messages when rcswriteopen fails.
- X * Look in $TMP and $TEMP if $TMPDIR isn't set. Tune.
- X *
- X * Revision 5.6 1991/04/21 11:58:23 eggert
- X * Fix errno bugs. Add -x, RCSINIT, MS-DOS support.
- X *
- X * Revision 5.5 1991/02/26 17:48:38 eggert
- X * Fix setuid bug. Support new link behavior.
- X * Define more portable getcwd().
- X *
- 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
- libId(fnmsId, "$Id: rcsfnms.c,v 5.8 1991/09/24 00:28:40 eggert Exp $")
- X
- char const *RCSfilename;
- char *workfilename;
- XFILE *workstdout;
- struct stat RCSstat;
- char const *suffixes;
- X
- static char const rcsdir[] = "RCS";
- X#define rcsdirlen (sizeof(rcsdir)-1)
- X
- static struct buf RCSbuf, RCSb;
- static int RCSerrno;
- X
- X
- X/* Temp file names to be unlinked when done, if they are not nil. */
- X#define TEMPNAMES 5 /* must be at least DIRTEMPNAMES (see rcsedit.c) */
- static char *volatile tfnames[TEMPNAMES];
- X
- X
- struct compair {
- X char const *suffix, *comlead;
- X};
- X
- static struct compair const 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 "ada", "-- ",
- X "asm", ";; ", /* assembler (MS-DOS) */
- X "bat", ":: ", /* batch (MS-DOS) */
- X "c", " * ", /* C */
- X "c++", "// ", /* C++ in all its infinite guises */
- X "cc", "// ",
- X "cpp", "// ",
- X "cxx", "// ",
- X "cl", ";;; ", /* Common Lisp */
- X "cmd", ":: ", /* command (OS/2) */
- X "cmf", "c ", /* CM Fortran */
- X "cs", " * ", /* C* */
- X "el", "; ", /* Emacs Lisp */
- X "f", "c ", /* Fortran */
- X "for", "c ",
- X "h", " * ", /* C-header */
- X "hpp", "// ", /* C++ header */
- X "hxx", "// ",
- X "l", " * ", /* lex NOTE: conflict between lex and franzlisp */
- X "lisp",";;; ", /* Lucid Lisp */
- X "lsp", ";; ", /* Microsoft Lisp */
- X "mac", ";; ", /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */
- 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 "pas", " * ",
- X "pl", "% ", /* Prolog */
- X "tex", "% ", /* TeX */
- X "y", " * ", /* yacc */
- X nil, "# " /* default for unknown suffix; must always be last */
- X};
- X
- X#if has_mktemp
- X static char const *
- tmp()
- X/* Yield the name of the tmp directory. */
- X{
- X static char const *s;
- X if (!s
- X && !(s = cgetenv("TMPDIR")) /* Unix tradition */
- X && !(s = cgetenv("TMP")) /* DOS tradition */
- X && !(s = cgetenv("TEMP")) /* another DOS tradition */
- X )
- X s = TMPDIR;
- X return s;
- X}
- X#endif
- X
- X char const *
- maketemp(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;
- X char const *t = tfnames[n];
- X
- X if (t)
- X return t;
- X
- X catchints();
- X {
- X# if has_mktemp
- X char const *tp = tmp();
- X p = testalloc(strlen(tp) + 10);
- X VOID sprintf(p, "%s%cT%cXXXXXX", tp, SLASH, '0'+n);
- X if (!mktemp(p) || !*p)
- X faterror("can't make temporary file name `%s%cT%cXXXXXX'",
- X tp, SLASH, '0'+n
- X );
- X# else
- X static char tfnamebuf[TEMPNAMES][L_tmpnam];
- X p = tfnamebuf[n];
- X if (!tmpnam(p) || !*p)
- X# ifdef P_tmpdir
- X faterror("can't make temporary file name `%s...'",P_tmpdir);
- X# else
- X faterror("can't make temporary file name");
- X# endif
- X# endif
- X }
- X
- X tfnames[n] = p;
- X return p;
- X}
- X
- X void
- tempunlink()
- X/* Clean up maketemp() files. May be invoked by signal handler.
- X */
- X{
- X register int i;
- X register char *p;
- X
- X for (i = TEMPNAMES; 0 <= --i; )
- X if ((p = tfnames[i])) {
- X VOID unlink(p);
- X /*
- X * We would tfree(p) here,
- X * but this might dump core if we're handing a signal.
- X * We're about to exit anyway, so we won't bother.
- X */
- X tfnames[i] = 0;
- X }
- X}
- X
- X
- X static char const *
- bindex(sp,ch)
- X register char const *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 char const c=ch, *r;
- X r = sp;
- X while (*sp) {
- X if (*sp++ == c) r=sp;
- X }
- X return r;
- X}
- X
- X
- X
- X static int
- suffix_matches(suffix, pattern)
- X register char const *suffix, *pattern;
- X{
- X register int c;
- X if (!pattern)
- X return true;
- X for (;;)
- X switch (*suffix++ - (c = *pattern++)) {
- X case 0:
- X if (!c)
- X return true;
- X break;
- X
- X case 'A'-'a':
- X if (ctab[c] == Letter)
- X break;
- X /* fall into */
- X default:
- X return false;
- X }
- X}
- X
- X
- X static void
- InitAdmin()
- X/* function: initializes an admin node */
- X{
- X register char const *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; !suffix_matches(Suffix,comtable[i].suffix); i++)
- X ;
- X Comment.string = comtable[i].comlead;
- X Comment.size = strlen(comtable[i].comlead);
- X Lexinit(); /* note: if !finptr, reads nothing; only initializes */
- 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(basename(path)))
- X#else
- X static int
- filenametoolong(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 *base;
- X register unsigned long namemax;
- X
- X base = path + dirlen(path);
- X namelen = strlen(base);
- X if (namelen <= _POSIX_NAME_MAX) /* fast check for shorties */
- X return false;
- X if (base != path) {
- X *--base = 0;
- X namemax = pathconf(path, _PC_NAME_MAX);
- X *base = 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
- bufalloc(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
- bufrealloc(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 = trealloc(char, b->string, b->size);
- X }
- X }
- X}
- X
- X void
- bufautoend(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 struct cbuf
- bufremember(b, s)
- X struct buf *b;
- X size_t s;
- X/*
- X * Free the buffer B with used size S.
- X * Yield a cbuf with identical contents.
- X * The cbuf will be reclaimed when this input file is finished.
- X */
- X{
- X struct cbuf cb;
- X
- X if ((cb.size = s))
- X cb.string = fremember(trealloc(char, b->string, s));
- X else {
- X bufautoend(b); /* not really auto */
- X cb.string = "";
- X }
- X return cb;
- X}
- X
- X char *
- bufenlarge(b, alim)
- X register struct buf *b;
- X char const **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
- bufscat(b, s)
- X struct buf *b;
- X char const *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
- bufscpy(b, s)
- X struct buf *b;
- X char const *s;
- X/* Copy S into B. */
- X{
- X bufalloc(b, strlen(s)+1);
- X VOID strcpy(b->string, s);
- X}
- X
- X
- X char const *
- basename(p)
- X char const *p;
- X/* Yield the address of the base filename of the pathname P. */
- X{
- X register char const *b = p, *q = p;
- X for (;;)
- X switch (*q++) {
- X case SLASHes: b = q; break;
- X case 0: return b;
- X }
- X}
- X
- X size_t
- dirlen(p)
- X char const *p;
- X/* Yield the length of P's directory, including its trailing SLASH. */
- X{
- X return basename(p) - p;
- X}
- X
- X
- X static size_t
- suffixlen(x)
- X char const *x;
- X/* Yield the length of X, an RCS filename suffix. */
- X{
- X register char const *p;
- X
- X p = x;
- X for (;;)
- X switch (*p) {
- X case 0: case SLASHes:
- X return p - x;
- X
- X default:
- X ++p;
- X continue;
- X }
- X}
- X
- X char const *
- rcssuffix(name)
- X char const *name;
- X/* Yield the suffix of NAME if it is an RCS filename, 0 otherwise. */
- X{
- X char const *x, *p, *nz;
- X size_t dl, nl, xl;
- X
- X nl = strlen(name);
- X nz = name + nl;
- X x = suffixes;
- X do {
- X if ((xl = suffixlen(x))) {
- X if (xl <= nl && memcmp(p = nz-xl, x, xl) == 0)
- X return p;
- X } else {
- X dl = dirlen(name);
- X if (
- X rcsdirlen < dl &&
- X !memcmp(p = name+(dl-=rcsdirlen+1), rcsdir, rcsdirlen) &&
- X (!dl || isSLASH(*--p))
- X )
- X return nz;
- X }
- X x += xl;
- X } while (*x++);
- X return 0;
- X}
- X
- X /*ARGSUSED*/ RILE *
- rcsreadopen(RCSname, status, mustread)
- X struct buf *RCSname;
- X struct stat *status;
- X int mustread;
- X/* Open RCSNAME for reading and yield its FILE* descriptor.
- X * If successful, set *STATUS to its status.
- X * Pass this routine to pairfilenames() for read-only access to the file. */
- X{
- X return Iopen(RCSname->string, FOPEN_R, status);
- X}
- X
- X static int
- finopen(rcsopen, mustread)
- X RILE *(*rcsopen)P((struct buf*,struct stat*,int));
- X int mustread;
- X/*
- X * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read.
- X * Set finptr to the result and yield true if successful.
- X * RCSb holds the file's name.
- X * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno.
- X * Yield true if successful or if an unusual failure.
- X */
- X{
- X int interesting, preferold;
- X
- X /*
- X * We prefer an old name to that of a nonexisting new RCS file,
- X * unless we tried locking the old name and failed.
- X */
- X preferold = RCSbuf.string[0] && (mustread||frewrite);
- X
- X finptr = (*rcsopen)(&RCSb, &RCSstat, mustread);
- X interesting = finptr || errno!=ENOENT;
- X if (interesting || !preferold) {
- X /* Use the new name. */
- X RCSerrno = errno;
- X bufscpy(&RCSbuf, RCSb.string);
- X }
- X return interesting;
- X}
- X
- X static int
- fin2open(d, dlen, base, baselen, x, xlen, rcsopen, mustread)
- X char const *d, *base, *x;
- X size_t dlen, baselen, xlen;
- X RILE *(*rcsopen)P((struct buf*,struct stat*,int));
- X int mustread;
- X/*
- X * D is a directory name with length DLEN (including trailing slash).
- X * BASE is a filename with length BASELEN.
- X * X is an RCS filename suffix with length XLEN.
- X * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read.
- X * Yield true if successful.
- X * Try dRCS/basex first; if that fails and x is nonempty, try dbasex.
- X * Put these potential names in RCSb.
- X * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno.
- X * Yield true if successful or if an unusual failure.
- X */
- X{
- X register char *p;
- X
- X bufalloc(&RCSb, dlen + rcsdirlen + 1 + baselen + xlen + 1);
- X
- X /* Try dRCS/basex. */
- X VOID memcpy(p = RCSb.string, d, dlen);
- X VOID memcpy(p += dlen, rcsdir, rcsdirlen);
- X p += rcsdirlen;
- X *p++ = SLASH;
- X VOID memcpy(p, base, baselen);
- X VOID memcpy(p += baselen, x, xlen);
- X p[xlen] = 0;
- X if (xlen) {
- X if (finopen(rcsopen, mustread))
- X return true;
- X
- X /* Try dbasex. */
- X /* Start from scratch, because finopen() may have changed RCSb. */
- X VOID memcpy(p = RCSb.string, d, dlen);
- X VOID memcpy(p += dlen, base, baselen);
- X VOID memcpy(p += baselen, x, xlen);
- X p[xlen] = 0;
- X }
- X return finopen(rcsopen, mustread);
- X}
- X
- X int
- pairfilenames(argc, argv, rcsopen, mustread, quiet)
- X int argc;
- X char **argv;
- X RILE *(*rcsopen)P((struct buf*,struct stat*,int));
- X int mustread, quiet;
- 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 workstdout
- X * is set, 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,
- X * print an error unless QUIET and return 0.
- X * Otherwise, initialize the admin node and return -1.
- X *
- X * 0 is returned on all errors, e.g. files that are not regular files.
- X */
- X{
- X static struct buf tempbuf;
- X
- X register char *p, *arg, *RCS1;
- X char const *purefname, *pureRCSname, *x;
- X int paired;
- X size_t arglen, dlen, baselen, xlen;
- 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 purefname = basename(arg);
- X
- X /* Allocate buffer temporary to hold the default paired file name. */
- X p = arg;
- X for (;;) {
- X switch (*p++) {
- 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 default:
- X continue;
- X case 0:
- X break;
- X }
- X break;
- X }
- X
- X paired = false;
- X
- X /* first check suffix to see whether it is an RCS file or not */
- X if ((x = rcssuffix(arg)))
- X {
- X /* RCS file name given*/
- X RCS1 = arg;
- X pureRCSname = purefname;
- X baselen = x - purefname;
- X if (
- X 1 < argc &&
- X !rcssuffix(workfilename = p = argv[1]) &&
- X baselen <= (arglen = strlen(p)) &&
- X ((p+=arglen-baselen) == workfilename || isSLASH(p[-1])) &&
- X memcmp(purefname, p, baselen) == 0
- X ) {
- X argv[1] = 0;
- X paired = true;
- X } else {
- X bufscpy(&tempbuf, purefname);
- X workfilename = p = tempbuf.string;
- X p[baselen] = 0;
- X }
- X } else {
- X /* working file given; now try to find RCS file */
- X workfilename = arg;
- X baselen = p - purefname - 1;
- X /* derive RCS file name*/
- X if (
- X 1 < argc &&
- X (x = rcssuffix(RCS1 = argv[1])) &&
- X baselen <= x - RCS1 &&
- X ((pureRCSname=x-baselen)==RCS1 || isSLASH(pureRCSname[-1])) &&
- X memcmp(purefname, pureRCSname, baselen) == 0
- X ) {
- X argv[1] = 0;
- X paired = true;
- X } else
- X pureRCSname = RCS1 = 0;
- 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 bufscpy(&RCSbuf, RCS1);
- X finptr = (*rcsopen)(&RCSbuf, &RCSstat, mustread);
- X RCSerrno = errno;
- X } else {
- X bufscpy(&RCSbuf, "");
- X if (RCS1)
- X /* RCS file name was given without path. */
- X VOID fin2open(arg, (size_t)0, pureRCSname, baselen,
- X x, strlen(x), rcsopen, mustread
- X );
- X else {
- X /* No RCS file name was given. */
- X /* Try each suffix in turn. */
- X dlen = purefname-arg;
- X x = suffixes;
- X while (! fin2open(arg, dlen, purefname, baselen,
- X x, xlen=suffixlen(x), rcsopen, mustread
- X )) {
- X x += xlen;
- X if (!*x++)
- X break;
- X }
- X }
- X }
- X RCSfilename = p = RCSbuf.string;
- X if (finptr) {
- 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 (RCSerrno!=ENOENT || mustread || !frewrite) {
- X if (RCSerrno == EEXIST)
- X error("RCS file %s is in use", p);
- X else if (!quiet || RCSerrno!=ENOENT)
- X enerror(RCSerrno, 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 too, even though it cannot be longer,
- 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 (paired && workstdout)
- X warn("Option -p is set; ignoring output file %s",workfilename);
- X
- X prevkeys = false;
- X return finptr ? 1 : -1;
- X}
- X
- X
- X char const *
- getfullRCSname()
- 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 char const *wdptr;
- X static struct buf rcsbuf, wdbuf;
- X static size_t pathlength;
- X
- X register char const *realname;
- X register size_t parentdirlength;
- X register unsigned dotdotcounter;
- X register char *d;
- X register char const *wd;
- X
- X if (ROOTPATH(RCSfilename)) {
- X return(RCSfilename);
- X } else {
- X if (!(wd = wdptr)) {
- X /* Get working directory for the first time. */
- X if (!(d = cgetenv("PWD"))) {
- X bufalloc(&wdbuf, SIZEABLE_PATH + 1);
- X# if !has_getcwd && has_getwd
- X d = getwd(wdbuf.string);
- X# else
- X while (
- X !(d = getcwd(wdbuf.string, wdbuf.size))
- X && errno==ERANGE
- X )
- X bufalloc(&wdbuf, wdbuf.size<<1);
- X# endif
- X if (!d)
- X efaterror("working directory");
- X }
- X parentdirlength = strlen(d);
- X while (parentdirlength && isSLASH(d[parentdirlength-1])) {
- X d[--parentdirlength] = 0;
- X /* Check needed because some getwd implementations */
- X /* generate "/" for the root. */
- X }
- X wdptr = wd = d;
- X pathlength = parentdirlength;
- 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 if (isSLASH(realname[1])) {
- X /* drop leading ./ */
- X realname += 2;
- X } else if (realname[1]=='.' && isSLASH(realname[2])) {
- X /* drop leading ../ and remember */
- X dotdotcounter++;
- X realname += 3;
- X } else
- X break;
- 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 (isSLASH(wd[--parentdirlength])) {
- X dotdotcounter--;
- X }
- X }
- X /* build full path name */
- X bufalloc(&rcsbuf, parentdirlength+strlen(realname)+2);
- X d = rcsbuf.string;
- X VOID memcpy(d, wd, parentdirlength);
- X d += parentdirlength;
- X *d++ = SLASH;
- X VOID strcpy(d, realname);
- X return rcsbuf.string;
- X }
- X}
- X
- X#ifndef isSLASH
- X int
- isSLASH(c)
- X int c;
- X{
- X switch (c) {
- X case SLASHes:
- X return true;
- X default:
- X return false;
- X }
- X}
- X#endif
- X
- X
- X#if !has_getcwd && !has_getwd
- X
- X char *
- getcwd(path, size)
- X char *path;
- X size_t size;
- X{
- X static char const usrbinpwd[] = "/usr/bin/pwd";
- X# define binpwd (usrbinpwd+4)
- X
- X register FILE *fp;
- X register int c;
- X register char *p, *lim;
- X int closeerrno, closeerror, e, fd[2], readerror, toolong, wstatus;
- X pid_t child;
- X# if !has_waitpid
- X pid_t w;
- X# endif
- X
- X if (!size) {
- X errno = EINVAL;
- X return 0;
- X }
- X if (pipe(fd) != 0)
- X return 0;
- X if (!(child = vfork())) {
- X if (
- X close(fd[0]) == 0 &&
- X (fd[1] == STDOUT_FILENO ||
- X# ifdef F_DUPFD
- X (VOID close(STDOUT_FILENO),
- X fcntl(fd[1], F_DUPFD, STDOUT_FILENO))
- X# else
- X dup2(fd[1], STDOUT_FILENO)
- X# endif
- X == STDOUT_FILENO &&
- X close(fd[1]) == 0
- X )
- X ) {
- X VOID close(STDERR_FILENO);
- X VOID execl(binpwd, binpwd, (char *)0);
- X VOID execl(usrbinpwd, usrbinpwd, (char *)0);
- X }
- X _exit(EXIT_FAILURE);
- X }
- X e = errno;
- X closeerror = close(fd[1]);
- X closeerrno = errno;
- X fp = 0;
- X readerror = toolong = wstatus = 0;
- X p = path;
- X if (0 <= child) {
- X fp = fdopen(fd[0], "r");
- X e = errno;
- X if (fp) {
- X lim = p + size;
- X for (p = path; ; *p++ = c) {
- X if ((c=getc(fp)) < 0) {
- X if (feof(fp))
- X break;
- X if (ferror(fp)) {
- X readerror = 1;
- X e = errno;
- X break;
- X }
- X }
- X if (p == lim) {
- X toolong = 1;
- X break;
- X }
- X }
- X }
- X# if has_waitpid
- X if (waitpid(child, &wstatus, 0) < 0)
- X wstatus = 1;
- X# else
- X do {
- X if ((w = wait(&wstatus)) < 0) {
- X wstatus = 1;
- X break;
- X }
- X } while (w != child);
- X# endif
- X }
- X if (!fp) {
- X VOID close(fd[0]);
- X errno = e;
- X return 0;
- X }
- X if (fclose(fp) != 0)
- X return 0;
- X if (readerror) {
- X errno = e;
- X return 0;
- X }
- X if (closeerror) {
- X errno = closeerrno;
- X return 0;
- X }
- X if (toolong) {
- X errno = ERANGE;
- X return 0;
- X }
- X if (wstatus || p == path || *--p != '\n') {
- X errno = EACCES;
- X return 0;
- X }
- X *p = '\0';
- X return path;
- X}
- X#endif
- X
- X
- X#ifdef PAIRTEST
- X/* test program for pairfilenames() and getfullRCSname() */
- X
- char const cmdid[] = "pair";
- X
- main(argc, argv)
- int argc; char *argv[];
- X{
- X int result;
- X int initflag;
- X quietflag = initflag = false;
- X
- X while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) {
- X switch ((*argv)[1]) {
- X
- X case 'p': workstdout = stdout;
- 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,quietflag);
- 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 Ifclose(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
- X exiting void
- exiterr()
- X{
- X dirtempunlink();
- X tempunlink();
- X _exit(EXIT_FAILURE);
- X}
- X#endif
- END_OF_FILE
- if test 27744 -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'\" \(27526 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, 1991 by Paul Eggert
- X Distributed under license by the Free Software Foundation, Inc.
- X
- This file is part of RCS.
- X
- RCS is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
- X
- RCS is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- X
- You should have received a copy of the GNU General Public License
- along with RCS; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- X
- Report 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.11 1991/11/03 03:30:44 eggert
- X * Fix porting bug to ancient hosts lacking vfprintf.
- X *
- X * Revision 5.10 1991/10/07 17:32:46 eggert
- X * Support piece tables even if !has_mmap.
- X *
- X * Revision 5.9 1991/09/24 00:28:42 eggert
- X * Don't export errsay().
- X *
- X * Revision 5.8 1991/08/19 03:13:55 eggert
- X * Add eoflex(), mmap support. Tune.
- X *
- X * Revision 5.7 1991/04/21 11:58:26 eggert
- X * Add MS-DOS support.
- X *
- X * Revision 5.6 1991/02/25 07:12:42 eggert
- X * Work around fputs bug. strsave -> str_save (DG/UX name clash)
- X *
- 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
- libId(lexId, "$Id: rcslex.c,v 5.11 1991/11/03 03:30:44 eggert Exp $")
- X
- static struct hshentry *nexthsh; /*pointer to next hash entry, set by lookup*/
- X
- enum tokens nexttok; /*next token, set by nextlex */
- X
- int hshenter; /*if true, next suitable lexeme will be entered */
- X /*into the symbol table. Handle with care. */
- int nextc; /*next input character, initialized by Lexinit */
- X
- unsigned long rcsline; /*current line-number of input */
- int nerror; /*counter for errors */
- int quietflag; /*indicates quiet mode */
- RILE * finptr; /*input file descriptor */
- X
- XFILE * frewrite; /*file descriptor for echoing input */
- X
- XFILE * foutptr; /* copy of frewrite, but 0 to suppress echo */
- X
- static struct buf tokbuf; /* token buffer */
- X
- char const * 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
- static struct hshentry *hshtab[hshsize]; /*hashtable */
- X
- static int ignored_phrases; /* have we ignored phrases in this RCS file? */
- X
- X void
- warnignore()
- 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
- lookup(str)
- X char const *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 char const *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 = fstr_save(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
- Lexinit()
- X/* Function: Initialization of lexical analyzer:
- X * initializes the hashtable,
- X * initializes nextc, nexttok if finptr != 0
- X */
- X{ register int c;
- X
- X for (c = hshsize; 0 <= --c; ) {
- X hshtab[c] = nil;
- X }
- X
- X nerror = 0;
- X if (finptr) {
- X foutptr = 0;
- X hshenter = true;
- X ignored_phrases = false;
- X rcsline = 1;
- X bufrealloc(&tokbuf, 2);
- X Iget(finptr, nextc);
- X nextlex(); /*initial token*/
- X }
- X}
- X
- X
- X
- X
- X
- X
- X
- X void
- nextlex()
- 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 declarecache;
- X register FILE *frew;
- X register char * sp;
- X char const *limit;
- X register enum tokens d;
- X register RILE *fin;
- X
- X fin=finptr; frew=foutptr;
- X setupcache(fin); cache(fin);
- X c = nextc;
- X
- X for (;;) { switch ((d = ctab[c])) {
- X
- X default:
- X fatserror("unknown character `%c'", c);
- 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(frew, c);
- X continue;
- X
- X case DIGIT:
- X sp = tokbuf.string;
- X limit = sp + tokbuf.size;
- X *sp++ = c;
- X for (;;) {
- X GETC(frew, c);
- X if ((d=ctab[c])!=DIGIT && d!=PERIOD)
- X break;
- X *sp++ = c; /* 1.2. and 1.2 are different */
- X if (limit <= sp)
- X sp = bufenlarge(&tokbuf, &limit);
- X }
- X *sp = 0;
- X if (hshenter)
- X lookup(tokbuf.string);
- X else
- X NextString = fstr_save(tokbuf.string);
- X d = NUM;
- X break;
- X
- X
- X case LETTER:
- X case Letter:
- X sp = tokbuf.string;
- X limit = sp + tokbuf.size;
- X *sp++ = c;
- X for (;;) {
- X GETC(frew, c);
- X if ((d=ctab[c])!=LETTER && d!=Letter && d!=DIGIT && d!=IDCHAR)
- X break;
- X *sp++ = c;
- X if (limit <= sp)
- X sp = bufenlarge(&tokbuf, &limit);
- X }
- X *sp = 0;
- X NextString = fstr_save(tokbuf.string);
- X d = ID; /* may be ID or keyword */
- X break;
- X
- X case SBEGIN: /* long string */
- X d = STRING;
- X /* note: only the initial SBEGIN has been read*/
- X /* read the string, and reset nextc afterwards*/
- X break;
- X
- X case COLON:
- X case SEMI:
- X GETC(frew, c);
- X break;
- X } break; }
- X nextc = c;
- X nexttok = d;
- X uncache(fin);
- X}
- X
- X int
- eoflex()
- X/*
- X * Yield true if we look ahead to the end of the input, false otherwise.
- X * nextc becomes undefined at end of file.
- X */
- X{
- X register int c;
- X declarecache;
- X register FILE *fout;
- X register RILE *fin;
- X
- X c = nextc;
- X fin = finptr;
- X fout = foutptr;
- X setupcache(fin); cache(fin);
- X
- X for (;;) {
- X switch (ctab[c]) {
- X default:
- X nextc = c;
- X uncache(fin);
- X return false;
- X
- X case NEWLN:
- X ++rcsline;
- X /* fall into */
- X case SPACE:
- X cachegeteof(c, {uncache(fin);return true;});
- X break;
- X }
- X if (fout)
- X aputc(c, fout);
- X }
- X}
- X
- X
- int getlex(token)
- enum 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
- getkeyopt(key)
- X char const *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
- getkey(key)
- X char const *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
- getkeystring(key)
- X char const *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 char const *
- getid()
- 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 char const *name;
- X if (nexttok==ID) {
- X name = NextString;
- X nextlex();
- X return name;
- X } else return nil;
- X}
- X
- X
- struct 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
- getphrases(key)
- X char const *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.
- X */
- X{
- X declarecache;
- X register int c;
- X register char *p;
- X char const *limit;
- X register char const *ki, *kn;
- X struct cbuf r;
- X struct buf b;
- X register RILE *fin;
- 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 setupcache(fin); cache(fin);
- X bufautobegin(&b);
- X bufscpy(&b, NextString);
- X ffree1(NextString);
- X p = b.string + strlen(b.string);
- X limit = b.string + b.size;
- X c = nextc;
- X for (;;) {
- X for (;;) {
- X if (limit <= p)
- X p = bufenlarge(&b, &limit);
- X *p++ = c;
- X switch (ctab[c]) {
- X default:
- X fatserror("unknown character `%c'", c);
- 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 cacheget(c);
- X continue;
- X case SBEGIN: /* long string */
- X for (;;) {
- X for (;;) {
- X if (limit <= p)
- X p = bufenlarge(&b, &limit);
- X cacheget(c);
- X *p++ = c;
- X switch (c) {
- X case '\n':
- X ++rcsline;
- X /* fall into */
- X default:
- X continue;
- X
- X case SDELIM:
- X break;
- X }
- X break;
- X }
- X cacheget(c);
- X if (c != SDELIM)
- X break;
- X if (limit <= p)
- X p = bufenlarge(&b, &limit);
- X *p++ = c;
- X }
- X continue;
- X case SEMI:
- X cacheget(c);
- X if (ctab[c] == NEWLN) {
- X ++rcsline;
- X if (limit <= p)
- X p = bufenlarge(&b, &limit);
- X *p++ = c;
- X cacheget(c);
- X }
- X for (;;) {
- X switch (ctab[c]) {
- X case NEWLN:
- X ++rcsline;
- X /* fall into */
- X case SPACE:
- X cacheget(c);
- X continue;
- X
- 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 cacheget(c);
- X if (!*kn)
- X switch (ctab[c]) {
- X case DIGIT: case LETTER: case Letter:
- X break;
- X default:
- X nextc = c;
- X NextString = fstr_save(key);
- X nexttok = ID;
- X uncache(fin);
- X goto returnit;
- X }
- X for (ki=key; ki<kn; ) {
- X if (limit <= p)
- X p = bufenlarge(&b, &limit);
- X *p++ = *ki++;
- X }
- X break;
- X
- X default:
- X nextc = c;
- X uncache(fin);
- X nextlex();
- X goto returnit;
- X }
- X }
- X returnit:
- X return bufremember(&b, (size_t)(p - b.string));
- X }
- X}
- X
- X
- X void
- readstring()
- 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 declarecache;
- X register FILE *frew;
- X register RILE *fin;
- X fin=finptr; frew=foutptr;
- X setupcache(fin); cache(fin);
- X for (;;) {
- X GETC(frew, c);
- X switch (c) {
- X case '\n':
- X ++rcsline;
- X break;
- X
- X case SDELIM:
- X GETC(frew, c);
- X if (c != SDELIM) {
- X /* end of string */
- X nextc = c;
- X uncache(fin);
- X return;
- X }
- X break;
- X }
- X }
- X}
- X
- X
- X void
- printstring()
- 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 declarecache;
- X register FILE *fout;
- X register RILE *fin;
- X fin=finptr;
- X fout = stdout;
- X setupcache(fin); cache(fin);
- X for (;;) {
- X cacheget(c);
- X switch (c) {
- X case '\n':
- X ++rcsline;
- X break;
- X case SDELIM:
- X cacheget(c);
- X if (c != SDELIM) {
- X nextc=c;
- X uncache(fin);
- X return;
- X }
- X break;
- X }
- X aputc(c,fout);
- X }
- X}
- X
- X
- X
- X struct cbuf
- savestring(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 declarecache;
- X register FILE *frew;
- X register char *tp;
- X register RILE *fin;
- X char const *limit;
- X struct cbuf r;
- X
- X fin=finptr; frew=foutptr;
- X setupcache(fin); cache(fin);
- X tp = target->string; limit = tp + target->size;
- X for (;;) {
- X GETC(frew, c);
- X switch (c) {
- X case '\n':
- X ++rcsline;
- X break;
- X case SDELIM:
- X GETC(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 uncache(fin);
- X return r;
- X }
- X break;
- X }
- X if (tp == limit)
- X tp = bufenlarge(target, &limit);
- X *tp++ = c;
- X }
- X}
- X
- X
- X char *
- checkid(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
- checksid(id)
- X char *id;
- X/* Check whether the string ID is an identifier. */
- X{
- X VOID checkid(id, 0);
- X}
- X
- X
- X static RILE *
- X#if has_mmap && large_memory
- fd2_RILE(fd, filename, status)
- X#else
- fd2RILE(fd, filename, mode, status)
- X char const *mode;
- X#endif
- X int fd;
- X char const *filename;
- X register struct stat *status;
- X{
- X struct stat st;
- X
- X if (!status)
- X status = &st;
- X if (fstat(fd, status) != 0)
- X efaterror(filename);
- X if (!S_ISREG(status->st_mode)) {
- X error("`%s' is not a regular file", filename);
- X VOID close(fd);
- X errno = EINVAL;
- X return 0;
- X } else {
- X
- X# if ! (has_mmap && large_memory)
- X FILE *stream;
- X if (!(stream = fdopen(fd, mode)))
- X efaterror(filename);
- X# endif
- X
- X# if !large_memory
- X return stream;
- X# else
- X# define RILES 3
- X {
- X static RILE rilebuf[RILES];
- X
- X register RILE *f;
- X size_t s = status->st_size;
- X
- X if (s != status->st_size)
- X faterror("`%s' is enormous", filename);
- X for (f = rilebuf; f->base; f++)
- X if (f == rilebuf+RILES)
- X faterror("too many RILEs");
- X if (!s) {
- X static unsigned char dummy;
- X f->base = &dummy;
- X } else {
- X# if has_mmap
- X if (
- X (f->base = (unsigned char *)mmap(
- X (caddr_t)0, s, PROT_READ, MAP_SHARED,
- X fd, (off_t)0
- X )) == (unsigned char *)-1
- X )
- X efaterror("mmap");
- X# else
- X f->base = tnalloc(unsigned char, s);
- X# endif
- X }
- X f->ptr = f->base;
- X f->lim = f->base + s;
- X# if has_mmap
- X f->fd = fd;
- X# else
- X f->readlim = f->base;
- X f->stream = stream;
- X# endif
- X if_advise_access(s, f, MADV_SEQUENTIAL);
- X return f;
- X }
- X# endif
- X }
- X}
- X
- X#if !has_mmap && large_memory
- X int
- Igetmore(f)
- X register RILE *f;
- X{
- X register fread_type r;
- X register size_t s = f->lim - f->readlim;
- X
- X if (BUFSIZ < s)
- X s = BUFSIZ;
- X if (!(r = Fread(f->readlim, sizeof(*f->readlim), s, f->stream))) {
- X testIerror(f->stream);
- X f->lim = f->readlim; /* The file might have shrunk! */
- X return 0;
- X }
- X f->readlim += r;
- X return 1;
- X}
- X#endif
- X
- X#if has_madvise && has_mmap && large_memory
- X void
- advise_access(f, advice)
- X register RILE *f;
- X int advice;
- X{
- X if (madvise((caddr_t)f->base, (size_t)(f->lim - f->base), advice) != 0)
- X efaterror("madvise");
- X}
- X#endif
- X
- X RILE *
- X#if has_mmap && large_memory
- I_open(filename, status)
- X#else
- Iopen(filename, mode, status)
- X char const *mode;
- X#endif
- X char const *filename;
- X struct stat *status;
- X/* Open FILENAME for reading, yield its descriptor, and set *STATUS. */
- X{
- X int fd;
- X
- X if ((fd = open(filename,O_RDONLY|O_BINARY)) < 0)
- X return 0;
- X# if has_mmap && large_memory
- X return fd2_RILE(fd, filename, status);
- X# else
- X return fd2RILE(fd, filename, mode, status);
- X# endif
- X}
- X
- X
- X#if !large_memory
- X# define Iclose(f) fclose(f)
- X#else
- X static int
- X Iclose(f)
- X register RILE *f;
- X {
- X# if has_mmap
- X size_t s = f->lim - f->base;
- X if (s && munmap((caddr_t)f->base, s) != 0)
- X return -1;
- X f->base = 0;
- X return close(f->fd);
- X# else
- X tfree(f->base);
- X f->base = 0;
- X return fclose(f->stream);
- X# endif
- X }
- X#endif
- X
- X
- static int Oerrloop;
- X
- X exiting void
- Oerror()
- X{
- X if (Oerrloop)
- X exiterr();
- X Oerrloop = true;
- X efaterror("output error");
- X}
- X
- exiting void Ieof() { fatserror("unexpected end of file"); }
- exiting void Ierror() { efaterror("input error"); }
- void testIerror(f) FILE *f; { if (ferror(f)) Ierror(); }
- void testOerror(o) FILE *o; { if (ferror(o)) Oerror(); }
- X
- void Ifclose(f) RILE *f; { if (f && Iclose(f)!=0) Ierror(); }
- void Ofclose(f) FILE *f; { if (f && fclose(f)!=0) Oerror(); }
- void Izclose(p) RILE **p; { Ifclose(*p); *p = 0; }
- void Ozclose(p) FILE **p; { Ofclose(*p); *p = 0; }
- X
- X#if !large_memory
- X void
- testIeof(f)
- X FILE *f;
- X{
- X testIerror(f);
- X if (feof(f))
- X Ieof();
- X}
- void Irewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Ierror(); }
- X#endif
- X
- void eflush()
- X{
- X if (fflush(stderr) != 0 && !Oerrloop)
- X Oerror();
- X}
- X
- void oflush()
- X{
- X if (fflush(workstdout ? workstdout : stdout) != 0 && !Oerrloop)
- X Oerror();
- X}
- X
- X static exiting void
- fatcleanup(already_newline)
- X int already_newline;
- X{
- X VOID fprintf(stderr, already_newline+"\n%s aborted\n", cmdid);
- X exiterr();
- X}
- X
- static void errsay() { oflush(); aprintf(stderr,"%s error: ",cmdid); nerror++; }
- static void fatsay() { oflush(); VOID fprintf(stderr,"%s error: ",cmdid); }
- X
- void eerror(s) char const *s; { enerror(errno,s); }
- X
- X void
- enerror(e,s)
- X int e;
- X char const *s;
- X{
- X errsay();
- X errno = e;
- X perror(s);
- X eflush();
- X}
- X
- exiting void efaterror(s) char const *s; { enfaterror(errno,s); }
- X
- X exiting void
- enfaterror(e,s)
- X int e;
- X char const *s;
- X{
- X fatsay();
- X errno = e;
- X perror(s);
- X fatcleanup(true);
- X}
- X
- X#if has_prototypes
- X void
- error(char const *format,...)
- X#else
- X /*VARARGS1*/ void error(format, va_alist) char const *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
- fatserror(char const *format,...)
- X#else
- X /*VARARGS1*/ exiting void
- X fatserror(format, va_alist) char const *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
- faterror(char const *format,...)
- X#else
- X /*VARARGS1*/ exiting void faterror(format, va_alist)
- X char const *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
- warn(char const *format,...)
- X#else
- X /*VARARGS1*/ void warn(format, va_alist) char const *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
- redefined(c)
- X int c;
- X{
- X warn("redefinition of -%c option", c);
- X}
- X
- X#if has_prototypes
- X void
- diagnose(char const *format,...)
- X#else
- X /*VARARGS1*/ void diagnose(format, va_alist) char const *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
- afputc(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
- aputs(s, iop)
- X char const *s;
- X FILE *iop;
- X/* Function: Put string s on file iop, abort on error.
- X */
- X{
- X#if has_fputs
- X if (fputs(s, iop) < 0)
- X Oerror();
- X#else
- X awrite(s, strlen(s), iop);
- X#endif
- X}
- X
- X
- X
- X void
- X#if has_prototypes
- fvfprintf(FILE *stream, char const *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) < 0)
- X#else
- X# if has__doprintf
- X _doprintf(stream, format, args);
- X# else
- X# if has__doprnt
- X _doprnt(format, args, stream);
- X# else
- X int *a = (int *)args;
- X VOID fprintf(stream, format,
- X a[0], a[1], a[2], a[3], a[4],
- X a[5], a[6], a[7], a[8], a[9]
- X );
- X# endif
- X# endif
- X if (ferror(stream))
- X#endif
- X Oerror();
- X}
- X
- X#if has_prototypes
- X void
- aprintf(FILE *iop, char const *fmt, ...)
- X#else
- X /*VARARGS2*/ void
- aprintf(iop, fmt, va_alist)
- XFILE *iop;
- char const *fmt;
- va_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
- main(argc,argv)
- int 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=Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
- X faterror("can't open input file %s",argv[1]);
- X }
- X Lexinit();
- X while (!eoflex()) {
- 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 exitmain(EXIT_SUCCESS);
- X}
- X
- exiting void exiterr() { _exit(EXIT_FAILURE); }
- X
- X
- X#endif
- END_OF_FILE
- if test 27526 -ne `wc -c <'src/rcslex.c'`; then
- echo shar: \"'src/rcslex.c'\" unpacked with wrong size!
- fi
- # end of 'src/rcslex.c'
- fi
- if test -f 'src/rlog.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'src/rlog.c'\"
- else
- echo shar: Extracting \"'src/rlog.c'\" \(32185 characters\)
- sed "s/^X//" >'src/rlog.c' <<'END_OF_FILE'
- X/*
- X * RLOG operation
- X */
- X/*****************************************************************************
- X * print contents of RCS files
- X *****************************************************************************
- X */
- X
- X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
- X Copyright 1990, 1991 by Paul Eggert
- X Distributed under license by the Free Software Foundation, Inc.
- X
- This file is part of RCS.
- X
- RCS is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
- X
- RCS is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- X
- You should have received a copy of the GNU General Public License
- along with RCS; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- X
- Report problems and direct all questions to:
- X
- X rcs-bugs@cs.purdue.edu
- X
- X*/
- X
- X
- X
- X
- X/* $Log: rlog.c,v $
- X * Revision 5.9 1991/09/17 19:07:40 eggert
- X * Getscript() didn't uncache partial lines.
- X *
- X * Revision 5.8 1991/08/19 03:13:55 eggert
- X * Revision separator is `:', not `-'.
- X * Check for missing and duplicate logs. Tune.
- X * Permit log messages that do not end in newline (including empty logs).
- X *
- X * Revision 5.7 1991/04/21 11:58:31 eggert
- X * Add -x, RCSINIT, MS-DOS support.
- X *
- X * Revision 5.6 1991/02/26 17:07:17 eggert
- X * Survive RCS files with missing logs.
- X * strsave -> str_save (DG/UX name clash)
- X *
- X * Revision 5.5 1990/11/01 05:03:55 eggert
- X * Permit arbitrary data in logs and comment leaders.
- X *
- X * Revision 5.4 1990/10/04 06:30:22 eggert
- X * Accumulate exit status across files.
- X *
- X * Revision 5.3 1990/09/11 02:41:16 eggert
- X * Plug memory leak.
- X *
- X * Revision 5.2 1990/09/04 08:02:33 eggert
- X * Count RCS lines better.
- X *
- X * Revision 5.0 1990/08/22 08:13:48 eggert
- X * Remove compile-time limits; use malloc instead. Add setuid support.
- X * Switch to GMT.
- X * Report dates in long form, to warn about dates past 1999/12/31.
- X * Change "added/del" message to make room for the longer dates.
- X * Don't generate trailing white space. Add -V. Ansify and Posixate.
- X *
- X * Revision 4.7 89/05/01 15:13:48 narten
- X * changed copyright header to reflect current distribution rules
- X *
- X * Revision 4.6 88/08/09 19:13:28 eggert
- X * Check for memory exhaustion; don't access freed storage.
- X * Shrink stdio code size; remove lint.
- X *
- X * Revision 4.5 87/12/18 11:46:38 narten
- X * more lint cleanups (Guy Harris)
- X *
- X * Revision 4.4 87/10/18 10:41:12 narten
- X * Updating version numbers
- X * Changes relative to 1.1 actually relative to 4.2
- X *
- X * Revision 1.3 87/09/24 14:01:10 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:45 jenkins
- X * Port to suns
- X *
- X * Revision 4.2 83/12/05 09:18:09 wft
- X * changed rewriteflag to external.
- X *
- X * Revision 4.1 83/05/11 16:16:55 wft
- X * Added -b, updated getnumericrev() accordingly.
- X * Replaced getpwuid() with getcaller().
- X *
- X * Revision 3.7 83/05/11 14:24:13 wft
- X * Added options -L and -R;
- X * Fixed selection bug with -l on multiple files.
- X * Fixed error on dates of the form -d'>date' (rewrote getdatepair()).
- X *
- X * Revision 3.6 82/12/24 15:57:53 wft
- X * shortened output format.
- X *
- X * Revision 3.5 82/12/08 21:45:26 wft
- X * removed call to checkaccesslist(); used DATEFORM to format all dates;
- X * removed unused variables.
- X *
- X * Revision 3.4 82/12/04 13:26:25 wft
- X * Replaced getdelta() with gettree(); removed updating of field lockedby.
- X *
- X * Revision 3.3 82/12/03 14:08:20 wft
- X * Replaced getlogin with getpwuid(), %02d with %.2d, fancydate with PRINTDATE.
- X * Fixed printing of nil, removed printing of Suffix,
- X * added shortcut if no revisions are printed, disambiguated struct members.
- X *
- X * Revision 3.2 82/10/18 21:09:06 wft
- X * call to curdir replaced with getfullRCSname(),
- X * fixed call to getlogin(), cosmetic changes on output,
- X * changed conflicting long identifiers.
- X *
- X * Revision 3.1 82/10/13 16:07:56 wft
- X * fixed type of variables receiving from getc() (char -> int).
- X */
- X
- X
- X
- X#include "rcsbase.h"
- X
- struct lockers { /* lockers in locker option; stored */
- X char const * login; /* lockerlist */
- X struct lockers * lockerlink;
- X } ;
- X
- struct stateattri { /* states in state option; stored in */
- X char const * status; /* statelist */
- X struct stateattri * nextstate;
- X } ;
- X
- struct authors { /* login names in author option; */
- X char const * login; /* stored in authorlist */
- X struct authors * nextauthor;
- X } ;
- X
- struct Revpairs{ /* revision or branch range in -r */
- X unsigned numfld; /* option; stored in revlist */
- X char const * strtrev;
- X char const * endrev;
- X struct Revpairs * rnext;
- X } ;
- X
- struct Datepairs{ /* date range in -d option; stored in */
- X char strtdate[datesize]; /* duelst and datelist */
- X char enddate[datesize];
- X struct Datepairs * dnext;
- X };
- X
- static char extractdelta P((struct hshentry const*));
- static int checkrevpair P((char const*,char const*));
- static struct hshentry const *readdeltalog P((void));
- static unsigned extdate P((struct hshentry*));
- static void cleanup P((void));
- static void exttree P((struct hshentry*));
- static void getauthor P((char*));
- static void getdatepair P((char*));
- static void getlocker P((char*));
- static void getnumericrev P((void));
- static void getrevpairs P((char*));
- static void getscript P((struct hshentry*));
- static void getstate P((char*));
- static void putabranch P((struct hshentry const*));
- static void putadelta P((struct hshentry const*,struct hshentry const*,int));
- static void putforest P((struct branchhead const*));
- static void putree P((struct hshentry const*));
- static void putrunk P((void));
- static void recentdate P((struct hshentry const*,struct Datepairs*));
- static void trunclocks P((void));
- X
- static char const *insDelFormat;
- static int branchflag; /*set on -b */
- static int exitstatus;
- static int lockflag;
- static struct Datepairs *datelist, *duelst;
- static struct Revpairs *revlist, *Revlst;
- static struct authors *authorlist;
- static struct lockers *lockerlist;
- static struct stateattri *statelist;
- X
- X
- mainProg(rlogId, "rlog", "$Id: rlog.c,v 5.9 1991/09/17 19:07:40 eggert Exp $")
- X{
- X static char const cmdusage[] =
- X "\nrlog usage: rlog -{bhLRt} -ddates -l[lockers] -rrevs -sstates -w[logins] -Vn file ...";
- X
- X register FILE *out;
- X char *a, **newargv;
- X struct Datepairs *currdate;
- X char const *accessListString, *accessFormat, *commentFormat;
- X char const *headFormat, *symbolFormat;
- X struct access const *curaccess;
- X struct assoc const *curassoc;
- X struct hshentry const *delta;
- X struct lock const *currlock;
- X int descflag, selectflag;
- X int onlylockflag; /* print only files with locks */
- X int onlyRCSflag; /* print only RCS file name */
- X unsigned revno;
- X
- X descflag = selectflag = true;
- X onlylockflag = onlyRCSflag = false;
- X out = stdout;
- X suffixes = X_DEFAULT;
- X
- X argc = getRCSINIT(argc, argv, &newargv);
- X argv = newargv;
- X while (a = *++argv, 0<--argc && *a++=='-') {
- X switch (*a++) {
- X
- X case 'L':
- X onlylockflag = true;
- X break;
- X
- X case 'R':
- X onlyRCSflag =true;
- X break;
- X
- X case 'l':
- X lockflag = true;
- X getlocker(a);
- X break;
- X
- X case 'b':
- X branchflag = true;
- X break;
- X
- X case 'r':
- X getrevpairs(a);
- X break;
- X
- X case 'd':
- X getdatepair(a);
- X break;
- X
- X case 's':
- X getstate(a);
- X break;
- X
- X case 'w':
- X getauthor(a);
- X break;
- X
- X case 'h':
- X descflag = false;
- X break;
- X
- X case 't':
- X selectflag = false;
- X break;
- X
- X case 'q':
- X /* This has no effect; it's here for consistency. */
- X quietflag = true;
- X break;
- X
- X case 'x':
- X suffixes = a;
- X break;
- X
- X case 'V':
- X setRCSversion(*argv);
- X break;
- X
- X default:
- X faterror("unknown option: %s%s", *argv, cmdusage);
- X
- X };
- X } /* end of option processing */
- X
- X if (argc<1) faterror("no input file%s", cmdusage);
- X
- X if (! (descflag|selectflag)) {
- X warn("-t overrides -h.");
- X descflag = true;
- X }
- X
- X if (RCSversion < VERSION(5)) {
- X accessListString = "\naccess list: ";
- X accessFormat = " %s";
- X commentFormat = "\ncomment leader: \"";
- X headFormat = "\nRCS file: %s; Working file: %s\nhead: %s%s\nbranch: %s%s\nlocks: ";
- X insDelFormat = " lines added/del: %lu/%lu";
- X symbolFormat = " %s: %s;";
- X } else {
- X accessListString = "\naccess list:";
- X accessFormat = "\n\t%s";
- X commentFormat = "\ncomment leader: \"";
- X headFormat = "\nRCS file: %s\nWorking file: %s\nhead:%s%s\nbranch:%s%s\nlocks:%s";
- X insDelFormat = " lines: +%lu -%lu";
- X symbolFormat = "\n\t%s: %s";
- X }
- X
- X /* now handle all filenames */
- X do {
- X ffree();
- X
- X if (pairfilenames(argc, argv, rcsreadopen, true, false) <= 0)
- X continue;
- X
- X /* now RCSfilename contains the name of the RCS file, and finptr
- X * the file descriptor. Workfilename contains the name of the
- X * working file.
- X */
- X
- X /* Keep only those locks given by -l. */
- X if (lockflag)
- X trunclocks();
- X
- X /* do nothing if -L is given and there are no locks*/
- X if (onlylockflag && !Locks)
- X continue;
- X
- X if ( onlyRCSflag ) {
- X aprintf(out, "%s\n", RCSfilename);
- X continue;
- X }
- X /* print RCS filename , working filename and optional
- X administrative information */
- X /* could use getfullRCSname() here, but that is very slow */
- X aprintf(out, headFormat, RCSfilename, workfilename,
- X Head ? " " : "", Head ? Head->num : "",
- X Dbranch ? " " : "", Dbranch ? Dbranch : "",
- X StrictLocks ? " strict" : ""
- X );
- X currlock = Locks;
- X while( currlock ) {
- X aprintf(out, symbolFormat, currlock->login,
- X currlock->delta->num);
- X currlock = currlock->nextlock;
- X }
- X if (StrictLocks && RCSversion<VERSION(5))
- X aputs(" strict", out);
- X
- X aputs(accessListString, out); /* print access list */
- X curaccess = AccessList;
- X while(curaccess) {
- X aprintf(out, accessFormat, curaccess->login);
- X curaccess = curaccess->nextaccess;
- X }
- X
- X aputs("\nsymbolic names:", out); /* print symbolic names */
- X for (curassoc=Symbols; curassoc; curassoc=curassoc->nextassoc)
- X aprintf(out, symbolFormat, curassoc->symbol, curassoc->num);
- X aputs(commentFormat, out);
- X awrite(Comment.string, Comment.size, out);
- X aputs("\"\n", out);
- X if (VERSION(5)<=RCSversion || Expand != KEYVAL_EXPAND)
- X aprintf(out, "keyword substitution: %s\n",
- X expand_names[Expand]
- X );
- X
- X gettree();
- X
- X aprintf(out, "total revisions: %u", TotalDeltas);
- X
- X revno = 0;
- X
- X if (Head && selectflag & descflag) {
- X
- X getnumericrev(); /* get numeric revision or branch names */
- X
- X exttree(Head);
- X
- X /* get most recently date of the dates pointed by duelst */
- X currdate = duelst;
- X while( currdate) {
- X VOID sprintf(currdate->strtdate,DATEFORM,0,0,0,0,0,0);
- X recentdate(Head, currdate);
- X currdate = currdate->dnext;
- X }
- X
- X revno = extdate(Head);
- X
- X aprintf(out, ";\tselected revisions: %u", revno);
- X }
- X
- X afputc('\n',out);
- X if (descflag) {
- X aputs("description:\n", out);
- X getdesc(true);
- X }
- X if (revno) {
- X while (! (delta = readdeltalog())->selector || --revno)
- X ;
- X if (delta->next && countnumflds(delta->num)==2)
- X /* Read through delta->next to get its insertlns. */
- X while (readdeltalog() != delta->next)
- X ;
- X putrunk();
- X putree(Head);
- X }
- X aputs("=============================================================================\n",out);
- X } while (cleanup(),
- X ++argv, --argc >= 1);
- X Ofclose(out);
- X exitmain(exitstatus);
- X}
- X
- X static void
- cleanup()
- X{
- X if (nerror) exitstatus = EXIT_FAILURE;
- X Izclose(&finptr);
- X}
- X
- X#if lint
- X# define exiterr rlogExit
- X#endif
- X exiting void
- exiterr()
- X{
- X _exit(EXIT_FAILURE);
- X}
- X
- X
- X
- X static void
- putrunk()
- X/* function: print revisions chosen, which are in trunk */
- X
- X{
- X register struct hshentry const *ptr;
- X
- X for (ptr = Head; ptr; ptr = ptr->next)
- X putadelta(ptr, ptr->next, true);
- X}
- X
- X
- X
- X static void
- putree(root)
- X struct hshentry const *root;
- X/* function: print delta tree (not including trunk) in reverse
- X order on each branch */
- X
- X{
- X if ( root == nil ) return;
- X
- X putree(root->next);
- X
- X putforest(root->branches);
- X}
- X
- X
- X
- X
- X static void
- putforest(branchroot)
- X struct branchhead const *branchroot;
- X/* function: print branches that has the same direct ancestor */
- X{
- X
- X if ( branchroot == nil ) return;
- X
- X putforest(branchroot->nextbranch);
- X
- X putabranch(branchroot->hsh);
- X putree(branchroot->hsh);
- X}
- X
- X
- X
- X
- X static void
- putabranch(root)
- X struct hshentry const *root;
- X/* function : print one branch */
- X
- X{
- X
- X if ( root == nil) return;
- X
- X putabranch(root->next);
- X
- X putadelta(root, root, false);
- X}
- X
- X
- X
- X
- X
- X static void
- putadelta(node,editscript,trunk)
- X register struct hshentry const *node, *editscript;
- X int trunk;
- X/* function: Print delta node if node->selector is set. */
- X/* editscript indicates where the editscript is stored */
- X/* trunk indicated whether this node is in trunk */
- X{
- X static char emptych[] = EMPTYLOG;
- X
- X register FILE *out;
- X char const *s;
- X size_t n;
- X struct branchhead const *newbranch;
- X struct buf branchnum;
- X char datebuf[datesize];
- X
- X if (!node->selector)
- X return;
- X
- X out = stdout;
- X aprintf(out,
- X "----------------------------\nrevision %s", node->num
- X );
- X if ( node->lockedby )
- X aprintf(out, "\tlocked by: %s;", node->lockedby);
- X
- X aprintf(out, "\ndate: %s; author: %s; state: %s;",
- X date2str(node->date, datebuf),
- X node->author, node->state
- X );
- X
- X if ( editscript )
- X if(trunk)
- X aprintf(out, insDelFormat,
- X editscript->deletelns, editscript->insertlns);
- X else
- X aprintf(out, insDelFormat,
- X editscript->insertlns, editscript->deletelns);
- X
- X newbranch = node->branches;
- X if ( newbranch ) {
- X bufautobegin(&branchnum);
- X aputs("\nbranches:", out);
- X while( newbranch ) {
- X getbranchno(newbranch->hsh->num, &branchnum);
- X aprintf(out, " %s;", branchnum.string);
- X newbranch = newbranch->nextbranch;
- X }
- X bufautoend(&branchnum);
- X }
- X
- X afputc('\n', out);
- X s = node->log.string;
- X if (!(n = node->log.size)) {
- X s = emptych;
- X n = sizeof(emptych)-1;
- X }
- X awrite(s, n, out);
- X if (s[n-1] != '\n')
- X afputc('\n', out);
- X}
- X
- X
- X
- X
- X
- X static struct hshentry const *
- readdeltalog()
- X/* Function : get the log message and skip the text of a deltatext node.
- X * Return the delta found.
- X * Assumes the current lexeme is not yet in nexttok; does not
- X * advance nexttok.
- X */
- X{
- X register struct hshentry * Delta;
- X struct buf logbuf;
- X struct cbuf cb;
- X
- X if (eoflex())
- X fatserror("missing delta log");
- X nextlex();
- X if (!(Delta = getnum()))
- X fatserror("delta number corrupted");
- X getkeystring(Klog);
- X if (Delta->log.string)
- X fatserror("duplicate delta log");
- X bufautobegin(&logbuf);
- X cb = savestring(&logbuf);
- X Delta->log = bufremember(&logbuf, cb.size);
- X
- X nextlex();
- X while (nexttok==ID && strcmp(NextString,Ktext)!=0)
- X ignorephrase();
- X getkeystring(Ktext);
- X Delta->insertlns = Delta->deletelns = 0;
- X if ( Delta != Head)
- X getscript(Delta);
- X else
- X readstring();
- X return Delta;
- X}
- X
- X
- X static void
- getscript(Delta)
- struct hshentry * Delta;
- X/* function: read edit script of Delta and count how many lines added */
- X/* and deleted in the script */
- X
- X{
- X int ed; /* editor command */
- X declarecache;
- X register RILE *fin;
- X register int c;
- X register unsigned long i;
- X struct diffcmd dc;
- X
- X fin = finptr;
- X setupcache(fin);
- X initdiffcmd(&dc);
- X while (0 <= (ed = getdiffcmd(fin,true,(FILE *)0,&dc)))
- X if (!ed)
- X Delta->deletelns += dc.nlines;
- X else {
- X /* skip scripted lines */
- X i = dc.nlines;
- X Delta->insertlns += i;
- X cache(fin);
- X do {
- X for (;;) {
- X cacheget(c);
- X switch (c) {
- X default:
- X continue;
- X case SDELIM:
- X cacheget(c);
- X if (c == SDELIM)
- X continue;
- X if (--i)
- X fatserror("unexpected end to edit script");
- X nextc = c;
- X uncache(fin);
- X return;
- X case '\n':
- X break;
- X }
- X break;
- X }
- X ++rcsline;
- X } while (--i);
- X uncache(fin);
- X }
- X}
- X
- X
- X
- X
- X
- X
- X
- X static void
- exttree(root)
- struct hshentry *root;
- X/* function: select revisions , starting with root */
- X
- X{
- X struct branchhead const *newbranch;
- X
- X if (root == nil) return;
- X
- X root->selector = extractdelta(root);
- X root->log.string = nil;
- X exttree(root->next);
- X
- X newbranch = root->branches;
- X while( newbranch ) {
- X exttree(newbranch->hsh);
- X newbranch = newbranch->nextbranch;
- X }
- X}
- X
- X
- X
- X
- X static void
- getlocker(argv)
- char * argv;
- X/* function : get the login names of lockers from command line */
- X/* and store in lockerlist. */
- X
- X{
- X register char c;
- X struct lockers * newlocker;
- X argv--;
- X while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
- X c == '\n' || c == ';') ;
- X if ( c == '\0') {
- X lockerlist=nil;
- X return;
- X }
- X
- X while( c != '\0' ) {
- X newlocker = talloc(struct lockers);
- X newlocker->lockerlink = lockerlist;
- X newlocker->login = argv;
- X lockerlist = newlocker;
- X while ( ( c = (*++argv)) != ',' && c != '\0' && c != ' '
- X && c != '\t' && c != '\n' && c != ';') ;
- X *argv = '\0';
- X if ( c == '\0' ) return;
- X while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
- X c == '\n' || c == ';') ;
- X }
- X}
- X
- X
- X
- X static void
- getauthor(argv)
- char *argv;
- X/* function: get the author's name from command line */
- X/* and store in authorlist */
- X
- X{
- X register c;
- X struct authors * newauthor;
- X
- X argv--;
- X while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
- X c == '\n' || c == ';') ;
- X if ( c == '\0' ) {
- X authorlist = talloc(struct authors);
- X authorlist->login = getusername(false);
- X authorlist->nextauthor = nil;
- X return;
- X }
- X
- X while( c != '\0' ) {
- X newauthor = talloc(struct authors);
- X newauthor->nextauthor = authorlist;
- X newauthor->login = argv;
- X authorlist = newauthor;
- X while( ( c = *++argv) != ',' && c != '\0' && c != ' '
- X && c != '\t' && c != '\n' && c != ';') ;
- X * argv = '\0';
- X if ( c == '\0') return;
- X while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
- X c == '\n' || c == ';') ;
- X }
- X}
- X
- X
- X
- X
- X static void
- getstate(argv)
- char * argv;
- X/* function : get the states of revisions from command line */
- X/* and store in statelist */
- X
- X{
- X register char c;
- X struct stateattri *newstate;
- X
- X argv--;
- X while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
- X c == '\n' || c == ';') ;
- X if ( c == '\0'){
- X warn("missing state attributes after -s options");
- X return;
- X }
- X
- X while( c != '\0' ) {
- X newstate = talloc(struct stateattri);
- X newstate->nextstate = statelist;
- X newstate->status = argv;
- X statelist = newstate;
- X while( (c = (*++argv)) != ',' && c != '\0' && c != ' '
- X && c != '\t' && c != '\n' && c != ';') ;
- X *argv = '\0';
- X if ( c == '\0' ) return;
- X while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
- X c == '\n' || c == ';') ;
- X }
- X}
- X
- X
- X
- X static void
- trunclocks()
- X/* Function: Truncate the list of locks to those that are held by the */
- X/* id's on lockerlist. Do not truncate if lockerlist empty. */
- X
- X{
- X struct lockers const *plocker;
- X struct lock * plocked, * nextlocked;
- X
- X if ( (lockerlist == nil) || (Locks == nil)) return;
- X
- X /* shorten Locks to those contained in lockerlist */
- X plocked = Locks;
- X Locks = nil;
- X while( plocked != nil) {
- X plocker = lockerlist;
- X while((plocker != nil) && ( strcmp(plocker->login, plocked->login)!=0))
- X plocker = plocker->lockerlink;
- X nextlocked = plocked->nextlock;
- X if ( plocker != nil) {
- X plocked->nextlock = Locks;
- X Locks = plocked;
- X }
- X plocked = nextlocked;
- X }
- X}
- X
- X
- X
- X static void
- recentdate(root, pd)
- X struct hshentry const *root;
- X struct Datepairs *pd;
- X/* function: Finds the delta that is closest to the cutoff date given by */
- X/* pd among the revisions selected by exttree. */
- X/* Successively narrows down the interval given by pd, */
- X/* and sets the strtdate of pd to the date of the selected delta */
- X{
- X struct branchhead const *newbranch;
- X
- X if ( root == nil) return;
- X if (root->selector) {
- X if ( cmpnum(root->date, pd->strtdate) >= 0 &&
- X cmpnum(root->date, pd->enddate) <= 0)
- X VOID strcpy(pd->strtdate, root->date);
- X }
- X
- X recentdate(root->next, pd);
- X newbranch = root->branches;
- X while( newbranch) {
- X recentdate(newbranch->hsh, pd);
- X newbranch = newbranch->nextbranch;
- X }
- X}
- X
- X
- X
- X
- X
- X
- X static unsigned
- extdate(root)
- struct hshentry * root;
- X/* function: select revisions which are in the date range specified */
- X/* in duelst and datelist, start at root */
- X/* Yield number of revisions selected, including those already selected. */
- X{
- X struct branchhead const *newbranch;
- X struct Datepairs const *pdate;
- X unsigned revno;
- X
- X if (!root)
- X return 0;
- X
- X if ( datelist || duelst) {
- X pdate = datelist;
- X while( pdate ) {
- X if ( (pdate->strtdate)[0] == '\0' || cmpnum(root->date,pdate->strtdate) >= 0){
- X if ((pdate->enddate)[0] == '\0' || cmpnum(pdate->enddate,root->date) >= 0)
- X break;
- X }
- X pdate = pdate->dnext;
- X }
- X if ( pdate == nil) {
- X pdate = duelst;
- X for (;;) {
- X if (!pdate) {
- X root->selector = false;
- X break;
- X }
- X if ( cmpnum(root->date, pdate->strtdate) == 0)
- X break;
- X pdate = pdate->dnext;
- X }
- X }
- X }
- X revno = root->selector + extdate(root->next);
- X
- X newbranch = root->branches;
- X while( newbranch ) {
- X revno += extdate(newbranch->hsh);
- X newbranch = newbranch->nextbranch;
- X }
- X return revno;
- X}
- X
- X
- X
- X static char
- extractdelta(pdelta)
- X struct hshentry const *pdelta;
- X/* function: compare information of pdelta to the authorlist, lockerlist,*/
- X/* statelist, revlist and yield true if pdelta is selected. */
- X
- X{
- X struct lock const *plock;
- X struct stateattri const *pstate;
- X struct authors const *pauthor;
- X struct Revpairs const *prevision;
- X unsigned length;
- X
- X if ((pauthor = authorlist)) /* only certain authors wanted */
- X while (strcmp(pauthor->login, pdelta->author) != 0)
- X if (!(pauthor = pauthor->nextauthor))
- X return false;
- X if ((pstate = statelist)) /* only certain states wanted */
- X while (strcmp(pstate->status, pdelta->state) != 0)
- X if (!(pstate = pstate->nextstate))
- X return false;
- X if (lockflag) /* only locked revisions wanted */
- X for (plock = Locks; ; plock = plock->nextlock)
- X if (!plock)
- X return false;
- X else if (plock->delta == pdelta)
- X break;
- X if ((prevision = Revlst)) /* only certain revs or branches wanted */
- X for (;;) {
- X length = prevision->numfld;
- X if (
- X countnumflds(pdelta->num) == length+(length&1) &&
- X 0 <= compartial(pdelta->num, prevision->strtrev, length) &&
- X 0 <= compartial(prevision->endrev, pdelta->num, length)
- X )
- X break;
- X if (!(prevision = prevision->rnext))
- X return false;
- X }
- X return true;
- X}
- X
- X
- X
- X static void
- getdatepair(argv)
- X char * argv;
- X/* function: get time range from command line and store in datelist if */
- X/* a time range specified or in duelst if a time spot specified */
- X
- X{
- X register char c;
- X struct Datepairs * nextdate;
- X char const * rawdate;
- X int switchflag;
- X
- X argv--;
- X while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
- X c == '\n' || c == ';') ;
- X if ( c == '\0' ) {
- X warn("missing date/time after -d");
- X return;
- X }
- X
- X while( c != '\0' ) {
- X switchflag = false;
- X nextdate = talloc(struct Datepairs);
- X if ( c == '<' ) { /* case: -d <date */
- X c = *++argv;
- X (nextdate->strtdate)[0] = '\0';
- X } else if (c == '>') { /* case: -d'>date' */
- X c = *++argv;
- X (nextdate->enddate)[0] = '\0';
- X switchflag = true;
- X } else {
- X rawdate = argv;
- X while( c != '<' && c != '>' && c != ';' && c != '\0')
- X c = *++argv;
- X *argv = '\0';
- X if ( c == '>' ) switchflag=true;
- X str2date(rawdate,
- X switchflag ? nextdate->enddate : nextdate->strtdate);
- X if ( c == ';' || c == '\0') { /* case: -d date */
- X VOID strcpy(nextdate->enddate,nextdate->strtdate);
- X nextdate->dnext = duelst;
- X duelst = nextdate;
- X goto end;
- X } else {
- X /* case: -d date< or -d date>; see switchflag */
- X while ( (c= *++argv) == ' ' || c=='\t' || c=='\n');
- X if ( c == ';' || c == '\0') {
- X /* second date missing */
- X if (switchflag)
- X *nextdate->strtdate= '\0';
- X else
- X *nextdate->enddate= '\0';
- X nextdate->dnext = datelist;
- X datelist = nextdate;
- X goto end;
- X }
- X }
- X }
- X rawdate = argv;
- X while( c != '>' && c != '<' && c != ';' && c != '\0')
- X c = *++argv;
- X *argv = '\0';
- X str2date(rawdate,
- X switchflag ? nextdate->strtdate : nextdate->enddate);
- X nextdate->dnext = datelist;
- X datelist = nextdate;
- X end:
- X if ( c == '\0') return;
- X while( (c = *++argv) == ';' || c == ' ' || c == '\t' || c =='\n');
- X }
- X}
- X
- X
- X
- X static void
- getnumericrev()
- X/* function: get the numeric name of revisions which stored in revlist */
- X/* and then stored the numeric names in Revlst */
- X/* if branchflag, also add default branch */
- X
- X{
- X struct Revpairs * ptr, *pt;
- X unsigned n;
- X struct buf s, e;
- X char const *lrev;
- X struct buf const *rstart, *rend;
- X
- X Revlst = nil;
- X ptr = revlist;
- X bufautobegin(&s);
- X bufautobegin(&e);
- X while( ptr ) {
- X n = 0;
- X rstart = &s;
- X rend = &e;
- X
- X switch (ptr->numfld) {
- X
- X case 1: /* -r rev */
- X if (expandsym(ptr->strtrev, &s)) {
- X rend = &s;
- X n = countnumflds(s.string);
- X if (!n && (lrev = tiprev())) {
- X bufscpy(&s, lrev);
- X n = countnumflds(lrev);
- X }
- X }
- X break;
- X
- X case 2: /* -r rev- */
- X if (expandsym(ptr->strtrev, &s)) {
- X bufscpy(&e, s.string);
- X n = countnumflds(s.string);
- X (n<2 ? e.string : strrchr(e.string,'.'))[0] = 0;
- X }
- X break;
- X
- X case 3: /* -r -rev */
- X if (expandsym(ptr->endrev, &e)) {
- X if ((n = countnumflds(e.string)) < 2)
- X bufscpy(&s, ".1");
- X else {
- X bufscpy(&s, e.string);
- X VOID strcpy(strrchr(s.string,'.'), ".1");
- X }
- X }
- X break;
- X
- X default: /* -r rev1-rev2 */
- X if (
- X expandsym(ptr->strtrev, &s)
- X && expandsym(ptr->endrev, &e)
- X && checkrevpair(s.string, e.string)
- X ) {
- X n = countnumflds(s.string);
- X /* Swap if out of order. */
- X if (compartial(s.string,e.string,n) > 0) {
- X rstart = &e;
- X rend = &s;
- X }
- X }
- X break;
- X }
- X
- X if (n) {
- X pt = ftalloc(struct Revpairs);
- X pt->numfld = n;
- X pt->strtrev = fstr_save(rstart->string);
- X pt->endrev = fstr_save(rend->string);
- X pt->rnext = Revlst;
- X Revlst = pt;
- X }
- X ptr = ptr->rnext;
- X }
- X /* Now take care of branchflag */
- X if (branchflag && (Dbranch||Head)) {
- X pt = ftalloc(struct Revpairs);
- X pt->strtrev = pt->endrev =
- X Dbranch ? Dbranch : fstr_save(partialno(&s,Head->num,1));
- X pt->rnext=Revlst; Revlst=pt;
- X pt->numfld = countnumflds(pt->strtrev);
- X }
- X bufautoend(&s);
- X bufautoend(&e);
- X}
- X
- X
- X
- X static int
- checkrevpair(num1,num2)
- X char const *num1, *num2;
- X/* function: check whether num1, num2 are legal pair,i.e.
- X only the last field are different and have same number of
- X fields( if length <= 2, may be different if first field) */
- X
- X{
- X unsigned length = countnumflds(num1);
- X
- X if (
- X countnumflds(num2) != length
- X || 2 < length && compartial(num1, num2, length-1) != 0
- X ) {
- X error("invalid branch or revision pair %s : %s", num1, num2);
- X return false;
- X }
- X
- X return true;
- X}
- X
- X
- X
- X static void
- getrevpairs(argv)
- register char * argv;
- X/* function: get revision or branch range from command line, and */
- X/* store in revlist */
- X
- X{
- X register char c;
- X struct Revpairs * nextrevpair;
- X int separator;
- X
- X c = *argv;
- X
- X /* Support old ambiguous '-' syntax; this will go away. */
- X if (strchr(argv,':'))
- X separator = ':';
- X else {
- X if (strchr(argv,'-') && VERSION(5) <= RCSversion)
- X warn("`-' is obsolete in `-r%s'; use `:' instead", argv);
- X separator = '-';
- X }
- X
- X for (;;) {
- X while (c==' ' || c=='\t' || c=='\n')
- X c = *++argv;
- X nextrevpair = talloc(struct Revpairs);
- X nextrevpair->rnext = revlist;
- X revlist = nextrevpair;
- X nextrevpair->numfld = 1;
- X nextrevpair->strtrev = argv;
- X for (;; c = *++argv) {
- X switch (c) {
- X default:
- X continue;
- X case '\0': case ' ': case '\t': case '\n':
- X case ',': case ';':
- X break;
- X case ':': case '-':
- X if (c == separator)
- X break;
- X continue;
- X }
- X break;
- X }
- X *argv = '\0';
- X while (c==' ' || c=='\t' || c=='\n')
- X c = *++argv;
- X if (c == separator) {
- X while( (c =(*++argv)) == ' ' || c == '\t' || c =='\n') ;
- X nextrevpair->endrev = argv;
- X for (;; c = *++argv) {
- X switch (c) {
- X default:
- X continue;
- X case '\0': case ' ': case '\t': case '\n':
- X case ',': case ';':
- X break;
- X case ':': case '-':
- X if (c == separator)
- X continue;
- X break;
- X }
- X break;
- X }
- X *argv = '\0';
- X while (c==' ' || c=='\t' || c =='\n')
- X c = *++argv;
- X nextrevpair->numfld =
- X !nextrevpair->endrev[0] ? 2 /* -rrev- */ :
- X !nextrevpair->strtrev[0] ? 3 /* -r-rev */ :
- X 4 /* -rrev1-rev2 */;
- X }
- X if (!c)
- X break;
- X if (c!=',' && c!=';')
- X error("missing `,' near `%c%s'", c, argv+1);
- X }
- X}
- END_OF_FILE
- if test 32185 -ne `wc -c <'src/rlog.c'`; then
- echo shar: \"'src/rlog.c'\" unpacked with wrong size!
- fi
- # end of 'src/rlog.c'
- fi
- echo shar: End of archive 6 \(of 11\).
- cp /dev/null ark6isdone
- MISSING=""
- for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 11 archives.
- rm -f ark[1-9]isdone ark[1-9][0-9]isdone
- else
- echo You still need to unpack the following archives:
- echo " " ${MISSING}
- fi
- ## End of shell archive.
- exit 0
-