v24i003: RCS source control system, Part03/12

Rich Salz rsalz at uunet.uu.net
Fri Feb 22 06:58:50 AEST 1991


Submitted-by: Adam Hammer <hammer at cs.purdue.edu>
Posting-number: Volume 24, Issue 3
Archive-name: rcs/part03

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix at uunet.uu.net if you want that tool.
# Contents:  src/Makefile src/rcs.c
# Wrapped by rsalz at litchi.bbn.com on Thu Feb 21 14:36:55 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 3 (of 12)."'
if test -f 'src/Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/Makefile'\"
else
  echo shar: Extracting \"'src/Makefile'\" \(8655 characters\)
  sed "s/^X//" >'src/Makefile' <<'END_OF_FILE'
X# $Id: Makefile,v 5.8 1990/12/13 06:54:06 eggert Exp $
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#
X# This file is part of RCS.
X#
X# RCS is free software; you can redistribute it and/or modify
X# it under the terms of the GNU General Public License as published by
X# the Free Software Foundation; either version 1, or (at your option)
X# any later version.
X#
X# RCS is distributed in the hope that it will be useful,
X# but WITHOUT ANY WARRANTY; without even the implied warranty of
X# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X# GNU General Public License for more details.
X#
X# You should have received a copy of the GNU General Public License
X# along with RCS; see the file COPYING.  If not, write to
X# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X#
X# Report problems and direct all questions to:
X#
X#    rcs-bugs at cs.purdue.edu
X#
X#               INSTRUCTIONS
X#               ============
X
X
X#	Figure out where to put the RCS commands; define RCSDIR accordingly.
X
XRCSDIR = /usr/local/bin
X
X
X#	Define INSTALL_SETID_FLAGS as needed to install RCS setgid or setuid.
X#	This makes sense only when setegid() and seteuid() work
X#	Setgid is better than setuid because it mixes with nonstrict locking.
X#INSTALL_SETID_FLAGS = ${INSTALL_NORMAL_FLAGS}
X#INSTALL_SETID_FLAGS = -g rcs -o root -m 2555
X INSTALL_SETID_FLAGS = ${INSTALL_NORMAL_FLAGS}
X
X
X#	Define RCSPREFIX to be empty if you want RCS to search the PATH for
X#	subsidiary RCS commands like co.  This lets you move RCS commands
X#	after building them, and permits multiple instances of setgid RCS
X#	commands on the same host for different groups.
X#
X#	Define RCSPREFIX to a path followed by / if you want RCS to look in
X#	just one place.  This makes execution faster.  Also, if your host's
X#	execvp() system call does not understand the BSD #!/bin/sh convention
X#	for starting shell files, you must use a nonempty RCSPREFIX, because
X#	in this case rcsmerge invokes `/bin/sh ${RCSPREFIX}merge'.
X
X#RCSPREFIX =
X#RCSPREFIX = ${RCSDIR}/
X RCSPREFIX = ${RCSDIR}/
X
X#	Define DIFF and DIFF3 to be the name of your diff and diff3 programs.
X#	DIFF must be an absolute path name if setgid or setuid is used.
X#	Define DIFF_FLAGS to be diff's options for RCS format output.
X#	If available, use the -a option for comparing arbitrary files.
X#	Define DIFF_L to be 1 if your diff understands GNU diff's -L option.
X#	Set DIFF3_TYPE=lib for traditional diff, =bin otherwise.
X#	If DIFF3_type=bin, make sure your diff3 understands -a, -L, and and -m.
X#	If DIFF3_type=lib, avoid the diff3 program visible to users, and
X#	use the one in /usr/lib instead; it may be called /usr/lib/diff3prog.
X
X# Traditional diff
X#DIFF = /bin/diff
X#DIFF_FLAGS = -n
X#DIFF_L = 0
X#DIFF3 = /usr/lib/diff3
X#DIFF3_TYPE = lib
X
X# GNU diff -- must be version 1.15 or later
X#DIFFPREFIX = ${RCSDIR}/
X#DIFF = ${DIFFPREFIX}diff
X#DIFF_FLAGS = -an
X#DIFF_L = 1
X#DIFF3 = ${DIFF}3
X#DIFF3_TYPE = bin
X
X DIFF = /bin/diff
X DIFF_FLAGS = -n
X DIFF_L = 0
X DIFF3 = /usr/lib/diff3
X DIFF3_TYPE = lib
X
X
X#	Set SENDMAIL to be a comma-separated list of strings that are a command
X#	to send mail.  The first string should be an absolute pathname.
X#	The name of the addressee will be appended as a separate argument,
X#	and the standard input will be the message (first line "Subject: xxxx",
X#	second line empty).
X
X#SENDMAIL = "/bin/mail"
X#SENDMAIL = "/etc/delivermail", "-w"
X#SENDMAIL = "/usr/lib/sendmail"
X SENDMAIL = "/bin/mail"
X
X
X#	Decide what loader libraries you need.
X#	Some older hosts need -lBSD, -ljobs, or -lPW.
X
XLDLIBS =
X
X
X#	Decide what C compiler flags you need.
X
X# Optimize.  Put in options that work well for your compiler.
X# Options to try with GCC include -fdelayed-branch, -finline-functions,
X# -fomit-frame-pointer, and -fstrength-reduce.
XCC_O = -O
X
X# Make all initialized data read-only (not just string literals).
X# This option can improve performance by making initialized data shared.
X# It's not worth worrying about if your compiler supports the `const' keyword.
X# 4.3BSD-based compilers
X#CC_R = -R
X# most other compilers
X#CC_R =
X CC_R =
X
X# Add this for SunOS 4.1 + GCC 1.37.1.
X#COMPILE.c = ${CC} ${CFLAGS} ${CPPFLAGS} -c
X
X
X# for GCC
X#CC = gcc
X#CC_W = -Wall -Wcast-qual -Wpointer-arith -Wshadow -Wwrite-strings
X#CFLAGS = ${CC_O} ${CC_R} ${CC_W}
X
X# for traditional C compilers
X#CC = cc
X#CFLAGS = ${CC_O} ${CC_R}
X
X CC = cc
X CFLAGS = ${CC_O} ${CC_R}
X
X
XLINT = lint
X
X# For traditional and BSD lint, use
X#LINTFLAGS = -abchx
X# For USG lint, use
X#LINTFLAGS =
X LINTFLAGS = -abchx
X
X
X#	If you have version 2 RCS files around, define COMPAT2 to be 1.
X#	(Version 2 became obsolete in 1982.)  This assures that version 2
X#	RCS files can still be read.  After all version 2 RCS files have
X#	been updated with later versions of ci or rcs, you can remake RCS with
X#	COMPAT2=0.
XCOMPAT2 = 0
X#	When you have RCS installed, rename old version 2 RCS files as follows
X#	(if you have any).  If the working file was "f.c" and the RCS file
X#	"f.c.v", rename the RCS file to "f.c,v". If the working file was "f.c"
X#	and the RCS file "f.v", rename the RCS file "f.c,v". Thus, suffixes
X#	are no longer dropped and RCS files end in ",v" rather than ".v".
X
X
X#	Now you are ready.  Try to make "conf.h".
X#	Check the resulting conf.h for plausibility.
X#	If it's wrong, there is a bug in conf.sh; please report it.
X#	You can patch conf.h if you're in a hurry, but it's better to fix it;
X#	look at a.h and conf.error for ideas.
X#	If all else fails, copy conf.heg to conf.h and edit it by hand.
X
X#	Make "all".
X#	If all went well, make "install".
X#	If installation succeeds, make "installtest";
X#	if this fails, make "installdebug" for detailed info.
X
X#	If you want to maintain RCS with itself, be sure you preserve the
X#	original revision numbers, dates, etc. by checking the
X#	files in with the -k option.
X
X# Avoid brain damage in some versions of 'make'.
XSHELL = /bin/sh
X
X# binary commands
XBCOMMANDS   =   ci ident rcs rcsdiff rcsmerge rlog co
X
X# all commands
XRCSCOMMANDS = merge ${BCOMMANDS}
X
Xall :: ${RCSCOMMANDS}
X
XINSTALL = install -c
XINSTALL_NORMAL_FLAGS = -g staff -m 775
X
Xinstall :: all
X	${INSTALL} ${INSTALL_SETID_FLAGS} ci ${RCSDIR}
X	${INSTALL} ${INSTALL_SETID_FLAGS} co ${RCSDIR}
X	${INSTALL} ${INSTALL_SETID_FLAGS} rcsdiff ${RCSDIR}
X	${INSTALL} ${INSTALL_SETID_FLAGS} rcsmerge ${RCSDIR}
X	${INSTALL} ${INSTALL_SETID_FLAGS} rlog ${RCSDIR}
X	${INSTALL} ${INSTALL_NORMAL_FLAGS} ident ${RCSDIR}
X	${INSTALL} ${INSTALL_NORMAL_FLAGS} rcs ${RCSDIR}
X	${INSTALL} ${INSTALL_NORMAL_FLAGS} merge ${RCSDIR}
X
Xinstalltest ::
X	sh rcstest
X
Xinstalldebug ::
X	sh rcstest -v
X
Xclean ::
X	rm -f a.* *.o conf.h conf.error ${RCSCOMMANDS}
X
Xconf.h : conf.sh # Makefile
X	C='${CC} ${CFLAGS}' \
X	COMPAT2='${COMPAT2}' \
X	DIFF='${DIFF}' \
X	DIFF_L='${DIFF_L}' \
X	DIFF_FLAGS='${DIFF_FLAGS}' \
X	RCSPREFIX='${RCSPREFIX}' \
X	SENDMAIL='${SENDMAIL}' \
X	L='${LDLIBS}' \
X	sh -x conf.sh 2>conf.error
X	mv a.h $@
X	rm -f a.*
X
XCIFILES = ci.o rcslex.o rcssyn.o rcsgen.o rcsedit.o rcskeys.o rcsmap.o \
X	rcsrev.o rcsutil.o rcsfnms.o partime.o maketime.o rcskeep.o rcsfcmp.o
Xci : ${CIFILES}
X	${CC} ${CFLAGS} ${CIFILES} ${LDLIBS} -o $@
X
XCOFILES = co.o rcslex.o rcssyn.o rcsgen.o rcsedit.o rcskeys.o rcsmap.o \
X	rcsrev.o rcsutil.o rcsfnms.o partime.o maketime.o
Xco : ${COFILES}
X	${CC} ${CFLAGS} ${COFILES} ${LDLIBS} -o $@
X
Xident : ident.o rcsmap.o
X	${CC} ${CFLAGS} ident.o rcsmap.o ${LDLIBS} -o $@
X
Xmerge : merge.sh
X	DIFF=${DIFF} DIFF3=${DIFF3} DIFF3_TYPE=${DIFF3_TYPE} sh $@.sh >$@.o
X	chmod +x $@.o
X	mv $@.o $@
X
XRLOG = rlog.o rcslex.o rcsmap.o rcssyn.o rcsrev.o rcsutil.o partime.o \
X	maketime.o rcsfnms.o
Xrlog : ${RLOG}
X	${CC} ${CFLAGS} ${RLOG} ${LDLIBS} -o $@
X
XRCS = rcs.o rcslex.o rcssyn.o rcsrev.o rcsutil.o rcsgen.o rcsedit.o rcskeys.o \
X	rcsmap.o rcsfnms.o
Xrcs : ${RCS}
X	${CC} ${CFLAGS} ${RCS} ${LDLIBS} -o $@
X
XRCSDIFF = rcsdiff.o rcsutil.o rcsfnms.o rcsmap.o rcsrev.o rcssyn.o rcslex.o \
X	maketime.o partime.o
Xrcsdiff : ${RCSDIFF}
X	${CC} ${CFLAGS} ${RCSDIFF} ${LDLIBS} -o $@
X
XRCSMERGE = rcsmerge.o rcsutil.o rcsfnms.o rcsmap.o rcsrev.o rcssyn.o rcslex.o
Xrcsmerge : ${RCSMERGE}
X	${CC} ${CFLAGS} ${RCSMERGE} ${LDLIBS} -o $@
X
XSOURCE=	ci.c co.c ident.c maketime.c partime.c rcs.c \
X	rcsdiff.c rcsedit.c rcsfcmp.c rcsfnms.c rcsgen.c \
X	rcskeep.c rcskeys.c rcslex.c rcsmap.c rcsmerge.c rcsrev.c rcssyn.c \
X	rcsutil.c rlog.c
XOBJECT=	ci.o co.o ident.o maketime.o partime.o rcs.o \
X	rcsdiff.o rcsedit.o rcsfcmp.o rcsfnms.o rcsgen.o \
X	rcskeep.o rcskeys.o rcslex.o rcsmap.o rcsmerge.o rcsrev.o rcssyn.o \
X	rcsutil.o rlog.o
X
Xlint : conf.h
X	${LINT} ${LINTFLAGS} -Dlint=1 ${SOURCE}
X
X${OBJECT} : conf.h rcsbase.h
END_OF_FILE
  if test 8655 -ne `wc -c <'src/Makefile'`; then
    echo shar: \"'src/Makefile'\" unpacked with wrong size!
  fi
  # end of 'src/Makefile'
