Subject: v24i003: RCS source control system, Part03/12 Newsgroups: comp.sources.unix Approved: rsalz@uunet.UU.NET X-Checksum-Snefru: db32cfc0 fb888ade 0a683c96 f996a50b Submitted-by: Adam Hammer 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@uunet.uu.net if you want that tool. # Contents: src/Makefile src/rcs.c # Wrapped by rsalz@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@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@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 (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 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 rev1end = 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) || 1num,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...