BSD 4_3_Reno development
authorCSRG <csrg@ucbvax.Berkeley.EDU>
Tue, 15 Aug 1989 15:34:15 +0000 (07:34 -0800)
committerCSRG <csrg@ucbvax.Berkeley.EDU>
Tue, 15 Aug 1989 15:34:15 +0000 (07:34 -0800)
Work on file usr/src/contrib/rcs/src/rcs.c

Synthesized-from: CSRG/cd2/4.3reno

usr/src/contrib/rcs/src/rcs.c [new file with mode: 0644]

diff --git a/usr/src/contrib/rcs/src/rcs.c b/usr/src/contrib/rcs/src/rcs.c
new file mode 100644 (file)
index 0000000..fd2ad97
--- /dev/null
@@ -0,0 +1,1559 @@
+/*
+ *                      RCS create/change operation
+ */
+#ifndef lint
+static char rcsid[]=
+"$Header: /usr/src/local/bin/rcs/src/RCS/rcs.c,v 4.11 89/05/01 15:12:06 narten Exp $ Purdue CS";
+#endif
+/* Copyright (C) 1982, 1988, 1989 Walter Tichy
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Walter Tichy.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Report all problems and direct all questions to:
+ *   rcs-bugs@cs.purdue.edu
+ * 
+
+
+
+
+
+
+
+*/
+
+
+
+
+/* $Log:       rcs.c,v $
+ * Revision 4.11  89/05/01  15:12:06  narten
+ * changed copyright header to reflect current distribution rules
+ * 
+ * Revision 4.10  88/11/08  16:01:54  narten
+ * didn't install previous patch correctly
+ * 
+ * Revision 4.9  88/11/08  13:56:01  narten
+ * removed include <sysexits.h> (not needed)
+ * minor fix for -A option
+ * 
+ * Revision 4.8  88/11/08  12:01:58  narten
+ * changes from  eggert@sm.unisys.com (Paul Eggert)
+ * 
+ * Revision 4.8  88/08/09  19:12:27  eggert
+ * Don't access freed storage.
+ * Use execv(), not system(); yield proper exit status; remove lint.
+ * 
+ * Revision 4.7  87/12/18  11:37:17  narten
+ * lint cleanups (Guy Harris)
+ * 
+ * Revision 4.6  87/10/18  10:28:48  narten
+ * Updating verison numbers. Changes relative to 1.1 are actually 
+ * relative to 4.3
+ * 
+ * Revision 1.4  87/09/24  13:58:52  narten
+ * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
+ * warnings)
+ * 
+ * Revision 1.3  87/03/27  14:21:55  jenkins
+ * Port to suns
+ * 
+ * Revision 1.2  85/12/17  13:59:09  albitz
+ * Changed setstate to rcs_setstate because of conflict with random.o.
+ * 
+ * Revision 1.1  84/01/23  14:50:09  kcs
+ * Initial revision
+ * 
+ * Revision 4.3  83/12/15  12:27:33  wft
+ * rcs -u now breaks most recent lock if it can't find a lock by the caller.
+ * 
+ * Revision 4.2  83/12/05  10:18:20  wft
+ * Added conditional compilation for sending mail.
+ * Alternatives: V4_2BSD, V6, USG, and other.
+ * 
+ * Revision 4.1  83/05/10  16:43:02  wft
+ * Simplified breaklock(); added calls to findlock() and getcaller().
+ * Added option -b (default branch). Updated -s and -w for -b.
+ * Removed calls to stat(); now done by pairfilenames().
+ * Replaced most catchints() calls with restoreints().
+ * Removed check for exit status of delivermail().
+ * Directed all interactive output to stderr.
+ * 
+ * Revision 3.9.1.1  83/12/02  22:08:51  wft
+ * Added conditional compilation for 4.2 sendmail and 4.1 delivermail.
+ * 
+ * Revision 3.9  83/02/15  15:38:39  wft
+ * Added call to fastcopy() to copy remainder of RCS file.
+ *
+ * Revision 3.8  83/01/18  17:37:51  wft
+ * Changed sendmail(): now uses delivermail, and asks whether to break the lock.
+ *
+ * Revision 3.7  83/01/15  18:04:25  wft
+ * Removed putree(); replaced with puttree() in rcssyn.c.
+ * Combined putdellog() and scanlogtext(); deleted putdellog().
+ * Cleaned up diagnostics and error messages. Fixed problem with
+ * mutilated files in case of deletions in 2 files in a single command.
+ * Changed marking of selector from 'D' to DELETE.
+ *
+ * Revision 3.6  83/01/14  15:37:31  wft
+ * Added ignoring of interrupts while new RCS file is renamed;
+ * Avoids deletion of RCS files by interrupts.
+ *
+ * Revision 3.5  82/12/10  21:11:39  wft
+ * Removed unused variables, fixed checking of return code from diff,
+ * introduced variant COMPAT2 for skipping Suffix on -A files.
+ *
+ * Revision 3.4  82/12/04  13:18:20  wft
+ * Replaced getdelta() with gettree(), changed breaklock to update
+ * field lockedby, added some diagnostics.
+ *
+ * Revision 3.3  82/12/03  17:08:04  wft
+ * Replaced getlogin() with getpwuid(), flcose() with ffclose(),
+ * /usr/ucb/Mail with macro MAIL. Removed handling of Suffix (-x).
+ * fixed -u for missing revno. Disambiguated structure members.
+ *
+ * Revision 3.2  82/10/18  21:05:07  wft
+ * rcs -i now generates a file mode given by the umask minus write permission;
+ * otherwise, rcs keeps the mode, but removes write permission.
+ * I added a check for write error, fixed call to getlogin(), replaced
+ * curdir() with getfullRCSname(), cleaned up handling -U/L, and changed
+ * conflicting, long identifiers.
+ *
+ * Revision 3.1  82/10/13  16:11:07  wft
+ * fixed type of variables receiving from getc() (char -> int).
+ */
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "rcsbase.h"
+#include <paths.h>
+#ifndef lint
+static char rcsbaseid[] = RCSBASE;
+#endif
+
+extern FILE * fopen();
+extern char * bindex();
+extern int  expandsym();                /* get numeric revision name        */
+extern struct  hshentry  * getnum();
+extern struct  lock      * addlock();   /* add a lock                       */
+extern char              * getid();
+extern char              Klog[], Khead[], Kaccess[], Kbranch[], Ktext[];
+#ifdef COMPAT2
+extern char Ksuffix[];
+#endif
+extern char * getcaller();              /* get login of caller              */
+extern struct hshentry   * findlock();  /* find and remove lock             */
+extern struct hshentry   * genrevs();
+extern char * checkid();                /* check an identifier              */
+extern char * getfullRCSname();         /* get full path name of RCS file   */
+extern char * mktempfile();             /* temporary file name generator    */
+extern free();
+extern void catchints();
+extern void ignoreints();
+extern int  nerror;                     /* counter for errors               */
+extern int  quietflag;                  /* diagnoses suppressed if true     */
+extern char curlogmsg[];                /* current log message              */
+extern char * resultfile;               /* filename for fcopy              */
+extern FILE *fcopy;                     /* result file during editing       */
+extern FILE * finptr;                   /* RCS input file                   */
+extern FILE * frewrite;                 /* new RCS file                     */
+extern int    rewriteflag;              /* indicates whether input should be*/
+                                       /* echoed to frewrite               */
+
+char * newRCSfilename, * diffilename, * cutfilename;
+char * RCSfilename, * workfilename;
+extern struct stat RCSstat, workstat; /* file status of RCS and work file   */
+extern int  haveRCSstat, haveworkstat;/* status indicators                  */
+
+char accessorlst[strtsize];
+FILE * fcut;        /* temporary file to rebuild delta tree                 */
+int    oldumask;    /* save umask                                           */
+
+int initflag, strictlock, strict_selected, textflag;
+char * textfile, * accessfile;
+char * caller, numrev[30];            /* caller's login;               */
+struct  access  * newaccessor,  * rmvaccessor,  * rplaccessor;
+struct  access  *curaccess,  *rmaccess;
+struct  hshentry        * gendeltas[hshsize];
+
+struct  Lockrev {
+        char    * revno;
+        struct  Lockrev   * nextrev;
+};
+
+struct  Symrev {
+        char    * revno;
+        char    * ssymbol;
+        int     override;
+        struct  Symrev  * nextsym;
+};
+
+struct  Status {
+        char    * revno;
+        char    * status;
+        struct  Status  * nextstatus;
+};
+
+struct delrevpair {
+        char    * strt;
+        char    * end;
+        int     code;
+};
+
+struct  Lockrev * newlocklst,   * rmvlocklst;
+struct  Symrev  * assoclst,  * lastassoc;
+struct  Status  * statelst,  * laststate;
+struct  delrevpair      * delrev;
+struct  hshentry        * cuthead,  *cuttail,  * delstrt;
+char    branchnum[revlength], * branchsym;
+struct  hshentry branchdummy;
+char   * commsyml;
+char    * headstate;
+int     lockhead,unlockcaller,chgheadstate,branchflag,commentflag;
+int     delaccessflag;
+enum    stringwork {copy, edit, empty}; /* expand and edit_expand not needed */
+
+
+main (argc, argv)
+int argc;
+char * argv[];
+{
+        char    *comdusge;
+        int     result;
+       struct  access  *removeaccess(),  * getaccessor();
+        struct  Lockrev *rmnewlocklst();
+        struct  Lockrev *curlock,  * rmvlock, *lockpt;
+        struct  Status  * curstate;
+        struct  access    *temp, *temptr;
+       int status;
+
+       status = 0;
+        nerror = 0;
+       catchints();
+        cmdid = "rcs";
+        quietflag = false;
+        comdusge ="command format:\nrcs -i -alogins -Alogins -e[logins] -b[rev] -c[commentleader] -l[rev] -u[rev] -L -U -nname[:rev] -Nname[:rev] -orange -sstate[:rev] -t[textfile] file....";
+        rplaccessor = nil;     delstrt = nil;
+        accessfile = textfile = caller = nil;
+        branchflag = commentflag = chgheadstate = false;
+        lockhead = false; unlockcaller=false;
+        initflag= textflag = false;
+        strict_selected = 0;
+
+       caller=getcaller();
+        laststate = statelst = nil;
+        lastassoc = assoclst = nil;
+        curlock = rmvlock = newlocklst = rmvlocklst = nil;
+        curaccess = rmaccess = rmvaccessor = newaccessor = nil;
+        delaccessflag = false;
+
+        /*  preprocessing command options    */
+        while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
+                switch ((*argv)[1]) {
+
+                case 'i':   /*  initail version  */
+                        initflag = true;
+                        break;
+
+                case 'b':  /* change default branch */
+                        if (branchflag)warn("Redfinition of option -b");
+                        branchflag= true;
+                        branchsym = (*argv)+2;
+                        break;
+
+                case 'c':   /*  change comment symbol   */
+                        if (commentflag)warn("Redefinition of option -c");
+                        commentflag = true;
+                        commsyml = (*argv)+2;
+                        break;
+
+                case 'a':  /*  add new accessor   */
+                        if ( (*argv)[2] == '\0') {
+                            error("Login name missing after -a");
+                        }
+                        if ( (temp = getaccessor((*argv)+1)) ) {
+                            if ( newaccessor )
+                                curaccess->nextaccess = temp->nextaccess;
+                            else
+                                newaccessor = temp->nextaccess;
+                            temp->nextaccess = nil;
+                            curaccess = temp;
+                        }
+                        break;
+
+                case 'A':  /*  append access list according to accessfile  */
+                        if ( (*argv)[2] == '\0') {
+                            error("Missing file name after -A");
+                            break;
+                        }
+                        if ( accessfile) warn("Redefinition of option -A");
+                        *argv = *argv+2;
+                        if( pairfilenames(1, argv, true, false) > 0) {
+                            releaselst(newaccessor);
+                            newaccessor = curaccess = nil;
+                            releaselst(rmvaccessor);
+                            rmvaccessor = rmaccess = nil;
+                            accessfile = RCSfilename;
+                        }
+                        else
+                            accessfile = nil;
+                        break;
+
+                case 'e':    /*  remove accessors   */
+                        if ( (*argv)[2] == '\0' ) {
+                            delaccessflag = true;
+                            break;
+                        }
+                        if ( (temp = getaccessor((*argv)+1))  ) {
+                            if ( rmvaccessor )
+                                rmaccess->nextaccess = temp->nextaccess;
+                            else
+                                rmvaccessor = temp->nextaccess;
+                            temptr = temp->nextaccess;
+                            temp->nextaccess = nil;
+                            rmaccess = temp;
+                            while( temptr ) {
+                                newaccessor = removeaccess(temptr,newaccessor,false);
+                                temptr = temptr->nextaccess;
+                            }
+                            curaccess = temp = newaccessor;
+                            while( temp){
+                                curaccess = temp;
+                                temp = temp->nextaccess;
+                            }
+                        }
+                        break;
+
+                case 'l':    /*   lock a revision if it is unlocked   */
+                        if ( (*argv)[2] == '\0'){ /* lock head or def. branch */
+                            lockhead = true;
+                            break;
+                        }
+                        lockpt = (struct Lockrev *)talloc(sizeof(struct Lockrev));
+                        lockpt->revno = (*argv)+2;
+                        lockpt->nextrev = nil;
+                        if ( curlock )
+                            curlock->nextrev = lockpt;
+                        else
+                            newlocklst = lockpt;
+                        curlock = lockpt;
+                        break;
+
+                case 'u':   /*  release lock of a locked revision   */
+                        if ( (*argv)[2] == '\0'){ /*  unlock head  */
+                            unlockcaller=true;
+                            break;
+                        }
+                        lockpt = (struct Lockrev *)talloc(sizeof(struct Lockrev));
+                        lockpt->revno = (*argv)+2;
+                        lockpt->nextrev = nil;
+                        if (rmvlock)
+                            rmvlock->nextrev = lockpt;
+                        else
+                            rmvlocklst = lockpt;
+                        rmvlock = lockpt;
+
+                        curlock = rmnewlocklst(lockpt);
+                        break;
+
+                case 'L':   /*  set strict locking */
+                        if (strict_selected++) {  /* Already selected L or U? */
+                          if (!strictlock)       /* Already selected -U? */
+                              warn("Option -L overrides -U");
+                        }
+                        strictlock = true;
+                        break;
+
+                case 'U':   /*  release strict locking */
+                        if (strict_selected++) {  /* Already selected L or U? */
+                          if (strictlock)        /* Already selected -L? */
+                              warn("Option -L overrides -U");
+                        }
+                       else
+                           strictlock = false;
+                        break;
+
+                case 'n':    /*  add new association: error, if name exists */
+                        if ( (*argv)[2] == '\0') {
+                            error("Missing symbolic name after -n");
+                            break;
+                        }
+                        getassoclst(false, (*argv)+1);
+                        break;
+
+                case 'N':   /*  add or change association   */
+                        if ( (*argv)[2] == '\0') {
+                            error("Missing symbolic name after -N");
+                            break;
+                        }
+                        getassoclst(true, (*argv)+1);
+                        break;
+
+                case 'o':   /*  delete revisins  */
+                        if (delrev) warn("Redefinition of option -o");
+                        if ( (*argv)[2] == '\0' ) {
+                            error("Missing revision range after -o");
+                            break;
+                        }
+                        getdelrev( (*argv)+1 );
+                        break;
+
+                case 's':   /*  change state attribute of a revision  */
+                        if ( (*argv)[2] == '\0') {
+                            error("State missing after -s");
+                            break;
+                        }
+                        getstates( (*argv)+1);
+                        break;
+
+                case 't':   /*  change descriptive text   */
+                        textflag=true;
+                        if ((*argv)[2]!='\0'){
+                                if (textfile!=nil)warn("Redefinition of -t option");
+                                textfile = (*argv)+2;
+                        }
+                        break;
+
+                case 'q':
+                        quietflag = true;
+                        break;
+                default:
+                        faterror("Unknown option: %s\n%s", *argv, comdusge);
+                };
+        }  /* end processing of options */
+
+        if (argc<1) faterror("No input file\n%s", comdusge);
+        if (nerror) {   /*  exit, if any error in command options  */
+            diagnose("%s aborted",cmdid);
+            exit(1);
+        }
+        if (accessfile) /*  get replacement for access list   */
+            getrplaccess();
+        if (nerror) {
+            diagnose("%s aborted",cmdid);
+            exit(1);
+        }
+
+        /* now handle all filenames */
+        do {
+        rewriteflag = false;
+        finptr=frewrite=NULL;
+
+        if ( initflag ) {
+            switch( pairfilenames(argc, argv, false, false) ) {
+                case -1: break;        /*  not exist; ok */
+                case  0: continue;     /*  error         */
+                case  1: error("file %s exists already", RCSfilename);
+                         VOID fclose(finptr);
+                         continue;
+            }
+       }
+        else  {
+            switch( pairfilenames(argc, argv, true, false) ) {
+                case -1: continue;    /*  not exist      */
+                case  0: continue;    /*  errors         */
+                case  1: break;       /*  file exists; ok*/
+            }
+       }
+
+
+        /* now RCSfilename contains the name of the RCS file, and
+         * workfilename contains the name of the working file.
+         * if !initflag, finptr contains the file descriptor for the
+         * RCS file. The admin node is initialized.
+         */
+
+        diagnose("RCS file: %s", RCSfilename);
+
+        if (!trydiraccess(RCSfilename))            continue; /* give up */
+        if (!initflag && !checkaccesslist(caller)) continue; /* give up */
+        if (!trysema(RCSfilename,true))            continue; /* give up */
+
+        gettree(); /* read in delta tree */
+
+        /*  update admin. node    */
+        if (strict_selected) StrictLocks = strictlock;
+        if (commentflag) Comment = commsyml;
+
+        /* update default branch */
+        if (branchflag && expandsym(branchsym, branchnum)) {
+            if (countnumflds(branchnum)>0) {
+                branchdummy.num=branchnum;
+                Dbranch = &branchdummy;
+            } else
+                Dbranch = nil;
+        }
+
+        /*  update access list   */
+        if ( delaccessflag ) AccessList = nil;
+        if ( accessfile ) {
+            temp = rplaccessor;
+            while( temp ) {
+                temptr = temp->nextaccess;
+                if ( addnewaccess(temp) )
+                    temp->nextaccess = nil;
+                temp = temptr;
+            }
+        }
+        temp = rmvaccessor;
+        while(temp)   {         /*  remove accessors from accesslist   */
+            AccessList = removeaccess(temp, AccessList,true);
+            temp = temp->nextaccess;
+        }
+        temp = newaccessor;
+        while( temp)  {         /*  add new accessors   */
+            temptr = temp->nextaccess;
+            if ( addnewaccess( temp ) )
+                temp->nextaccess = nil;
+            temp = temptr;
+        }
+
+        updateassoc();          /*  update association list   */
+
+        updatelocks();          /*  update locks              */
+
+        /*  update state attribution  */
+        if (chgheadstate) {
+            /* change state of default branch or head */
+            if (Dbranch==nil) {
+                if (Head==nil)
+                     warn("Can't change states in an empty tree");
+                else Head->state = headstate;
+            } else {
+                rcs_setstate(Dbranch->num,headstate); /* Can't set directly */
+            }
+        }
+        curstate = statelst;
+        while( curstate ) {
+            rcs_setstate(curstate->revno,curstate->status);
+            curstate = curstate->nextstatus;
+        }
+
+        cuthead = cuttail = nil;
+        if ( delrev && removerevs()) {
+            /*  rebuild delta tree if some deltas are deleted   */
+            if ( cuttail )
+               VOID genrevs(cuttail->num, (char *)nil,(char *)nil,
+                            (char *)nil, gendeltas);
+            buildtree();
+        }
+
+
+        /* prepare for rewriting the RCS file */
+        newRCSfilename=mktempfile(RCSfilename,NEWRCSFILE);
+        oldumask = umask(0222); /* turn off write bits */
+        if ((frewrite=fopen(newRCSfilename, "w"))==NULL) {
+                VOID fclose(finptr);
+                error("Can't open file %s",newRCSfilename);
+                continue;
+        }
+        VOID umask(oldumask);
+        putadmin(frewrite);
+        if ( Head )
+           puttree(Head, frewrite);
+       putdesc(initflag,textflag,textfile,quietflag);
+        rewriteflag = false;
+
+        if ( Head) {
+            if (!delrev) {
+                /* no revision deleted */
+                fastcopy(finptr,frewrite);
+            } else {
+                if ( cuttail )
+                    buildeltatext(gendeltas);
+                else
+                    scanlogtext((struct hshentry *)nil,empty);
+                    /* copy rest of delta text nodes that are not deleted      */
+            }
+        }
+        ffclose(frewrite);   frewrite = NULL;
+        if ( ! nerror ) {  /*  move temporary file to RCS file if no error */
+           ignoreints();               /* ignore interrupts */
+            if(rename(newRCSfilename,RCSfilename)<0) {
+                error("Can't create RCS file %s; saved in %s",
+                   RCSfilename, newRCSfilename);
+                newRCSfilename[0] = '\0';  /*  avoid deletion by cleanup  */
+                restoreints();
+                VOID cleanup();
+                break;
+            }
+            newRCSfilename[0]='\0'; /* avoid re-unlinking by cleanup()*/
+            /* update mode */
+            result=0;
+            if (!initflag) /* preserve mode bits */
+                result=chmod(RCSfilename,RCSstat.st_mode & ~0222);
+            elsif (haveworkstat==0)  /* initialization, and work file exists */
+                result=chmod(RCSfilename,workstat.st_mode & ~0222);
+            if (result<0) warn("Can't set mode of %s",RCSfilename);
+
+            restoreints();                /* catch them all again */
+            diagnose("done");
+        } else {
+           diagnose("%s aborted; %s unchanged.",cmdid,RCSfilename);
+           status = 1;
+           nerror = 0;
+        }
+        } while (cleanup(),
+                 ++argv, --argc >=1);
+
+        exit(status);
+}       /* end of main (rcs) */
+
+
+
+getassoclst(flag, sp)
+int     flag;
+char    * sp;
+/*  Function:   associate a symbolic name to a revision or branch,      */
+/*              and store in assoclst                                   */
+
+{
+        struct   Symrev  * pt;
+        char             * temp, *temp2;
+        int                c;
+
+        while( (c=(*++sp)) == ' ' || c == '\t' || c =='\n')  ;
+        temp = sp;
+        temp2=checkid(sp, ':');  /*  check for invalid symbolic name  */
+        sp = temp2; c = *sp;   *sp = '\0';
+        while( c == ' ' || c == '\t' || c == '\n')  c = *++sp;
+
+        if ( c != ':' && c != '\0') {
+           error("Invalid string %s after option -n or -N",sp);
+            return;
+        }
+
+        pt = (struct Symrev *)talloc(sizeof(struct Symrev));
+        pt->ssymbol = temp;
+        pt->override = flag;
+       if (c == '\0')  /*  delete symbol  */
+            pt->revno = nil;
+        else {
+            while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
+           if ( c == '\0' )
+                pt->revno = nil;
+           else
+                pt->revno = sp;
+        }
+        pt->nextsym = nil;
+        if (lastassoc)
+            lastassoc->nextsym = pt;
+        else
+            assoclst = pt;
+        lastassoc = pt;
+        return;
+}
+
+
+
+struct access * getaccessor( sp)
+char            *sp;
+/*   Function:  get the accessor list of options -e and -a,     */
+/*              and store in curpt                              */
+
+
+{
+        struct  access  * curpt, * pt,  *pre;
+        char    *temp;
+        register c;
+
+        while( ( c = *++sp) == ' ' || c == '\n' || c == '\t' || c == ',') ;
+        if ( c == '\0') {
+            error("Missing login name after option -a or -e");
+            return nil;
+        }
+
+        curpt = pt = nil;
+        while( c != '\0') {
+                temp=checkid(sp,',');
+                pt = (struct access *)talloc(sizeof(struct access));
+                pt->login = sp;
+                if ( curpt )
+                    pre->nextaccess = pt;
+                else
+                    curpt = pt;
+                pt->nextaccess = curpt;
+                pre = pt;
+                sp = temp;    c = *sp;   *sp = '\0';
+                while( c == ' ' || c == '\n' || c == '\t'|| c == ',')c =(*++sp);
+        }
+        return pt;
+}
+
+
+
+getstates(sp)
+char    *sp;
+/*   Function:  get one state attribute and the corresponding   */
+/*              revision and store in statelst                  */
+
+{
+        char    *temp, *temp2;
+        struct  Status  *pt;
+        register        c;
+
+        while( (c=(*++sp)) ==' ' || c == '\t' || c == '\n')  ;
+        temp = sp;
+        temp2=checkid(sp,':');  /* check for invalid state attribute */
+        sp = temp2;   c = *sp;   *sp = '\0';
+        while( c == ' ' || c == '\t' || c == '\n' )  c = *++sp;
+
+        if ( c == '\0' ) {  /*  change state of def. branch or Head  */
+            chgheadstate = true;
+            headstate  = temp;
+            return;
+        }
+        else if ( c != ':' ) {
+            error("Missing ':' after state in option -s");
+            return;
+        }
+
+        while( (c = *++sp) == ' ' || c == '\t' || c == '\n')  ;
+        pt = (struct Status *)talloc(sizeof(struct Status));
+        pt->status     = temp;
+        pt->revno      = sp;
+        pt->nextstatus = nil;
+        if (laststate)
+            laststate->nextstatus = pt;
+        else
+            statelst = pt;
+        laststate = pt;
+}
+
+
+
+getrplaccess()
+/*   Function : get the accesslist of the 'accessfile'  */
+/*              and place in rplaccessor                */
+{
+        register        char    *id, *nextp;
+        struct          access  *newaccess, *oldaccess;
+
+        if ( (finptr=fopen(accessfile, "r")) == NULL) {
+            faterror("Can't open file %s", accessfile);
+        }
+        Lexinit();
+        nextp = &accessorlst[0];
+
+        if ( ! getkey(Khead)) faterror("Missing head in %s", accessfile);
+        VOID getnum();
+        if ( ! getlex(SEMI) ) serror("Missing ';' after head in %s",accessfile);
+
+        if (getkey(Kbranch)) { /* optional */
+                Dbranch=getnum();
+                if (!getlex(SEMI)) serror("Missing ';' after branch list");
+        }
+
+
+#ifdef COMPAT2
+        /* read suffix. Only in release 2 format */
+        if (getkey(Ksuffix)) {
+            if (nexttok==STRING) {
+                readstring(); nextlex(); /*through away the suffix*/
+            } elsif(nexttok==ID) {
+                nextlex();
+            }
+            if ( ! getlex(SEMI) ) serror("Missing ';' after suffix in %s",accessfile);
+        }
+#endif
+
+        if (! getkey(Kaccess))fatserror("Missing access list in %s",accessfile);
+        oldaccess = nil;
+        while( id =getid() ) {
+            newaccess = (struct access *)talloc(sizeof(struct access));
+            newaccess->login = nextp;
+            newaccess->nextaccess = nil;
+            while( ( *nextp++ = *id++) != '\0')  ;
+            if ( oldaccess )
+                oldaccess->nextaccess = newaccess;
+            else
+                rplaccessor = newaccess;
+            oldaccess = newaccess;
+        }
+        if ( ! getlex(SEMI))serror("Missing ';' after access list in %s",accessfile);
+        return;
+}
+
+
+
+getdelrev(sp)
+char    *sp;
+/*   Function:  get revision range or branch to be deleted,     */
+/*              and place in delrev                             */
+{
+        int    c;
+        struct  delrevpair      *pt;
+
+        if (delrev) free((char *)delrev);
+
+        pt = (struct delrevpair *)talloc(sizeof(struct delrevpair));
+        while((c = (*++sp)) == ' ' || c == '\n' || c == '\t') ;
+
+        if ( c == '<' || c == '-' ) {  /*  -o  -rev  or <rev  */
+            while( (c = (*++sp)) == ' ' || c == '\n' || c == '\t')  ;
+            pt->strt = sp;    pt->code = 1;
+            while( c != ' ' && c != '\n' && c != '\t' && c != '\0') c =(*++sp);
+            *sp = '\0';
+            pt->end = nil;  delrev = pt;
+            return;
+        }
+        else {
+            pt->strt = sp;
+            while( c != ' ' && c != '\n' && c != '\t' && c != '\0'
+                   && c != '-' && c != '<' )  c = *++sp;
+            *sp = '\0';
+            while( c == ' ' || c == '\n' || c == '\t' )  c = *++sp;
+            if ( c == '\0' )  {  /*   -o rev or branch   */
+                pt->end = nil;   pt->code = 0;
+                delrev = pt;
+                return;
+            }
+            if ( c != '-' && c != '<') {
+                faterror("Invalid range %s %s after -o", pt->strt, sp);
+                free((char *)pt);
+                return;
+            }
+            while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
+            if ( c == '\0') {  /*  -o   rev-   or   rev<   */
+                pt->end = nil;   pt->code = 2;
+                delrev = pt;
+                return;
+            }
+        }
+        /*   -o   rev1-rev2    or   rev1<rev2   */
+        pt->end = sp;  pt->code = 3;   delrev = pt;
+        while( c!= ' ' && c != '\n' && c != '\t' && c != '\0') c = *++sp;
+        *sp = '\0';
+}
+
+
+
+
+scanlogtext(delta,func)
+struct hshentry * delta; enum stringwork func;
+/* Function: Scans delta text nodes up to and including the one given
+ * by delta, or up to last one present, if delta==nil.
+ * For the one given by delta (if delta!=nil), the log message is saved into
+ * curlogmsg and the text is processed according to parameter func.
+ * Assumes the initial lexeme must be read in first.
+ * Does not advance nexttok after it is finished, except if delta==nil.
+ */
+{       struct hshentry * nextdelta;
+
+        do {
+                rewriteflag = false;
+                nextlex();
+                if (!(nextdelta=getnum())) {
+                    if(delta)
+                        faterror("Can't find delta for revision %s", delta->num);
+                    else return; /* no more delta text nodes */
+                }
+                if ( nextdelta->selector != DELETE) {
+                        rewriteflag = true;
+                        VOID fprintf(frewrite,DELNUMFORM,nextdelta->num,Klog);
+                }
+                if (!getkey(Klog) || nexttok!=STRING)
+                        serror("Missing log entry");
+                elsif (delta==nextdelta) {
+                        VOID savestring(curlogmsg,logsize);
+                        delta->log=curlogmsg;
+                } else {readstring();
+                        if (delta!=nil) delta->log="";
+                }
+                nextlex();
+                if (!getkey(Ktext) || nexttok!=STRING)
+                        fatserror("Missing delta text");
+
+                if(delta==nextdelta)
+                        /* got the one we're looking for */
+                        switch (func) {
+                        case copy:      copystring();
+                                        break;
+                        case edit:      editstring((struct hshentry *)nil);
+                                        break;
+                        default:        faterror("Wrong scanlogtext");
+                        }
+                else    readstring(); /* skip over it */
+
+        } while (delta!=nextdelta);
+}
+
+
+
+releaselst(sourcelst)
+struct  access  * sourcelst;
+/*   Function:  release the storages whose address are in sourcelst   */
+
+{
+        struct  access  * pt;
+
+        pt = sourcelst;
+        while(pt) {
+           struct access *pn = pt->nextaccess;
+            free((char *)pt);
+            pt = pn;
+        }
+}
+
+
+
+struct  Lockrev  * rmnewlocklst(which)
+struct  Lockrev  * which;
+/*   Function:  remove lock to revision which->revno from newlocklst   */
+
+{
+        struct  Lockrev   * pt, *pre;
+
+        while( newlocklst && (! strcmp(newlocklst->revno, which->revno))){
+           struct Lockrev *pn = newlocklst->nextrev;
+            free((char *)newlocklst);
+           newlocklst = pn;
+        }
+
+        pt = pre = newlocklst;
+        while( pt ) {
+            if ( ! strcmp(pt->revno, which->revno) ) {
+                pre->nextrev = pt->nextrev;
+                free((char *)pt);
+               pt = pre->nextrev;
+            }
+            else {
+                pre = pt;
+                pt = pt->nextrev;
+            }
+        }
+        return pre;
+}
+
+
+
+struct  access  * removeaccess( who, sourcelst,flag)
+struct  access  * who, * sourcelst;
+int     flag;
+/*   Function:  remove the accessor-- who from sourcelst     */
+
+{
+        struct  access  *pt, *pre;
+
+        pt = sourcelst;
+        while( pt && (! strcmp(who->login, pt->login) )) {
+           pre = pt->nextaccess;
+            free((char *)pt);
+           pt = pre;
+            flag = false;
+       }
+        pre = sourcelst = pt;
+        while( pt ) {
+            if ( ! strcmp(who->login, pt->login) ) {
+               pre->nextaccess = pt->nextaccess;
+               free((char *)pt);
+               pt = pre->nextaccess;
+                flag = false;
+            }
+            else {
+                pre = pt;
+                pt = pt->nextaccess;
+            }
+        }
+        if ( flag ) warn("Can't remove a nonexisting accessor %s",who->login);
+        return sourcelst;
+}
+
+
+
+int addnewaccess( who )
+struct  access  * who;
+/*   Function:  add new accessor-- who into AccessList    */
+
+{
+        struct  access  *pt,  *pre;
+
+        pre = pt = AccessList;
+
+        while( pt ) {
+            if ( strcmp( who->login, pt->login) ) {
+                pre = pt;
+                pt = pt->nextaccess;
+            }
+            else
+                return 0;
+        }
+        if ( pre == pt )
+            AccessList = who;
+        else
+            pre->nextaccess = who;
+        return 1;
+}
+
+
+sendmail(Delta, who)
+char    * Delta,  *who;
+/*   Function:  mail to who, informing him that his lock on delta was
+ *   broken by caller. Ask first whether to go ahead. Return false on
+ *   error or if user decides not to break the lock.
+ */
+{
+        char    * messagefile;
+        int   old1, old2, c, response;
+        FILE    * mailmess;
+
+
+       VOID fprintf(stderr, "Revision %s is already locked by %s.\n", Delta, who);
+        VOID fprintf(stderr, "Do you want to break the lock? [ny](n): ");
+        response=c=getchar();
+        while (!(c==EOF || c=='\n')) c=getchar();/*skip to end of line*/
+        if (response=='\n'||response=='n'||response=='N') return false;
+
+        /* go ahead with breaking  */
+        messagefile=mktempfile("/tmp/", "RCSmailXXXXXX");
+        if ( (mailmess = fopen(messagefile, "w")) == NULL) {
+            faterror("Can't open file %s", messagefile);
+        }
+
+       VOID fprintf(mailmess, "Subject: Broken lock on %s\n\n",bindex(RCSfilename,'/'));
+        VOID fprintf(mailmess, "Your lock on revision %s of file %s\n",Delta, getfullRCSname());
+        VOID fprintf(mailmess,"has been broken by %s for the following reason:\n",caller);
+        VOID fputs("State the reason for breaking the lock:\n", stderr);
+        VOID fputs("(terminate with ^D or single '.')\n>> ", stderr);
+
+        old1 = '\n';    old2 = ' ';
+        for (; ;) {
+            c = getchar();
+            if ( c == EOF ) {
+                VOID putc('\n',stderr);
+                VOID fprintf(mailmess, "%c\n", old1);
+                break;
+            }
+            else if ( c == '\n' && old1 == '.' && old2 == '\n')
+                break;
+            else {
+                VOID fputc( old1, mailmess);
+                old2 = old1;   old1 = c;
+                if (c== '\n') VOID fputs(">> ", stderr);
+            }
+        }
+        ffclose(mailmess);
+
+       /* ignore the exit status, even if delivermail unsuccessful */
+        VOID run(messagefile,(char*)nil,
+               _PATH_SENDMAIL,
+               who,(char*)nil);
+        VOID unlink(messagefile);
+       return(true);
+}
+
+
+
+static breaklock(who,delta)
+char * who; struct hshentry * delta;
+/* function: Finds the lock held by who on delta,
+ * and removes it.
+ * Sends mail if a lock different from the caller's is broken.
+ * Prints an error message if there is no such lock or error.
+ */
+{
+        register struct lock * next, * trail;
+        char * num;
+        struct lock dummy;
+        int whor, numr;
+
+       num=delta->num;
+        dummy.nextlock=next=Locks;
+        trail = &dummy;
+        while (next!=nil) {
+               if (num != nil)
+                       numr = strcmp(num, next->delta->num);
+
+               whor=strcmp(who,next->login);
+               if (whor==0 && numr==0) break; /* exact match */
+               if (numr==0 && whor !=0) {
+                        if (!sendmail( num, next->login)){
+                            diagnose("%s still locked by %s",num,next->login);
+                           return;
+                        } else break; /* continue after loop */
+                }
+                trail=next;
+                next=next->nextlock;
+        }
+        if (next!=nil) {
+                /*found one */
+                diagnose("%s unlocked",next->delta->num);
+                trail->nextlock=next->nextlock;
+                next->delta->lockedby=nil;
+                Locks=dummy.nextlock;
+        } else  {
+               error("no lock set on revision %s", num);
+        }
+}
+
+
+
+struct hshentry *searchcutpt(object, length, store)
+char    * object;
+int     length;
+struct  hshentry   * * store;
+/*   Function:  Search store and return entry with number being object. */
+/*              cuttail = nil, if the entry is Head; otherwise, cuttail */
+/*              is the entry point to the one with number being object  */
+
+{
+        while( compartial( (*store++)->num, object, length)  )  ;
+        store--;
+
+        if ( *store == Head)
+            cuthead = nil;
+        else
+            cuthead = *(store -1);
+        return *store;
+}
+
+
+
+int  branchpoint(strt, tail)
+struct  hshentry        *strt,  *tail;
+/*   Function: check whether the deltas between strt and tail  */
+/*             are locked or branch point, return 1 if any is  */
+/*             locked or branch point; otherwise, return 0 and */
+/*              mark DELETE on selector                         */
+
+{
+        struct  hshentry    *pt;
+       struct lock  *lockpt;
+        int     flag;
+
+
+        pt = strt;
+        flag = false;
+        while( pt != tail) {
+            if ( pt->branches ){ /*  a branch point  */
+                flag = true;
+                error("Can't remove branch point %s", pt->num);
+            }
+           lockpt = Locks;
+           while(lockpt && lockpt->delta != pt)
+               lockpt = lockpt->nextlock;
+           if ( lockpt ) {
+               flag = true;
+               error("Can't remove locked revision %s",pt->num);
+           }
+            pt = pt->next;
+        }
+
+        if ( ! flag ) {
+            pt = strt;
+            while( pt != tail ) {
+                pt->selector = DELETE;
+                diagnose("deleting revision %s ",pt->num);
+                pt = pt->next;
+            }
+        }
+        return flag;
+}
+
+
+
+removerevs()
+/*   Function:  get the revision range to be removed, and place the     */
+/*              first revision removed in delstrt, the revision before  */
+/*              delstrt in cuthead( nil, if delstrt is head), and the   */
+/*              revision after the last removed revision in cuttail(nil */
+/*              if the last is a leaf                                   */
+
+{
+        struct  hshentry    *target, *target2, * temp, *searchcutpt();
+        int     length, flag;
+
+        flag = false;
+        if ( ! expandsym(delrev->strt, &numrev[0]) ) return 0;
+        target = genrevs(&numrev[0], (char *)nil, (char *)nil, (char *)nil, gendeltas);
+        if ( ! target ) return 0;
+        if ( cmpnum(target->num, &numrev[0]) ) flag = true;
+        length = countnumflds( &numrev[0] );
+
+        if ( delrev->code == 0 ) {  /*  -o  rev    or    -o  branch   */
+           if ( length % 2)
+               temp=searchcutpt(target->num,length+1,gendeltas);
+           else if (flag) {
+                error("Revision %s does not exist", &numrev[0]);
+               return 0;
+           }
+           else
+               temp = searchcutpt(&numrev[0],length,gendeltas);
+           cuttail = target->next;
+            if ( branchpoint(temp, cuttail) ) {
+                cuttail = nil;
+                return 0;
+            }
+            delstrt = temp;     /* first revision to be removed   */
+            return 1;
+        }
+
+        if ( length % 2 ) {   /*  invalid branch after -o   */
+            error("Invalid branch range %s after -o", &numrev[0]);
+            return 0;
+        }
+
+        if ( delrev->code == 1 )  {  /*  -o  -rev   */
+            if ( length > 2 ) {
+                temp = searchcutpt( target->num, length-1, gendeltas);
+                cuttail = target->next;
+            }
+            else {
+                temp = searchcutpt(target->num, length, gendeltas);
+                cuttail = target;
+                while( cuttail && ! cmpnumfld(target->num,cuttail->num,1) )
+                    cuttail = cuttail->next;
+            }
+            if ( branchpoint(temp, cuttail) ){
+                cuttail = nil;
+                return 0;
+            }
+            delstrt = temp;
+            return 1;
+        }
+
+        if ( delrev->code == 2 )  {   /*  -o  rev-   */
+            if ( length == 2 ) {
+                temp = searchcutpt(target->num, 1,gendeltas);
+                if ( flag)
+                    cuttail = target;
+                else
+                    cuttail = target->next;
+            }
+            else  {
+                if ( flag){
+                    cuthead = target;
+                    if ( !(temp = target->next) ) return 0;
+                }
+                else
+                    temp = searchcutpt(target->num, length, gendeltas);
+                getbranchno(temp->num, &numrev[0]);  /*  get branch number  */
+                target = genrevs(&numrev[0], (char *)nil, (char *)nil, (char *)nil, gendeltas);
+            }
+            if ( branchpoint( temp, cuttail ) ) {
+                cuttail = nil;
+                return 0;
+            }
+            delstrt = temp;
+            return 1;
+        }
+
+        /*   -o   rev1-rev2   */
+        if ( ! expandsym(delrev->end, &numrev[0])  )  return 0;
+        if ( length != countnumflds( &numrev[0] ) ) {
+            error("Invalid revision range %s-%s", target->num, &numrev[0]);
+            return 0;
+        }
+        if ( length > 2 && compartial( &numrev[0], target->num, length-1) ) {
+            error("Invalid revision range %s-%s", target->num, &numrev[0]);
+            return 0;
+        }
+
+        target2 = genrevs( &numrev[0], (char *)nil, (char *)nil, (char *)nil,gendeltas);
+        if ( ! target2 ) return 0;
+
+        if ( length > 2) {  /* delete revisions on branches  */
+            if ( cmpnum(target->num, target2->num) > 0) {
+                if ( cmpnum(target2->num, &numrev[0]) )
+                    flag = true;
+                else
+                    flag = false;
+                temp = target;
+                target = target2;
+                target2 = temp;
+            }
+            if ( flag ) {
+                if ( ! cmpnum(target->num, target2->num) ) {
+                    error("Revisions %s-%s don't exist", delrev->strt,delrev->end);
+                    return 0;
+                }
+                cuthead = target;
+                temp = target->next;
+            }
+            else
+                temp = searchcutpt(target->num, length, gendeltas);
+            cuttail = target2->next;
+        }
+        else { /*  delete revisions on trunk  */
+            if ( cmpnum( target->num, target2->num) < 0 ) {
+                temp = target;
+                target = target2;
+                target2 = temp;
+            }
+            else
+                if ( cmpnum(target2->num, &numrev[0]) )
+                    flag = true;
+                else
+                    flag = false;
+            if ( flag ) {
+                if ( ! cmpnum(target->num, target2->num) ) {
+                    error("Revisions %s-%s don't exist", delrev->strt, delrev->end);
+                    return 0;
+                }
+                cuttail = target2;
+            }
+            else
+                cuttail = target2->next;
+            temp = searchcutpt(target->num, length, gendeltas);
+        }
+        if ( branchpoint(temp, cuttail) )  {
+            cuttail = nil;
+            return 0;
+        }
+        delstrt = temp;
+        return 1;
+}
+
+
+
+updateassoc()
+/*   Function: add or delete(if revno is nil) association      */
+/*             which is stored in assoclst                     */
+
+{
+        struct  Symrev  * curassoc;
+       struct  assoc   * pre,  * pt;
+        struct  hshentry    * target;
+
+        /*  add new associations   */
+        curassoc = assoclst;
+        while( curassoc ) {
+            if ( curassoc->revno == nil ) {  /* delete symbol  */
+               pre = pt = Symbols;
+                while( pt && strcmp(pt->symbol,curassoc->ssymbol) ) {
+                   pre = pt;
+                   pt = pt->nextassoc;
+               }
+               if ( pt )
+                   if ( pre == pt )
+                       Symbols = pt->nextassoc;
+                   else
+                       pre->nextassoc = pt->nextassoc;
+               else
+                    warn("Can't delete nonexisting symbol %s",curassoc->ssymbol);
+           }
+            else if ( expandsym( curassoc->revno, &numrev[0] ) ) {
+           /*   add symbol  */
+               target = (struct hshentry *) talloc(sizeof(struct hshentry));
+               target->num = &numrev[0];
+               VOID addsymbol(target, curassoc->ssymbol, curassoc->override);
+            }
+            curassoc = curassoc->nextsym;
+        }
+
+}
+
+
+
+updatelocks()
+/* Function: remove lock for caller or first lock if unlockcaller==true;
+ *           remove locks which are stored in rmvlocklst,
+ *           add new locks which are stored in newlocklst,
+ *           add lock for Dbranch or Head if lockhead==true.
+ */
+{
+        struct  hshentry        *target;
+        struct  Lockrev         *lockpt;
+
+        if(unlockcaller == true) { /*  find lock for caller  */
+            if ( Head ) {
+               if (Locks) {
+                   target=findlock(caller,true);
+                   if (target==nil) {
+                       breaklock(caller, Locks->delta); /* remove most recent lock */
+                   } else {
+                       diagnose("%s unlocked",target->num);
+                   }
+               } else {
+                   warn("There are no locks set.");
+               }
+            } else {
+                warn("Can't unlock an empty tree");
+            }
+        }
+
+        /*  remove locks which are stored in rmvlocklst   */
+        lockpt = rmvlocklst;
+        while( lockpt ) {
+           if (expandsym(lockpt->revno, numrev)) {
+               target = genrevs(numrev, (char *)nil, (char *)nil, (char *)nil, gendeltas);
+                if ( target )
+                  if ( !(countnumflds(numrev)%2) && cmpnum(target->num,numrev))
+                       error("Can't unlock nonexisting revision %s",lockpt->revno);
+                   else
+                        breaklock(caller, target);
+                        /* breaklock does its own diagnose */
+            }
+            lockpt = lockpt->nextrev;
+        }
+
+        /*  add new locks which stored in newlocklst  */
+        lockpt = newlocklst;
+        while( lockpt ) {
+            setlock(lockpt->revno,caller);
+            lockpt = lockpt->nextrev;
+        }
+
+        if ( lockhead == true) {  /*  lock default branch or head  */
+            if (Dbranch) {
+                setlock(Dbranch->num,caller);
+            } elsif ( Head) {
+                if (addlock(Head, caller))
+                    diagnose("%s locked",Head->num);
+            } else {
+                warn("Can't lock an empty tree");
+            }
+        }
+
+}
+
+
+
+setlock(rev,who)
+char * rev, * who;
+/* Function: Given a revision or branch number, finds the correponding
+ * delta and locks it for who.
+ */
+{
+        struct  lock     *lpt;
+        struct  hshentry *target;
+
+        if (expandsym(rev, &numrev[0]) ){
+            target = genrevs(&numrev[0],(char *) nil,(char *) nil,
+                            (char *)nil, gendeltas);
+            if ( target )
+               if ( !(countnumflds(&numrev[0])%2) && cmpnum(target->num,&numrev[0]))
+                    error("Can't lock nonexisting revision %s",numrev);
+               else
+                    if(lpt=addlock(target, who))
+                        diagnose("%s locked",lpt->delta->num);
+        }
+}
+
+
+
+rcs_setstate(rev,status)
+char * rev, * status;
+/* Function: Given a revision or branch number, finds the corresponding delta
+ * and sets its state to status.
+ */
+{
+        struct  hshentry *target;
+
+        if ( expandsym(rev, &numrev[0]) ) {
+            target = genrevs(&numrev[0],(char *) nil, (char *)nil,
+                            (char *) nil, gendeltas);
+            if ( target )
+               if ( !(countnumflds(&numrev[0])%2) && cmpnum(target->num, &numrev[0]) )
+                    error("Can't set state of nonexisting revision %s to %s",
+                           numrev,status);
+               else
+                    target->state = status;
+        }
+}
+
+
+
+
+
+buildeltatext(deltas)
+struct  hshentry        ** deltas;
+/*   Function:  put the delta text on frewrite and make necessary   */
+/*              change to delta text                                */
+{
+        int  i, c, exit_stats;
+
+        cuttail->selector = DELETE;
+        initeditfiles("/tmp/");
+        scanlogtext(deltas[0], copy);
+        i = 1;
+        if ( cuthead )  {
+            cutfilename=mktempfile("/tmp/", "RCScutXXXXXX");
+            if ( (fcut = fopen(cutfilename, "w")) == NULL) {
+                faterror("Can't open temporary file %s", cutfilename);
+            }
+
+            while( deltas[i-1] != cuthead )  {
+                scanlogtext(deltas[i++], edit);
+            }
+
+            finishedit((struct hshentry *)nil);    rewind(fcopy);
+            while( (c = getc(fcopy)) != EOF) VOID putc(c, fcut);
+            swapeditfiles(false);
+            ffclose(fcut);
+        }
+
+        while( deltas[i-1] != cuttail)
+            scanlogtext(deltas[i++], edit);
+        finishedit((struct hshentry *)nil);    ffclose(fcopy);
+
+        if ( cuthead ) {
+            diffilename=mktempfile("/tmp/", "RCSdifXXXXXX");
+            exit_stats = run((char*)nil,diffilename,
+                       DIFF,"-n",cutfilename,resultfile,(char*)nil);
+            if (exit_stats != 0 && exit_stats != (1 << BYTESIZ))
+                faterror ("diff failed");
+            if(!putdtext(cuttail->num,curlogmsg,diffilename,frewrite)) return;
+        }
+        else
+            if (!putdtext(cuttail->num,curlogmsg,resultfile,frewrite)) return;
+
+        scanlogtext((struct hshentry *)nil,empty); /* read the rest of the deltas */
+}
+
+
+
+buildtree()
+/*   Function:  actually removes revisions whose selector field  */
+/*              is DELETE, and rebuilds  the linkage of deltas.  */
+/*              asks for reconfirmation if deleting last revision*/
+{
+       int c,  response;
+
+       struct  hshentry   * Delta;
+        struct  branchhead      *pt, *pre;
+
+        if ( cuthead )
+           if ( cuthead->next == delstrt )
+                cuthead->next = cuttail;
+           else {
+                pre = pt = cuthead->branches;
+                while( pt && pt->hsh != delstrt )  {
+                    pre = pt;
+                    pt = pt->nextbranch;
+                }
+                if ( cuttail )
+                    pt->hsh = cuttail;
+                else if ( pt == pre )
+                    cuthead->branches = pt->nextbranch;
+                else
+                    pre->nextbranch = pt->nextbranch;
+            }
+       else {
+            if ( cuttail == nil && !quietflag) {
+                VOID fprintf(stderr,"Do you really want to delete all revisions ?[ny](n): ");
+               c = response = getchar();
+               while( c != EOF && c != '\n') c = getchar();
+                if ( response != 'y' && response != 'Y') {
+                    diagnose("No revision deleted");
+                   Delta = delstrt;
+                   while( Delta) {
+                       Delta->selector = 'S';
+                       Delta = Delta->next;
+                   }
+                   return;
+               }
+           }
+            Head = cuttail;
+       }
+        return;
+}
+