head	5.12;
branch	5.12.1;
access;
symbols
	HWGRCSP7F:5.12.1.3
	HWGRCSP6F:5.12.1.3
	HWGRCSP5F:5.12.1.3
	HWGRCSp4:5.12.1.3
	HWGRCSp3:5.12.1
	HWGRCS_Fish:5.12.1
	HWGRCS:5.12.1;
locks; strict;
comment	@ * @;


5.12
date	91.11.20.17.58.08;	author eggert;	state Exp;
branches
	5.12.1.1;
next	;

5.12.1.1
date	93.01.18.14.37.52;	author heinz;	state Exp;
branches;
next	5.12.1.2;

5.12.1.2
date	93.03.20.21.18.12;	author heinz;	state Exp;
branches;
next	5.12.1.3;

5.12.1.3
date	93.03.22.19.13.42;	author heinz;	state Exp;
branches;
next	5.12.1.4;

5.12.1.4
date	94.02.14.19.29.45;	author heinz;	state Exp;
branches;
next	;


desc
@Checked in with -k 16.jan.93 HWG
@


5.12
log
@checked in with -k by heinz at 1993/01/17 01:53:17
@
text
@/*
 *                      RCS create/change operation
 */
/* Copyright (C) 1982, 1988, 1989 Walter Tichy
   Copyright 1990, 1991 by Paul Eggert
   Distributed under license by the Free Software Foundation, Inc.

This file is part of RCS.

RCS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

RCS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with RCS; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.

Report problems and direct all questions to:

    rcs-bugs@@cs.purdue.edu

*/




/* $Log: rcs.c,v $
 * Revision 5.12  1991/11/20  17:58:08  eggert
 * Don't read the delta tree from a nonexistent RCS file.
 *
 * Revision 5.11  1991/10/07  17:32:46  eggert
 * Remove lint.
 *
 * Revision 5.10  1991/08/19  23:17:54  eggert
 * Add -m, -r$, piece tables.  Revision separator is `:', not `-'.  Tune.
 *
 * Revision 5.9  1991/04/21  11:58:18  eggert
 * Add -x, RCSINIT, MS-DOS support.
 *
 * Revision 5.8  1991/02/25  07:12:38  eggert
 * strsave -> str_save (DG/UX name clash)
 * 0444 -> S_IRUSR|S_IRGRP|S_IROTH for portability
 *
 * Revision 5.7  1990/12/18  17:19:21  eggert
 * Fix bug with multiple -n and -N options.
 *
 * Revision 5.6  1990/12/04  05:18:40  eggert
 * Use -I for prompts and -q for diagnostics.
 *
 * Revision 5.5  1990/11/11  00:06:35  eggert
 * Fix `rcs -e' core dump.
 *
 * Revision 5.4  1990/11/01  05:03:33  eggert
 * Add -I and new -t behavior.  Permit arbitrary data in logs.
 *
 * Revision 5.3  1990/10/04  06:30:16  eggert
 * Accumulate exit status across files.
 *
 * Revision 5.2  1990/09/04  08:02:17  eggert
 * Standardize yes-or-no procedure.
 *
 * Revision 5.1  1990/08/29  07:13:51  eggert
 * Remove unused setuid support.  Clean old log messages too.
 *
 * Revision 5.0  1990/08/22  08:12:42  eggert
 * Don't lose names when applying -a option to multiple files.
 * Remove compile-time limits; use malloc instead.  Add setuid support.
 * Permit dates past 1999/12/31.  Make lock and temp files faster and safer.
 * Ansify and Posixate.  Add -V.  Fix umask bug.  Make linting easier.  Tune.
 * Yield proper exit status.  Check diff's output.
 *
 * 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/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 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 "rcsbase.h"

struct  Lockrev {
	char const *revno;
        struct  Lockrev   * nextrev;
};

struct  Symrev {
	char const *revno;
	char const *ssymbol;
        int     override;
        struct  Symrev  * nextsym;
};

struct Message {
	char const *revno;
	struct cbuf message;
	struct Message *nextmessage;
};

struct  Status {
	char const *revno;
	char const *status;
        struct  Status  * nextstatus;
};

enum changeaccess {append, erase};
struct chaccess {
	char const *login;
	enum changeaccess command;
	struct chaccess *nextchaccess;
};

struct delrevpair {
	char const *strt;
	char const *end;
        int     code;
};

static int buildeltatext P((struct hshentries const*));
static int removerevs P((void));
static int sendmail P((char const*,char const*));
static struct Lockrev *rmnewlocklst P((struct Lockrev const*));
static void breaklock P((struct hshentry const*));
static void buildtree P((void));
static void cleanup P((void));
static void doaccess P((void));
static void doassoc P((void));
static void dolocks P((void));
static void domessages P((void));
static void getaccessor P((char*,enum changeaccess));
static void getassoclst P((int,char*));
static void getchaccess P((char const*,enum changeaccess));
static void getdelrev P((char*));
static void getmessage P((char*));
static void getstates P((char*));
static void rcs_setstate P((char const*,char const*));
static void scanlogtext P((struct hshentry*,int));
static void setlock P((char const*));

static struct buf numrev;
static char const *headstate;
static int chgheadstate, exitstatus, lockhead, unlockcaller;
static struct Lockrev *newlocklst, *rmvlocklst;
static struct Message *messagelst, *lastmessage;
static struct Status *statelst, *laststate;
static struct Symrev *assoclst, *lastassoc;
static struct chaccess *chaccess, **nextchaccess;
static struct delrevpair delrev;
static struct hshentry *cuthead, *cuttail, *delstrt;
static struct hshentries *gendeltas;

mainProg(rcsId, "rcs", "$Id: rcs.c,v 5.12 1991/11/20 17:58:08 eggert Exp $")
{
	static char const cmdusage[] =
		"\nrcs usage: rcs -{ae}logins -Afile -{blu}[rev] -cstring -{iLU} -{nNs}name[:rev] -orange -t[file] -Vn file ...";

	char *a, **newargv, *textfile;
	char const *branchsym, *commsyml;
	int branchflag, expmode, initflag;
	int e, r, strictlock, strict_selected, textflag;
	mode_t defaultRCSmode;	/* default mode for new RCS files */
	mode_t RCSmode;
	struct buf branchnum;
	struct stat workstat;
        struct  Lockrev *curlock,  * rmvlock, *lockpt;
        struct  Status  * curstate;

	nosetid();

	nextchaccess = &chaccess;
	branchsym = commsyml = textfile = nil;
	branchflag = strictlock = false;
	bufautobegin(&branchnum);
	curlock = rmvlock = nil;
	defaultRCSmode = 0;
	expmode = -1;
	suffixes = X_DEFAULT;
        initflag= textflag = false;
        strict_selected = 0;

        /*  preprocessing command options    */
	argc = getRCSINIT(argc, argv, &newargv);
	argv = newargv;
	while (a = *++argv,  0<--argc && *a++=='-') {
		switch (*a++) {

		case 'i':   /*  initial version  */
                        initflag = true;
                        break;

                case 'b':  /* change default branch */
			if (branchflag) redefined('b');
                        branchflag= true;
			branchsym = a;
                        break;

                case 'c':   /*  change comment symbol   */
			if (commsyml) redefined('c');
			commsyml = a;
                        break;

                case 'a':  /*  add new accessor   */
			getaccessor(*argv+1, append);
                        break;

                case 'A':  /*  append access list according to accessfile  */
			if (!*a) {
			    error("missing file name after -A");
                            break;
                        }
			*argv = a;
			if (0 < pairfilenames(1,argv,rcsreadopen,true,false)) {
			    while (AccessList) {
				getchaccess(str_save(AccessList->login),append);
				AccessList = AccessList->nextaccess;
			    }
			    Izclose(&finptr);
                        }
                        break;

                case 'e':    /*  remove accessors   */
			getaccessor(*argv+1, erase);
                        break;

                case 'l':    /*   lock a revision if it is unlocked   */
			if (!*a) {
			    /* Lock head or default branch.  */
                            lockhead = true;
                            break;
                        }
			lockpt = talloc(struct Lockrev);
			lockpt->revno = a;
                        lockpt->nextrev = nil;
                        if ( curlock )
                            curlock->nextrev = lockpt;
                        else
                            newlocklst = lockpt;
                        curlock = lockpt;
                        break;

                case 'u':   /*  release lock of a locked revision   */
			if (!*a) {
                            unlockcaller=true;
                            break;
                        }
			lockpt = talloc(struct Lockrev);
			lockpt->revno = a;
                        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("-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("-L overrides -U.");
                        }
			else
			    strictlock = false;
                        break;

                case 'n':    /*  add new association: error, if name exists */
			if (!*a) {
			    error("missing symbolic name after -n");
                            break;
                        }
                        getassoclst(false, (*argv)+1);
                        break;

                case 'N':   /*  add or change association   */
			if (!*a) {
			    error("missing symbolic name after -N");
                            break;
                        }
                        getassoclst(true, (*argv)+1);
                        break;

		case 'm':   /*  change log message  */
			getmessage(a);
			break;

		case 'o':   /*  delete revisions  */
			if (delrev.strt) redefined('o');
			if (!*a) {
			    error("missing revision range after -o");
                            break;
                        }
                        getdelrev( (*argv)+1 );
                        break;

                case 's':   /*  change state attribute of a revision  */
			if (!*a) {
			    error("state missing after -s");
                            break;
                        }
                        getstates( (*argv)+1);
                        break;

                case 't':   /*  change descriptive text   */
                        textflag=true;
			if (*a) {
				if (textfile) redefined('t');
				textfile = a;
                        }
                        break;

		case 'I':
			interactiveflag = true;
			break;

                case 'q':
                        quietflag = true;
                        break;

		case 'x':
			suffixes = a;
			break;

		case 'V':
			setRCSversion(*argv);
			break;

		case 'k':    /*  set keyword expand mode  */
			if (0 <= expmode) redefined('k');
			if (0 <= (expmode = str2expmode(a)))
			    break;
			/* fall into */
                default:
			faterror("unknown option: %s%s", *argv, cmdusage);
                };
        }  /* end processing of options */

	if (argc<1) faterror("no input file%s", cmdusage);
        if (nerror) {
	    diagnose("%s aborted\n",cmdid);
	    exitmain(EXIT_FAILURE);
        }
	if (initflag) {
	    defaultRCSmode = umask((mode_t)0);
	    VOID umask(defaultRCSmode);
	    defaultRCSmode = (S_IRUSR|S_IRGRP|S_IROTH) & ~defaultRCSmode;
	}

        /* now handle all filenames */
        do {
	ffree();

        if ( initflag ) {
	    switch (pairfilenames(argc, argv, rcswriteopen, false, false)) {
                case -1: break;        /*  not exist; ok */
                case  0: continue;     /*  error         */
                case  1: error("file %s exists already", RCSfilename);
                         continue;
            }
	}
        else  {
	    switch (pairfilenames(argc, argv, rcswriteopen, 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\n", RCSfilename);

	RCSmode = defaultRCSmode;
	if (initflag) {
		if (stat(workfilename, &workstat) == 0)
			RCSmode = workstat.st_mode;
	} else {
		if (!checkaccesslist()) continue;
		gettree(); /* Read the delta tree.  */
		RCSmode = RCSstat.st_mode;
	}
	RCSmode &= ~(S_IWUSR|S_IWGRP|S_IWOTH);

        /*  update admin. node    */
        if (strict_selected) StrictLocks = strictlock;
	if (commsyml) {
		Comment.string = commsyml;
		Comment.size = strlen(commsyml);
	}
	if (0 <= expmode) Expand = expmode;

        /* update default branch */
	if (branchflag && expandsym(branchsym, &branchnum)) {
	    if (countnumflds(branchnum.string)) {
		Dbranch = branchnum.string;
            } else
                Dbranch = nil;
        }

	doaccess();	/* Update access list.  */

	doassoc();	/* Update association list.  */

	dolocks();	/* Update locks.  */

	domessages();	/* Update log messages.  */

        /*  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,headstate); /* Can't set directly */
            }
        }
        curstate = statelst;
        while( curstate ) {
            rcs_setstate(curstate->revno,curstate->status);
            curstate = curstate->nextstatus;
        }

        cuthead = cuttail = nil;
	if (delrev.strt && removerevs()) {
            /*  rebuild delta tree if some deltas are deleted   */
            if ( cuttail )
		VOID genrevs(cuttail->num, (char *)nil,(char *)nil,
			     (char *)nil, &gendeltas);
            buildtree();
        }

	if (nerror)
		continue;

        putadmin(frewrite);
        if ( Head )
           puttree(Head, frewrite);
	putdesc(textflag,textfile);

        if ( Head) {
	    if (!delrev.strt && !messagelst) {
		/* No revision was deleted and no message was changed.  */
		fastcopy(finptr, frewrite);
            } else {
		if (!cuttail || buildeltatext(gendeltas)) {
		    advise_access(finptr, MADV_SEQUENTIAL);
		    scanlogtext((struct hshentry *)nil, false);
                    /* copy rest of delta text nodes that are not deleted      */
		}
            }
        }
	Izclose(&finptr);
        if ( ! nerror ) {  /*  move temporary file to RCS file if no error */
	    /* update mode */
	    ignoreints();
	    r = chnamemod(&frewrite, newRCSfilename, RCSfilename, RCSmode);
	    e = errno;
	    keepdirtemp(newRCSfilename);
	    restoreints();
	    if (r != 0) {
		enerror(e, RCSfilename);
		error("saved in %s", newRCSfilename);
		dirtempunlink();
                break;
            }
	    diagnose("done\n");
        } else {
	    diagnose("%s aborted; %s unchanged.\n",cmdid,RCSfilename);
        }
	} while (cleanup(),
                 ++argv, --argc >=1);

	tempunlink();
	exitmain(exitstatus);
}       /* end of main (rcs) */

	static void