fi
if test -f 'src/rcs.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/rcs.c'\"
else
  echo shar: Extracting \"'src/rcs.c'\" \(42394 characters\)
  sed "s/^X//" >'src/rcs.c' <<'END_OF_FILE'
X/*
X *                      RCS create/change operation
X */
X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
X   Copyright 1990 by Paul Eggert
X   Distributed under license by the Free Software Foundation, Inc.
X
XThis file is part of RCS.
X
XRCS is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XRCS is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with RCS; see the file COPYING.  If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X
XReport problems and direct all questions to:
X
X    rcs-bugs at cs.purdue.edu
X
X*/
X
X
X
X
X/* $Log: rcs.c,v $
X * Revision 5.7  1990/12/18  17:19:21  eggert
X * Fix bug with multiple -n and -N options.
X *
X * Revision 5.6  1990/12/04  05:18:40  eggert
X * Use -I for prompts and -q for diagnostics.
X *
X * Revision 5.5  1990/11/11  00:06:35  eggert
X * Fix `rcs -e' core dump.
X *
X * Revision 5.4  1990/11/01  05:03:33  eggert
X * Add -I and new -t behavior.  Permit arbitrary data in logs.
X *
X * Revision 5.3  1990/10/04  06:30:16  eggert
X * Accumulate exit status across files.
X *
X * Revision 5.2  1990/09/04  08:02:17  eggert
X * Standardize yes-or-no procedure.
X *
X * Revision 5.1  1990/08/29  07:13:51  eggert
X * Remove unused setuid support.  Clean old log messages too.
X *
X * Revision 5.0  1990/08/22  08:12:42  eggert
X * Don't lose names when applying -a option to multiple files.
X * Remove compile-time limits; use malloc instead.  Add setuid support.
X * Permit dates past 1999/12/31.  Make lock and temp files faster and safer.
X * Ansify and Posixate.  Add -V.  Fix umask bug.  Make linting easier.  Tune.
X * Yield proper exit status.  Check diff's output.
X *
X * Revision 4.11  89/05/01  15:12:06  narten
X * changed copyright header to reflect current distribution rules
X * 
X * Revision 4.10  88/11/08  16:01:54  narten
X * didn't install previous patch correctly
X * 
X * Revision 4.9  88/11/08  13:56:01  narten
X * removed include <sysexits.h> (not needed)
X * minor fix for -A option
X * 
X * Revision 4.8  88/08/09  19:12:27  eggert
X * Don't access freed storage.
X * Use execv(), not system(); yield proper exit status; remove lint.
X * 
X * Revision 4.7  87/12/18  11:37:17  narten
X * lint cleanups (Guy Harris)
X * 
X * Revision 4.6  87/10/18  10:28:48  narten
X * Updating verison numbers. Changes relative to 1.1 are actually 
X * relative to 4.3
X * 
X * Revision 1.4  87/09/24  13:58:52  narten
X * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
X * warnings)
X * 
X * Revision 1.3  87/03/27  14:21:55  jenkins
X * Port to suns
X * 
X * Revision 1.2  85/12/17  13:59:09  albitz
X * Changed setstate to rcs_setstate because of conflict with random.o.
X * 
X * Revision 4.3  83/12/15  12:27:33  wft
X * rcs -u now breaks most recent lock if it can't find a lock by the caller.
X * 
X * Revision 4.2  83/12/05  10:18:20  wft
X * Added conditional compilation for sending mail.
X * Alternatives: V4_2BSD, V6, USG, and other.
X * 
X * Revision 4.1  83/05/10  16:43:02  wft
X * Simplified breaklock(); added calls to findlock() and getcaller().
X * Added option -b (default branch). Updated -s and -w for -b.
X * Removed calls to stat(); now done by pairfilenames().
X * Replaced most catchints() calls with restoreints().
X * Removed check for exit status of delivermail().
X * Directed all interactive output to stderr.
X * 
X * Revision 3.9.1.1  83/12/02  22:08:51  wft
X * Added conditional compilation for 4.2 sendmail and 4.1 delivermail.
X * 
X * Revision 3.9  83/02/15  15:38:39  wft
X * Added call to fastcopy() to copy remainder of RCS file.
X *
X * Revision 3.8  83/01/18  17:37:51  wft
X * Changed sendmail(): now uses delivermail, and asks whether to break the lock.
X *
X * Revision 3.7  83/01/15  18:04:25  wft
X * Removed putree(); replaced with puttree() in rcssyn.c.
X * Combined putdellog() and scanlogtext(); deleted putdellog().
X * Cleaned up diagnostics and error messages. Fixed problem with
X * mutilated files in case of deletions in 2 files in a single command.
X * Changed marking of selector from 'D' to DELETE.
X *
X * Revision 3.6  83/01/14  15:37:31  wft
X * Added ignoring of interrupts while new RCS file is renamed;
X * Avoids deletion of RCS files by interrupts.
X *
X * Revision 3.5  82/12/10  21:11:39  wft
X * Removed unused variables, fixed checking of return code from diff,
X * introduced variant COMPAT2 for skipping Suffix on -A files.
X *
X * Revision 3.4  82/12/04  13:18:20  wft
X * Replaced getdelta() with gettree(), changed breaklock to update
X * field lockedby, added some diagnostics.
X *
X * Revision 3.3  82/12/03  17:08:04  wft
X * Replaced getlogin() with getpwuid(), flcose() with ffclose(),
X * /usr/ucb/Mail with macro MAIL. Removed handling of Suffix (-x).
X * fixed -u for missing revno. Disambiguated structure members.
X *
X * Revision 3.2  82/10/18  21:05:07  wft
X * rcs -i now generates a file mode given by the umask minus write permission;
X * otherwise, rcs keeps the mode, but removes write permission.
X * I added a check for write error, fixed call to getlogin(), replaced
X * curdir() with getfullRCSname(), cleaned up handling -U/L, and changed
X * conflicting, long identifiers.
X *
X * Revision 3.1  82/10/13  16:11:07  wft
X * fixed type of variables receiving from getc() (char -> int).
X */
X
X
X#include "rcsbase.h"
X
Xstruct  Lockrev {
X	const char *revno;
X        struct  Lockrev   * nextrev;
X};
X
Xstruct  Symrev {
X	const char *revno;
X	const char *ssymbol;
X        int     override;
X        struct  Symrev  * nextsym;
X};
X
Xstruct  Status {
X	const char *revno;
X	const char *status;
X        struct  Status  * nextstatus;
X};
X
Xenum changeaccess {append, erase};
Xstruct chaccess {
X	const char *login;
X	enum changeaccess command;
X	struct chaccess *nextchaccess;
X};
X
Xstruct delrevpair {
X	const char *strt;
X	const char *end;
X        int     code;
X};
X
Xstatic int buildeltatext P((const struct hshentries*));
Xstatic int removerevs P((void));
Xstatic int sendmail P((const char*,const char*));
Xstatic struct Lockrev *rmnewlocklst P((const struct Lockrev*));
Xstatic void breaklock P((const struct hshentry*));
Xstatic void buildtree P((void));
Xstatic void cleanup P((void));
Xstatic void getaccessor P((char*,enum changeaccess));
Xstatic void getassoclst P((int,char*));
Xstatic void getchaccess P((const char*,enum changeaccess));
Xstatic void getdelrev P((char*));
Xstatic void getstates P((char*));
Xstatic void rcs_setstate P((const char*,const char*));
Xstatic void scanlogtext P((struct hshentry*,int));
Xstatic void setlock P((const char*));
Xstatic void updateaccess P((void));
Xstatic void updateassoc P((void));
Xstatic void updatelocks P((void));
X
Xstatic struct buf numrev;
Xstatic const char *headstate;
Xstatic int chgheadstate, exitstatus, lockhead, unlockcaller;
Xstatic struct Lockrev *newlocklst, *rmvlocklst;
Xstatic struct Status *statelst, *laststate;
Xstatic struct Symrev *assoclst, *lastassoc;
Xstatic struct chaccess *chaccess, **nextchaccess;
Xstatic struct delrevpair delrev;
Xstatic struct hshentry *cuthead, *cuttail, *delstrt;
Xstatic struct hshentries *gendeltas;
X
XmainProg(rcsId, "rcs", "$Id: rcs.c,v 5.7 1990/12/18 17:19:21 eggert Exp $")
X{
X	static const char cmdusage[] =
X		"\nrcs usage: rcs -alogins -Aoldfile -{blu}[rev] -cstring -e[logins] -i -{LU} -{nN}name[:rev] -orange -sstate[:rev] -t[textfile] -Vn file ...";
X
X	const char *branchsym, *commsyml, *textfile;
X	int branchflag, expmode, initflag;
X	int r, strictlock, strict_selected, textflag;
X	mode_t defaultRCSmode;	/* default mode for new RCS files */
X	struct buf branchnum;
X        struct  Lockrev *curlock,  * rmvlock, *lockpt;
X        struct  Status  * curstate;
X
X	initid();
X	catchints();
X
X	nextchaccess = &chaccess;
X	branchsym = commsyml = textfile = nil;
X	branchflag = strictlock = false;
X	bufautobegin(&branchnum);
X	curlock = rmvlock = nil;
X	defaultRCSmode = 0;
X	expmode = -1;
X        initflag= textflag = false;
X        strict_selected = 0;
X
X        /*  preprocessing command options    */
X        while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
X                switch ((*argv)[1]) {
X
X		case 'i':   /*  initial version  */
X                        initflag = true;
X                        break;
X
X                case 'b':  /* change default branch */
X			if (branchflag) redefined('b');
X                        branchflag= true;
X                        branchsym = (*argv)+2;
X                        break;
X
X                case 'c':   /*  change comment symbol   */
X			if (commsyml) redefined('c');
X                        commsyml = (*argv)+2;
X                        break;
X
X                case 'a':  /*  add new accessor   */
X			getaccessor(*argv+1, append);
X                        break;
X
X                case 'A':  /*  append access list according to accessfile  */
X			*argv += 2;
X			if (!**argv) {
X			    error("missing file name after -A");
X                            break;
X                        }
X			if (0 < pairfilenames(1,argv,rcsreadopen,true,false)) {
X			    while (AccessList) {
X				getchaccess(strsave(AccessList->login), append);
X				AccessList = AccessList->nextaccess;
X			    }
X			    ffclose(finptr);
X                        }
X                        break;
X
X                case 'e':    /*  remove accessors   */
X			getaccessor(*argv+1, erase);
X                        break;
X
X                case 'l':    /*   lock a revision if it is unlocked   */
X                        if ( (*argv)[2] == '\0'){ /* lock head or def. branch */
X                            lockhead = true;
X                            break;
X                        }
X			lockpt = talloc(struct Lockrev);
X                        lockpt->revno = (*argv)+2;
X                        lockpt->nextrev = nil;
X                        if ( curlock )
X                            curlock->nextrev = lockpt;
X                        else
X                            newlocklst = lockpt;
X                        curlock = lockpt;
X                        break;
X
X                case 'u':   /*  release lock of a locked revision   */
X                        if ( (*argv)[2] == '\0'){ /*  unlock head  */
X                            unlockcaller=true;
X                            break;
X                        }
X			lockpt = talloc(struct Lockrev);
X                        lockpt->revno = (*argv)+2;
X                        lockpt->nextrev = nil;
X                        if (rmvlock)
X                            rmvlock->nextrev = lockpt;
X                        else
X                            rmvlocklst = lockpt;
X                        rmvlock = lockpt;
X
X                        curlock = rmnewlocklst(lockpt);
X                        break;
X
X                case 'L':   /*  set strict locking */
X                        if (strict_selected++) {  /* Already selected L or U? */
X			   if (!strictlock)	  /* Already selected -U? */
X			       warn("-L overrides -U.");
X                        }
X                        strictlock = true;
X                        break;
X
X                case 'U':   /*  release strict locking */
X                        if (strict_selected++) {  /* Already selected L or U? */
X			   if (strictlock)	  /* Already selected -L? */
X			       warn("-L overrides -U.");
X                        }
X			else
X			    strictlock = false;
X                        break;
X
X                case 'n':    /*  add new association: error, if name exists */
X                        if ( (*argv)[2] == '\0') {
X			    error("missing symbolic name after -n");
X                            break;
X                        }
X                        getassoclst(false, (*argv)+1);
X                        break;
X
X                case 'N':   /*  add or change association   */
X                        if ( (*argv)[2] == '\0') {
X			    error("missing symbolic name after -N");
X                            break;
X                        }
X                        getassoclst(true, (*argv)+1);
X                        break;
X
X		case 'o':   /*  delete revisions  */
X			if (delrev.strt) redefined('o');
X                        if ( (*argv)[2] == '\0' ) {
X			    error("missing revision range after -o");
X                            break;
X                        }
X                        getdelrev( (*argv)+1 );
X                        break;
X
X                case 's':   /*  change state attribute of a revision  */
X                        if ( (*argv)[2] == '\0') {
X			    error("state missing after -s");
X                            break;
X                        }
X                        getstates( (*argv)+1);
X                        break;
X
X                case 't':   /*  change descriptive text   */
X                        textflag=true;
X                        if ((*argv)[2]!='\0'){
X				if (textfile) redefined('t');
X                                textfile = (*argv)+2;
X                        }
X                        break;
X
X		case 'I':
X			interactiveflag = true;
X			break;
X
X                case 'q':
X                        quietflag = true;
X                        break;
X
X		case 'V':
X			setRCSversion(*argv);
X			break;
X
X		case 'k':    /*  set keyword expand mode  */
X			if (0 <= expmode) redefined('k');
X			if (0 <= (expmode = str2expmode(*argv+2)))
X			    break;
X			/* fall into */
X                default:
X			faterror("unknown option: %s%s", *argv, cmdusage);
X                };
X        }  /* end processing of options */
X
X	if (argc<1) faterror("no input file%s", cmdusage);
X        if (nerror) {
X	    diagnose("%s aborted\n",cmdid);
X	    exitmain(EXIT_FAILURE);
X        }
X	if (initflag) {
X	    defaultRCSmode = umask((mode_t)0);
X	    VOID umask(defaultRCSmode);
X	    defaultRCSmode = ~defaultRCSmode & 0444;
X	}
X
X        /* now handle all filenames */
X        do {
X	foutptr = NULL;
X        finptr=frewrite=NULL;
X	ffree();
X
X        if ( initflag ) {
X	    switch (pairfilenames(argc, argv, rcswriteopen, false, false)) {
X                case -1: break;        /*  not exist; ok */
X                case  0: continue;     /*  error         */
X                case  1: error("file %s exists already", RCSfilename);
X                         continue;
X            }
X	}
X        else  {
X	    switch (pairfilenames(argc, argv, rcswriteopen, true, false)) {
X                case -1: continue;    /*  not exist      */
X                case  0: continue;    /*  errors         */
X                case  1: break;       /*  file exists; ok*/
X            }
X	}
X
X
X        /* now RCSfilename contains the name of the RCS file, and
X         * workfilename contains the name of the working file.
X         * if !initflag, finptr contains the file descriptor for the
X         * RCS file. The admin node is initialized.
X         */
X
X	diagnose("RCS file: %s\n", RCSfilename);
X
X	if (initflag && !getworkstat())		   continue; /* give up */
X	if (!initflag && !checkaccesslist())	   continue; /* give up */
X
X        gettree(); /* read in delta tree */
X
X        /*  update admin. node    */
X        if (strict_selected) StrictLocks = strictlock;
X	if (commsyml) {
X		Comment.string = commsyml;
X		Comment.size = strlen(commsyml);
X	}
X	if (0 <= expmode) Expand = expmode;
X
X        /* update default branch */
X	if (branchflag && expandsym(branchsym, &branchnum)) {
X	    if (countnumflds(branchnum.string)) {
X		Dbranch = branchnum.string;
X            } else
X                Dbranch = nil;
X        }
X
X	updateaccess();		/*  update access list        */
X
X        updateassoc();          /*  update association list   */
X
X        updatelocks();          /*  update locks              */
X
X        /*  update state attribution  */
X        if (chgheadstate) {
X            /* change state of default branch or head */
X            if (Dbranch==nil) {
X                if (Head==nil)
X		     warn("can't change states in an empty tree");
X                else Head->state = headstate;
X            } else {
X		rcs_setstate(Dbranch,headstate); /* Can't set directly */
X            }
X        }
X        curstate = statelst;
X        while( curstate ) {
X            rcs_setstate(curstate->revno,curstate->status);
X            curstate = curstate->nextstatus;
X        }
X
X        cuthead = cuttail = nil;
X	if (delrev.strt && removerevs()) {
X            /*  rebuild delta tree if some deltas are deleted   */
X            if ( cuttail )
X		VOID genrevs(cuttail->num, (char *)nil,(char *)nil,
X			     (char *)nil, &gendeltas);
X            buildtree();
X        }
X
X
X        putadmin(frewrite);
X        if ( Head )
X           puttree(Head, frewrite);
X	putdesc(textflag,textfile);
X	foutptr = NULL;
X
X        if ( Head) {
X	    if (!delrev.strt) {
X                /* no revision deleted */
X                fastcopy(finptr,frewrite);
X            } else {
X		if (!cuttail || buildeltatext(gendeltas))
X                    scanlogtext((struct hshentry *)nil,nil);
X                    /* copy rest of delta text nodes that are not deleted      */
X            }
X        }
X	if (finptr) {ffclose(finptr); finptr=NULL;} /* Help the file system. */
X        ffclose(frewrite);   frewrite = NULL;
X        if ( ! nerror ) {  /*  move temporary file to RCS file if no error */
X	    /* update mode */
X	    seteid();
X	    r = chmod(newRCSfilename,
X			 (
X			       !initflag ? RCSstat.st_mode
X			     : haveworkstat==0 ? workstat.st_mode
X			     : defaultRCSmode
X			 ) & ~(S_IWUSR|S_IWGRP|S_IWOTH)
X	    );
X	    if (r == 0) {
X		ignoreints();
X		r = re_name(newRCSfilename,RCSfilename);
X		keepdirtemp(newRCSfilename);
X		restoreints();
X	    }
X	    setrid();
X	    if (r != 0) {
X		eerror(RCSfilename);
X		error("saved in %s", newRCSfilename);
X		dirtempunlink();
X                break;
X            }
X	    diagnose("done\n");
X        } else {
X	    diagnose("%s aborted; %s unchanged.\n",cmdid,RCSfilename);
X        }
X	} while (cleanup(),
X                 ++argv, --argc >=1);
X
X	tempunlink();
X	exitmain(exitstatus);
X}       /* end of main (rcs) */
X
X	static void
Xcleanup()
X{
X	if (nerror) exitstatus = EXIT_FAILURE;
X	if (finptr) ffclose(finptr);
X	if (frewrite) ffclose(frewrite);
X	dirtempunlink();
X}
X
X	exiting void
Xexiterr()
X{
X	dirtempunlink();
X	tempunlink();
X	_exit(EXIT_FAILURE);
X}
X
X
X	static void
Xgetassoclst(flag, sp)
Xint     flag;
Xchar    * sp;
X/*  Function:   associate a symbolic name to a revision or branch,      */
X/*              and store in assoclst                                   */
X
X{
X        struct   Symrev  * pt;
X	const char *temp;
X        int                c;
X
X        while( (c=(*++sp)) == ' ' || c == '\t' || c =='\n')  ;
X        temp = sp;
X	sp = checkid(sp, ':');  /*  check for invalid symbolic name  */
X	c = *sp;   *sp = '\0';
X        while( c == ' ' || c == '\t' || c == '\n')  c = *++sp;
X
X        if ( c != ':' && c != '\0') {
X	    error("invalid string %s after option -n or -N",sp);
X            return;
X        }
X
X	pt = talloc(struct Symrev);
X        pt->ssymbol = temp;
X        pt->override = flag;
X	if (c == '\0')  /*  delete symbol  */
X            pt->revno = nil;
X        else {
X            while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
X	    if ( c == '\0' )
X                pt->revno = nil;
X	    else
X                pt->revno = sp;
X        }
X        pt->nextsym = nil;
X        if (lastassoc)
X            lastassoc->nextsym = pt;
X        else
X            assoclst = pt;
X        lastassoc = pt;
X        return;
X}
X
X
X	static void
Xgetchaccess(login, command)
X	const char *login;
X	enum changeaccess command;
X{
X	register struct chaccess *pt;
X
X	*nextchaccess = pt = talloc(struct chaccess);
X	pt->login = login;
X	pt->command = command;
X	pt->nextchaccess = nil;
X	nextchaccess = &pt->nextchaccess;
X}
X
X
X
X	static void
Xgetaccessor(opt, command)
X	char *opt;
X	enum changeaccess command;
X/*   Function:  get the accessor list of options -e and -a,     */
X/*		and store in chaccess				*/
X
X
X{
X        register c;
X	register char *sp;
X
X	sp = opt;
X        while( ( c = *++sp) == ' ' || c == '\n' || c == '\t' || c == ',') ;
X        if ( c == '\0') {
X	    if (command == erase  &&  sp-opt == 1) {
X		getchaccess((const char*)nil, command);
X		return;
X	    }
X	    error("missing login name after option -a or -e");
X	    return;
X        }
X
X        while( c != '\0') {
X		getchaccess(sp, command);
X		sp = checkid(sp,',');
X		c = *sp;   *sp = '\0';
X                while( c == ' ' || c == '\n' || c == '\t'|| c == ',')c =(*++sp);
X        }
X}
X
X
X
X	static void
Xgetstates(sp)
Xchar    *sp;
X/*   Function:  get one state attribute and the corresponding   */
X/*              revision and store in statelst                  */
X
X{
X	const char *temp;
X        struct  Status  *pt;
X        register        c;
X
X        while( (c=(*++sp)) ==' ' || c == '\t' || c == '\n')  ;
X        temp = sp;
X	sp = checkid(sp,':');  /* check for invalid state attribute */
X	c = *sp;   *sp = '\0';
X        while( c == ' ' || c == '\t' || c == '\n' )  c = *++sp;
X
X        if ( c == '\0' ) {  /*  change state of def. branch or Head  */
X            chgheadstate = true;
X            headstate  = temp;
X            return;
X        }
X        else if ( c != ':' ) {
X	    error("missing ':' after state in option -s");
X            return;
X        }
X
X        while( (c = *++sp) == ' ' || c == '\t' || c == '\n')  ;
X	pt = talloc(struct Status);
X        pt->status     = temp;
X        pt->revno      = sp;
X        pt->nextstatus = nil;
X        if (laststate)
X            laststate->nextstatus = pt;
X        else
X            statelst = pt;
X        laststate = pt;
X}
X
X
X
X	static void
Xgetdelrev(sp)
Xchar    *sp;
X/*   Function:  get revision range or branch to be deleted,     */
X/*              and place in delrev                             */
X{
X        int    c;
X        struct  delrevpair      *pt;
X
X	pt = &delrev;
X        while((c = (*++sp)) == ' ' || c == '\n' || c == '\t') ;
X
X        if ( c == '<' || c == '-' ) {  /*  -o  -rev  or <rev  */
X            while( (c = (*++sp)) == ' ' || c == '\n' || c == '\t')  ;
X            pt->strt = sp;    pt->code = 1;
X            while( c != ' ' && c != '\n' && c != '\t' && c != '\0') c =(*++sp);
X            *sp = '\0';
X	    pt->end = nil;
X            return;
X        }
X        else {
X            pt->strt = sp;
X            while( c != ' ' && c != '\n' && c != '\t' && c != '\0'
X                   && c != '-' && c != '<' )  c = *++sp;
X            *sp = '\0';
X            while( c == ' ' || c == '\n' || c == '\t' )  c = *++sp;
X            if ( c == '\0' )  {  /*   -o rev or branch   */
X                pt->end = nil;   pt->code = 0;
X                return;
X            }
X            if ( c != '-' && c != '<') {
X		faterror("invalid range %s %s after -o", pt->strt, sp);
X            }
X            while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
X            if ( c == '\0') {  /*  -o   rev-   or   rev<   */
X                pt->end = nil;   pt->code = 2;
X                return;
X            }
X        }
X        /*   -o   rev1-rev2    or   rev1<rev2   */
X	pt->end = sp;  pt->code = 3;
X        while( c!= ' ' && c != '\n' && c != '\t' && c != '\0') c = *++sp;
X        *sp = '\0';
X}
X
X
X
X
X	static void
Xscanlogtext(delta,edit)
X	struct hshentry *delta;
X	int edit;
X/* Function: Scans delta text nodes up to and including the one given
X * by delta, or up to last one present, if delta==nil.
X * For the one given by delta (if delta!=nil), the log message is saved into
X * curlogmsg and the text is edited if 'edit' is set, copied otherwise.
X * Assumes the initial lexeme must be read in first.
X * Does not advance nexttok after it is finished, except if delta==nil.
X */
X{
X	const struct hshentry *nextdelta;
X	struct cbuf cb;
X
X	for (;;) {
X		foutptr = NULL;
X                nextlex();
X                if (!(nextdelta=getnum())) {
X                    if(delta)
X			faterror("can't find delta for revision %s", delta->num);
X		    if (nexttok != EOFILE)
X			fatserror("expecting EOF");
X		    return; /* no more delta text nodes */
X                }
X		if (nextdelta->selector) {
X			foutptr = frewrite;
X			aprintf(frewrite,DELNUMFORM,nextdelta->num,Klog);
X                }
X		getkeystring(Klog);
X		if (delta==nextdelta) {
X			cb = savestring(&curlogbuf);
X			delta->log = curlogmsg =
X				cleanlogmsg(curlogbuf.string, cb.size);
X                } else {readstring();
X                }
X                nextlex();
X		while (nexttok==ID && strcmp(NextString,Ktext)!=0)
X			ignorephrase();
X		getkeystring(Ktext);
X
X		if (delta==nextdelta)
X			break;
X		readstring(); /* skip over it */
X
X	}
X	/* got the one we're looking for */
X	if (edit)
X		editstring((struct hshentry *)nil);
X	else
X		copystring();
X}
X
X
X
X	static struct Lockrev *
Xrmnewlocklst(which)
X	const struct Lockrev *which;
X/*   Function:  remove lock to revision which->revno from newlocklst   */
X
X{
X        struct  Lockrev   * pt, *pre;
X
X        while( newlocklst && (! strcmp(newlocklst->revno, which->revno))){
X	    struct Lockrev *pn = newlocklst->nextrev;
X	    tfree(newlocklst);
X	    newlocklst = pn;
X        }
X
X        pt = pre = newlocklst;
X        while( pt ) {
X            if ( ! strcmp(pt->revno, which->revno) ) {
X                pre->nextrev = pt->nextrev;
X		tfree(pt);
X		pt = pre->nextrev;
X            }
X            else {
X                pre = pt;
X                pt = pt->nextrev;
X            }
X        }
X        return pre;
X}
X
X
X
X	static void
Xupdateaccess()
X{
X	register struct chaccess *ch;
X	register struct access **p, *t;
X
X	for (ch = chaccess;  ch;  ch = ch->nextchaccess) {
X		switch (ch->command) {
X		case erase:
X			if (!ch->login)
X			    AccessList = nil;
X			else
X			    for (p = &AccessList;  (t = *p);  )
X				if (strcmp(ch->login, t->login) == 0)
X					*p = t->nextaccess;
X				else
X					p = &t->nextaccess;
X			break;
X		case append:
X			for (p = &AccessList;  ;  p = &t->nextaccess)
X				if (!(t = *p)) {
X					*p = t = ftalloc(struct access);
X					t->login = ch->login;
X					t->nextaccess = nil;
X					break;
X				} else if (strcmp(ch->login, t->login) == 0)
X					break;
X			break;
X		}
X	}
X}
X
X
X	static int
Xsendmail(Delta, who)
X	const char *Delta, *who;
X/*   Function:  mail to who, informing him that his lock on delta was
X *   broken by caller. Ask first whether to go ahead. Return false on
X *   error or if user decides not to break the lock.
X */
X{
X	const char *messagefile;
X	int old1, old2, c;
X        FILE    * mailmess;
X
X
X	aprintf(stderr, "Revision %s is already locked by %s.\n", Delta, who);
X	if (!yesorno(false, "Do you want to break the lock? [ny](n): "))
X		return false;
X
X        /* go ahead with breaking  */
X	messagefile = maketemp(0);
X	errno = 0;
X        if ( (mailmess = fopen(messagefile, "w")) == NULL) {
X	    efaterror(messagefile);
X        }
X
X	aprintf(mailmess, "Subject: Broken lock on %s\n\nYour lock on revision %s of file %s\nhas been broken by %s for the following reason:\n",
X		bindex(RCSfilename,SLASH), Delta, getfullRCSname(), getcaller()
X	);
X	aputs("State the reason for breaking the lock:\n(terminate with single '.' or end of file)\n>> ", stderr);
X
X        old1 = '\n';    old2 = ' ';
X        for (; ;) {
X	    c = getcstdin();
X            if ( c == EOF ) {
X		aprintf(mailmess, "%c\n", old1);
X                break;
X            }
X            else if ( c == '\n' && old1 == '.' && old2 == '\n')
X                break;
X            else {
X		afputc(old1, mailmess);
X                old2 = old1;   old1 = c;
X		if (c=='\n') aputs(">> ", stderr);
X            }
X        }
X        ffclose(mailmess);
X
X	/* ignore the exit status, even if delivermail unsuccessful */
X	VOID run(messagefile, (char*)nil, SENDMAIL, who, (char*)nil);
X	return(true);
X}
X
X
X
X	static void
Xbreaklock(delta)
X	const struct hshentry *delta;
X/* function: Finds the lock held by caller on delta,
X * and removes it.
X * Sends mail if a lock different from the caller's is broken.
X * Prints an error message if there is no such lock or error.
X */
X{
X        register struct lock * next, * trail;
X	const char *num;
X        struct lock dummy;
X
X	num=delta->num;
X        dummy.nextlock=next=Locks;
X        trail = &dummy;
X        while (next!=nil) {
X		if (strcmp(num, next->delta->num) == 0) {
X			if (
X				strcmp(getcaller(),next->login) != 0
X			    &&	!sendmail(num, next->login)
X			) {
X			    error("%s still locked by %s", num, next->login);
X			    return;
X			}
X			break; /* exact match */
X                }
X                trail=next;
X                next=next->nextlock;
X        }
X        if (next!=nil) {
X                /*found one */
X		diagnose("%s unlocked\n",next->delta->num);
X                trail->nextlock=next->nextlock;
X                next->delta->lockedby=nil;
X                Locks=dummy.nextlock;
X        } else  {
X		error("no lock set on revision %s", num);
X        }
X}
X
X
X
X	static struct hshentry *
Xsearchcutpt(object, length, store)
X	const char *object;
X	unsigned length;
X	struct hshentries *store;
X/*   Function:  Search store and return entry with number being object. */
X/*              cuttail = nil, if the entry is Head; otherwise, cuttail */
X/*              is the entry point to the one with number being object  */
X
X{
X	cuthead = nil;
X	while (compartial(store->first->num, object, length)) {
X		cuthead = store->first;
X		store = store->rest;
X	}
X	return store->first;
X}
X
X
X
X	static int
Xbranchpoint(strt, tail)
Xstruct  hshentry        *strt,  *tail;
X/*   Function: check whether the deltas between strt and tail	*/
X/*		are locked or branch point, return 1 if any is  */
X/*		locked or branch point; otherwise, return 0 and */
X/*		mark deleted					*/
X
X{
X        struct  hshentry    *pt;
X	const struct lock *lockpt;
X        int     flag;
X
X
X        pt = strt;
X        flag = false;
X        while( pt != tail) {
X            if ( pt->branches ){ /*  a branch point  */
X                flag = true;
X		error("can't remove branch point %s", pt->num);
X            }
X	    lockpt = Locks;
X	    while(lockpt && lockpt->delta != pt)
X		lockpt = lockpt->nextlock;
X	    if ( lockpt ) {
X		flag = true;
X		error("can't remove locked revision %s",pt->num);
X	    }
X            pt = pt->next;
X        }
X
X        if ( ! flag ) {
X            pt = strt;
X            while( pt != tail ) {
X		pt->selector = false;
X		diagnose("deleting revision %s\n",pt->num);
X                pt = pt->next;
X            }
X        }
X        return flag;
X}
X
X
X
X	static int
Xremoverevs()
X/*   Function:  get the revision range to be removed, and place the     */
X/*              first revision removed in delstrt, the revision before  */
X/*              delstrt in cuthead( nil, if delstrt is head), and the   */
X/*              revision after the last removed revision in cuttail(nil */
X/*              if the last is a leaf                                   */
X
X{
X	struct  hshentry *target, *target2, *temp;
X	unsigned length;
X	int flag;
X
X        flag = false;
X	if (!expandsym(delrev.strt, &numrev)) return 0;
X	target = genrevs(numrev.string, (char*)nil, (char*)nil, (char*)nil, &gendeltas);
X        if ( ! target ) return 0;
X	if (cmpnum(target->num, numrev.string)) flag = true;
X	length = countnumflds(numrev.string);
X
X	if (delrev.code == 0) {  /*  -o  rev    or    -o  branch   */
X	    if (length & 1)
X		temp=searchcutpt(target->num,length+1,gendeltas);
X	    else if (flag) {
X		error("Revision %s doesn't exist.", numrev.string);
X		return 0;
X	    }
X	    else
X		temp = searchcutpt(numrev.string, length, gendeltas);
X	    cuttail = target->next;
X            if ( branchpoint(temp, cuttail) ) {
X                cuttail = nil;
X                return 0;
X            }
X            delstrt = temp;     /* first revision to be removed   */
X            return 1;
X        }
X
X	if (length & 1) {   /*  invalid branch after -o   */
X	    error("invalid branch range %s after -o", numrev.string);
X            return 0;
X        }
X
X	if (delrev.code == 1) {  /*  -o  -rev   */
X            if ( length > 2 ) {
X                temp = searchcutpt( target->num, length-1, gendeltas);
X                cuttail = target->next;
X            }
X            else {
X                temp = searchcutpt(target->num, length, gendeltas);
X                cuttail = target;
X                while( cuttail && ! cmpnumfld(target->num,cuttail->num,1) )
X                    cuttail = cuttail->next;
X            }
X            if ( branchpoint(temp, cuttail) ){
X                cuttail = nil;
X                return 0;
X            }
X            delstrt = temp;
X            return 1;
X        }
X
X	if (delrev.code == 2) {   /*  -o  rev-   */
X            if ( length == 2 ) {
X                temp = searchcutpt(target->num, 1,gendeltas);
X                if ( flag)
X                    cuttail = target;
X                else
X                    cuttail = target->next;
X            }
X            else  {
X                if ( flag){
X                    cuthead = target;
X                    if ( !(temp = target->next) ) return 0;
X                }
X                else
X                    temp = searchcutpt(target->num, length, gendeltas);
X		getbranchno(temp->num, &numrev);  /* get branch number */
X		target = genrevs(numrev.string, (char*)nil, (char*)nil, (char*)nil, &gendeltas);
X            }
X            if ( branchpoint( temp, cuttail ) ) {
X                cuttail = nil;
X                return 0;
X            }
X            delstrt = temp;
X            return 1;
X        }
X
X        /*   -o   rev1-rev2   */
X	if (!expandsym(delrev.end, &numrev)) return 0;
X	if (
X		length != countnumflds(numrev.string)
X	    ||	length>2 && compartial(numrev.string, target->num, length-1)
X	) {
X	    error("invalid revision range %s-%s", target->num, numrev.string);
X            return 0;
X        }
X
X	target2 = genrevs(numrev.string,(char*)nil,(char*)nil,(char*)nil,&gendeltas);
X        if ( ! target2 ) return 0;
X
X        if ( length > 2) {  /* delete revisions on branches  */
X            if ( cmpnum(target->num, target2->num) > 0) {
X		if (cmpnum(target2->num, numrev.string))
X                    flag = true;
X                else
X                    flag = false;
X                temp = target;
X                target = target2;
X                target2 = temp;
X            }
X            if ( flag ) {
X                if ( ! cmpnum(target->num, target2->num) ) {
X		    error("Revisions %s-%s don't exist.", delrev.strt,delrev.end);
X                    return 0;
X                }
X                cuthead = target;
X                temp = target->next;
X            }
X            else
X                temp = searchcutpt(target->num, length, gendeltas);
X            cuttail = target2->next;
X        }
X        else { /*  delete revisions on trunk  */
X            if ( cmpnum( target->num, target2->num) < 0 ) {
X                temp = target;
X                target = target2;
X                target2 = temp;
X            }
X            else
X		if (cmpnum(target2->num, numrev.string))
X                    flag = true;
X                else
X                    flag = false;
X            if ( flag ) {
X                if ( ! cmpnum(target->num, target2->num) ) {
X		    error("Revisions %s-%s don't exist.", delrev.strt, delrev.end);
X                    return 0;
X                }
X                cuttail = target2;
X            }
X            else
X                cuttail = target2->next;
X            temp = searchcutpt(target->num, length, gendeltas);
X        }
X        if ( branchpoint(temp, cuttail) )  {
X            cuttail = nil;
X            return 0;
X        }
X        delstrt = temp;
X        return 1;
X}
X
X
X
X	static void
Xupdateassoc()
X/*   Function: add or delete(if revno is nil) association	*/
X/*		which is stored in assoclst			*/
X
X{
X	const struct Symrev *curassoc;
X	struct  assoc   * pre,  * pt;
X
X        /*  add new associations   */
X        curassoc = assoclst;
X        while( curassoc ) {
X            if ( curassoc->revno == nil ) {  /* delete symbol  */
X		pre = pt = Symbols;
X                while( pt && strcmp(pt->symbol,curassoc->ssymbol) ) {
X		    pre = pt;
X		    pt = pt->nextassoc;
X		}
X		if ( pt )
X		    if ( pre == pt )
X			Symbols = pt->nextassoc;
X		    else
X			pre->nextassoc = pt->nextassoc;
X		else
X		    warn("can't delete nonexisting symbol %s",curassoc->ssymbol);
X	    }
X	    else if (expandsym(curassoc->revno, &numrev)) {
X	    /*   add symbol  */
X		VOID addsymbol(fstrsave(numrev.string), curassoc->ssymbol, curassoc->override);
X            }
X            curassoc = curassoc->nextsym;
X        }
X
X}
X
X
X
X	static void
Xupdatelocks()
X/* Function: remove lock for caller or first lock if unlockcaller is set;
X *           remove locks which are stored in rmvlocklst,
X *           add new locks which are stored in newlocklst,
X *           add lock for Dbranch or Head if lockhead is set.
X */
X{
X	const struct Lockrev *lockpt;
X	struct hshentry *target;
X
X	if (unlockcaller) { /*  find lock for caller  */
X            if ( Head ) {
X		if (Locks) {
X		    switch (findlock(true, &target)) {
X		      case 0:
X			breaklock(Locks->delta); /* remove most recent lock */
X			break;
X		      case 1:
X			diagnose("%s unlocked\n",target->num);
X			break;
X		    }
X		} else {
X		    warn("No locks are set.");
X		}
X            } else {
X		warn("can't unlock an empty tree");
X            }
X        }
X
X        /*  remove locks which are stored in rmvlocklst   */
X        lockpt = rmvlocklst;
X        while( lockpt ) {
X	    if (expandsym(lockpt->revno, &numrev)) {
X		target = genrevs(numrev.string, (char *)nil, (char *)nil, (char *)nil, &gendeltas);
X                if ( target )
X		   if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string))
X			error("can't unlock nonexisting revision %s",lockpt->revno);
X                   else
X			breaklock(target);
X                        /* breaklock does its own diagnose */
X            }
X            lockpt = lockpt->nextrev;
X        }
X
X        /*  add new locks which stored in newlocklst  */
X        lockpt = newlocklst;
X        while( lockpt ) {
X	    setlock(lockpt->revno);
X            lockpt = lockpt->nextrev;
X        }
X
X	if (lockhead) {  /*  lock default branch or head  */
X            if (Dbranch) {
X		setlock(Dbranch);
X	    } else if (Head) {
X		if (0 <= addlock(Head))
X		    diagnose("%s locked\n",Head->num);
X            } else {
X		warn("can't lock an empty tree");
X            }
X        }
X
X}
X
X
X
X	static void
Xsetlock(rev)
X	const char *rev;
X/* Function: Given a revision or branch number, finds the corresponding
X * delta and locks it for caller.
X */
X{
X        struct  hshentry *target;
X
X	if (expandsym(rev, &numrev)) {
X	    target = genrevs(numrev.string, (char*)nil, (char*)nil,
X			     (char*)nil, &gendeltas);
X            if ( target )
X	       if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string))
X		    error("can't lock nonexisting revision %s", numrev.string);
X               else
X		    if (0 <= addlock(target))
X			diagnose("%s locked\n", target->num);
X        }
X}
X
X
X
X	static void
Xrcs_setstate(rev,status)
X	const char *rev, *status;
X/* Function: Given a revision or branch number, finds the corresponding delta
X * and sets its state to status.
X */
X{
X        struct  hshentry *target;
X
X	if (expandsym(rev, &numrev)) {
X	    target = genrevs(numrev.string, (char*)nil, (char*)nil,
X			     (char*)nil, &gendeltas);
X            if ( target )
X	       if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string))
X		    error("can't set state of nonexisting revision %s to %s",
X			  numrev.string, status);
X               else
X                    target->state = status;
X        }
X}
X
X
X
X
X
X	static int
Xbuildeltatext(deltas)
X	const struct hshentries *deltas;
X/*   Function:  put the delta text on frewrite and make necessary   */
X/*              change to delta text                                */
X{
X	int exit_stats;
X	register FILE *fcut;	/* temporary file to rebuild delta tree */
X	const char *cutfilename, *diffilename;
X
X	cutfilename = nil;
X	cuttail->selector = false;
X	inittmpeditfiles();
X	scanlogtext(deltas->first, false);
X        if ( cuthead )  {
X	    cutfilename = maketemp(3);
X	    errno = 0;
X            if ( (fcut = fopen(cutfilename, "w")) == NULL) {
X		efaterror(cutfilename);
X            }
X
X	    while (deltas->first != cuthead) {
X		deltas = deltas->rest;
X		scanlogtext(deltas->first, true);
X            }
X
X	    finishedit((struct hshentry *)nil);
X	    arewind(fcopy);
X	    fastcopy(fcopy, fcut);
X            swapeditfiles(false);
X            ffclose(fcut);
X        }
X
X	while (deltas->first != cuttail)
X	    scanlogtext((deltas = deltas->rest)->first, true);
X        finishedit((struct hshentry *)nil);    ffclose(fcopy);
X
X        if ( cuthead ) {
X	    diffilename = maketemp(0);
X            exit_stats = run((char*)nil,diffilename,
X			DIFF DIFF_FLAGS, cutfilename, resultfile, (char*)nil);
X	    if (!WIFEXITED(exit_stats) || 1<WEXITSTATUS(exit_stats))
X                faterror ("diff failed");
X	    return putdtext(cuttail->num,curlogmsg,diffilename,frewrite,true);
X	} else
X	    return putdtext(cuttail->num,curlogmsg,resultfile,frewrite,false);
X}
X
X
X
X	static void
Xbuildtree()
X/*   Function:  actually removes revisions whose selector field  */
X/*		is false, and rebuilds the linkage of deltas.	 */
X/*              asks for reconfirmation if deleting last revision*/
X{
X	struct	hshentry   * Delta;
X        struct  branchhead      *pt, *pre;
X
X        if ( cuthead )
X           if ( cuthead->next == delstrt )
X                cuthead->next = cuttail;
X           else {
X                pre = pt = cuthead->branches;
X                while( pt && pt->hsh != delstrt )  {
X                    pre = pt;
X                    pt = pt->nextbranch;
X                }
X                if ( cuttail )
X                    pt->hsh = cuttail;
X                else if ( pt == pre )
X                    cuthead->branches = pt->nextbranch;
X                else
X                    pre->nextbranch = pt->nextbranch;
X            }
X	else {
X            if ( cuttail == nil && !quietflag) {
X		if (!yesorno(false, "Do you really want to delete all revisions? [ny](n): ")) {
X		    error("No revision deleted");
X		    Delta = delstrt;
X		    while( Delta) {
X			Delta->selector = true;
X			Delta = Delta->next;
X		    }
X		    return;
X		}
X	    }
X            Head = cuttail;
X	}
X        return;
X}
X
X#if lint
X/* This lets us lint everything all at once. */
X
Xconst char cmdid[] = "";
X
X#define go(p,e) {int p P((int,char**)); void e P((void)); if(*argv)return p(argc,argv);if(*argv[1])e();return 0;}
X
X	int
Xmain(argc, argv)
X	int argc;
X	char **argv;
X{
X	switch (argc) {
X		case 0:	go(ciId,	ciExit);
X		case 1:	go(coId,	coExit);
X		case 2:	go(identId,	identExit);
X		case 3:	go(rcsdiffId,	rdiffExit);
X		case 4:	go(rcsmergeId,	rmergeExit);
X		case 5:	go(rlogId,	rlogExit);
X		case 6:	go(rcsId,	exiterr);
X		default: return 0;
X	}
X}
X#endif
END_OF_FILE
  if test 42394 -ne `wc -c <'src/rcs.c'`; then
    echo shar: \"'src/rcs.c'\" unpacked with wrong size!
  fi
  # end of 'src/rcs.c'
fi
echo shar: End of archive 3 \(of 12\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 12 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still must unpack the following archives:
    echo "        " ${MISSING}
fi
exit 0
exit 0 # Just in case...
-- 
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.



More information about the Comp.sources.unix mailing list