[comp.sources.amiga] v89i222: rcs - revision control system, Part07/14

page%swap@Sun.COM (Bob Page) (11/19/89)

Submitted-by: rsbx@cbmvax.commodore.com (Raymond S. Brand)
Posting-number: Volume 89, Issue 222
Archive-name: unix/rcs.07

# This is a shell archive.
# Remove anything above and including the cut line.
# Then run the rest of the file through 'sh'.
# Unpacked files will be owned by you and have default permissions.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: SHell ARchive
# Run the following text through 'sh' to create:
#	rcs/rcs.rcsfiles/partime.c,v
#	rcs/rcs.rcsfiles/rcs.c,v
# This is archive 7 of a 14-part kit.
# This archive created: Sun Nov 19 01:12:08 1989
if `test ! -d rcs`
then
  mkdir rcs
  echo "mkdir rcs"
fi
if `test ! -d rcs/rcs.rcsfiles`
then
  mkdir rcs/rcs.rcsfiles
  echo "mkdir rcs/rcs.rcsfiles"
fi
echo "extracting rcs/rcs.rcsfiles/partime.c,v"
sed 's/^X//' << \SHAR_EOF > rcs/rcs.rcsfiles/partime.c,v
Xhead     1.4;
Xbranch   1.4.2;
Xaccess   ;
Xsymbols  amiga_rcs:1.4.2 cbmvax_source:1.4.1 uunet_june89_dist:1.4;
Xlocks    ; strict;
Xcomment  @ * @;
X
X
X1.4
Xdate     89.05.01.14.48.46;  author narten;  state Exp;
Xbranches 1.4.1.1 1.4.2.1;
Xnext     ;
X
X1.4.1.1
Xdate     89.08.11.01.41.57;  author rsbx;  state Exp;
Xbranches ;
Xnext     ;
X
X1.4.2.1
Xdate     89.10.13.19.17.41;  author rsbx;  state Exp;
Xbranches ;
Xnext     1.4.2.2;
X
X1.4.2.2
Xdate     89.10.15.15.43.31;  author rsbx;  state Exp;
Xbranches ;
Xnext     1.4.2.3;
X
X1.4.2.3
Xdate     89.10.29.14.47.46;  author rsbx;  state Exp;
Xbranches ;
Xnext     ;
X
X
Xdesc
X@Parse date/time string into a TM structure.
X@
X
X
X
X1.4
Xlog
X@checked in with -k by rsbx at 89.08.10.16.06.05.
X@
Xtext
X@/*
X * PARTIME		parse date/time string into a TM structure
X *
X * Usage:
X *      #include "time.h"             -- expanded tm structure
X *	char *str; struct tm *tp;
X *	partime(str,tp);
X * Returns:
X *	0 if parsing failed
X *	else time values in specified TM structure (unspecified values
X *		set to TMNULL)
X * Notes:
X *	This code is quasi-public; it may be used freely in like software.
X *	It is not to be sold, nor used in licensed software without
X *	permission of the author.
X *	For everyone's benefit, please report bugs and improvements!
X * 	Copyright 1980 by Ken Harrenstien, SRI International.
X *	(ARPANET: KLH @@ SRI)
X */
X
X/* Hacknotes:
X *	If parsing changed so that no backup needed, could perhaps modify
X *		to use a FILE input stream.  Need terminator, though.
X *	Perhaps should return 0 on success, else a non-zero error val?
X *	Flush AMPM from TM structure and handle locally within PARTIME,
X *		like midnight/noon?
X */
X
X#ifndef lint
Xstatic char rcsid[]=
X"$Header: /usr/src/local/bin/rcs/src/RCS/partime.c,v 1.4 89/05/01 14:48:46 narten Exp $";
X#endif
X
X/* $Log:	partime.c,v $
X * Revision 1.4  89/05/01  14:48:46  narten
X * fixed #ifdef DEBUG construct
X * 
X * Revision 1.3  88/11/08  12:02:15  narten
X * changes from  eggert@@sm.unisys.com (Paul Eggert)
X * 
X * Revision 1.3  88/08/28  14:53:40  eggert
X * Remove unportable "#endif XXX"s.
X * 
X * Revision 1.2  87/03/27  14:21:53  jenkins
X * Port to suns
X * 
X * Revision 1.1  84/01/23  14:50:07  kcs
X * Initial revision
X * 
X * Revision 1.1  82/05/06  11:38:26  wft
X * Initial revision
X * 
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include "time.h"
X
X#ifndef lint
Xstatic char timeid[] = TIMEID;
X#endif
X
Xstruct tmwent {
X	char *went;
X	long wval;	/* must be big enough to hold pointer or integer */
X	char wflgs;
X	char wtype;
X};
X	/* wflgs */
X#define TWSPEC 01	/* Word wants special processing */
X#define TWTIME 02	/* Word is a time value (absence implies date) */
X#define TWDST  04	/* Word is a DST-type timezone */
X#define TW1200 010	/* Word is NOON or MIDNIGHT (sigh) */
X
Xint pt12hack();
Xint ptnoise();
Xstruct tmwent tmwords [] = {
X	{"january",      0, 0, TM_MON},
X	{"february",     1, 0, TM_MON},
X	{"march",        2, 0, TM_MON},
X	{"april",        3, 0, TM_MON},
X	{"may",          4, 0, TM_MON},
X	{"june",         5, 0, TM_MON},
X	{"july",         6, 0, TM_MON},
X	{"august",       7, 0, TM_MON},
X	{"september",    8, 0, TM_MON},
X	{"october",      9, 0, TM_MON},
X	{"november",     10, 0, TM_MON},
X	{"december",     11, 0, TM_MON},
X
X	{"sunday",       0, 0, TM_WDAY},
X	{"monday",       1, 0, TM_WDAY},
X	{"tuesday",      2, 0, TM_WDAY},
X	{"wednesday",    3, 0, TM_WDAY},
X	{"thursday",     4, 0, TM_WDAY},
X	{"friday",       5, 0, TM_WDAY},
X	{"saturday",     6, 0, TM_WDAY},
X
X	{"gmt",          0*60, TWTIME, TM_ZON},   /* Greenwich */
X	{"gst",          0*60, TWTIME, TM_ZON},
X	{"gdt",          0*60, TWTIME+TWDST, TM_ZON},     /* ?? */
X
X	{"ast",          4*60, TWTIME, TM_ZON},   /* Atlantic */
X	{"est",          5*60, TWTIME, TM_ZON},   /* Eastern */
X	{"cst",          6*60, TWTIME, TM_ZON},   /* Central */
X	{"mst",          7*60, TWTIME, TM_ZON},   /* Mountain */
X	{"pst",          8*60, TWTIME, TM_ZON},   /* Pacific */
X	{"yst",          9*60, TWTIME, TM_ZON},   /* Yukon */
X	{"hst",          10*60, TWTIME, TM_ZON},  /* Hawaii */
X	{"bst",          11*60, TWTIME, TM_ZON},  /* Bering */
X
X	{"adt",          4*60, TWTIME+TWDST, TM_ZON},     /* Atlantic */
X	{"edt",          5*60, TWTIME+TWDST, TM_ZON},     /* Eastern */
X	{"cdt",          6*60, TWTIME+TWDST, TM_ZON},     /* Central */
X	{"mdt",          7*60, TWTIME+TWDST, TM_ZON},     /* Mountain */
X	{"pdt",          8*60, TWTIME+TWDST, TM_ZON},     /* Pacific */
X	{"ydt",          9*60, TWTIME+TWDST, TM_ZON},     /* Yukon */
X	{"hdt",          10*60, TWTIME+TWDST, TM_ZON},    /* Hawaii */
X	{"bdt",          11*60, TWTIME+TWDST, TM_ZON},    /* Bering */
X
X	{"daylight",     1, TWTIME+TWDST, TM_ZON},        /* Local Daylight */
X	{"standard",     1, TWTIME, TM_ZON},      /* Local Standard */
X	{"std",          1, TWTIME, TM_ZON},      /*   "       "    */
X
X	{"am",           1, TWTIME, TM_AMPM},
X	{"pm",           2, TWTIME, TM_AMPM},
X	{"noon",         12,TWTIME+TW1200, 0},    /* Special frobs */
X	{"midnight",     0, TWTIME+TW1200, 0},
X	{"at",           (long)ptnoise, TWSPEC, 0},    /* Noise word */
X
X	{0, 0, 0, 0},             /* Zero entry to terminate searches */
X};
X
X#define TMWILD (-2)	/* Value meaning item specified as wild-card */
X			/* (May use someday...) */
X
Xstruct token {
X	char *tcp;	/* pointer to string */
X	int tcnt;	/* # chars */
X	char tbrk;	/* "break" char */
X	char tbrkl;	/* last break char */
X	char tflg;	/* 0 = alpha, 1 = numeric */
X	union {         /* Resulting value; */
X		int tnum;/* either a #, or */
X		struct tmwent *ttmw;/* ptr to a tmwent. */
X	} tval;
X};
X
Xpartime(astr, atm)
Xchar *astr;
Xstruct tm *atm;
X{	register int *tp;
X	register struct tmwent *twp;
X	register int i;
X	struct token btoken, atoken;
X	char *cp, ch;
X	int ord, midnoon;
X	int (*aproc)();
X
X	tp = (int *)atm;
X	zaptime(tp);			 /* Initialize the TM structure */
X	midnoon = TMNULL;		/* and our own temp stuff */
X	btoken.tcnt = btoken.tbrkl = 0;
X	btoken.tcp = astr;
X
Xdomore:
X	if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken))	/* Get a token */
X	  {     if(btoken.tval.tnum) return(0);         /* Read error? */
X		if(midnoon != TMNULL)			/* EOF, wrap up */
X			return(pt12hack(tp, midnoon));
X		return(1);				/* Win return! */
X	  }
X	if(btoken.tflg == 0)		/* Alpha? */
X	  {     twp = btoken.tval.ttmw;         /* Yes, get ptr to entry */
X		if(twp->wflgs&TWSPEC)		/* Special alpha crock */
X		  {     aproc = (int (*) ()) (twp->wval);
X			if(!(*aproc)(tp, twp, &btoken))
X				return(0);	/* ERR: special word err */
X			goto domore;
X		  }
X		if(twp->wflgs&TW1200)
X			if(ptstash(&midnoon,(int)twp->wval))
X				return(0);	/* ERR: noon/midnite clash */
X			else goto domore;
X		if(ptstash(&tp[twp->wtype],(int)twp->wval))
X			return(0);		/* ERR: val already set */
X		if(twp->wtype == TM_ZON)	/* If was zone, hack DST */
X			if(ptstash(&tp[TM_ISDST],(twp->wflgs&TWDST)))
X				return(0);	/* ERR: DST conflict */
X		goto domore;
X	  }
X
X	/* Token is number.  Lots of hairy heuristics. */
X	if(btoken.tcnt >= 7)	/* More than 6 digits in string? */
X		return(0);	/* ERR: number too big */
X	if(btoken.tcnt == 6)	/* 6 digits = HHMMSS.  Needs special crock */
X	  {			/* since 6 digits are too big for integer! */
X		i = (btoken.tcp[0]-'0')*10	/* Gobble 1st 2 digits */
X		   + btoken.tcp[1]-'0';
X		btoken.tcnt = 2;		/* re-read last 4 chars */
X		goto coltime;
X	  }
X
X	i = btoken.tval.tnum;   /* Value now known to be valid; get it. */
X	if( btoken.tcnt == 5	/*  5 digits = HMMSS */
X	 || btoken.tcnt == 3)	/*  3 digits = HMM   */
X	  {	if(btoken.tcnt != 3)
X			if(ptstash(&tp[TM_SEC], i%100))
X				return(0);	/* ERR: sec conflict */
X			else i /= 100;
Xhhmm4:		if(ptstash(&tp[TM_MIN], i%100))
X			return(0);		/* ERR: min conflict */
X		i /= 100;
Xhh2:            if(ptstash(&tp[TM_HOUR], i))
X			return(0);		/* ERR: hour conflict */
X		goto domore;
X	  }
X
X	if(btoken.tcnt == 4)	/* 4 digits = YEAR or HHMM */
X	  {	if(tp[TM_YEAR] != TMNULL) goto hhmm4;	/* Already got yr? */
X		if(tp[TM_HOUR] != TMNULL) goto year4;	/* Already got hr? */
X		if((i%100) > 59) goto year4;		/* MM >= 60? */
X		if(btoken.tbrk == ':')			/* HHMM:SS ? */
X			if( ptstash(&tp[TM_HOUR],i/100)
X			 || ptstash(&tp[TM_MIN], i%100))
X				return(0);		/* ERR: hr/min clash */
X			else goto coltm2;		/* Go handle SS */
X		if(btoken.tbrk != ',' && btoken.tbrk != '/'
X		  && ptitoken(btoken.tcp+btoken.tcnt,&atoken)	/* Peek */
X		  && atoken.tflg == 0			/* alpha */
X		  && (atoken.tval.ttmw->wflgs&TWTIME))  /* HHMM-ZON */
X			goto hhmm4;
X		if(btoken.tbrkl == '-'		/* DD-Mon-YYYY */
X		  || btoken.tbrkl == ','	/* Mon DD, YYYY */
X		  || btoken.tbrkl == '/'	/* MM/DD/YYYY */
X		  || btoken.tbrkl == '.'	/* DD.MM.YYYY */
X		  || btoken.tbrk == '-'		/* YYYY-MM-DD */
X			) goto year4;
X		goto hhmm4;			/* Give up, assume HHMM. */
X	  }
X
X	/* From this point on, assume tcnt == 1 or 2 */
X	/* 2 digits = YY, MM, DD, or HH (MM and SS caught at coltime) */
X	if(btoken.tbrk == ':')		/* HH:MM[:SS] */
X		goto coltime;		/*  must be part of time. */
X	if(i > 31) goto yy2;		/* If >= 32, only YY poss. */
X
X	/* Check for numerical-format date */
X	for (cp = "/-."; ch = *cp++;)
X	  {	ord = (ch == '.' ? 0 : 1);	/* n/m = D/M or M/D */
X		if(btoken.tbrk == ch)			/* "NN-" */
X		  {	if(btoken.tbrkl != ch)
X			  {	if(ptitoken(btoken.tcp+btoken.tcnt,&atoken)
X				  && atoken.tflg == 0
X				  && atoken.tval.ttmw->wtype == TM_MON)
X					goto dd2;
X				if(ord)goto mm2; else goto dd2; /* "NN-" */
X			  }				/* "-NN-" */
X			if(tp[TM_DAY] == TMNULL
X			&& tp[TM_YEAR] != TMNULL)	/* If "YY-NN-" */
X				goto mm2;		/* then always MM */
X			if(ord)goto dd2; else goto mm2;
X		  }
X		if(btoken.tbrkl == ch			/* "-NN" */
X		  && tp[ord ? TM_MON : TM_DAY] != TMNULL)
X			if(tp[ord ? TM_DAY : TM_MON] == TMNULL)	/* MM/DD */
X				if(ord)goto dd2; else goto mm2;
X			else goto yy2;			/* "-YY" */
X	  }
X
X	/* At this point only YY, DD, and HH are left.
X	 * YY is very unlikely since value is <= 32 and there was
X	 * no numerical format date.  Make one last try at YY
X	 * before dropping through to DD vs HH code.
X	 */
X	if(btoken.tcnt == 2		/* If 2 digits */
X	  && tp[TM_HOUR] != TMNULL	/* and already have hour */
X	  && tp[TM_DAY] != TMNULL	/* and day, but  */
X	  && tp[TM_YEAR] == TMNULL)	/* no year, then assume */
X		goto yy2;		/* that's what we have. */
X
X	/* Now reduced to choice between HH and DD */
X	if(tp[TM_HOUR] != TMNULL) goto dd2;	/* Have hour? Assume day. */
X	if(tp[TM_DAY] != TMNULL) goto hh2;	/* Have day? Assume hour. */
X	if(i > 24) goto dd2;			/* Impossible HH means DD */
X	if(!ptitoken(btoken.tcp+btoken.tcnt, &atoken))	/* Read ahead! */
X		if(atoken.tval.tnum) return(0); /* ERR: bad token */
X		else goto dd2;			/* EOF, assume day. */
X	if( atoken.tflg == 0		/* If next token is an alpha */
X	 && atoken.tval.ttmw->wflgs&TWTIME)  /* time-spec, assume hour */
X		goto hh2;		/* e.g. "3 PM", "11-EDT"  */
X
Xdd2:	if(ptstash(&tp[TM_DAY],i))	/* Store day (1 based) */
X		return(0);
X	goto domore;
X
Xmm2:	if(ptstash(&tp[TM_MON], i-1))	/* Store month (make zero based) */
X		return(0);
X	goto domore;
X
Xyy2:	i += 1900;
Xyear4:	if(ptstash(&tp[TM_YEAR],i))	/* Store year (full number) */
X		return(0);		/* ERR: year conflict */
X	goto domore;
X
X	/* Hack HH:MM[[:]SS] */
Xcoltime:
X	if(ptstash(&tp[TM_HOUR],i)) return(0);
X	if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken))
X		return(!btoken.tval.tnum);
X	if(!btoken.tflg) return(0);	/* ERR: HH:<alpha> */
X	if(btoken.tcnt == 4)		/* MMSS */
X		if(ptstash(&tp[TM_MIN],btoken.tval.tnum/100)
X		  || ptstash(&tp[TM_SEC],btoken.tval.tnum%100))
X			return(0);
X		else goto domore;
X	if(btoken.tcnt != 2
X	  || ptstash(&tp[TM_MIN],btoken.tval.tnum))
X		return(0);		/* ERR: MM bad */
X	if(btoken.tbrk != ':') goto domore;	/* Seconds follow? */
Xcoltm2:	if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken))
X		return(!btoken.tval.tnum);
X	if(!btoken.tflg || btoken.tcnt != 2	/* Verify SS */
X	  || ptstash(&tp[TM_SEC], btoken.tval.tnum))
X		return(0);		/* ERR: SS bad */
X	goto domore;
X}
X
X/* Store date/time value, return 0 if successful.
X * Fails if entry already set to a different value.
X */
Xptstash(adr,val)
Xint *adr;
X{	register int *a;
X	if( *(a=adr) != TMNULL)
X		return(*a != val);
X	*a = val;
X	return(0);
X}
X
X/* This subroutine is invoked for NOON or MIDNIGHT when wrapping up
X * just prior to returning from partime.
X */
Xpt12hack(atp, aval)
Xint *atp, aval;
X{	register int *tp, i, h;
X	tp = atp;
X	if (((i=tp[TM_MIN]) && i != TMNULL)	/* Ensure mins, secs */
X	 || ((i=tp[TM_SEC]) && i != TMNULL))	/* are 0 or unspec'd */
X		return(0);			/* ERR: MM:SS not 00:00 */
X	i = aval;			/* Get 0 or 12 (midnite or noon) */
X	if ((h = tp[TM_HOUR]) == TMNULL	/* If hour unspec'd, win */
X	 || h == 12)			/* or if 12:00 (matches either) */
X		tp[TM_HOUR] = i;	/* Then set time */
X	else if(!(i == 0		/* Nope, but if midnight and */
X		&&(h == 0 || h == 24)))	/* time matches, can pass. */
X			return(0);	/* ERR: HH conflicts */
X	tp[TM_AMPM] = TMNULL;		/* Always reset this value if won */
X	return(1);
X}
X
X/* Null routine for no-op tokens */
X
Xptnoise() { return(1); }
X
X/* Get a token and identify it to some degree.
X * Returns 0 on failure; token.tval will be 0 for normal EOF, otherwise
X * hit error of some sort
X */
X
Xptitoken(astr, tkp)
Xregister struct token *tkp;
Xchar *astr;
X{
X	register char *cp;
X	register int i;
X
X	tkp->tval.tnum = 0;
X	if(pttoken(astr,tkp) == 0)
X#ifdef DEBUG
X	    {
X		VOID printf("EOF\n");
X		return(0);
X	    }
X#else
X		return(0);
X#endif	
X	cp = tkp->tcp;
X
X#ifdef DEBUG
X	i = cp[tkp->tcnt];
X	cp[tkp->tcnt] = 0;
X	VOID printf("Token: \"%s\" ",cp);
X	cp[tkp->tcnt] = i;
X#endif
X
X	if(tkp->tflg)
X		for(i = tkp->tcnt; i > 0; i--)
X			tkp->tval.tnum = (int)tkp->tval.tnum*10 + ((*cp++)-'0');
X	else
X	  {     i = ptmatchstr(cp, tkp->tcnt, tmwords);
X		tkp->tval.tnum = i ? i : -1;         /* Set -1 for error */
X
X#ifdef DEBUG
X		if(!i) VOID printf("Not found!\n");
X#endif
X
X		if(!i) return(0);
X	  }
X
X#ifdef DEBUG
X	if(tkp->tflg)
X		VOID printf("Val: %d.\n",tkp->tval.tnum);
X	else VOID printf("Found: \"%s\", val: %d., type %d\n",
X		tkp->tval.ttmw->went,tkp->tval.ttmw->wval,tkp->tval.ttmw->wtype);
X#endif
X
X	return(1);
X}
X
X/* Read token from input string into token structure */
Xpttoken(astr,tkp)
Xregister struct token *tkp;
Xchar *astr;
X{
X	register char *cp;
X	register int c;
X
X	tkp->tcp = cp = astr;
X	tkp->tbrkl = tkp->tbrk;		/* Set "last break" */
X	tkp->tcnt = tkp->tbrk = tkp->tflg = 0;
X
X	while(c = *cp++)
X	  {	switch(c)
X		  {	case ' ': case '\t':	/* Flush all whitespace */
X				while((c = *cp++) && isspace(c));
X				cp--;		/* Drop thru to handle brk */
X			case '(': case ')':	/* Perhaps any non-alphanum */
X			case '-': case ',':	/* shd qualify as break? */
X			case '/': case ':': case '.':	/* Break chars */
X				if(tkp->tcnt == 0)	/* If no token yet */
X				  {	tkp->tcp = cp;	/* ignore the brk */
X					tkp->tbrkl = c;
X				  	continue;	/* and go on. */
X				  }
X				tkp->tbrk = c;
X				return(tkp->tcnt);
X		  }
X		if(tkp->tcnt == 0)		/* If first char of token, */
X			tkp->tflg = isdigit(c);	/*    determine type */
X	  	if(( isdigit(c) &&  tkp->tflg)	/* If not first, make sure */
X		 ||(!isdigit(c) && !tkp->tflg))	/*    char matches type */
X			tkp->tcnt++;		/* Win, add to token. */
X		else {
X			cp--;			/* Wrong type, back up */
X			tkp->tbrk = c;
X			return(tkp->tcnt);
X		  }
X	  }
X	return(tkp->tcnt);		/* When hit EOF */
X}
X
X
Xptmatchstr(astr,cnt,astruc)
Xchar *astr;
Xint cnt;
Xstruct tmwent *astruc;
X{	register char *cp, *mp;
X	register int c;
X	struct tmwent *lastptr;
X	struct integ { int word; };   /* For getting at array ptr */
X	int i;
X
X	lastptr = 0;
X	for(;mp = (char *)((struct integ *)astruc)->word; astruc += 1)
X	  {	cp = astr;
X		for(i = cnt; i > 0; i--)
X		  {	switch((c = *cp++) ^ *mp++)	/* XOR the chars */
X			  {	case 0: continue;	/* Exact match */
X				case 040: if(isalpha(c))
X					continue;
X			  }
X			break;
X		  }
X		if(i==0)
X			if(*mp == 0) return((unsigned int)astruc);    /* Exact match */
X			else if(lastptr) return(0);	/* Ambiguous */
X			else lastptr = astruc;		/* 1st ambig */
X	  }
X	return((unsigned int)lastptr);
X}
X
X
X
Xzaptime(tp)
Xregister int *tp;
X/* clears tm structure pointed to by tp */
X{	register int i;
X	i = (sizeof (struct tm))/(sizeof (int));
X	do *tp++ = TMNULL;		/* Set entry to "unspecified" */
X	while(--i);			/* Faster than FOR */
X}
X@
X
X
X1.4.2.1
Xlog
X@Start of Amiga RCS port branch.
X@
Xtext
X@d31 1
Xa31 5
X<<<<<<< partime.c
X"$Header: /u/softeng/rsbx/rcs/amiga/RCS.cbmvax/partime.c,v 1.4.1.1 89/08/11 01:41:57 rsbx Exp Locker: rsbx $";
X=======
X"$Header: /u/softeng/rsbx/rcs/amiga/RCS/partime.c,v 1.2 89/09/17 13:34:54 rick Exp $";
X>>>>>>> 1.2
Xa34 11
X<<<<<<< partime.c
X * Revision 1.4.1.1  89/08/11  01:41:57  rsbx
X * Start of cbmvax RCS source branch.
X=======
X * Revision 1.2  89/09/17  13:34:54  rick
X * Port to AmigaDos done by Rick Schaeffer (ricks@@iscuva.iscs.com)
X * All changes done with conditional compile (#ifdef AMIGA).  This version
X * compiles correctly with Lattice C version 5.02 or later.
X>>>>>>> 1.2
X * 
X<<<<<<< partime.c
Xa35 3
X * checked in with -k by rsbx at 89.08.10.16.06.05.
X * 
X * Revision 1.4  89/05/01  14:48:46  narten
Xa43 8
X=======
X * Revision 1.3  89/09/16  09:42:36  rick
X * Modified AMIGA changes to work with Lattice C
X * 
X * Revision 1.2  88/09/03  15:08:16  rick
X * Port to AmigaDos.  All done with conditional compiles
X * 
X>>>>>>> 1.2
Xa58 1
X#ifndef AMIGA
Xa61 1
X#endif
Xd76 1
Xa76 1
Xlong ptnoise();
Xd129 1
Xa129 1
X	{"at",           1, 0, 0},    /* Noise word */
Xd152 1
Xa152 2
X{
X	register int *tp;
Xa332 3
X#ifdef AMIGA
Xshort *adr;
X#else
Xd334 1
Xa334 7
X#endif
X{
X#ifdef AMIGA
X	register short *a;
X#else
X	register int *a;
X#endif
Xa344 3
X#ifdef AMIGA
Xshort *atp, aval;
X#else
Xd346 1
Xa346 7
X#endif
X{
X#ifdef AMIGA
X	register short *tp, i, h;
X#else
X	register int *tp, i, h;
X#endif
Xd364 1
Xa364 1
Xlong ptnoise() { return(1); }
Xa379 1
X<<<<<<< partime.c
Xa382 5
X=======
X#ifdef MYDEBUG
X	VOID printf("EOF\n");
X#endif MYDEBUG
X>>>>>>> 1.2
Xd390 1
Xa390 1
X#ifdef MYDEBUG
Xa394 1
X<<<<<<< partime.c
Xa395 3
X=======
X#endif MYDEBUG
X>>>>>>> 1.2
Xd404 1
Xa404 1
X#ifdef MYDEBUG
Xa405 1
X<<<<<<< partime.c
Xa406 3
X=======
X#endif MYDEBUG
X>>>>>>> 1.2
Xd411 1
Xa411 1
X#ifdef MYDEBUG
Xa415 1
X<<<<<<< partime.c
Xa416 3
X=======
X#endif MYDEBUG
X>>>>>>> 1.2
Xa495 3
X#ifdef AMIGA
Xregister short *tp;
X#else
Xa496 1
X#endif
Xa498 3
X#ifdef AMIGA
X	i = (sizeof (struct tm))/(sizeof (short));
X#else
Xa499 1
X#endif
X@
X
X
X1.4.2.2
Xlog
X@Finished the integration of Rick Schaeffer's RCS Amiga port with the RCS
Xsources I have here (and are later than the ones Rick used).
X@
Xtext
X@d31 5
Xa35 1
X"$Header: /u/softeng/rsbx/rcs/amiga/RCS/partime.c,v 1.4.2.1 89/10/13 19:17:41 rsbx Exp Locker: rsbx $";
Xd39 1
Xa39 3
X * Revision 1.4.2.1  89/10/13  19:17:41  rsbx
X * Start of Amiga RCS port branch.
X * 
Xd42 6
Xd49 1
Xd62 8
Xd427 2
Xa428 1
X#ifdef MYDEBUG
Xd431 5
Xd448 1
Xd450 3
Xd463 1
Xd465 3
Xd477 1
Xd479 3
Xd520 1
X@
X
X
X1.4.2.3
Xlog
X@Changed $Header$ to $Id$.
X@
Xtext
X@d31 1
Xa31 1
X"$Id$";
Xa34 4
X * Revision 1.4.2.2  89/10/15  15:43:31  rsbx
X * Finished the integration of Rick Schaeffer's RCS Amiga port with the RCS
X * sources I have here (and are later than the ones Rick used).
X * 
X@
X
X
X1.4.1.1
Xlog
X@Start of cbmvax RCS source branch.
X@
Xtext
X@d31 1
Xa31 1
X"$Header: /u/softeng/rsbx/rcs/rcs.uunet/src/RCS/partime.c,v 1.4 89/05/01 14:48:46 narten Exp $";
Xa34 3
X * Revision 1.4  89/05/01  14:48:46  narten
X * checked in with -k by rsbx at 89.08.10.16.06.05.
X * 
X@
SHAR_EOF
echo "extracting rcs/rcs.rcsfiles/rcs.c,v"
sed 's/^X//' << \SHAR_EOF > rcs/rcs.rcsfiles/rcs.c,v
Xhead     4.11;
Xbranch   4.11.2;
Xaccess   ;
Xsymbols  amiga_rcs:4.11.2 cbmvax_source:4.11.1 uunet_june89_dist:4.11;
Xlocks    ; strict;
Xcomment  @ * @;
X
X
X4.11
Xdate     89.05.01.15.12.06;  author narten;  state Exp;
Xbranches 4.11.1.1 4.11.2.1;
Xnext     ;
X
X4.11.1.1
Xdate     89.08.11.01.42.01;  author rsbx;  state Exp;
Xbranches ;
Xnext     ;
X
X4.11.2.1
Xdate     89.10.13.19.17.47;  author rsbx;  state Exp;
Xbranches ;
Xnext     4.11.2.2;
X
X4.11.2.2
Xdate     89.10.15.15.43.36;  author rsbx;  state Exp;
Xbranches ;
Xnext     4.11.2.3;
X
X4.11.2.3
Xdate     89.10.16.19.06.13;  author rsbx;  state Exp;
Xbranches ;
Xnext     4.11.2.4;
X
X4.11.2.4
Xdate     89.10.17.13.17.35;  author rsbx;  state Exp;
Xbranches ;
Xnext     4.11.2.5;
X
X4.11.2.5
Xdate     89.10.30.13.38.35;  author rsbx;  state Exp;
Xbranches ;
Xnext     4.11.2.6;
X
X4.11.2.6
Xdate     89.11.01.14.42.32;  author rsbx;  state Exp;
Xbranches ;
Xnext     4.11.2.7;
X
X4.11.2.7
Xdate     89.11.05.23.14.17;  author rsbx;  state Exp;
Xbranches ;
Xnext     4.11.2.8;
X
X4.11.2.8
Xdate     89.11.12.15.07.03;  author rsbx;  state Exp;
Xbranches ;
Xnext     ;
X
X
Xdesc
X@RCS create/change operation.
X@
X
X
X
X4.11
Xlog
X@checked in with -k by rsbx at 89.08.10.16.06.26.
X@
Xtext
X@/*
X *                      RCS create/change operation
X */
X#ifndef lint
Xstatic char rcsid[]=
X"$Header: /usr/src/local/bin/rcs/src/RCS/rcs.c,v 4.11 89/05/01 15:12:06 narten Exp $ Purdue CS";
X#endif
X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that the above copyright notice and this paragraph are
X * duplicated in all such forms and that any documentation,
X * advertising materials, and other materials related to such
X * distribution and use acknowledge that the software was developed
X * by Walter Tichy.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X *
X * Report all problems and direct all questions to:
X *   rcs-bugs@@cs.purdue.edu
X * 
X
X
X
X
X
X
X
X*/
X
X
X
X
X/* $Log:	rcs.c,v $
X * Revision 4.11  89/05/01  15:12:06  narten
X * changed copyright header to reflect current distribution rules
X * 
X * Revision 4.10  88/11/08  16:01:54  narten
X * didn't install previous patch correctly
X * 
X * Revision 4.9  88/11/08  13:56:01  narten
X * removed include <sysexits.h> (not needed)
X * minor fix for -A option
X * 
X * Revision 4.8  88/11/08  12:01:58  narten
X * changes from  eggert@@sm.unisys.com (Paul Eggert)
X * 
X * Revision 4.8  88/08/09  19:12:27  eggert
X * Don't access freed storage.
X * Use execv(), not system(); yield proper exit status; remove lint.
X * 
X * Revision 4.7  87/12/18  11:37:17  narten
X * lint cleanups (Guy Harris)
X * 
X * Revision 4.6  87/10/18  10:28:48  narten
X * Updating verison numbers. Changes relative to 1.1 are actually 
X * relative to 4.3
X * 
X * Revision 1.4  87/09/24  13:58:52  narten
X * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
X * warnings)
X * 
X * Revision 1.3  87/03/27  14:21:55  jenkins
X * Port to suns
X * 
X * Revision 1.2  85/12/17  13:59:09  albitz
X * Changed setstate to rcs_setstate because of conflict with random.o.
X * 
X * Revision 1.1  84/01/23  14:50:09  kcs
X * Initial revision
X * 
X * Revision 4.3  83/12/15  12:27:33  wft
X * rcs -u now breaks most recent lock if it can't find a lock by the caller.
X * 
X * Revision 4.2  83/12/05  10:18:20  wft
X * Added conditional compilation for sending mail.
X * Alternatives: V4_2BSD, V6, USG, and other.
X * 
X * Revision 4.1  83/05/10  16:43:02  wft
X * Simplified breaklock(); added calls to findlock() and getcaller().
X * Added option -b (default branch). Updated -s and -w for -b.
X * Removed calls to stat(); now done by pairfilenames().
X * Replaced most catchints() calls with restoreints().
X * Removed check for exit status of delivermail().
X * Directed all interactive output to stderr.
X * 
X * Revision 3.9.1.1  83/12/02  22:08:51  wft
X * Added conditional compilation for 4.2 sendmail and 4.1 delivermail.
X * 
X * Revision 3.9  83/02/15  15:38:39  wft
X * Added call to fastcopy() to copy remainder of RCS file.
X *
X * Revision 3.8  83/01/18  17:37:51  wft
X * Changed sendmail(): now uses delivermail, and asks whether to break the lock.
X *
X * Revision 3.7  83/01/15  18:04:25  wft
X * Removed putree(); replaced with puttree() in rcssyn.c.
X * Combined putdellog() and scanlogtext(); deleted putdellog().
X * Cleaned up diagnostics and error messages. Fixed problem with
X * mutilated files in case of deletions in 2 files in a single command.
X * Changed marking of selector from 'D' to DELETE.
X *
X * Revision 3.6  83/01/14  15:37:31  wft
X * Added ignoring of interrupts while new RCS file is renamed;
X * Avoids deletion of RCS files by interrupts.
X *
X * Revision 3.5  82/12/10  21:11:39  wft
X * Removed unused variables, fixed checking of return code from diff,
X * introduced variant COMPAT2 for skipping Suffix on -A files.
X *
X * Revision 3.4  82/12/04  13:18:20  wft
X * Replaced getdelta() with gettree(), changed breaklock to update
X * field lockedby, added some diagnostics.
X *
X * Revision 3.3  82/12/03  17:08:04  wft
X * Replaced getlogin() with getpwuid(), flcose() with ffclose(),
X * /usr/ucb/Mail with macro MAIL. Removed handling of Suffix (-x).
X * fixed -u for missing revno. Disambiguated structure members.
X *
X * Revision 3.2  82/10/18  21:05:07  wft
X * rcs -i now generates a file mode given by the umask minus write permission;
X * otherwise, rcs keeps the mode, but removes write permission.
X * I added a check for write error, fixed call to getlogin(), replaced
X * curdir() with getfullRCSname(), cleaned up handling -U/L, and changed
X * conflicting, long identifiers.
X *
X * Revision 3.1  82/10/13  16:11:07  wft
X * fixed type of variables receiving from getc() (char -> int).
X */
X
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include "rcsbase.h"
X#ifndef lint
Xstatic char rcsbaseid[] = RCSBASE;
X#endif
X
Xextern FILE * fopen();
Xextern char * bindex();
Xextern int  expandsym();                /* get numeric revision name        */
Xextern struct  hshentry  * getnum();
Xextern struct  lock      * addlock();   /* add a lock                       */
Xextern char              * getid();
Xextern char              Klog[], Khead[], Kaccess[], Kbranch[], Ktext[];
X#ifdef COMPAT2
Xextern char Ksuffix[];
X#endif
Xextern char * getcaller();              /* get login of caller              */
Xextern struct hshentry   * findlock();  /* find and remove lock             */
Xextern struct hshentry   * genrevs();
Xextern char * checkid();                /* check an identifier              */
Xextern char * getfullRCSname();         /* get full path name of RCS file   */
Xextern char * mktempfile();             /* temporary file name generator    */
Xextern free();
Xextern void catchints();
Xextern void ignoreints();
Xextern int  nerror;                     /* counter for errors               */
Xextern int  quietflag;                  /* diagnoses suppressed if true     */
Xextern char curlogmsg[];                /* current log message              */
Xextern char * resultfile;               /* filename for fcopy		    */
Xextern FILE *fcopy;                     /* result file during editing       */
Xextern FILE * finptr;                   /* RCS input file                   */
Xextern FILE * frewrite;                 /* new RCS file                     */
Xextern int    rewriteflag;              /* indicates whether input should be*/
X					/* echoed to frewrite               */
X
Xchar * newRCSfilename, * diffilename, * cutfilename;
Xchar * RCSfilename, * workfilename;
Xextern struct stat RCSstat, workstat; /* file status of RCS and work file   */
Xextern int  haveRCSstat, haveworkstat;/* status indicators                  */
X
Xchar accessorlst[strtsize];
XFILE * fcut;        /* temporary file to rebuild delta tree                 */
Xint    oldumask;    /* save umask                                           */
X
Xint initflag, strictlock, strict_selected, textflag;
Xchar * textfile, * accessfile;
Xchar * caller, numrev[30];            /* caller's login;               */
Xstruct  access  * newaccessor,  * rmvaccessor,  * rplaccessor;
Xstruct  access  *curaccess,  *rmaccess;
Xstruct  hshentry        * gendeltas[hshsize];
X
Xstruct  Lockrev {
X        char    * revno;
X        struct  Lockrev   * nextrev;
X};
X
Xstruct  Symrev {
X        char    * revno;
X        char    * ssymbol;
X        int     override;
X        struct  Symrev  * nextsym;
X};
X
Xstruct  Status {
X        char    * revno;
X        char    * status;
X        struct  Status  * nextstatus;
X};
X
Xstruct delrevpair {
X        char    * strt;
X        char    * end;
X        int     code;
X};
X
Xstruct  Lockrev * newlocklst,   * rmvlocklst;
Xstruct  Symrev  * assoclst,  * lastassoc;
Xstruct  Status  * statelst,  * laststate;
Xstruct  delrevpair      * delrev;
Xstruct  hshentry        * cuthead,  *cuttail,  * delstrt;
Xchar    branchnum[revlength], * branchsym;
Xstruct  hshentry branchdummy;
Xchar	* commsyml;
Xchar    * headstate;
Xint     lockhead,unlockcaller,chgheadstate,branchflag,commentflag;
Xint     delaccessflag;
Xenum    stringwork {copy, edit, empty}; /* expand and edit_expand not needed */
X
X
Xmain (argc, argv)
Xint argc;
Xchar * argv[];
X{
X        char    *comdusge;
X        int     result;
X	struct	access	*removeaccess(),  * getaccessor();
X        struct  Lockrev *rmnewlocklst();
X        struct  Lockrev *curlock,  * rmvlock, *lockpt;
X        struct  Status  * curstate;
X        struct  access    *temp, *temptr;
X	int status;
X
X	status = 0;
X        nerror = 0;
X	catchints();
X        cmdid = "rcs";
X        quietflag = false;
X        comdusge ="command format:\nrcs -i -alogins -Alogins -e[logins] -b[rev] -c[commentleader] -l[rev] -u[rev] -L -U -nname[:rev] -Nname[:rev] -orange -sstate[:rev] -t[textfile] file....";
X        rplaccessor = nil;     delstrt = nil;
X        accessfile = textfile = caller = nil;
X        branchflag = commentflag = chgheadstate = false;
X        lockhead = false; unlockcaller=false;
X        initflag= textflag = false;
X        strict_selected = 0;
X
X	caller=getcaller();
X        laststate = statelst = nil;
X        lastassoc = assoclst = nil;
X        curlock = rmvlock = newlocklst = rmvlocklst = nil;
X        curaccess = rmaccess = rmvaccessor = newaccessor = nil;
X        delaccessflag = false;
X
X        /*  preprocessing command options    */
X        while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
X                switch ((*argv)[1]) {
X
X                case 'i':   /*  initail version  */
X                        initflag = true;
X                        break;
X
X                case 'b':  /* change default branch */
X                        if (branchflag)warn("Redfinition of option -b");
X                        branchflag= true;
X                        branchsym = (*argv)+2;
X                        break;
X
X                case 'c':   /*  change comment symbol   */
X                        if (commentflag)warn("Redefinition of option -c");
X                        commentflag = true;
X                        commsyml = (*argv)+2;
X                        break;
X
X                case 'a':  /*  add new accessor   */
X                        if ( (*argv)[2] == '\0') {
X                            error("Login name missing after -a");
X                        }
X                        if ( (temp = getaccessor((*argv)+1)) ) {
X                            if ( newaccessor )
X                                curaccess->nextaccess = temp->nextaccess;
X                            else
X                                newaccessor = temp->nextaccess;
X                            temp->nextaccess = nil;
X                            curaccess = temp;
X                        }
X                        break;
X
X                case 'A':  /*  append access list according to accessfile  */
X                        if ( (*argv)[2] == '\0') {
X                            error("Missing file name after -A");
X                            break;
X                        }
X                        if ( accessfile) warn("Redefinition of option -A");
X                        *argv = *argv+2;
X                        if( pairfilenames(1, argv, true, false) > 0) {
X                            releaselst(newaccessor);
X                            newaccessor = curaccess = nil;
X                            releaselst(rmvaccessor);
X                            rmvaccessor = rmaccess = nil;
X                            accessfile = RCSfilename;
X                        }
X                        else
X                            accessfile = nil;
X                        break;
X
X                case 'e':    /*  remove accessors   */
X                        if ( (*argv)[2] == '\0' ) {
X                            delaccessflag = true;
X                            break;
X                        }
X                        if ( (temp = getaccessor((*argv)+1))  ) {
X                            if ( rmvaccessor )
X                                rmaccess->nextaccess = temp->nextaccess;
X                            else
X                                rmvaccessor = temp->nextaccess;
X                            temptr = temp->nextaccess;
X                            temp->nextaccess = nil;
X                            rmaccess = temp;
X                            while( temptr ) {
X                                newaccessor = removeaccess(temptr,newaccessor,false);
X                                temptr = temptr->nextaccess;
X                            }
X                            curaccess = temp = newaccessor;
X                            while( temp){
X                                curaccess = temp;
X                                temp = temp->nextaccess;
X                            }
X                        }
X                        break;
X
X                case 'l':    /*   lock a revision if it is unlocked   */
X                        if ( (*argv)[2] == '\0'){ /* lock head or def. branch */
X                            lockhead = true;
X                            break;
X                        }
X                        lockpt = (struct Lockrev *)talloc(sizeof(struct Lockrev));
X                        lockpt->revno = (*argv)+2;
X                        lockpt->nextrev = nil;
X                        if ( curlock )
X                            curlock->nextrev = lockpt;
X                        else
X                            newlocklst = lockpt;
X                        curlock = lockpt;
X                        break;
X
X                case 'u':   /*  release lock of a locked revision   */
X                        if ( (*argv)[2] == '\0'){ /*  unlock head  */
X                            unlockcaller=true;
X                            break;
X                        }
X                        lockpt = (struct Lockrev *)talloc(sizeof(struct Lockrev));
X                        lockpt->revno = (*argv)+2;
X                        lockpt->nextrev = nil;
X                        if (rmvlock)
X                            rmvlock->nextrev = lockpt;
X                        else
X                            rmvlocklst = lockpt;
X                        rmvlock = lockpt;
X
X                        curlock = rmnewlocklst(lockpt);
X                        break;
X
X                case 'L':   /*  set strict locking */
X                        if (strict_selected++) {  /* Already selected L or U? */
X			   if (!strictlock)	  /* Already selected -U? */
X			       warn("Option -L overrides -U");
X                        }
X                        strictlock = true;
X                        break;
X
X                case 'U':   /*  release strict locking */
X                        if (strict_selected++) {  /* Already selected L or U? */
X			   if (strictlock)	  /* Already selected -L? */
X			       warn("Option -L overrides -U");
X                        }
X			else
X			    strictlock = false;
X                        break;
X
X                case 'n':    /*  add new association: error, if name exists */
X                        if ( (*argv)[2] == '\0') {
X                            error("Missing symbolic name after -n");
X                            break;
X                        }
X                        getassoclst(false, (*argv)+1);
X                        break;
X
X                case 'N':   /*  add or change association   */
X                        if ( (*argv)[2] == '\0') {
X                            error("Missing symbolic name after -N");
X                            break;
X                        }
X                        getassoclst(true, (*argv)+1);
X                        break;
X
X                case 'o':   /*  delete revisins  */
X                        if (delrev) warn("Redefinition of option -o");
X                        if ( (*argv)[2] == '\0' ) {
X                            error("Missing revision range after -o");
X                            break;
X                        }
X                        getdelrev( (*argv)+1 );
X                        break;
X
X                case 's':   /*  change state attribute of a revision  */
X                        if ( (*argv)[2] == '\0') {
X                            error("State missing after -s");
X                            break;
X                        }
X                        getstates( (*argv)+1);
X                        break;
X
X                case 't':   /*  change descriptive text   */
X                        textflag=true;
X                        if ((*argv)[2]!='\0'){
X                                if (textfile!=nil)warn("Redefinition of -t option");
X                                textfile = (*argv)+2;
X                        }
X                        break;
X
X                case 'q':
X                        quietflag = true;
X                        break;
X                default:
X                        faterror("Unknown option: %s\n%s", *argv, comdusge);
X                };
X        }  /* end processing of options */
X
X        if (argc<1) faterror("No input file\n%s", comdusge);
X        if (nerror) {   /*  exit, if any error in command options  */
X            diagnose("%s aborted",cmdid);
X            exit(1);
X        }
X        if (accessfile) /*  get replacement for access list   */
X            getrplaccess();
X        if (nerror) {
X            diagnose("%s aborted",cmdid);
X            exit(1);
X        }
X
X        /* now handle all filenames */
X        do {
X        rewriteflag = false;
X        finptr=frewrite=NULL;
X
X        if ( initflag ) {
X            switch( pairfilenames(argc, argv, false, false) ) {
X                case -1: break;        /*  not exist; ok */
X                case  0: continue;     /*  error         */
X                case  1: error("file %s exists already", RCSfilename);
X                         VOID fclose(finptr);
X                         continue;
X            }
X	}
X        else  {
X            switch( pairfilenames(argc, argv, true, false) ) {
X                case -1: continue;    /*  not exist      */
X                case  0: continue;    /*  errors         */
X                case  1: break;       /*  file exists; ok*/
X            }
X	}
X
X
X        /* now RCSfilename contains the name of the RCS file, and
X         * workfilename contains the name of the working file.
X         * if !initflag, finptr contains the file descriptor for the
X         * RCS file. The admin node is initialized.
X         */
X
X        diagnose("RCS file: %s", RCSfilename);
X
X        if (!trydiraccess(RCSfilename))            continue; /* give up */
X        if (!initflag && !checkaccesslist(caller)) continue; /* give up */
X        if (!trysema(RCSfilename,true))            continue; /* give up */
X
X        gettree(); /* read in delta tree */
X
X        /*  update admin. node    */
X        if (strict_selected) StrictLocks = strictlock;
X        if (commentflag) Comment = commsyml;
X
X        /* update default branch */
X        if (branchflag && expandsym(branchsym, branchnum)) {
X            if (countnumflds(branchnum)>0) {
X                branchdummy.num=branchnum;
X                Dbranch = &branchdummy;
X            } else
X                Dbranch = nil;
X        }
X
X        /*  update access list   */
X        if ( delaccessflag ) AccessList = nil;
X        if ( accessfile ) {
X            temp = rplaccessor;
X            while( temp ) {
X                temptr = temp->nextaccess;
X                if ( addnewaccess(temp) )
X                    temp->nextaccess = nil;
X                temp = temptr;
X            }
X        }
X        temp = rmvaccessor;
X        while(temp)   {         /*  remove accessors from accesslist   */
X            AccessList = removeaccess(temp, AccessList,true);
X            temp = temp->nextaccess;
X        }
X        temp = newaccessor;
X        while( temp)  {         /*  add new accessors   */
X            temptr = temp->nextaccess;
X            if ( addnewaccess( temp ) )
X                temp->nextaccess = nil;
X            temp = temptr;
X        }
X
X        updateassoc();          /*  update association list   */
X
X        updatelocks();          /*  update locks              */
X
X        /*  update state attribution  */
X        if (chgheadstate) {
X            /* change state of default branch or head */
X            if (Dbranch==nil) {
X                if (Head==nil)
X                     warn("Can't change states in an empty tree");
X                else Head->state = headstate;
X            } else {
X                rcs_setstate(Dbranch->num,headstate); /* Can't set directly */
X            }
X        }
X        curstate = statelst;
X        while( curstate ) {
X            rcs_setstate(curstate->revno,curstate->status);
X            curstate = curstate->nextstatus;
X        }
X
X        cuthead = cuttail = nil;
X        if ( delrev && removerevs()) {
X            /*  rebuild delta tree if some deltas are deleted   */
X            if ( cuttail )
X		VOID genrevs(cuttail->num, (char *)nil,(char *)nil,
X			     (char *)nil, gendeltas);
X            buildtree();
X        }
X
X
X        /* prepare for rewriting the RCS file */
X        newRCSfilename=mktempfile(RCSfilename,NEWRCSFILE);
X        oldumask = umask(0222); /* turn off write bits */
X        if ((frewrite=fopen(newRCSfilename, "w"))==NULL) {
X                VOID fclose(finptr);
X                error("Can't open file %s",newRCSfilename);
X                continue;
X        }
X        VOID umask(oldumask);
X        putadmin(frewrite);
X        if ( Head )
X           puttree(Head, frewrite);
X	putdesc(initflag,textflag,textfile,quietflag);
X        rewriteflag = false;
X
X        if ( Head) {
X            if (!delrev) {
X                /* no revision deleted */
X                fastcopy(finptr,frewrite);
X            } else {
X                if ( cuttail )
X                    buildeltatext(gendeltas);
X                else
X                    scanlogtext((struct hshentry *)nil,empty);
X                    /* copy rest of delta text nodes that are not deleted      */
X            }
X        }
X        ffclose(frewrite);   frewrite = NULL;
X        if ( ! nerror ) {  /*  move temporary file to RCS file if no error */
X	    ignoreints();		/* ignore interrupts */
X            if(rename(newRCSfilename,RCSfilename)<0) {
X                error("Can't create RCS file %s; saved in %s",
X                   RCSfilename, newRCSfilename);
X                newRCSfilename[0] = '\0';  /*  avoid deletion by cleanup  */
X                restoreints();
X                VOID cleanup();
X                break;
X            }
X            newRCSfilename[0]='\0'; /* avoid re-unlinking by cleanup()*/
X            /* update mode */
X            result=0;
X            if (!initflag) /* preserve mode bits */
X                result=chmod(RCSfilename,RCSstat.st_mode & ~0222);
X            elsif (haveworkstat==0)  /* initialization, and work file exists */
X                result=chmod(RCSfilename,workstat.st_mode & ~0222);
X            if (result<0) warn("Can't set mode of %s",RCSfilename);
X
X            restoreints();                /* catch them all again */
X            diagnose("done");
X        } else {
X	    diagnose("%s aborted; %s unchanged.",cmdid,RCSfilename);
X	    status = 1;
X	    nerror = 0;
X        }
X        } while (cleanup(),
X                 ++argv, --argc >=1);
X
X        exit(status);
X}       /* end of main (rcs) */
X
X
X
Xgetassoclst(flag, sp)
Xint     flag;
Xchar    * sp;
X/*  Function:   associate a symbolic name to a revision or branch,      */
X/*              and store in assoclst                                   */
X
X{
X        struct   Symrev  * pt;
X        char             * temp, *temp2;
X        int                c;
X
X        while( (c=(*++sp)) == ' ' || c == '\t' || c =='\n')  ;
X        temp = sp;
X        temp2=checkid(sp, ':');  /*  check for invalid symbolic name  */
X        sp = temp2; c = *sp;   *sp = '\0';
X        while( c == ' ' || c == '\t' || c == '\n')  c = *++sp;
X
X        if ( c != ':' && c != '\0') {
X	    error("Invalid string %s after option -n or -N",sp);
X            return;
X        }
X
X        pt = (struct Symrev *)talloc(sizeof(struct Symrev));
X        pt->ssymbol = temp;
X        pt->override = flag;
X	if (c == '\0')  /*  delete symbol  */
X            pt->revno = nil;
X        else {
X            while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
X	    if ( c == '\0' )
X                pt->revno = nil;
X	    else
X                pt->revno = sp;
X        }
X        pt->nextsym = nil;
X        if (lastassoc)
X            lastassoc->nextsym = pt;
X        else
X            assoclst = pt;
X        lastassoc = pt;
X        return;
X}
X
X
X
Xstruct access * getaccessor( sp)
Xchar            *sp;
X/*   Function:  get the accessor list of options -e and -a,     */
X/*              and store in curpt                              */
X
X
X{
X        struct  access  * curpt, * pt,  *pre;
X        char    *temp;
X        register c;
X
X        while( ( c = *++sp) == ' ' || c == '\n' || c == '\t' || c == ',') ;
X        if ( c == '\0') {
X            error("Missing login name after option -a or -e");
X            return nil;
X        }
X
X        curpt = pt = nil;
X        while( c != '\0') {
X                temp=checkid(sp,',');
X                pt = (struct access *)talloc(sizeof(struct access));
X                pt->login = sp;
X                if ( curpt )
X                    pre->nextaccess = pt;
X                else
X                    curpt = pt;
X                pt->nextaccess = curpt;
X                pre = pt;
X                sp = temp;    c = *sp;   *sp = '\0';
X                while( c == ' ' || c == '\n' || c == '\t'|| c == ',')c =(*++sp);
X        }
X        return pt;
X}
X
X
X
Xgetstates(sp)
Xchar    *sp;
X/*   Function:  get one state attribute and the corresponding   */
X/*              revision and store in statelst                  */
X
X{
X        char    *temp, *temp2;
X        struct  Status  *pt;
X        register        c;
X
X        while( (c=(*++sp)) ==' ' || c == '\t' || c == '\n')  ;
X        temp = sp;
X        temp2=checkid(sp,':');  /* check for invalid state attribute */
X        sp = temp2;   c = *sp;   *sp = '\0';
X        while( c == ' ' || c == '\t' || c == '\n' )  c = *++sp;
X
X        if ( c == '\0' ) {  /*  change state of def. branch or Head  */
X            chgheadstate = true;
X            headstate  = temp;
X            return;
X        }
X        else if ( c != ':' ) {
X            error("Missing ':' after state in option -s");
X            return;
X        }
X
X        while( (c = *++sp) == ' ' || c == '\t' || c == '\n')  ;
X        pt = (struct Status *)talloc(sizeof(struct Status));
X        pt->status     = temp;
X        pt->revno      = sp;
X        pt->nextstatus = nil;
X        if (laststate)
X            laststate->nextstatus = pt;
X        else
X            statelst = pt;
X        laststate = pt;
X}
X
X
X
Xgetrplaccess()
X/*   Function : get the accesslist of the 'accessfile'  */
X/*              and place in rplaccessor                */
X{
X        register        char    *id, *nextp;
X        struct          access  *newaccess, *oldaccess;
X
X        if ( (finptr=fopen(accessfile, "r")) == NULL) {
X            faterror("Can't open file %s", accessfile);
X        }
X        Lexinit();
X        nextp = &accessorlst[0];
X
X        if ( ! getkey(Khead)) faterror("Missing head in %s", accessfile);
X        VOID getnum();
X        if ( ! getlex(SEMI) ) serror("Missing ';' after head in %s",accessfile);
X
X        if (getkey(Kbranch)) { /* optional */
X                Dbranch=getnum();
X                if (!getlex(SEMI)) serror("Missing ';' after branch list");
X        }
X
X
X#ifdef COMPAT2
X        /* read suffix. Only in release 2 format */
X        if (getkey(Ksuffix)) {
X            if (nexttok==STRING) {
X                readstring(); nextlex(); /*through away the suffix*/
X            } elsif(nexttok==ID) {
X                nextlex();
X            }
X            if ( ! getlex(SEMI) ) serror("Missing ';' after suffix in %s",accessfile);
X        }
X#endif
X
X        if (! getkey(Kaccess))fatserror("Missing access list in %s",accessfile);
X        oldaccess = nil;
X        while( id =getid() ) {
X            newaccess = (struct access *)talloc(sizeof(struct access));
X            newaccess->login = nextp;
X            newaccess->nextaccess = nil;
X            while( ( *nextp++ = *id++) != '\0')  ;
X            if ( oldaccess )
X                oldaccess->nextaccess = newaccess;
X            else
X                rplaccessor = newaccess;
X            oldaccess = newaccess;
X        }
X        if ( ! getlex(SEMI))serror("Missing ';' after access list in %s",accessfile);
X        return;
X}
X
X
X
Xgetdelrev(sp)
Xchar    *sp;
X/*   Function:  get revision range or branch to be deleted,     */
X/*              and place in delrev                             */
X{
X        int    c;
X        struct  delrevpair      *pt;
X
X        if (delrev) free((char *)delrev);
X
X        pt = (struct delrevpair *)talloc(sizeof(struct delrevpair));
X        while((c = (*++sp)) == ' ' || c == '\n' || c == '\t') ;
X
X        if ( c == '<' || c == '-' ) {  /*  -o  -rev  or <rev  */
X            while( (c = (*++sp)) == ' ' || c == '\n' || c == '\t')  ;
X            pt->strt = sp;    pt->code = 1;
X            while( c != ' ' && c != '\n' && c != '\t' && c != '\0') c =(*++sp);
X            *sp = '\0';
X            pt->end = nil;  delrev = pt;
X            return;
X        }
X        else {
X            pt->strt = sp;
X            while( c != ' ' && c != '\n' && c != '\t' && c != '\0'
X                   && c != '-' && c != '<' )  c = *++sp;
X            *sp = '\0';
X            while( c == ' ' || c == '\n' || c == '\t' )  c = *++sp;
X            if ( c == '\0' )  {  /*   -o rev or branch   */
X                pt->end = nil;   pt->code = 0;
X                delrev = pt;
X                return;
X            }
X            if ( c != '-' && c != '<') {
X                faterror("Invalid range %s %s after -o", pt->strt, sp);
X                free((char *)pt);
X                return;
X            }
X            while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
X            if ( c == '\0') {  /*  -o   rev-   or   rev<   */
X                pt->end = nil;   pt->code = 2;
X                delrev = pt;
X                return;
X            }
X        }
X        /*   -o   rev1-rev2    or   rev1<rev2   */
X        pt->end = sp;  pt->code = 3;   delrev = pt;
X        while( c!= ' ' && c != '\n' && c != '\t' && c != '\0') c = *++sp;
X        *sp = '\0';
X}
X
X
X
X
Xscanlogtext(delta,func)
Xstruct hshentry * delta; enum stringwork func;
X/* Function: Scans delta text nodes up to and including the one given
X * by delta, or up to last one present, if delta==nil.
X * For the one given by delta (if delta!=nil), the log message is saved into
X * curlogmsg and the text is processed according to parameter func.
X * Assumes the initial lexeme must be read in first.
X * Does not advance nexttok after it is finished, except if delta==nil.
X */
X{       struct hshentry * nextdelta;
X
X        do {
X                rewriteflag = false;
X                nextlex();
X                if (!(nextdelta=getnum())) {
X                    if(delta)
X                        faterror("Can't find delta for revision %s", delta->num);
X                    else return; /* no more delta text nodes */
X                }
X                if ( nextdelta->selector != DELETE) {
X                        rewriteflag = true;
X                        VOID fprintf(frewrite,DELNUMFORM,nextdelta->num,Klog);
X                }
X                if (!getkey(Klog) || nexttok!=STRING)
X                        serror("Missing log entry");
X                elsif (delta==nextdelta) {
X                        VOID savestring(curlogmsg,logsize);
X                        delta->log=curlogmsg;
X                } else {readstring();
X                        if (delta!=nil) delta->log="";
X                }
X                nextlex();
X                if (!getkey(Ktext) || nexttok!=STRING)
X                        fatserror("Missing delta text");
X
X                if(delta==nextdelta)
X                        /* got the one we're looking for */
X                        switch (func) {
X                        case copy:      copystring();
X                                        break;
X                        case edit:      editstring((struct hshentry *)nil);
X                                        break;
X                        default:        faterror("Wrong scanlogtext");
X                        }
X                else    readstring(); /* skip over it */
X
X        } while (delta!=nextdelta);
X}
X
X
X
Xreleaselst(sourcelst)
Xstruct  access  * sourcelst;
X/*   Function:  release the storages whose address are in sourcelst   */
X
X{
X        struct  access  * pt;
X
X        pt = sourcelst;
X        while(pt) {
X	    struct access *pn = pt->nextaccess;
X            free((char *)pt);
X            pt = pn;
X        }
X}
X
X
X
Xstruct  Lockrev  * rmnewlocklst(which)
Xstruct  Lockrev  * which;
X/*   Function:  remove lock to revision which->revno from newlocklst   */
X
X{
X        struct  Lockrev   * pt, *pre;
X
X        while( newlocklst && (! strcmp(newlocklst->revno, which->revno))){
X	    struct Lockrev *pn = newlocklst->nextrev;
X            free((char *)newlocklst);
X	    newlocklst = pn;
X        }
X
X        pt = pre = newlocklst;
X        while( pt ) {
X            if ( ! strcmp(pt->revno, which->revno) ) {
X                pre->nextrev = pt->nextrev;
X                free((char *)pt);
X		pt = pre->nextrev;
X            }
X            else {
X                pre = pt;
X                pt = pt->nextrev;
X            }
X        }
X        return pre;
X}
X
X
X
Xstruct  access  * removeaccess( who, sourcelst,flag)
Xstruct  access  * who, * sourcelst;
Xint     flag;
X/*   Function:  remove the accessor-- who from sourcelst     */
X
X{
X        struct  access  *pt, *pre;
X
X        pt = sourcelst;
X        while( pt && (! strcmp(who->login, pt->login) )) {
X	    pre = pt->nextaccess;
X            free((char *)pt);
X	    pt = pre;
X            flag = false;
X	}
X        pre = sourcelst = pt;
X        while( pt ) {
X            if ( ! strcmp(who->login, pt->login) ) {
X		pre->nextaccess = pt->nextaccess;
X		free((char *)pt);
X		pt = pre->nextaccess;
X                flag = false;
X            }
X            else {
X                pre = pt;
X                pt = pt->nextaccess;
X            }
X        }
X        if ( flag ) warn("Can't remove a nonexisting accessor %s",who->login);
X        return sourcelst;
X}
X
X
X
Xint addnewaccess( who )
Xstruct  access  * who;
X/*   Function:  add new accessor-- who into AccessList    */
X
X{
X        struct  access  *pt,  *pre;
X
X        pre = pt = AccessList;
X
X        while( pt ) {
X            if ( strcmp( who->login, pt->login) ) {
X                pre = pt;
X                pt = pt->nextaccess;
X            }
X            else
X                return 0;
X        }
X        if ( pre == pt )
X            AccessList = who;
X        else
X            pre->nextaccess = who;
X        return 1;
X}
X
X
Xsendmail(Delta, who)
Xchar    * Delta,  *who;
X/*   Function:  mail to who, informing him that his lock on delta was
X *   broken by caller. Ask first whether to go ahead. Return false on
X *   error or if user decides not to break the lock.
X */
X{
X        char    * messagefile;
X        int   old1, old2, c, response;
X        FILE    * mailmess;
X
X
X	VOID fprintf(stderr, "Revision %s is already locked by %s.\n", Delta, who);
X        VOID fprintf(stderr, "Do you want to break the lock? [ny](n): ");
X        response=c=getchar();
X        while (!(c==EOF || c=='\n')) c=getchar();/*skip to end of line*/
X        if (response=='\n'||response=='n'||response=='N') return false;
X
X        /* go ahead with breaking  */
X        messagefile=mktempfile("/tmp/", "RCSmailXXXXXX");
X        if ( (mailmess = fopen(messagefile, "w")) == NULL) {
X            faterror("Can't open file %s", messagefile);
X        }
X
X	VOID fprintf(mailmess, "Subject: Broken lock on %s\n\n",bindex(RCSfilename,'/'));
X        VOID fprintf(mailmess, "Your lock on revision %s of file %s\n",Delta, getfullRCSname());
X        VOID fprintf(mailmess,"has been broken by %s for the following reason:\n",caller);
X        VOID fputs("State the reason for breaking the lock:\n", stderr);
X        VOID fputs("(terminate with ^D or single '.')\n>> ", stderr);
X
X        old1 = '\n';    old2 = ' ';
X        for (; ;) {
X            c = getchar();
X            if ( c == EOF ) {
X                VOID putc('\n',stderr);
X                VOID fprintf(mailmess, "%c\n", old1);
X                break;
X            }
X            else if ( c == '\n' && old1 == '.' && old2 == '\n')
X                break;
X            else {
X                VOID fputc( old1, mailmess);
X                old2 = old1;   old1 = c;
X                if (c== '\n') VOID fputs(">> ", stderr);
X            }
X        }
X        ffclose(mailmess);
X
X	/* ignore the exit status, even if delivermail unsuccessful */
X        VOID run(messagefile,(char*)nil,
X#ifdef SENDMAIL
X		"/usr/lib/sendmail",
X#else
X#  ifdef DELIVERMAIL
X		"/etc/delivermail","-w",
X#  else
X		"/bin/mail",
X#  endif
X#endif
X		who,(char*)nil);
X        VOID unlink(messagefile);
X	return(true);
X}
X
X
X
Xstatic breaklock(who,delta)
Xchar * who; struct hshentry * delta;
X/* function: Finds the lock held by who on delta,
X * and removes it.
X * Sends mail if a lock different from the caller's is broken.
X * Prints an error message if there is no such lock or error.
X */
X{
X        register struct lock * next, * trail;
X        char * num;
X        struct lock dummy;
X        int whor, numr;
X
X	num=delta->num;
X        dummy.nextlock=next=Locks;
X        trail = &dummy;
X        while (next!=nil) {
X		if (num != nil)
X			numr = strcmp(num, next->delta->num);
X
X		whor=strcmp(who,next->login);
X		if (whor==0 && numr==0) break; /* exact match */
X		if (numr==0 && whor !=0) {
X                        if (!sendmail( num, next->login)){
X                            diagnose("%s still locked by %s",num,next->login);
X			    return;
X                        } else break; /* continue after loop */
X                }
X                trail=next;
X                next=next->nextlock;
X        }
X        if (next!=nil) {
X                /*found one */
X                diagnose("%s unlocked",next->delta->num);
X                trail->nextlock=next->nextlock;
X                next->delta->lockedby=nil;
X                Locks=dummy.nextlock;
X        } else  {
X		error("no lock set on revision %s", num);
X        }
X}
X
X
X
Xstruct hshentry *searchcutpt(object, length, store)
Xchar    * object;
Xint     length;
Xstruct  hshentry   * * store;
X/*   Function:  Search store and return entry with number being object. */
X/*              cuttail = nil, if the entry is Head; otherwise, cuttail */
X/*              is the entry point to the one with number being object  */
X
X{
X        while( compartial( (*store++)->num, object, length)  )  ;
X        store--;
X
X        if ( *store == Head)
X            cuthead = nil;
X        else
X            cuthead = *(store -1);
X        return *store;
X}
X
X
X
Xint  branchpoint(strt, tail)
Xstruct  hshentry        *strt,  *tail;
X/*   Function: check whether the deltas between strt and tail	*/
X/*		are locked or branch point, return 1 if any is  */
X/*		locked or branch point; otherwise, return 0 and */
X/*              mark DELETE on selector                         */
X
X{
X        struct  hshentry    *pt;
X	struct lock  *lockpt;
X        int     flag;
X
X
X        pt = strt;
X        flag = false;
X        while( pt != tail) {
X            if ( pt->branches ){ /*  a branch point  */
X                flag = true;
X                error("Can't remove branch point %s", pt->num);
X            }
X	    lockpt = Locks;
X	    while(lockpt && lockpt->delta != pt)
X		lockpt = lockpt->nextlock;
X	    if ( lockpt ) {
X		flag = true;
X		error("Can't remove locked revision %s",pt->num);
X	    }
X            pt = pt->next;
X        }
X
X        if ( ! flag ) {
X            pt = strt;
X            while( pt != tail ) {
X                pt->selector = DELETE;
X                diagnose("deleting revision %s ",pt->num);
X                pt = pt->next;
X            }
X        }
X        return flag;
X}
X
X
X
Xremoverevs()
X/*   Function:  get the revision range to be removed, and place the     */
X/*              first revision removed in delstrt, the revision before  */
X/*              delstrt in cuthead( nil, if delstrt is head), and the   */
X/*              revision after the last removed revision in cuttail(nil */
X/*              if the last is a leaf                                   */
X
X{
X        struct  hshentry    *target, *target2, * temp, *searchcutpt();
X        int     length, flag;
X
X        flag = false;
X        if ( ! expandsym(delrev->strt, &numrev[0]) ) return 0;
X        target = genrevs(&numrev[0], (char *)nil, (char *)nil, (char *)nil, gendeltas);
X        if ( ! target ) return 0;
X        if ( cmpnum(target->num, &numrev[0]) ) flag = true;
X        length = countnumflds( &numrev[0] );
X
X        if ( delrev->code == 0 ) {  /*  -o  rev    or    -o  branch   */
X	    if ( length % 2)
X		temp=searchcutpt(target->num,length+1,gendeltas);
X	    else if (flag) {
X                error("Revision %s does not exist", &numrev[0]);
X		return 0;
X	    }
X	    else
X		temp = searchcutpt(&numrev[0],length,gendeltas);
X	    cuttail = target->next;
X            if ( branchpoint(temp, cuttail) ) {
X                cuttail = nil;
X                return 0;
X            }
X            delstrt = temp;     /* first revision to be removed   */
X            return 1;
X        }
X
X        if ( length % 2 ) {   /*  invalid branch after -o   */
X            error("Invalid branch range %s after -o", &numrev[0]);
X            return 0;
X        }
X
X        if ( delrev->code == 1 )  {  /*  -o  -rev   */
X            if ( length > 2 ) {
X                temp = searchcutpt( target->num, length-1, gendeltas);
X                cuttail = target->next;
X            }
X            else {
X                temp = searchcutpt(target->num, length, gendeltas);
X                cuttail = target;
X                while( cuttail && ! cmpnumfld(target->num,cuttail->num,1) )
X                    cuttail = cuttail->next;
X            }
X            if ( branchpoint(temp, cuttail) ){
X                cuttail = nil;
X                return 0;
X            }
X            delstrt = temp;
X            return 1;
X        }
X
X        if ( delrev->code == 2 )  {   /*  -o  rev-   */
X            if ( length == 2 ) {
X                temp = searchcutpt(target->num, 1,gendeltas);
X                if ( flag)
X                    cuttail = target;
X                else
X                    cuttail = target->next;
X            }
X            else  {
X                if ( flag){
X                    cuthead = target;
X                    if ( !(temp = target->next) ) return 0;
X                }
X                else
X                    temp = searchcutpt(target->num, length, gendeltas);
X                getbranchno(temp->num, &numrev[0]);  /*  get branch number  */
X                target = genrevs(&numrev[0], (char *)nil, (char *)nil, (char *)nil, gendeltas);
X            }
X            if ( branchpoint( temp, cuttail ) ) {
X                cuttail = nil;
X                return 0;
X            }
X            delstrt = temp;
X            return 1;
X        }
X
X        /*   -o   rev1-rev2   */
X        if ( ! expandsym(delrev->end, &numrev[0])  )  return 0;
X        if ( length != countnumflds( &numrev[0] ) ) {
X            error("Invalid revision range %s-%s", target->num, &numrev[0]);
X            return 0;
X        }
X        if ( length > 2 && compartial( &numrev[0], target->num, length-1) ) {
X            error("Invalid revision range %s-%s", target->num, &numrev[0]);
X            return 0;
X        }
X
X        target2 = genrevs( &numrev[0], (char *)nil, (char *)nil, (char *)nil,gendeltas);
X        if ( ! target2 ) return 0;
X
X        if ( length > 2) {  /* delete revisions on branches  */
X            if ( cmpnum(target->num, target2->num) > 0) {
X                if ( cmpnum(target2->num, &numrev[0]) )
X                    flag = true;
X                else
X                    flag = false;
X                temp = target;
X                target = target2;
X                target2 = temp;
X            }
X            if ( flag ) {
X                if ( ! cmpnum(target->num, target2->num) ) {
X                    error("Revisions %s-%s don't exist", delrev->strt,delrev->end);
X                    return 0;
X                }
X                cuthead = target;
X                temp = target->next;
X            }
X            else
X                temp = searchcutpt(target->num, length, gendeltas);
X            cuttail = target2->next;
X        }
X        else { /*  delete revisions on trunk  */
X            if ( cmpnum( target->num, target2->num) < 0 ) {
X                temp = target;
X                target = target2;
X                target2 = temp;
X            }
X            else
X                if ( cmpnum(target2->num, &numrev[0]) )
X                    flag = true;
X                else
X                    flag = false;
X            if ( flag ) {
X                if ( ! cmpnum(target->num, target2->num) ) {
X                    error("Revisions %s-%s don't exist", delrev->strt, delrev->end);
X                    return 0;
X                }
X                cuttail = target2;
X            }
X            else
X                cuttail = target2->next;
X            temp = searchcutpt(target->num, length, gendeltas);
X        }
X        if ( branchpoint(temp, cuttail) )  {
X            cuttail = nil;
X            return 0;
X        }
X        delstrt = temp;
X        return 1;
X}
X
X
X
Xupdateassoc()
X/*   Function: add or delete(if revno is nil) association	*/
X/*		which is stored in assoclst			*/
X
X{
X        struct  Symrev  * curassoc;
X	struct  assoc   * pre,  * pt;
X        struct  hshentry    * target;
X
X        /*  add new associations   */
X        curassoc = assoclst;
X        while( curassoc ) {
X            if ( curassoc->revno == nil ) {  /* delete symbol  */
X		pre = pt = Symbols;
X                while( pt && strcmp(pt->symbol,curassoc->ssymbol) ) {
X		    pre = pt;
X		    pt = pt->nextassoc;
X		}
X		if ( pt )
X		    if ( pre == pt )
X			Symbols = pt->nextassoc;
X		    else
X			pre->nextassoc = pt->nextassoc;
X		else
X                    warn("Can't delete nonexisting symbol %s",curassoc->ssymbol);
X	    }
X            else if ( expandsym( curassoc->revno, &numrev[0] ) ) {
X	    /*   add symbol  */
X               target = (struct hshentry *) talloc(sizeof(struct hshentry));
X               target->num = &numrev[0];
X               VOID addsymbol(target, curassoc->ssymbol, curassoc->override);
X            }
X            curassoc = curassoc->nextsym;
X        }
X
X}
X
X
X
Xupdatelocks()
X/* Function: remove lock for caller or first lock if unlockcaller==true;
X *           remove locks which are stored in rmvlocklst,
X *           add new locks which are stored in newlocklst,
X *           add lock for Dbranch or Head if lockhead==true.
X */
X{
X        struct  hshentry        *target;
X        struct  Lockrev         *lockpt;
X
X        if(unlockcaller == true) { /*  find lock for caller  */
X            if ( Head ) {
X		if (Locks) {
X		    target=findlock(caller,true);
X		    if (target==nil) {
X			breaklock(caller, Locks->delta); /* remove most recent lock */
X		    } else {
X			diagnose("%s unlocked",target->num);
X		    }
X		} else {
X		    warn("There are no locks set.");
X		}
X            } else {
X                warn("Can't unlock an empty tree");
X            }
X        }
X
X        /*  remove locks which are stored in rmvlocklst   */
X        lockpt = rmvlocklst;
X        while( lockpt ) {
X	    if (expandsym(lockpt->revno, numrev)) {
X		target = genrevs(numrev, (char *)nil, (char *)nil, (char *)nil, gendeltas);
X                if ( target )
X		   if ( !(countnumflds(numrev)%2) && cmpnum(target->num,numrev))
X			error("Can't unlock nonexisting revision %s",lockpt->revno);
X                   else
X                        breaklock(caller, target);
X                        /* breaklock does its own diagnose */
X            }
X            lockpt = lockpt->nextrev;
X        }
X
X        /*  add new locks which stored in newlocklst  */
X        lockpt = newlocklst;
X        while( lockpt ) {
X            setlock(lockpt->revno,caller);
X            lockpt = lockpt->nextrev;
X        }
X
X        if ( lockhead == true) {  /*  lock default branch or head  */
X            if (Dbranch) {
X                setlock(Dbranch->num,caller);
X            } elsif ( Head) {
X                if (addlock(Head, caller))
X                    diagnose("%s locked",Head->num);
X            } else {
X                warn("Can't lock an empty tree");
X            }
X        }
X
X}
X
X
X
Xsetlock(rev,who)
Xchar * rev, * who;
X/* Function: Given a revision or branch number, finds the correponding
X * delta and locks it for who.
X */
X{
X        struct  lock     *lpt;
X        struct  hshentry *target;
X
X        if (expandsym(rev, &numrev[0]) ){
X            target = genrevs(&numrev[0],(char *) nil,(char *) nil,
X			     (char *)nil, gendeltas);
X            if ( target )
X               if ( !(countnumflds(&numrev[0])%2) && cmpnum(target->num,&numrev[0]))
X                    error("Can't lock nonexisting revision %s",numrev);
X               else
X                    if(lpt=addlock(target, who))
X                        diagnose("%s locked",lpt->delta->num);
X        }
X}
X
X
X
Xrcs_setstate(rev,status)
Xchar * rev, * status;
X/* Function: Given a revision or branch number, finds the corresponding delta
X * and sets its state to status.
X */
X{
X        struct  hshentry *target;
X
X        if ( expandsym(rev, &numrev[0]) ) {
X            target = genrevs(&numrev[0],(char *) nil, (char *)nil,
X			     (char *) nil, gendeltas);
X            if ( target )
X               if ( !(countnumflds(&numrev[0])%2) && cmpnum(target->num, &numrev[0]) )
X                    error("Can't set state of nonexisting revision %s to %s",
X                           numrev,status);
X               else
X                    target->state = status;
X        }
X}
X
X
X
X
X
Xbuildeltatext(deltas)
Xstruct  hshentry        ** deltas;
X/*   Function:  put the delta text on frewrite and make necessary   */
X/*              change to delta text                                */
X{
X        int  i, c, exit_stats;
X
X        cuttail->selector = DELETE;
X        initeditfiles("/tmp/");
X        scanlogtext(deltas[0], copy);
X        i = 1;
X        if ( cuthead )  {
X            cutfilename=mktempfile("/tmp/", "RCScutXXXXXX");
X            if ( (fcut = fopen(cutfilename, "w")) == NULL) {
X                faterror("Can't open temporary file %s", cutfilename);
X            }
X
X            while( deltas[i-1] != cuthead )  {
X                scanlogtext(deltas[i++], edit);
X            }
X
X            finishedit((struct hshentry *)nil);    rewind(fcopy);
X            while( (c = getc(fcopy)) != EOF) VOID putc(c, fcut);
X            swapeditfiles(false);
X            ffclose(fcut);
X        }
X
X        while( deltas[i-1] != cuttail)
X            scanlogtext(deltas[i++], edit);
X        finishedit((struct hshentry *)nil);    ffclose(fcopy);
X
X        if ( cuthead ) {
X            diffilename=mktempfile("/tmp/", "RCSdifXXXXXX");
X            exit_stats = run((char*)nil,diffilename,
X			DIFF,"-n",cutfilename,resultfile,(char*)nil);
X            if (exit_stats != 0 && exit_stats != (1 << BYTESIZ))
X                faterror ("diff failed");
X            if(!putdtext(cuttail->num,curlogmsg,diffilename,frewrite)) return;
X        }
X        else
X            if (!putdtext(cuttail->num,curlogmsg,resultfile,frewrite)) return;
X
X        scanlogtext((struct hshentry *)nil,empty); /* read the rest of the deltas */
X}
X
X
X
Xbuildtree()
X/*   Function:  actually removes revisions whose selector field  */
X/*              is DELETE, and rebuilds  the linkage of deltas.  */
X/*              asks for reconfirmation if deleting last revision*/
X{
X	int c,  response;
X
X	struct	hshentry   * Delta;
X        struct  branchhead      *pt, *pre;
X
X        if ( cuthead )
X           if ( cuthead->next == delstrt )
X                cuthead->next = cuttail;
X           else {
X                pre = pt = cuthead->branches;
X                while( pt && pt->hsh != delstrt )  {
X                    pre = pt;
X                    pt = pt->nextbranch;
X                }
X                if ( cuttail )
X                    pt->hsh = cuttail;
X                else if ( pt == pre )
X                    cuthead->branches = pt->nextbranch;
X                else
X                    pre->nextbranch = pt->nextbranch;
X            }
X	else {
X            if ( cuttail == nil && !quietflag) {
X                VOID fprintf(stderr,"Do you really want to delete all revisions ?[ny](n): ");
X		c = response = getchar();
X		while( c != EOF && c != '\n') c = getchar();
X                if ( response != 'y' && response != 'Y') {
X                    diagnose("No revision deleted");
X		    Delta = delstrt;
X		    while( Delta) {
X			Delta->selector = 'S';
X			Delta = Delta->next;
X		    }
X		    return;
X		}
X	    }
X            Head = cuttail;
X	}
X        return;
X}
X
X@
X
X
X4.11.2.1
Xlog
X@Start of Amiga RCS port branch.
X@
Xtext
X@d6 1
Xa6 5
X<<<<<<< rcs.c
X"$Header: /u/softeng/rsbx/rcs/amiga/RCS.cbmvax/rcs.c,v 4.11.1.1 89/08/11 01:42:01 rsbx Exp Locker: rsbx $ Purdue CS";
X=======
X"$Header: /u/softeng/rsbx/rcs/amiga/RCS/rcs.c,v 1.2 89/09/17 13:35:01 rick Exp $ Purdue CS";
X>>>>>>> 1.2
Xa36 11
X<<<<<<< rcs.c
X * Revision 4.11.1.1  89/08/11  01:42:01  rsbx
X * Start of cbmvax RCS source branch.
X=======
X * Revision 1.2  89/09/17  13:35:01  rick
X * Port to AmigaDos done by Rick Schaeffer (ricks@@iscuva.iscs.com)
X * All changes done with conditional compile (#ifdef AMIGA).  This version
X * compiles correctly with Lattice C version 5.02 or later.
X>>>>>>> 1.2
X * 
X<<<<<<< rcs.c
Xa37 3
X * checked in with -k by rsbx at 89.08.10.16.06.26.
X * 
X * Revision 4.11  89/05/01  15:12:06  narten
Xa53 11
X=======
X * Revision 1.4  89/09/16  09:42:43  rick
X * Modified AMIGA changes to work with Lattice C
X * 
X * Revision 1.3  89/09/10  09:26:56  rick
X * Moved TARGETDIR to rcs:
X * 
X * Revision 1.2  88/09/03  15:08:35  rick
X * Port to AmigaDos.  All done with conditional compiles
X * 
X>>>>>>> 1.2
Xa133 3
X#ifdef AMIGA
X#include "stat.h"
X#else
Xa135 9
X<<<<<<< rcs.c
X=======
X#endif
X#ifdef BSD
X#include <sysexits.h>
X#else
X#define EX_OK 0
X#endif
X>>>>>>> 1.2
Xa578 5
X#ifdef AMIGA
X	    if (finptr != NULL)
X	    	fclose(finptr);
X	    unlink(RCSfilename);
X#endif
Xa590 3
X#ifdef AMIGA
X                result=chmod(RCSfilename,RCSstat.st_attr | S_IWRITE);
X#else
Xa591 1
X#endif
Xa592 3
X#ifdef AMIGA
X                result=chmod(RCSfilename,workstat.st_attr | S_IWRITE);
X#else
Xa593 1
X#endif
Xd673 1
Xa673 1
X        curpt = pt = pre = nil;
Xd1007 1
Xa1007 3
X#ifdef AMIGA
X	return false;
X#else
Xa1057 1
X#endif AMIGA
Xa1060 1
X<<<<<<< rcs.c
Xa1062 3
X=======
Xstruct hshentry * breaklock(who,delta)
X>>>>>>> 1.2
Xa1485 3
X#ifdef AMIGA
X            cutfilename=mktempfile("t:", "RCScutXXXXXX");
X#else
Xa1486 1
X#endif
Xa1505 3
X#ifdef AMIGA
X            diffilename=mktempfile("t:", "RCSdifXXXXXX");
X#else
Xa1506 1
X<<<<<<< rcs.c
Xa1508 5
X=======
X#endif
X            VOID sprintf(command, "%s -n %s %s >%s", DIFF,cutfilename, resultfile, diffilename);
X            exit_stats = system (command);
X>>>>>>> 1.2
Xa1549 3
X#ifdef AMIGA
X		fflush(stderr);
X#endif
X@
X
X
X4.11.2.2
Xlog
X@Finished the integration of Rick Schaeffer's RCS Amiga port with the RCS
Xsources I have here (and are later than the ones Rick used).
X@
Xtext
X@d6 5
Xa10 1
X"$Header: /u/softeng/rsbx/rcs/amiga/RCS/rcs.c,v 4.11.2.1 89/10/13 19:17:47 rsbx Exp Locker: rsbx $ Purdue CS";
Xd41 1
Xa41 3
X * Revision 4.11.2.1  89/10/13  19:17:47  rsbx
X * Start of Amiga RCS port branch.
X * 
Xd44 6
Xd51 1
Xd72 11
Xd168 2
Xd176 1
Xa1049 1
X#ifdef AMIGA
Xd1061 2
Xa1062 40
X	VOID fprintf(stderr, "Revision %s is already locked by %s.\n", Delta, who);
X        VOID fprintf(stderr, "Do you want to break the lock? [ny](n): ");
X        response=c=getchar();
X        while (!(c==EOF || c=='\n')) c=getchar();/*skip to end of line*/
X        if (response=='\n'||response=='n'||response=='N') return false;
X
X        /* go ahead with breaking  */
X        messagefile=mktempfile("/tmp/", "RCSmailXXXXXX");
X        if ( (mailmess = fopen(messagefile, "w")) == NULL) {
X            faterror("Can't open file %s", messagefile);
X        }
X
X	VOID fprintf(mailmess, "Subject: Broken lock on %s\n\n",bindex(RCSfilename,'/'));
X        VOID fprintf(mailmess, "Your lock on revision %s of file %s\n",Delta, getfullRCSname());
X        VOID fprintf(mailmess,"has been broken by %s for the following reason:\n",caller);
X        VOID fputs("State the reason for breaking the lock:\n", stderr);
X        VOID fputs("(terminate with ^D or single '.')\n>> ", stderr);
X
X        old1 = '\n';    old2 = ' ';
X        for (; ;) {
X            c = getchar();
X            if ( c == EOF ) {
X                VOID putc('\n',stderr);
X                VOID fprintf(mailmess, "%c\n", old1);
X                break;
X            }
X            else if ( c == '\n' && old1 == '.' && old2 == '\n')
X                break;
X            else {
X                VOID fputc( old1, mailmess);
X                old2 = old1;   old1 = c;
X                if (c== '\n') VOID fputs(">> ", stderr);
X            }
X        }
X        ffclose(mailmess);
X
X	/* ignore the exit status, even if delivermail unsuccessful */
X        VOID run(messagefile,(char*)nil,
X#ifdef SENDMAIL
X		"/usr/lib/sendmail",
Xa1063 24
X#  ifdef DELIVERMAIL
X		"/etc/delivermail","-w",
X#  else
X		"/bin/mail",
X#  endif
X#endif
X		who,(char*)nil);
X        VOID unlink(messagefile);
X	return(true);
X}
X
X#else
X
Xsendmail(Delta, who)
Xchar    * Delta,  *who;
X/*   Function:  mail to who, informing him that his lock on delta was
X *   broken by caller. Ask first whether to go ahead. Return false on
X *   error or if user decides not to break the lock.
X */
X{
X        char    * messagefile;
X        int   old1, old2, c, response;
X        FILE    * mailmess;
X
Xd1114 1
Xa1115 1
X#endif AMIGA
Xd1118 1
Xd1120 4
Xa1123 1
Xstatic void breaklock(who,delta)
Xd1307 1
Xa1307 1
X                genrevs(&numrev[0], (char *)nil, (char *)nil, (char *)nil, gendeltas);
Xd1575 1
Xa1575 2
X#endif
X/* <<<<<<< rcs.c */
Xd1578 2
Xa1579 1
X/* =======
Xd1582 1
Xa1582 1
X>>>>>>> 1.2 */
X@
X
X
X4.11.2.3
Xlog
X@Changed file path handling to deal with Amiga file path sematics.
X@
Xtext
X@d6 1
Xa6 1
X"$Header: /u/softeng/rsbx/rcs/amiga/RCS/rcs.c,v 4.11.2.2 89/10/15 15:43:36 rsbx Exp $ Purdue CS";
Xa36 4
X * Revision 4.11.2.2  89/10/15  15:43:36  rsbx
X * Finished the integration of Rick Schaeffer's RCS Amiga port with the RCS
X * sources I have here (and are later than the ones Rick used).
X * 
Xd160 1
Xa160 1
Xextern char * fnamepart();
Xd1051 1
Xa1051 1
X	VOID fprintf(mailmess, "Subject: Broken lock on %s\n\n",fnamepart(RCSfilename));
Xd1116 1
Xa1116 1
X	VOID fprintf(mailmess, "Subject: Broken lock on %s\n\n",fnamepart(RCSfilename));
X@
X
X
X4.11.2.4
Xlog
X@Changes to sendmail().
X@
Xtext
X@d6 1
Xa6 1
X"$Header: /u/softeng/rsbx/rcs/amiga/RCS/rcs.c,v 4.11.2.3 89/10/16 19:06:13 rsbx Exp $ Purdue CS";
Xa36 3
X * Revision 4.11.2.3  89/10/16  19:06:13  rsbx
X * Changed file path handling to deal with Amiga file path sematics.
X * 
Xd1031 11
Xd1043 54
Xa1103 4
X#ifdef AMIGA
X	VOID fprintf(stderr, "Breaking of locks not supported by this Amiga RCS revision\n");
X	return false;
X#else
Xd1158 1
Xa1159 1
X}
Xd1616 1
Xd1619 4
X@
X
X
X4.11.2.5
Xlog
X@NULL finptr after closing it.
X@
Xtext
X@d6 1
Xa6 1
X"$Header: /u/softeng/rsbx/rcs/amiga/RCS/rcs.c,v 4.11.2.4 89/10/17 13:17:35 rsbx Exp $ Purdue CS";
Xa36 3
X * Revision 4.11.2.4  89/10/17  13:17:35  rsbx
X * Changes to sendmail().
X * 
Xd606 1
Xa606 1
X	    	fclose(finptr); finptr = NULL;
X@
X
X
X4.11.2.6
Xlog
X@Changes to make the delete bit to track the write bit. Made protection
Xbit manipulation less insane.
X@
Xtext
X@d6 1
Xa6 1
X"$Header: /u/softeng/rsbx/rcs/amiga/RCS/rcs.c,v 4.11.2.5 89/10/30 13:38:35 rsbx Exp $ Purdue CS";
Xa36 3
X * Revision 4.11.2.5  89/10/30  13:38:35  rsbx
X * NULL finptr after closing it.
X * 
Xd608 3
Xa610 8
X	    if (finptr != NULL) {
X		fclose(finptr); finptr = NULL;
X		if (chmod(RCSfilename, RCSstat.st_attr|S_IDELETE)<0)
X			warn("Can't adjust mode of %s",RCSfilename);
X		if (unlink(RCSfilename) != 0) {            /* Remove failed   */
X			error("Can't unlink %s",RCSfilename);
X			}
X		}
Xd625 1
Xa625 1
X                result=chmod(RCSfilename,RCSstat.st_attr & ~(S_IWRITE|S_IDELETE));
Xd631 1
Xa631 1
X                result=chmod(RCSfilename,workstat.st_attr & ~(S_IWRITE|S_IDELETE));
Xd1533 1
Xa1533 1
X            cutfilename=mktempfile("t:", "RCScutXXXXXXXX");
Xd1535 1
Xa1535 1
X            cutfilename=mktempfile("/tmp/", "RCScutXXXXXXXX");
Xd1557 1
Xa1557 1
X            diffilename=mktempfile("t:", "RCSdifXXXXXXXX");
Xd1559 1
Xa1559 1
X            diffilename=mktempfile("/tmp/", "RCSdifXXXXXXXX");
X@
X
X
X4.11.2.7
Xlog
X@Fixed a "/tmp/" that slipped through.
X@
Xtext
X@d6 1
Xa6 1
X"$Header: /u/softeng/rsbx/rcs/amiga/RCS/rcs.c,v 4.11.2.6 89/11/01 14:42:32 rsbx Exp $ Purdue CS";
Xa36 4
X * Revision 4.11.2.6  89/11/01  14:42:32  rsbx
X * Changes to make the delete bit to track the write bit. Made protection
X * bit manipulation less insane.
X * 
Xa1535 3
X#ifdef AMIGA
X        initeditfiles("t:");
X#else
Xa1536 1
X#endif
X@
X
X
X4.11.2.8
Xlog
X@WaitChild() on the Amiga returns the completion code from a child process
Xwithout shifting it up 8 bits like the Unix wait() does.
X@
Xtext
X@d6 1
Xa6 1
X"$Header: /u/softeng/rsbx/rcs/amiga/RCS/rcs.c,v 4.11.2.7 89/11/05 23:14:17 rsbx Exp $ Purdue CS";
Xa36 3
X * Revision 4.11.2.7  89/11/05  23:14:17  rsbx
X * Fixed a "/tmp/" that slipped through.
X * 
Xa1578 3
X#ifdef AMIGA
X            if (exit_stats != 0 && exit_stats != 1)
X#else
Xa1579 1
X#endif
X@
X
X
X4.11.1.1
Xlog
X@Start of cbmvax RCS source branch.
X@
Xtext
X@d6 1
Xa6 1
X"$Header: /u/softeng/rsbx/rcs/rcs.uunet/src/RCS/rcs.c,v 4.11 89/05/01 15:12:06 narten Exp $ Purdue CS";
Xa36 3
X * Revision 4.11  89/05/01  15:12:06  narten
X * checked in with -k by rsbx at 89.08.10.16.06.26.
X * 
X@
SHAR_EOF
echo "End of archive 7 (of 14)"
# if you want to concatenate archives, remove anything after this line
exit