cleanup()
{
	if (nerror) exitstatus = EXIT_FAILURE;
	Izclose(&finptr);
	Ozclose(&fcopy);
	Ozclose(&frewrite);
	dirtempunlink();
}

	exiting void
exiterr()
{
	dirtempunlink();
	tempunlink();
	_exit(EXIT_FAILURE);
}


	static void
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 const *temp;
        int                c;

        while( (c=(*++sp)) == ' ' || c == '\t' || c =='\n')  ;
        temp = sp;
	sp = checkid(sp, ':');  /*  check for invalid symbolic name  */
	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 = talloc(struct Symrev);
        pt->ssymbol = temp;
        pt->override = flag;
	if (c == '\0')  /*  delete symbol  */
            pt->revno = nil;
        else {
            while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
	    pt->revno = sp;
        }
        pt->nextsym = nil;
        if (lastassoc)
            lastassoc->nextsym = pt;
        else
            assoclst = pt;
        lastassoc = pt;
        return;
}


	static void
getchaccess(login, command)
	char const *login;
	enum changeaccess command;
{
	register struct chaccess *pt;

	*nextchaccess = pt = talloc(struct chaccess);
	pt->login = login;
	pt->command = command;
	pt->nextchaccess = nil;
	nextchaccess = &pt->nextchaccess;
}



	static void
getaccessor(opt, command)
	char *opt;
	enum changeaccess command;
/*   Function:  get the accessor list of options -e and -a,     */
/*		and store in chaccess				*/


{
        register c;
	register char *sp;

	sp = opt;
        while( ( c = *++sp) == ' ' || c == '\n' || c == '\t' || c == ',') ;
        if ( c == '\0') {
	    if (command == erase  &&  sp-opt == 1) {
		getchaccess((char const*)nil, command);
		return;
	    }
	    error("missing login name after option -a or -e");
	    return;
        }

        while( c != '\0') {
		getchaccess(sp, command);
		sp = checkid(sp,',');
		c = *sp;   *sp = '\0';
                while( c == ' ' || c == '\n' || c == '\t'|| c == ',')c =(*++sp);
        }
}


	static void
getmessage(option)
	char *option;
{
	struct Message *pt;
	struct cbuf cb;
	char *m;

	if (!(m = strchr(option, ':'))) {
		error("-m option lacks revision number");
		return;
	}
	*m++ = 0;
	cb = cleanlogmsg(m, strlen(m));
	if (!cb.size) {
		error("-m option lacks log message");
		return;
	}
	pt = talloc(struct Message);
	pt->revno = option;
	pt->message = cb;
	pt->nextmessage = 0;
	if (lastmessage)
		lastmessage->nextmessage = pt;
	else
		messagelst = pt;
	lastmessage = pt;
}


	static void
getstates(sp)
char    *sp;
/*   Function:  get one state attribute and the corresponding   */
/*              revision and store in statelst                  */

{
	char const *temp;
        struct  Status  *pt;
        register        c;

        while( (c=(*++sp)) ==' ' || c == '\t' || c == '\n')  ;
        temp = sp;
	sp = checkid(sp,':');  /* check for invalid state attribute */
	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 = talloc(struct Status);
        pt->status     = temp;
        pt->revno      = sp;
        pt->nextstatus = nil;
        if (laststate)
            laststate->nextstatus = pt;
        else
            statelst = pt;
        laststate = pt;
}



	static void
getdelrev(sp)
char    *sp;
/*   Function:  get revision range or branch to be deleted,     */
/*              and place in delrev                             */
{
        int    c;
        struct  delrevpair      *pt;
	int separator;

	pt = &delrev;
        while((c = (*++sp)) == ' ' || c == '\n' || c == '\t') ;

	/* Support old ambiguous '-' syntax; this will go away.  */
	if (strchr(sp,':'))
		separator = ':';
	else {
		if (strchr(sp,'-')  &&  VERSION(5) <= RCSversion)
		    warn("`-' is obsolete in `-o%s'; use `:' instead", sp);
		separator = '-';
	}

	if (c == separator) { /* -o: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;
            return;
        }
        else {
            pt->strt = sp;
            while( c != ' ' && c != '\n' && c != '\t' && c != '\0'
		   && c != separator )  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;
                return;
            }
	    if (c != separator) {
		faterror("invalid range %s %s after -o", pt->strt, sp);
            }
            while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
	    if (!c) {  /* -orev: */
                pt->end = nil;   pt->code = 2;
                return;
            }
        }
	/* -orev1:rev2 */
	pt->end = sp;  pt->code = 3;
        while( c!= ' ' && c != '\n' && c != '\t' && c != '\0') c = *++sp;
        *sp = '\0';
}




	static void
scanlogtext(delta,edit)
	struct hshentry *delta;
	int edit;
/* 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
 * delta->log if delta==cuttail; the text is edited if EDIT is set, else copied.
 * Assumes the initial lexeme must be read in first.
 * Does not advance nexttok after it is finished, except if delta==nil.
 */
{
	struct hshentry const *nextdelta;
	struct cbuf cb;

	for (;;) {
		foutptr = 0;
		if (eoflex()) {
                    if(delta)
			faterror("can't find delta for revision %s", delta->num);
		    return; /* no more delta text nodes */
                }
		nextlex();
		if (!(nextdelta=getnum()))
		    faterror("delta number corrupted");
		if (nextdelta->selector) {
			foutptr = frewrite;
			aprintf(frewrite,DELNUMFORM,nextdelta->num,Klog);
                }
		getkeystring(Klog);
		if (nextdelta == cuttail) {
			cb = savestring(&curlogbuf);
			if (!delta->log.string)
			    delta->log = cleanlogmsg(curlogbuf.string, cb.size);
		} else if (nextdelta->log.string && nextdelta->selector) {
			foutptr = 0;
			readstring();
			foutptr = frewrite;
			putstring(foutptr, false, nextdelta->log, true);
			afputc(nextc, foutptr);
                } else {readstring();
                }
                nextlex();
		while (nexttok==ID && strcmp(NextString,Ktext)!=0)
			ignorephrase();
		getkeystring(Ktext);

		if (delta==nextdelta)
			break;
		readstring(); /* skip over it */

	}
	/* got the one we're looking for */
	if (edit)
		editstring((struct hshentry *)nil);
	else
		enterstring();
}



	static struct Lockrev *
