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