rmnewlocklst(which)
	struct Lockrev const *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;
	    tfree(newlocklst);
	    newlocklst = pn;
        }

        pt = pre = newlocklst;
        while( pt ) {
            if ( ! strcmp(pt->revno, which->revno) ) {
                pre->nextrev = pt->nextrev;
		tfree(pt);
		pt = pre->nextrev;
            }
            else {
                pre = pt;
                pt = pt->nextrev;
            }
        }
        return pre;
}



	static void
doaccess()
{
	register struct chaccess *ch;
	register struct access **p, *t;

	for (ch = chaccess;  ch;  ch = ch->nextchaccess) {
		switch (ch->command) {
		case erase:
			if (!ch->login)
			    AccessList = nil;
			else
			    for (p = &AccessList;  (t = *p);  )
				if (strcmp(ch->login, t->login) == 0)
					*p = t->nextaccess;
				else
					p = &t->nextaccess;
			break;
		case append:
			for (p = &AccessList;  ;  p = &t->nextaccess)
				if (!(t = *p)) {
					*p = t = ftalloc(struct access);
					t->login = ch->login;
					t->nextaccess = nil;
					break;
				} else if (strcmp(ch->login, t->login) == 0)
					break;
			break;
		}
	}
}


	static int
sendmail(Delta, who)
	char const *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.
 */
{
#ifdef SENDMAIL
	char const *messagefile;
	int old1, old2, c;
        FILE    * mailmess;
#endif


	aprintf(stderr, "Revision %s is already locked by %s.\n", Delta, who);
	if (!yesorno(false, "Do you want to break the lock? [ny](n): "))
		return false;

        /* go ahead with breaking  */
#ifdef SENDMAIL
	messagefile = maketemp(0);
	if (!(mailmess = fopen(messagefile, "w"))) {
	    efaterror(messagefile);
        }

	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",
		basename(RCSfilename), Delta, getfullRCSname(), getcaller()
	);
	aputs("State the reason for breaking the lock:\n(terminate with single '.' or end of file)\n>> ", stderr);

        old1 = '\n';    old2 = ' ';
        for (; ;) {
	    c = getcstdin();
	    if (feof(stdin)) {
		aprintf(mailmess, "%c\n", old1);
                break;
            }
            else if ( c == '\n' && old1 == '.' && old2 == '\n')
                break;
            else {
		afputc(old1, mailmess);
                old2 = old1;   old1 = c;
		if (c=='\n') aputs(">> ", stderr);
            }
        }
	Ozclose(&mailmess);

	if (run(messagefile, (char*)nil, SENDMAIL, who, (char*)nil))
		warn("Mail may have failed."),
#else
		warn("Mail notification of broken locks is not available."),
#endif
		warn("Please tell `%s' why you broke the lock.", who);
	return(true);
}



	static void
breaklock(delta)
	struct hshentry const *delta;
/* function: Finds the lock held by caller 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 const *num;
        struct lock dummy;

	num=delta->num;
        dummy.nextlock=next=Locks;
        trail = &dummy;
        while (next!=nil) {
		if (strcmp(num, next->delta->num) == 0) {
			if (
				strcmp(getcaller(),next->login) != 0
			    &&	!sendmail(num, next->login)
			) {
			    error("%s still locked by %s", num, next->login);
			    return;
			}
			break; /* exact match */
                }
                trail=next;
                next=next->nextlock;
        }
        if (next!=nil) {
                /*found one */
		diagnose("%s unlocked\n",next->delta->num);
                trail->nextlock=next->nextlock;
                next->delta->lockedby=nil;
                Locks=dummy.nextlock;
        } else  {
		error("no lock set on revision %s", num);
        }
}



	static struct hshentry *
searchcutpt(object, length, store)
	char const *object;
	unsigned length;
	struct hshentries *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  */

{
	cuthead = nil;
	while (compartial(store->first->num, object, length)) {
		cuthead = store->first;
		store = store->rest;
	}
	return store->first;
}



	static 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 deleted					*/

{
        struct  hshentry    *pt;
	struct lock const *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 = false;
		diagnose("deleting revision %s\n",pt->num);
                pt = pt->next;
            }
        }
        return flag;
}



	static int
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;
	unsigned length;
	int flag;

        flag = false;
	if (!expandsym(delrev.strt, &numrev)) return 0;
	target = genrevs(numrev.string, (char*)nil, (char*)nil, (char*)nil, &gendeltas);
        if ( ! target ) return 0;
	if (cmpnum(target->num, numrev.string)) flag = true;
	length = countnumflds(numrev.string);

	if (delrev.code == 0) {  /*  -o  rev    or    -o  branch   */
	    if (length & 1)
		temp=searchcutpt(target->num,length+1,gendeltas);
	    else if (flag) {
		error("Revision %s doesn't exist.", numrev.string);
		return 0;
	    }
	    else
		temp = searchcutpt(numrev.string, length, gendeltas);
	    cuttail = target->next;
            if ( branchpoint(temp, cuttail) ) {
                cuttail = nil;
                return 0;
            }
            delstrt = temp;     /* first revision to be removed   */
            return 1;
        }

	if (length & 1) {   /*  invalid branch after -o   */
	    error("invalid branch range %s after -o", numrev.string);
            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);  /* get branch number */
		target = genrevs(numrev.string, (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)) return 0;
	if (
		length != countnumflds(numrev.string)
	    ||	length>2 && compartial(numrev.string, target->num, length-1)
	) {
	    error("invalid revision range %s-%s", target->num, numrev.string);
            return 0;
        }

	target2 = genrevs(numrev.string,(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.string))
                    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.string))
                    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;
}



	static void
doassoc()
/*   Function: add or delete(if revno is nil) association	*/
/*		which is stored in assoclst			*/

{
	char const *p;
	struct Symrev const *curassoc;
	struct  assoc   * pre,  * pt;

        /*  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 (curassoc->revno[0]) {
		    p = 0;
		    if (expandsym(curassoc->revno, &numrev))
			p = fstr_save(numrev.string);
		} else if (!(p = tiprev()))
		    error("no latest revision to associate with symbol %s",
			    curassoc->ssymbol
		    );
		if (p)
		    VOID addsymbol(p, curassoc->ssymbol, curassoc->override);
	    }
            curassoc = curassoc->nextsym;
        }

}



	static void
dolocks()
/* Function: remove lock for caller or first lock if unlockcaller is set;
 *           remove locks which are stored in rmvlocklst,
 *           add new locks which are stored in newlocklst,
 *           add lock for Dbranch or Head if lockhead is set.
 */
{
	struct Lockrev const *lockpt;
	struct hshentry *target;

	if (unlockcaller) { /*  find lock for caller  */
            if ( Head ) {
		if (Locks) {
		    switch (findlock(true, &target)) {
		      case 0:
			breaklock(Locks->delta); /* remove most recent lock */
			break;
		      case 1:
			diagnose("%s unlocked\n",target->num);
			break;
		    }
		} else {
		    warn("No locks are 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.string, (char *)nil, (char *)nil, (char *)nil, &gendeltas);
                if ( target )
		   if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string))
			error("can't unlock nonexisting revision %s",lockpt->revno);
                   else
			breaklock(target);
                        /* breaklock does its own diagnose */
            }
            lockpt = lockpt->nextrev;
        }

        /*  add new locks which stored in newlocklst  */
        lockpt = newlocklst;
        while( lockpt ) {
	    setlock(lockpt->revno);
            lockpt = lockpt->nextrev;
        }

	if (lockhead) {  /*  lock default branch or head  */
            if (Dbranch) {
		setlock(Dbranch);
	    } else if (Head) {
		if (0 <= addlock(Head))
		    diagnose("%s locked\n",Head->num);
            } else {
		warn("can't lock an empty tree");
            }
        }

}



	static void
setlock(rev)
	char const *rev;
/* Function: Given a revision or branch number, finds the corresponding
 * delta and locks it for caller.
 */
{
        struct  hshentry *target;

	if (expandsym(rev, &numrev)) {
	    target = genrevs(numrev.string, (char*)nil, (char*)nil,
			     (char*)nil, &gendeltas);
            if ( target )
	       if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string))
		    error("can't lock nonexisting revision %s", numrev.string);
               else
		    if (0 <= addlock(target))
			diagnose("%s locked\n", target->num);
        }
}


	static void
domessages()
{
	struct hshentry *target;
	struct Message *p;

	for (p = messagelst;  p;  p = p->nextmessage)
	    if (
		expandsym(p->revno, &numrev)  &&
		(target = genrevs(
			numrev.string, (char*)0, (char*)0, (char*)0, &gendeltas
		))
	    )
		target->log = p->message;
}


	static void
rcs_setstate(rev,status)
	char const *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)) {
	    target = genrevs(numrev.string, (char*)nil, (char*)nil,
			     (char*)nil, &gendeltas);
            if ( target )
	       if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string))
		    error("can't set state of nonexisting revision %s to %s",
			  numrev.string, status);
               else
                    target->state = status;
        }
}





	static int
buildeltatext(deltas)
	struct hshentries const *deltas;
/*   Function:  put the delta text on frewrite and make necessary   */
/*              change to delta text                                */
{
	register FILE *fcut;	/* temporary file to rebuild delta tree */
	char const *cutfilename, *diffilename;

	cutfilename = nil;
	cuttail->selector = false;
	scanlogtext(deltas->first, false);
        if ( cuthead )  {
	    cutfilename = maketemp(3);
	    if (!(fcut = fopen(cutfilename, FOPEN_W_WORK))) {
		efaterror(cutfilename);
            }

	    while (deltas->first != cuthead) {
		deltas = deltas->rest;
		scanlogtext(deltas->first, true);
            }

	    snapshotedit(fcut);
	    Ofclose(fcut);
        }

	while (deltas->first != cuttail)
	    scanlogtext((deltas = deltas->rest)->first, true);
	finishedit((struct hshentry *)nil, (FILE*)0, true);
	Ozclose(&fcopy);

        if ( cuthead ) {
	    diffilename = maketemp(0);
	    switch (run((char*)nil,diffilename,
			DIFF DIFF_FLAGS, cutfilename, resultfile, (char*)nil
	    )) {
		case DIFF_FAILURE: case DIFF_SUCCESS: break;
		default: faterror ("diff failed");
	    }
	    return putdtext(cuttail->num,cuttail->log,diffilename,frewrite,true);
	} else
	    return putdtext(cuttail->num,cuttail->log,resultfile,frewrite,false);
}



	static void
buildtree()
/*   Function:  actually removes revisions whose selector field  */
/*		is false, and rebuilds the linkage of deltas.	 */
/*              asks for reconfirmation if deleting last revision*/
{
	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) {
		if (!yesorno(false, "Do you really want to delete all revisions? [ny](n): ")) {
		    error("No revision deleted");
		    Delta = delstrt;
		    while( Delta) {
			Delta->selector = true;
			Delta = Delta->next;
		    }
		    return;
		}
	    }
            Head = cuttail;
	}
        return;
}

#if lint
/* This lets us lint everything all at once. */

char const cmdid[] = "";

#define go(p,e) {int p P((int,char**)); void e P((void)); if(*argv)return p(argc,argv);if(*argv[1])e();}

	int
main(argc, argv)
	int argc;
	char **argv;
{
	go(ciId,	ciExit);
	go(coId,	coExit);
	go(identId,	identExit);
	go(mergeId,	mergeExit);
	go(rcsId,	exiterr);
	go(rcscleanId,	rcscleanExit);
	go(rcsdiffId,	rdiffExit);
	go(rcsmergeId,	rmergeExit);
	go(rlogId,	rlogExit);
	return 0;
}
#endif
@


5.12.1.1
log
@Start of the AMIGA port of RCS 5.6. I call it HWGRCS now ;^)
@
text
@d2 1
a2 1
 *			RCS create/change operation
d34 1
a34 1
 * Revision 5.12  1991/11/20  17:58:08	eggert
d37 1
a37 1
 * Revision 5.11  1991/10/07  17:32:46	eggert
d40 1
a40 1
 * Revision 5.10  1991/08/19  23:17:54	eggert
d60 1
a60 1
 * Add -I and new -t behavior.	Permit arbitrary data in logs.
d75 1
a75 1
 * Ansify and Posixate.  Add -V.  Fix umask bug.  Make linting easier.	Tune.
d80 1
a80 1
 *
d83 1
a83 1
 *
d87 1
a87 1
 *
d91 1
a91 1
 *
d94 1
a94 1
 *
d96 1
a96 1
 * Updating verison numbers. Changes relative to 1.1 are actually
d98 1
a98 1
 *
d100 1
a100 1
 * Sources now pass through lint (if you ignore printf/sprintf/fprintf
d102 1
a102 1
 *
d105 1
a105 1
 *
d108 1
a108 1
 *
d111 1
a111 1
 *
d115 1
a115 1
 *
d123 1
a123 1
 *
d126 1
a126 1
 *
d171 1
a171 1
struct	Lockrev {
d173 1
a173 1
	struct	Lockrev   * nextrev;
d176 1
a176 1
struct	Symrev {
d179 2
a180 2
	int	override;
	struct	Symrev	* nextsym;
d189 1
a189 1
struct	Status {
d192 1
a192 1
	struct	Status	* nextstatus;
d205 1
a205 1
	int	code;
d254 2
a255 2
	struct	Lockrev *curlock,  * rmvlock, *lockpt;
	struct	Status	* curstate;
d267 2
a268 2
	initflag= textflag = false;
	strict_selected = 0;
d270 1
a270 1
	/*  preprocessing command options    */
d277 2
a278 2
			initflag = true;
			break;
d280 1
a280 1
		case 'b':  /* change default branch */
d282 1
a282 1
			branchflag= true;
d284 1
a284 1
			break;
d286 1
a286 1
		case 'c':   /*  change comment symbol   */
d289 1
a289 1
			break;
d291 1
a291 1
		case 'a':  /*  add new accessor   */
d293 1
a293 1
			break;
d295 1
a295 1
		case 'A':  /*  append access list according to accessfile  */
d298 2
a299 2
			    break;
			}
d307 2
a308 2
			}
			break;
d310 1
a310 1
		case 'e':    /*  remove accessors   */
d312 1
a312 1
			break;
d314 1
a314 1
		case 'l':    /*   lock a revision if it is unlocked   */
d317 3
a319 3
			    lockhead = true;
			    break;
			}
d322 7
a328 7
			lockpt->nextrev = nil;
			if ( curlock )
			    curlock->nextrev = lockpt;
			else
			    newlocklst = lockpt;
			curlock = lockpt;
			break;
d330 1
a330 1
		case 'u':   /*  release lock of a locked revision   */
d332 3
a334 3
			    unlockcaller=true;
			    break;
			}
d337 13
a349 13
			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? */
d351 7
a357 7
			}
			strictlock = true;
			break;

		case 'U':   /*  release strict locking */
			if (strict_selected++) {  /* Already selected L or U? */
			   if (strictlock)        /* Already selected -L? */
d359 1
a359 1
			}
d362 1
a362 1
			break;
d364 1
a364 1
		case 'n':    /*  add new association: error, if name exists */
d367 4
a370 4
			    break;
			}
			getassoclst(false, (*argv)+1);
			break;
d372 1
a372 1
		case 'N':   /*  add or change association   */
d375 4
a378 4
			    break;
			}
			getassoclst(true, (*argv)+1);
			break;
d388 4
a391 4
			    break;
			}
			getdelrev( (*argv)+1 );
			break;
d393 1
a393 1
		case 's':   /*  change state attribute of a revision  */
d396 4
a399 4
			    break;
			}
			getstates( (*argv)+1);
			break;
d401 2
a402 2
		case 't':   /*  change descriptive text   */
			textflag=true;
d406 2
a407 2
			}
			break;
d413 3
a415 3
		case 'q':
			quietflag = true;
			break;
d430 1
a430 1
		default:
d432 2
a433 2
		};
	}  /* end processing of options */
d436 1
a436 1
	if (nerror) {
d439 1
a439 1
	}
d446 2
a447 2
	/* now handle all filenames */
	do {
d450 1
a450 1
	if ( initflag ) {
d452 5
a456 5
		case -1: break;        /*  not exist; ok */
		case  0: continue;     /*  error	 */
		case  1: error("file %s exists already", RCSfilename);
			 continue;
	    }
d458 1
a458 1
	else  {
d460 4
a463 4
		case -1: continue;    /*  not exist	 */
		case  0: continue;    /*  errors	 */
		case  1: break;       /*  file exists; ok*/
	    }
d467 5
a471 5
	/* 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.
	 */
d486 2
a487 2
	/*  update admin. node	  */
	if (strict_selected) StrictLocks = strictlock;
d494 1
a494 1
	/* update default branch */
d498 3
a500 3
	    } else
		Dbranch = nil;
	}
d502 1
a502 1
	doaccess();     /* Update access list.  */
d504 1
a504 1
	doassoc();      /* Update association list.  */
d506 1
a506 1
	dolocks();      /* Update locks.  */
d508 1
a508 1
	domessages();   /* Update log messages.  */
d510 5
a514 5
	/*  update state attribution  */
	if (chgheadstate) {
	    /* change state of default branch or head */
	    if (Dbranch==nil) {
		if (Head==nil)
d516 2
a517 2
		else Head->state = headstate;
	    } else {
d519 7
a525 7
	    }
	}
	curstate = statelst;
	while( curstate ) {
	    rcs_setstate(curstate->revno,curstate->status);
	    curstate = curstate->nextstatus;
	}
d527 1
a527 1
	cuthead = cuttail = nil;
d529 2
a530 2
	    /*	rebuild delta tree if some deltas are deleted	*/
	    if ( cuttail )
d533 2
a534 2
	    buildtree();
	}
d539 3
a541 3
	putadmin(frewrite);
	if ( Head )
	   puttree(Head, frewrite);
d544 1
a544 1
	if ( Head) {
d546 1
a546 1
		/* No revision was deleted and no message was changed.	*/
d548 1
a548 1
	    } else {
d552 1
a552 1
		    /* copy rest of delta text nodes that are not deleted      */
d554 2
a555 2
	    }
	}
d557 1
a557 1
	if ( ! nerror ) {  /*  move temporary file to RCS file if no error */
d568 2
a569 2
		break;
	    }
d571 1
a571 1
	} else {
d573 1
a573 1
	}
d575 1
a575 1
		 ++argv, --argc >=1);
d579 1
a579 1
}	/* end of main (rcs) */
d602 4
a605 4
int	flag;
char	* sp;
/*  Function:	associate a symbolic name to a revision or branch,	*/
/*		and store in assoclst					*/
d608 1
a608 1
	struct	 Symrev  * pt;
d610 1
a610 1
	int		   c;
d612 2
a613 2
	while( (c=(*++sp)) == ' ' || c == '\t' || c =='\n')  ;
	temp = sp;
d616 1
a616 1
	while( c == ' ' || c == '\t' || c == '\n')  c = *++sp;
d618 1
a618 1
	if ( c != ':' && c != '\0') {
d620 2
a621 2
	    return;
	}
d624 2
a625 2
	pt->ssymbol = temp;
	pt->override = flag;
d627 3
a629 3
	    pt->revno = nil;
	else {
	    while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
d631 8
a638 8
	}
	pt->nextsym = nil;
	if (lastassoc)
	    lastassoc->nextsym = pt;
	else
	    assoclst = pt;
	lastassoc = pt;
	return;
d662 1
a662 1
/*   Function:	get the accessor list of options -e and -a,	*/
d667 1
a667 1
	register c;
d671 2
a672 2
	while( ( c = *++sp) == ' ' || c == '\n' || c == '\t' || c == ',') ;
	if ( c == '\0') {
d679 1
a679 1
	}
d681 1
a681 1
	while( c != '\0') {
d685 2
a686 2
		while( c == ' ' || c == '\n' || c == '\t'|| c == ',')c =(*++sp);
	}
d722 3
a724 3
char	*sp;
/*   Function:	get one state attribute and the corresponding	*/
/*		revision and store in statelst			*/
d728 2
a729 2
	struct	Status	*pt;
	register	c;
d731 2
a732 2
	while( (c=(*++sp)) ==' ' || c == '\t' || c == '\n')  ;
	temp = sp;
d735 1
a735 1
	while( c == ' ' || c == '\t' || c == '\n' )  c = *++sp;
d737 6
a742 6
	if ( c == '\0' ) {  /*  change state of def. branch or Head  */
	    chgheadstate = true;
	    headstate  = temp;
	    return;
	}
	else if ( c != ':' ) {
d744 2
a745 2
	    return;
	}
d747 1
a747 1
	while( (c = *++sp) == ' ' || c == '\t' || c == '\n')  ;
d749 8
a756 8
	pt->status     = temp;
	pt->revno      = sp;
	pt->nextstatus = nil;
	if (laststate)
	    laststate->nextstatus = pt;
	else
	    statelst = pt;
	laststate = pt;
d763 3
a765 3
char	*sp;
/*   Function:	get revision range or branch to be deleted,	*/
/*		and place in delrev				*/
d767 2
a768 2
	int    c;
	struct	delrevpair	*pt;
d772 1
a772 1
	while((c = (*++sp)) == ' ' || c == '\n' || c == '\t') ;
d784 4
a787 4
	    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';
d789 12
a800 12
	    return;
	}
	else {
	    pt->strt = sp;
	    while( c != ' ' && c != '\n' && c != '\t' && c != '\0'
		   && c != separator )	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;
		return;
	    }
d803 2
a804 2
	    }
	    while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
d806 4
a809 4
		pt->end = nil;	 pt->code = 2;
		return;
	    }
	}
d812 2
a813 2
	while( c!= ' ' && c != '\n' && c != '\t' && c != '\0') c = *++sp;
	*sp = '\0';
d837 1
a837 1
		    if(delta)
d840 1
a840 1
		}
d847 1
a847 1
		}
d859 3
a861 3
		} else {readstring();
		}
		nextlex();
d883 1
a883 1
/*   Function:	remove lock to revision which->revno from newlocklst   */
d886 1
a886 1
	struct	Lockrev   * pt, *pre;
d888 1
a888 1
	while( newlocklst && (! strcmp(newlocklst->revno, which->revno))){
d892 1
a892 1
	}
d894 4
a897 4
	pt = pre = newlocklst;
	while( pt ) {
	    if ( ! strcmp(pt->revno, which->revno) ) {
		pre->nextrev = pt->nextrev;
d900 7
a906 7
	    }
	    else {
		pre = pt;
		pt = pt->nextrev;
	    }
	}
	return pre;
d947 1
a947 1
/*   Function:	mail to who, informing him that his lock on delta was
d955 1
a955 1
	FILE	* mailmess;
d963 1
a963 1
	/* go ahead with breaking  */
d968 1
a968 1
	}
d975 2
a976 2
	old1 = '\n';    old2 = ' ';
	for (; ;) {
d980 5
a984 5
		break;
	    }
	    else if ( c == '\n' && old1 == '.' && old2 == '\n')
		break;
	    else {
d986 1
a986 1
		old2 = old1;   old1 = c;
d988 2
a989 2
	    }
	}
d1012 1
a1012 1
	register struct lock * next, * trail;
d1014 1
a1014 1
	struct lock dummy;
d1017 3
a1019 3
	dummy.nextlock=next=Locks;
	trail = &dummy;
	while (next!=nil) {
d1029 6
a1034 6
		}
		trail=next;
		next=next->nextlock;
	}
	if (next!=nil) {
		/*found one */
d1036 4
a1039 4
		trail->nextlock=next->nextlock;
		next->delta->lockedby=nil;
		Locks=dummy.nextlock;
	} else	{
d1041 1
a1041 1
	}
d1051 3
a1053 3
/*   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	*/
d1068 1
a1068 1
struct	hshentry	*strt,	*tail;
d1070 1
a1070 1
/*		are locked or branch point, return 1 if any is	*/
d1075 1
a1075 1
	struct	hshentry    *pt;
d1077 1
a1077 1
	int	flag;
d1080 5
a1084 5
	pt = strt;
	flag = false;
	while( pt != tail) {
	    if ( pt->branches ){ /*  a branch point  */
		flag = true;
d1086 1
a1086 1
	    }
d1094 2
a1095 2
	    pt = pt->next;
	}
d1097 3
a1099 3
	if ( ! flag ) {
	    pt = strt;
	    while( pt != tail ) {
d1102 4
a1105 4
		pt = pt->next;
	    }
	}
	return flag;
d1112 5
a1116 5
/*   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					*/
d1119 1
a1119 1
	struct	hshentry *target, *target2, *temp;
d1123 1
a1123 1
	flag = false;
d1126 1
a1126 1
	if ( ! target ) return 0;
d1140 7
a1146 7
	    if ( branchpoint(temp, cuttail) ) {
		cuttail = nil;
		return 0;
	    }
	    delstrt = temp;	/* first revision to be removed   */
	    return 1;
	}
d1150 2
a1151 2
	    return 0;
	}
d1154 17
a1170 17
	    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;
	}
d1173 14
a1186 14
	    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);
d1189 8
a1196 8
	    }
	    if ( branchpoint( temp, cuttail ) ) {
		cuttail = nil;
		return 0;
	    }
	    delstrt = temp;
	    return 1;
	}
d1198 1
a1198 1
	/*   -o   rev1-rev2   */
d1205 2
a1206 2
	    return 0;
	}
d1209 1
a1209 1
	if ( ! target2 ) return 0;
d1211 2
a1212 2
	if ( length > 2) {  /* delete revisions on branches  */
	    if ( cmpnum(target->num, target2->num) > 0) {
d1214 9
a1222 9
		    flag = true;
		else
		    flag = false;
		temp = target;
		target = target2;
		target2 = temp;
	    }
	    if ( flag ) {
		if ( ! cmpnum(target->num, target2->num) ) {
d1224 16
a1239 16
		    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
d1241 5
a1245 5
		    flag = true;
		else
		    flag = false;
	    if ( flag ) {
		if ( ! cmpnum(target->num, target2->num) ) {
d1247 14
a1260 14
		    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;
d1267 1
a1267 1
/*   Function: add or delete(if revno is nil) association       */
d1273 1
a1273 1
	struct	assoc	* pre,	* pt;
d1275 4
a1278 4
	/*  add new associations   */
	curassoc = assoclst;
	while( curassoc ) {
	    if ( curassoc->revno == nil ) {  /* delete symbol  */
d1280 1
a1280 1
		while( pt && strcmp(pt->symbol,curassoc->ssymbol) ) {
d1304 2
a1305 2
	    curassoc = curassoc->nextsym;
	}
d1314 3
a1316 3
 *	     remove locks which are stored in rmvlocklst,
 *	     add new locks which are stored in newlocklst,
 *	     add lock for Dbranch or Head if lockhead is set.
d1323 1
a1323 1
	    if ( Head ) {
d1336 1
a1336 1
	    } else {
d1338 2
a1339 2
	    }
	}
d1341 3
a1343 3
	/*  remove locks which are stored in rmvlocklst   */
	lockpt = rmvlocklst;
	while( lockpt ) {
d1346 1
a1346 1
		if ( target )
d1349 1
a1349 1
		   else
d1351 8
a1358 8
			/* breaklock does its own diagnose */
	    }
	    lockpt = lockpt->nextrev;
	}

	/*  add new locks which stored in newlocklst  */
	lockpt = newlocklst;
	while( lockpt ) {
d1360 2
a1361 2
	    lockpt = lockpt->nextrev;
	}
d1364 1
a1364 1
	    if (Dbranch) {
d1369 1
a1369 1
	    } else {
d1371 2
a1372 2
	    }
	}
d1385 1
a1385 1
	struct	hshentry *target;
d1390 1
a1390 1
	    if ( target )
d1393 1
a1393 1
	       else
d1396 1
a1396 1
	}
d1424 1
a1424 1
	struct	hshentry *target;
d1429 1
a1429 1
	    if ( target )
d1433 3
a1435 3
	       else
		    target->state = status;
	}
d1445 2
a1446 2
/*   Function:	put the delta text on frewrite and make necessary   */
/*		change to delta text				    */
d1454 1
a1454 1
	if ( cuthead )  {
d1458 1
a1458 1
	    }
d1463 1
a1463 1
	    }
d1467 1
a1467 1
	}
d1474 1
a1474 1
	if ( cuthead ) {
d1491 1
a1491 1
/*   Function:	actually removes revisions whose selector field  */
d1493 1
a1493 1
/*		asks for reconfirmation if deleting last revision*/
d1496 1
a1496 1
	struct	branchhead	*pt, *pre;
d1498 16
a1513 16
	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;
	    }
d1515 1
a1515 1
	    if ( cuttail == nil && !quietflag) {
d1526 1
a1526 1
	    Head = cuttail;
d1528 1
a1528 1
	return;
d1543 9
a1551 9
	go(ciId,        ciExit);
	go(coId,        coExit);
	go(identId,     identExit);
	go(mergeId,     mergeExit);
	go(rcsId,       exiterr);
	go(rcscleanId,  rcscleanExit);
	go(rcsdiffId,   rdiffExit);
	go(rcsmergeId,  rmergeExit);
	go(rlogId,      rlogExit);
@


5.12.1.2
log
@Amiga ARCHIVE bit will now always be cleared
@
text
@d2 1
a2 1
 *                      RCS create/change operation
d34 1
a34 4
 * Revision 5.12.1.1  1993/01/18  14:37:52  heinz
 * Start of the AMIGA port of RCS 5.6. I call it HWGRCS now ;^)
 *
 * Revision 5.12  1991/11/20  17:58:08  eggert
d37 1
a37 1
 * Revision 5.11  1991/10/07  17:32:46  eggert
d40 1
a40 1
 * Revision 5.10  1991/08/19  23:17:54  eggert
d60 1
a60 1
 * Add -I and new -t behavior.  Permit arbitrary data in logs.
d75 1
a75 1
 * Ansify and Posixate.  Add -V.  Fix umask bug.  Make linting easier.  Tune.
d171 3
a173 3
struct  Lockrev {
        char const *revno;
        struct  Lockrev   * nextrev;
d176 5
a180 5
struct  Symrev {
        char const *revno;
        char const *ssymbol;
        int     override;
        struct  Symrev  * nextsym;
d184 3
a186 3
        char const *revno;
        struct cbuf message;
        struct Message *nextmessage;
d189 4
a192 4
struct  Status {
        char const *revno;
        char const *status;
        struct  Status  * nextstatus;
d197 3
a199 3
        char const *login;
        enum changeaccess command;
        struct chaccess *nextchaccess;
d203 3
a205 3
        char const *strt;
        char const *end;
        int     code;
d241 1
a241 1
mainProg(rcsId, "rcs", "$Id: rcs.c,v 5.12.1.1 1993/01/18 14:37:52 heinz Exp heinz $")
d243 2
a244 2
        static char const cmdusage[] =
                "\nrcs usage: rcs -{ae}logins -Afile -{blu}[rev] -cstring -{iLU} -{nNs}name[:rev] -orange -t[file] -Vn file ...";
d246 334
a579 347
        char *a, **newargv, *textfile;
        char const *branchsym, *commsyml;
        int branchflag, expmode, initflag;
        int e, r, strictlock, strict_selected, textflag;
        mode_t defaultRCSmode;  /* default mode for new RCS files */
        mode_t RCSmode;
        struct buf branchnum;
        struct stat workstat;
        struct  Lockrev *curlock,  * rmvlock, *lockpt;
        struct  Status  * curstate;

        nosetid();

        nextchaccess = &chaccess;
        branchsym = commsyml = textfile = nil;
        branchflag = strictlock = false;
        bufautobegin(&branchnum);
        curlock = rmvlock = nil;
        defaultRCSmode = 0;
        expmode = -1;
        suffixes = X_DEFAULT;
        initflag= textflag = false;
        strict_selected = 0;

        /*  preprocessing command options    */
        argc = getRCSINIT(argc, argv, &newargv);
        argv = newargv;
        while (a = *++argv,  0<--argc && *a++=='-') {
                switch (*a++) {

                case 'i':   /*  initial version  */
                        initflag = true;
                        break;

                case 'b':  /* change default branch */
                        if (branchflag) redefined('b');
                        branchflag= true;
                        branchsym = a;
                        break;

                case 'c':   /*  change comment symbol   */
                        if (commsyml) redefined('c');
                        commsyml = a;
                        break;

                case 'a':  /*  add new accessor   */
                        getaccessor(*argv+1, append);
                        break;

                case 'A':  /*  append access list according to accessfile  */
                        if (!*a) {
                            error("missing file name after -A");
                            break;
                        }
                        *argv = a;
                        if (0 < pairfilenames(1,argv,rcsreadopen,true,false)) {
                            while (AccessList) {
                                getchaccess(str_save(AccessList->login),append);
                                AccessList = AccessList->nextaccess;
                            }
                            Izclose(&finptr);
                        }
                        break;

                case 'e':    /*  remove accessors   */
                        getaccessor(*argv+1, erase);
                        break;

                case 'l':    /*   lock a revision if it is unlocked   */
                        if (!*a) {
                            /* Lock head or default branch.  */
                            lockhead = true;
                            break;
                        }
                        lockpt = talloc(struct Lockrev);
                        lockpt->revno = a;
                        lockpt->nextrev = nil;
                        if ( curlock )
                            curlock->nextrev = lockpt;
                        else
                            newlocklst = lockpt;
                        curlock = lockpt;
                        break;

                case 'u':   /*  release lock of a locked revision   */
                        if (!*a) {
                            unlockcaller=true;
                            break;
                        }
                        lockpt = talloc(struct Lockrev);
                        lockpt->revno = a;
                        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("-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("-L overrides -U.");
                        }
                        else
                            strictlock = false;
                        break;

                case 'n':    /*  add new association: error, if name exists */
                        if (!*a) {
                            error("missing symbolic name after -n");
                            break;
                        }
                        getassoclst(false, (*argv)+1);
                        break;

                case 'N':   /*  add or change association   */
                        if (!*a) {
                            error("missing symbolic name after -N");
                            break;
                        }
                        getassoclst(true, (*argv)+1);
                        break;

                case 'm':   /*  change log message  */
                        getmessage(a);
                        break;

                case 'o':   /*  delete revisions  */
                        if (delrev.strt) redefined('o');
                        if (!*a) {
                            error("missing revision range after -o");
                            break;
                        }
                        getdelrev( (*argv)+1 );
                        break;

                case 's':   /*  change state attribute of a revision  */
                        if (!*a) {
                            error("state missing after -s");
                            break;
                        }
                        getstates( (*argv)+1);
                        break;

                case 't':   /*  change descriptive text   */
                        textflag=true;
                        if (*a) {
                                if (textfile) redefined('t');
                                textfile = a;
                        }
                        break;

                case 'I':
                        interactiveflag = true;
                        break;

                case 'q':
                        quietflag = true;
                        break;

                case 'x':
                        suffixes = a;
                        break;

                case 'V':
                        setRCSversion(*argv);
                        break;

                case 'k':    /*  set keyword expand mode  */
                        if (0 <= expmode) redefined('k');
                        if (0 <= (expmode = str2expmode(a)))
                            break;
                        /* fall into */
                default:
                        faterror("unknown option: %s%s", *argv, cmdusage);
                };
        }  /* end processing of options */

        if (argc<1) faterror("no input file%s", cmdusage);
        if (nerror) {
            diagnose("%s aborted\n",cmdid);
            exitmain(EXIT_FAILURE);
        }
        if (initflag) {
            defaultRCSmode = umask((mode_t)0);
            VOID umask(defaultRCSmode);
            defaultRCSmode = (S_IRUSR|S_IRGRP|S_IROTH) & ~defaultRCSmode;
        }

        /* now handle all filenames */
        do {
        ffree();

        if ( initflag ) {
            switch (pairfilenames(argc, argv, rcswriteopen, false, false)) {
                case -1: break;        /*  not exist; ok */
                case  0: continue;     /*  error         */
                case  1: error("file %s exists already", RCSfilename);
                         continue;
            }
        }
        else  {
            switch (pairfilenames(argc, argv, rcswriteopen, 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\n", RCSfilename);

        RCSmode = defaultRCSmode;
        if (initflag) {
                if (stat(workfilename, &workstat) == 0)
#ifdef AMIGA
        /* No matter what we do, we uncoditionally clear the archive bit of the
           Amiga. This doesn't hurt RCS as it isn't used or looked at
           anywhere and it helps backup SW a lot. Of course I could check if
           this mode is written back anywhere before I patch into RCS source.
           But it is a lot safer to just clean it at every stat call no matter
           what. */
                {
                        workstat.st_mode &= ~S_IARCHIVE;
                        RCSmode = workstat.st_mode;
                } /* if */
#else
                        RCSmode = workstat.st_mode;
#endif
        } else {
                if (!checkaccesslist()) continue;
                gettree(); /* Read the delta tree.  */
                RCSmode = RCSstat.st_mode;
        }
        RCSmode &= ~(S_IWUSR|S_IWGRP|S_IWOTH);

        /*  update admin. node    */
        if (strict_selected) StrictLocks = strictlock;
        if (commsyml) {
                Comment.string = commsyml;
                Comment.size = strlen(commsyml);
        }
        if (0 <= expmode) Expand = expmode;

        /* update default branch */
        if (branchflag && expandsym(branchsym, &branchnum)) {
            if (countnumflds(branchnum.string)) {
                Dbranch = branchnum.string;
            } else
                Dbranch = nil;
        }

        doaccess();     /* Update access list.  */

        doassoc();      /* Update association list.  */

        dolocks();      /* Update locks.  */

        domessages();   /* Update log messages.  */

        /*  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,headstate); /* Can't set directly */
            }
        }
        curstate = statelst;
        while( curstate ) {
            rcs_setstate(curstate->revno,curstate->status);
            curstate = curstate->nextstatus;
        }

        cuthead = cuttail = nil;
        if (delrev.strt && removerevs()) {
            /*  rebuild delta tree if some deltas are deleted   */
            if ( cuttail )
                VOID genrevs(cuttail->num, (char *)nil,(char *)nil,
                             (char *)nil, &gendeltas);
            buildtree();
        }

        if (nerror)
                continue;

        putadmin(frewrite);
        if ( Head )
           puttree(Head, frewrite);
        putdesc(textflag,textfile);

        if ( Head) {
            if (!delrev.strt && !messagelst) {
                /* No revision was deleted and no message was changed.  */
                fastcopy(finptr, frewrite);
            } else {
                if (!cuttail || buildeltatext(gendeltas)) {
                    advise_access(finptr, MADV_SEQUENTIAL);
                    scanlogtext((struct hshentry *)nil, false);
                    /* copy rest of delta text nodes that are not deleted      */
                }
            }
        }
        Izclose(&finptr);
        if ( ! nerror ) {  /*  move temporary file to RCS file if no error */
            /* update mode */
            ignoreints();
            r = chnamemod(&frewrite, newRCSfilename, RCSfilename, RCSmode);
            e = errno;
            keepdirtemp(newRCSfilename);
            restoreints();
            if (r != 0) {
                enerror(e, RCSfilename);
                error("saved in %s", newRCSfilename);
                dirtempunlink();
                break;
            }
            diagnose("done\n");
        } else {
            diagnose("%s aborted; %s unchanged.\n",cmdid,RCSfilename);
        }
        } while (cleanup(),
                 ++argv, --argc >=1);

        tempunlink();
        exitmain(exitstatus);
}       /* end of main (rcs) */
d581 1
a581 1
        static void
d584 5
a588 5
        if (nerror) exitstatus = EXIT_FAILURE;
        Izclose(&finptr);
        Ozclose(&fcopy);
        Ozclose(&frewrite);
        dirtempunlink();
d591 1
a591 1
        exiting void
d594 3
a596 3
        dirtempunlink();
        tempunlink();
        _exit(EXIT_FAILURE);
d600 1
a600 1
        static void
d602 37
a638 37
int     flag;
char    * sp;
/*  Function:   associate a symbolic name to a revision or branch,      */
/*              and store in assoclst                                   */

{
        struct   Symrev  * pt;
        char const *temp;
        int                c;

        while( (c=(*++sp)) == ' ' || c == '\t' || c =='\n')  ;
        temp = sp;
        sp = checkid(sp, ':');  /*  check for invalid symbolic name  */
        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 = talloc(struct Symrev);
        pt->ssymbol = temp;
        pt->override = flag;
        if (c == '\0')  /*  delete symbol  */
            pt->revno = nil;
        else {
            while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
            pt->revno = sp;
        }
        pt->nextsym = nil;
        if (lastassoc)
            lastassoc->nextsym = pt;
        else
            assoclst = pt;
        lastassoc = pt;
        return;
d642 1
a642 1
        static void
d644 2
a645 2
        char const *login;
        enum changeaccess command;
d647 1
a647 1
        register struct chaccess *pt;
d649 5
a653 5
        *nextchaccess = pt = talloc(struct chaccess);
        pt->login = login;
        pt->command = command;
        pt->nextchaccess = nil;
        nextchaccess = &pt->nextchaccess;
d658 1
a658 1
        static void
d660 4
a663 4
        char *opt;
        enum changeaccess command;
/*   Function:  get the accessor list of options -e and -a,     */
/*              and store in chaccess                           */
d667 2
a668 2
        register c;
        register char *sp;
d670 10
a679 10
        sp = opt;
        while( ( c = *++sp) == ' ' || c == '\n' || c == '\t' || c == ',') ;
        if ( c == '\0') {
            if (command == erase  &&  sp-opt == 1) {
                getchaccess((char const*)nil, command);
                return;
            }
            error("missing login name after option -a or -e");
            return;
        }
d681 6
a686 6
        while( c != '\0') {
                getchaccess(sp, command);
                sp = checkid(sp,',');
                c = *sp;   *sp = '\0';
                while( c == ' ' || c == '\n' || c == '\t'|| c == ',')c =(*++sp);
        }
d690 1
a690 1
        static void
d692 1
a692 1
        char *option;
d694 23
a716 23
        struct Message *pt;
        struct cbuf cb;
        char *m;

        if (!(m = strchr(option, ':'))) {
                error("-m option lacks revision number");
                return;
        }
        *m++ = 0;
        cb = cleanlogmsg(m, strlen(m));
        if (!cb.size) {
                error("-m option lacks log message");
                return;
        }
        pt = talloc(struct Message);
        pt->revno = option;
        pt->message = cb;
        pt->nextmessage = 0;
        if (lastmessage)
                lastmessage->nextmessage = pt;
        else
                messagelst = pt;
        lastmessage = pt;
d720 1
a720 1
        static void
d722 35
a756 35
char    *sp;
/*   Function:  get one state attribute and the corresponding   */
/*              revision and store in statelst                  */

{
        char const *temp;
        struct  Status  *pt;
        register        c;

        while( (c=(*++sp)) ==' ' || c == '\t' || c == '\n')  ;
        temp = sp;
        sp = checkid(sp,':');  /* check for invalid state attribute */
        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 = talloc(struct Status);
        pt->status     = temp;
        pt->revno      = sp;
        pt->nextstatus = nil;
        if (laststate)
            laststate->nextstatus = pt;
        else
            statelst = pt;
        laststate = pt;
d761 1
a761 1
        static void
d763 51
a813 51
char    *sp;
/*   Function:  get revision range or branch to be deleted,     */
/*              and place in delrev                             */
{
        int    c;
        struct  delrevpair      *pt;
        int separator;

        pt = &delrev;
        while((c = (*++sp)) == ' ' || c == '\n' || c == '\t') ;

        /* Support old ambiguous '-' syntax; this will go away.  */
        if (strchr(sp,':'))
                separator = ':';
        else {
                if (strchr(sp,'-')  &&  VERSION(5) <= RCSversion)
                    warn("`-' is obsolete in `-o%s'; use `:' instead", sp);
                separator = '-';
        }

        if (c == separator) { /* -o: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;
            return;
        }
        else {
            pt->strt = sp;
            while( c != ' ' && c != '\n' && c != '\t' && c != '\0'
                   && c != separator )  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;
                return;
            }
            if (c != separator) {
                faterror("invalid range %s %s after -o", pt->strt, sp);
            }
            while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
            if (!c) {  /* -orev: */
                pt->end = nil;   pt->code = 2;
                return;
            }
        }
        /* -orev1:rev2 */
        pt->end = sp;  pt->code = 3;
        while( c!= ' ' && c != '\n' && c != '\t' && c != '\0') c = *++sp;
        *sp = '\0';
d819 1
a819 1
        static void
d821 2
a822 2
        struct hshentry *delta;
        int edit;
d831 2
a832 2
        struct hshentry const *nextdelta;
        struct cbuf cb;
d834 42
a875 42
        for (;;) {
                foutptr = 0;
                if (eoflex()) {
                    if(delta)
                        faterror("can't find delta for revision %s", delta->num);
                    return; /* no more delta text nodes */
                }
                nextlex();
                if (!(nextdelta=getnum()))
                    faterror("delta number corrupted");
                if (nextdelta->selector) {
                        foutptr = frewrite;
                        aprintf(frewrite,DELNUMFORM,nextdelta->num,Klog);
                }
                getkeystring(Klog);
                if (nextdelta == cuttail) {
                        cb = savestring(&curlogbuf);
                        if (!delta->log.string)
                            delta->log = cleanlogmsg(curlogbuf.string, cb.size);
                } else if (nextdelta->log.string && nextdelta->selector) {
                        foutptr = 0;
                        readstring();
                        foutptr = frewrite;
                        putstring(foutptr, false, nextdelta->log, true);
                        afputc(nextc, foutptr);
                } else {readstring();
                }
                nextlex();
                while (nexttok==ID && strcmp(NextString,Ktext)!=0)
                        ignorephrase();
                getkeystring(Ktext);

                if (delta==nextdelta)
                        break;
                readstring(); /* skip over it */

        }
        /* got the one we're looking for */
        if (edit)
                editstring((struct hshentry *)nil);
        else
                enterstring();
d880 1
a880 1
        static struct Lockrev *
d882 2
a883 2
        struct Lockrev const *which;
/*   Function:  remove lock to revision which->revno from newlocklst   */
d886 1
a886 1
        struct  Lockrev   * pt, *pre;
d888 5
a892 5
        while( newlocklst && (! strcmp(newlocklst->revno, which->revno))){
            struct Lockrev *pn = newlocklst->nextrev;
            tfree(newlocklst);
            newlocklst = pn;
        }
d894 13
a906 13
        pt = pre = newlocklst;
        while( pt ) {
            if ( ! strcmp(pt->revno, which->revno) ) {
                pre->nextrev = pt->nextrev;
                tfree(pt);
                pt = pre->nextrev;
            }
            else {
                pre = pt;
                pt = pt->nextrev;
            }
        }
        return pre;
d911 1
a911 1
        static void
d914 2
a915 2
        register struct chaccess *ch;
        register struct access **p, *t;
d917 24
a940 24
        for (ch = chaccess;  ch;  ch = ch->nextchaccess) {
                switch (ch->command) {
                case erase:
                        if (!ch->login)
                            AccessList = nil;
                        else
                            for (p = &AccessList;  (t = *p);  )
                                if (strcmp(ch->login, t->login) == 0)
                                        *p = t->nextaccess;
                                else
                                        p = &t->nextaccess;
                        break;
                case append:
                        for (p = &AccessList;  ;  p = &t->nextaccess)
                                if (!(t = *p)) {
                                        *p = t = ftalloc(struct access);
                                        t->login = ch->login;
                                        t->nextaccess = nil;
                                        break;
                                } else if (strcmp(ch->login, t->login) == 0)
                                        break;
                        break;
                }
        }
d944 1
a944 1
        static int
d946 2
a947 2
        char const *Delta, *who;
/*   Function:  mail to who, informing him that his lock on delta was
d953 3
a955 3
        char const *messagefile;
        int old1, old2, c;
        FILE    * mailmess;
d959 3
a961 3
        aprintf(stderr, "Revision %s is already locked by %s.\n", Delta, who);
        if (!yesorno(false, "Do you want to break the lock? [ny](n): "))
                return false;
d963 1
a963 1
        /* go ahead with breaking  */
d965 26
a990 26
        messagefile = maketemp(0);
        if (!(mailmess = fopen(messagefile, "w"))) {
            efaterror(messagefile);
        }

        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",
                basename(RCSfilename), Delta, getfullRCSname(), getcaller()
        );
        aputs("State the reason for breaking the lock:\n(terminate with single '.' or end of file)\n>> ", stderr);

        old1 = '\n';    old2 = ' ';
        for (; ;) {
            c = getcstdin();
            if (feof(stdin)) {
                aprintf(mailmess, "%c\n", old1);
                break;
            }
            else if ( c == '\n' && old1 == '.' && old2 == '\n')
                break;
            else {
                afputc(old1, mailmess);
                old2 = old1;   old1 = c;
                if (c=='\n') aputs(">> ", stderr);
            }
        }
        Ozclose(&mailmess);
d992 2
a993 2
        if (run(messagefile, (char*)nil, SENDMAIL, who, (char*)nil))
                warn("Mail may have failed."),
d995 1
a995 1
                warn("Mail notification of broken locks is not available."),
d997 2
a998 2
                warn("Please tell `%s' why you broke the lock.", who);
        return(true);
d1003 1
a1003 1
        static void
d1005 1
a1005 1
        struct hshentry const *delta;
d1012 30
a1041 30
        register struct lock * next, * trail;
        char const *num;
        struct lock dummy;

        num=delta->num;
        dummy.nextlock=next=Locks;
        trail = &dummy;
        while (next!=nil) {
                if (strcmp(num, next->delta->num) == 0) {
                        if (
                                strcmp(getcaller(),next->login) != 0
                            &&  !sendmail(num, next->login)
                        ) {
                            error("%s still locked by %s", num, next->login);
                            return;
                        }
                        break; /* exact match */
                }
                trail=next;
                next=next->nextlock;
        }
        if (next!=nil) {
                /*found one */
                diagnose("%s unlocked\n",next->delta->num);
                trail->nextlock=next->nextlock;
                next->delta->lockedby=nil;
                Locks=dummy.nextlock;
        } else  {
                error("no lock set on revision %s", num);
        }
d1046 1
a1046 1
        static struct hshentry *
d1048 6
a1053 6
        char const *object;
        unsigned length;
        struct hshentries *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  */
d1056 6
a1061 6
        cuthead = nil;
        while (compartial(store->first->num, object, length)) {
                cuthead = store->first;
                store = store->rest;
        }
        return store->first;
d1066 1
a1066 1
        static int
d1068 38
a1105 38
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 deleted                                    */

{
        struct  hshentry    *pt;
        struct lock const *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 = false;
                diagnose("deleting revision %s\n",pt->num);
                pt = pt->next;
            }
        }
        return flag;
d1110 1
a1110 1
        static int
d1112 149
a1260 149
/*   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;
        unsigned length;
        int flag;

        flag = false;
        if (!expandsym(delrev.strt, &numrev)) return 0;
        target = genrevs(numrev.string, (char*)nil, (char*)nil, (char*)nil, &gendeltas);
        if ( ! target ) return 0;
        if (cmpnum(target->num, numrev.string)) flag = true;
        length = countnumflds(numrev.string);

        if (delrev.code == 0) {  /*  -o  rev    or    -o  branch   */
            if (length & 1)
                temp=searchcutpt(target->num,length+1,gendeltas);
            else if (flag) {
                error("Revision %s doesn't exist.", numrev.string);
                return 0;
            }
            else
                temp = searchcutpt(numrev.string, length, gendeltas);
            cuttail = target->next;
            if ( branchpoint(temp, cuttail) ) {
                cuttail = nil;
                return 0;
            }
            delstrt = temp;     /* first revision to be removed   */
            return 1;
        }

        if (length & 1) {   /*  invalid branch after -o   */
            error("invalid branch range %s after -o", numrev.string);
            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);  /* get branch number */
                target = genrevs(numrev.string, (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)) return 0;
        if (
                length != countnumflds(numrev.string)
            ||  length>2 && compartial(numrev.string, target->num, length-1)
        ) {
            error("invalid revision range %s-%s", target->num, numrev.string);
            return 0;
        }

        target2 = genrevs(numrev.string,(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.string))
                    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.string))
                    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;
d1265 1
a1265 1
        static void
d1268 1
a1268 1
/*              which is stored in assoclst                     */
d1271 35
a1305 35
        char const *p;
        struct Symrev const *curassoc;
        struct  assoc   * pre,  * pt;

        /*  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 (curassoc->revno[0]) {
                    p = 0;
                    if (expandsym(curassoc->revno, &numrev))
                        p = fstr_save(numrev.string);
                } else if (!(p = tiprev()))
                    error("no latest revision to associate with symbol %s",
                            curassoc->ssymbol
                    );
                if (p)
                    VOID addsymbol(p, curassoc->ssymbol, curassoc->override);
            }
            curassoc = curassoc->nextsym;
        }
d1311 1
a1311 1
        static void
d1314 3
a1316 3
 *           remove locks which are stored in rmvlocklst,
 *           add new locks which are stored in newlocklst,
 *           add lock for Dbranch or Head if lockhead is set.
d1319 2
a1320 2
        struct Lockrev const *lockpt;
        struct hshentry *target;
d1322 51
a1372 51
        if (unlockcaller) { /*  find lock for caller  */
            if ( Head ) {
                if (Locks) {
                    switch (findlock(true, &target)) {
                      case 0:
                        breaklock(Locks->delta); /* remove most recent lock */
                        break;
                      case 1:
                        diagnose("%s unlocked\n",target->num);
                        break;
                    }
                } else {
                    warn("No locks are 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.string, (char *)nil, (char *)nil, (char *)nil, &gendeltas);
                if ( target )
                   if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string))
                        error("can't unlock nonexisting revision %s",lockpt->revno);
                   else
                        breaklock(target);
                        /* breaklock does its own diagnose */
            }
            lockpt = lockpt->nextrev;
        }

        /*  add new locks which stored in newlocklst  */
        lockpt = newlocklst;
        while( lockpt ) {
            setlock(lockpt->revno);
            lockpt = lockpt->nextrev;
        }

        if (lockhead) {  /*  lock default branch or head  */
            if (Dbranch) {
                setlock(Dbranch);
            } else if (Head) {
                if (0 <= addlock(Head))
                    diagnose("%s locked\n",Head->num);
            } else {
                warn("can't lock an empty tree");
            }
        }
d1378 1
a1378 1
        static void
d1380 1
a1380 1
        char const *rev;
d1385 1
a1385 1
        struct  hshentry *target;
d1387 10
a1396 10
        if (expandsym(rev, &numrev)) {
            target = genrevs(numrev.string, (char*)nil, (char*)nil,
                             (char*)nil, &gendeltas);
            if ( target )
               if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string))
                    error("can't lock nonexisting revision %s", numrev.string);
               else
                    if (0 <= addlock(target))
                        diagnose("%s locked\n", target->num);
        }
d1400 1
a1400 1
        static void
d1403 2
a1404 2
        struct hshentry *target;
        struct Message *p;
d1406 8
a1413 8
        for (p = messagelst;  p;  p = p->nextmessage)
            if (
                expandsym(p->revno, &numrev)  &&
                (target = genrevs(
                        numrev.string, (char*)0, (char*)0, (char*)0, &gendeltas
                ))
            )
                target->log = p->message;
d1417 1
a1417 1
        static void
d1419 1
a1419 1
        char const *rev, *status;
d1424 1
a1424 1
        struct  hshentry *target;
d1426 10
a1435 10
        if (expandsym(rev, &numrev)) {
            target = genrevs(numrev.string, (char*)nil, (char*)nil,
                             (char*)nil, &gendeltas);
            if ( target )
               if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string))
                    error("can't set state of nonexisting revision %s to %s",
                          numrev.string, status);
               else
                    target->state = status;
        }
d1442 1
a1442 1
        static int
d1444 41
a1484 41
        struct hshentries const *deltas;
/*   Function:  put the delta text on frewrite and make necessary   */
/*              change to delta text                                */
{
        register FILE *fcut;    /* temporary file to rebuild delta tree */
        char const *cutfilename, *diffilename;

        cutfilename = nil;
        cuttail->selector = false;
        scanlogtext(deltas->first, false);
        if ( cuthead )  {
            cutfilename = maketemp(3);
            if (!(fcut = fopen(cutfilename, FOPEN_W_WORK))) {
                efaterror(cutfilename);
            }

            while (deltas->first != cuthead) {
                deltas = deltas->rest;
                scanlogtext(deltas->first, true);
            }

            snapshotedit(fcut);
            Ofclose(fcut);
        }

        while (deltas->first != cuttail)
            scanlogtext((deltas = deltas->rest)->first, true);
        finishedit((struct hshentry *)nil, (FILE*)0, true);
        Ozclose(&fcopy);

        if ( cuthead ) {
            diffilename = maketemp(0);
            switch (run((char*)nil,diffilename,
                        DIFF DIFF_FLAGS, cutfilename, resultfile, (char*)nil
            )) {
                case DIFF_FAILURE: case DIFF_SUCCESS: break;
                default: faterror ("diff failed");
            }
            return putdtext(cuttail->num,cuttail->log,diffilename,frewrite,true);
        } else
            return putdtext(cuttail->num,cuttail->log,resultfile,frewrite,false);
d1489 1
a1489 1
        static void
d1491 38
a1528 38
/*   Function:  actually removes revisions whose selector field  */
/*              is false, and rebuilds the linkage of deltas.    */
/*              asks for reconfirmation if deleting last revision*/
{
        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) {
                if (!yesorno(false, "Do you really want to delete all revisions? [ny](n): ")) {
                    error("No revision deleted");
                    Delta = delstrt;
                    while( Delta) {
                        Delta->selector = true;
                        Delta = Delta->next;
                    }
                    return;
                }
            }
            Head = cuttail;
        }
        return;
d1538 1
a1538 1
        int
d1540 2
a1541 2
        int argc;
        char **argv;
d1543 10
a1552 10
        go(ciId,        ciExit);
        go(coId,        coExit);
        go(identId,     identExit);
        go(mergeId,     mergeExit);
        go(rcsId,       exiterr);
        go(rcscleanId,  rcscleanExit);
        go(rcsdiffId,   rdiffExit);
        go(rcsmergeId,  rmergeExit);
        go(rlogId,      rlogExit);
        return 0;
@


5.12.1.3
log
@The ARCHIVE fix really wasn't a good way to fix it. I took it out.
It cured the symptoms, not the problem. For networking support I need to
'fix' stat(), fstat() and chmod() inside amiga.c.
@
text
@a33 3
 * Revision 5.12.1.2  1993/03/20  21:18:12  heinz
 * Amiga ARCHIVE bit will now always be cleared
 *
d244 1
a244 1
mainProg(rcsId, "rcs", "$Id: rcs.c,v 5.12.1.2 1993/03/20 21:18:12 heinz Exp heinz $")
d481 12
d494 1
@


5.12.1.4
log
@First version of Amiga keywords implemented. Ugly, but acceptable for now.
@
text
@a33 5
 * Revision 5.12.1.3  1993/03/22  19:13:42  heinz
 * The ARCHIVE fix really wasn't a good way to fix it. I took it out.
 * It cured the symptoms, not the problem. For networking support I need to
 * 'fix' stat(), fstat() and chmod() inside amiga.c.
 *
d247 1
a247 4
#ifdef _AMIGA
char HWGversion[] = "$" "VER: " PRGPREFIX "rcs " PRGVERSION "." PRGREVISION " (" PRGDATE ")" PRGVERTEXT;
#endif /* _AMIGA */
mainProg(rcsId, "rcs", "$Id: rcs.c,v 5.12.1.3 1993/03/22 19:13:42 heinz Exp heinz $")
@




















































































