12150Seric # include "../hdr/defines.h"
22150Seric # include "../hdr/had.h"
32150Seric 
4*33423Sbostic static char Sccsid[] = "@(#)rmchg.c	4.4	02/02/88";
52150Seric 
62150Seric /*
72150Seric 	Program to remove a specified delta from an SCCS file,
82150Seric 	when invoked as 'rmdel',
92150Seric 	or to change the MRs and/or comments of a specified delta,
102150Seric 	when invoked as 'chghist'.
112150Seric 	(The program has two links to it, one called 'rmdel', the
122150Seric 	other 'chghist'.)
132150Seric 
142150Seric 	The delta to be removed (or whose MRs and/or comments
152150Seric 	are to be changed) is specified via the
162150Seric 	r argument, in the form of an SID.
172150Seric 
182150Seric 	If the delta is to be removed, it must be the most recent one
192150Seric 	in its branch in the delta tree (a so-called 'leaf' delta).
202150Seric 	For either function, the delta being processed must not
212150Seric 	have any 'delivered' MRs, and the user must have basically
222150Seric 	the same permissions as are required to make deltas.
232150Seric 
242150Seric 	If a directory is given as an argument, each SCCS file
252150Seric 	within the directory will be processed as if it had been
262150Seric 	specifically named. If a name of '-' is given, the standard
272150Seric 	input will be read for a list of names of SCCS files to be
282150Seric 	processed. Non SCCS files are ignored.
292150Seric */
302150Seric 
312150Seric # define COPY 0
322150Seric # define NOCOPY 1
332150Seric 
342150Seric struct sid sid;
352150Seric int num_files;
362150Seric char had[26];
372150Seric char D_type;
382150Seric int D_serial;
392150Seric 
main(argc,argv)402150Seric main(argc,argv)
412150Seric int argc;
422150Seric char *argv[];
432150Seric {
442150Seric 	register int i;
452150Seric 	register char *p;
462150Seric 	char c;
472150Seric 	extern rmchg();
482150Seric 	extern int Fcnt;
492150Seric 
502150Seric 	/*
512150Seric 	Set flags for 'fatal' to issue message, call clean-up
522150Seric 	routine, and terminate processing.
532150Seric 	*/
542150Seric 	Fflags = FTLMSG | FTLCLN | FTLEXIT;
552150Seric 
562150Seric 	for(i=1; i<argc; i++)
572150Seric 		if(argv[i][0] == '-' && (c = argv[i][1])) {
582150Seric 			p = &argv[i][2];
592150Seric 			switch (c) {
602150Seric 
612150Seric 			case 'r':
622150Seric 				if (!(*p))
632150Seric 					fatal("r has no sid (rc11)");
642150Seric 				chksid(sid_ab(p,&sid),&sid);
652150Seric 				break;
662150Seric 			default:
672150Seric 				fatal("unknown key letter (cm1)");
682150Seric 			}
692150Seric 
702150Seric 			if (had[c - 'a']++)
712150Seric 				fatal("key letter twice (cm2)");
722150Seric 			argv[i] = 0;
732150Seric 		}
742150Seric 		else
752150Seric 			num_files++;
762150Seric 
772150Seric 	if(num_files == 0)
782150Seric 		fatal("missing file arg (cm3)");
792150Seric 
802150Seric 	if (*(p = sname(argv[0])) == 'n')
812150Seric 		p++;
822150Seric 	if (equal(p,"rmdel"))
832150Seric 		D_type = 'R';		/* invoked as 'rmdel' */
842150Seric 	else if (equal(p,"chghist"))
852150Seric 		D_type = 'D';		/* invoked as 'chghist' */
862150Seric 	else
872150Seric 		fatal("bad invocation (rc10)");
882150Seric 
892150Seric 	setsig();
902150Seric 
912150Seric 	/*
922150Seric 	Change flags for 'fatal' so that it will return to this
932150Seric 	routine (main) instead of terminating processing.
942150Seric 	*/
9530499Slepreau 	Fflags &= ~FTLEXIT;
9630499Slepreau 	Fflags |= FTLJMP;
972150Seric 
982150Seric 	/*
992150Seric 	Call 'rmchg' routine for each file argument.
1002150Seric 	*/
1012150Seric 	for (i=1; i<argc; i++)
1022150Seric 		if (p = argv[i])
1032150Seric 			do_file(p,rmchg);
1042150Seric 
1052150Seric 	exit(Fcnt ? 1 : 0);
1062150Seric }
1072150Seric 
1082150Seric 
1092150Seric /*
1102150Seric 	Routine that actually causes processing of the delta.
1112150Seric 	Processing on the file takes place on a
1122150Seric 	temporary copy of the SCCS file (the x-file).
1132150Seric 	The name of the x-file is the same as that of the
1142150Seric 	s-file (SCCS file) with the 's.' replaced by 'x.'.
1152150Seric 	At end of processing, the s-file is removed
1162150Seric 	and the x-file is renamed with the name of the old s-file.
1172150Seric 
1182150Seric 	This routine makes use of the z-file to lock out simultaneous
1192150Seric 	updates to the SCCS file by more than one user.
1202150Seric */
1212150Seric 
1222150Seric struct packet gpkt;	/* see file s.h */
1232150Seric char line[BUFSIZ];
1242150Seric char *Mrs;
1252150Seric char *Comments;
1262150Seric int Domrs;
1272150Seric 
1282150Seric USXALLOC();		/* defines alloc() and free() */
1292150Seric 
rmchg(file)1302150Seric rmchg(file)
1312150Seric char *file;
1322150Seric {
13330499Slepreau 	static int first_time = 1;
1342150Seric 	struct deltab dt;	/* see file s.defines.h */
1352150Seric 	struct stats stats;	/* see file s.defines.h */
1362150Seric 	extern char *Sflags[];
1372150Seric 	int n;
1382150Seric 	char *p, *cp;
1392150Seric 	int keep;
1402150Seric 	extern char Pgmr[8];
1412150Seric 	int fowner, downer, user;
1422150Seric 
1432150Seric 	if (setjmp(Fjmp))	/* set up to return here from 'fatal' */
1442150Seric 		return;		/* and return to caller of rmchg */
1452150Seric 
1462150Seric 	if (!HADR)
1472150Seric 		fatal("missing r (rc1)");
1482150Seric 
1492150Seric 	if (D_type == 'D' && first_time) {
1502150Seric 		first_time = 0;
1512150Seric 		dohist(file);
1522150Seric 	}
1532150Seric 
154*33423Sbostic 	if (!exists(file)) {
155*33423Sbostic 		sprintf(Error,"file %s does not exist (rc2)",file);
156*33423Sbostic 		fatal(Error);
157*33423Sbostic 	}
1582150Seric 
1592150Seric 	/*
1602150Seric 	Lock out any other user who may be trying to process
1612150Seric 	the same file.
1622150Seric 	*/
1632150Seric 	if (lockit(auxf(file,'z'),2,getpid()))
1642150Seric 		fatal("cannot create lock file (cm4)");
1652150Seric 
1662150Seric 	sinit(&gpkt,file,1);	/* initialize packet and open s-file */
1672150Seric 
1682150Seric 	/*
1692150Seric 	Flag for 'putline' routine to tell it to open x-file
1702150Seric 	and allow writing on it.
1712150Seric 	*/
1722150Seric 	gpkt.p_upd = 1;
1732150Seric 
1742150Seric 	/*
1752150Seric 	Save requested SID for later checking of
1762150Seric 	permissions (by 'permiss').
1772150Seric 	*/
17819943Ssam 	bcopy(&sid,&gpkt.p_reqsid,sizeof(gpkt.p_reqsid));
1792150Seric 
1802150Seric 	/*
1812150Seric 	Now read-in delta table. The 'dodelt' routine
1822150Seric 	will read the table and change the delta entry of the
1832150Seric 	requested SID to be of type 'R' if this is
1842150Seric 	being executed as 'rmdel'; otherwise, for 'chghist', only
1852150Seric 	the MR and comments sections will be changed
1862150Seric 	(by 'escdodelt', called by 'dodelt').
1872150Seric 	*/
1882150Seric 	if (dodelt(&gpkt,&stats,&sid,D_type) == 0)
1892150Seric 		fmterr(&gpkt);
1902150Seric 
1912150Seric 	/*
1922150Seric 	Get serial number of requested SID from
1932150Seric 	delta table just processed.
1942150Seric 	*/
1952150Seric 	D_serial = sidtoser(&gpkt.p_reqsid,&gpkt);
1962150Seric 
1972150Seric 	/*
1982150Seric 	If SID has not been zeroed (by 'dodelt'),
1992150Seric 	SID was not found in file.
2002150Seric 	*/
2012150Seric 	if (sid.s_rel != 0)
2022150Seric 		fatal("nonexistent sid (rc3)");
2032150Seric 	/*
2042150Seric 	Replace 'sid' with original 'sid'
2052150Seric 	requested.
2062150Seric 	*/
20719943Ssam 	bcopy(&gpkt.p_reqsid,&sid,sizeof(gpkt.p_reqsid));
2082150Seric 
2092150Seric 	/*
2102150Seric 	Now check permissions.
2112150Seric 	*/
2122150Seric 	finduser(&gpkt);
2132150Seric 	doflags(&gpkt);
2142150Seric 	permiss(&gpkt);
2152150Seric 
2162150Seric 	/*
2172150Seric 	Check that user is either owner of file or
2182150Seric 	directory, or is one who made the delta.
2192150Seric 	*/
2202150Seric 	fstat(fileno(gpkt.p_iop),&Statbuf);
2212150Seric 	fowner = Statbuf.st_uid & 0377;
2222150Seric 	copy(gpkt.p_file,line);		/* temporary for dname() */
2232150Seric 	if (stat(dname(line),&Statbuf))
2242150Seric 		downer = -1;
2252150Seric 	else
2262150Seric 		downer = Statbuf.st_uid & 0377;
2272150Seric 	user = getuid() & 0377;
2282150Seric 	if (user != fowner || user != downer)
229*33423Sbostic 		if (!equal(Pgmr,logname())) {
230*33423Sbostic 			sprintf(Error, "you are neither owner nor '%s' (rc4)",Pgmr);
231*33423Sbostic 			fatal(Error);
232*33423Sbostic 		}
2332150Seric 
2342150Seric 	/*
2352150Seric 	For 'rmdel', check that delta being removed is a
2362150Seric 	'leaf' delta, and if ok,
2372150Seric 	process the body.
2382150Seric 	*/
2392150Seric 	if (D_type == 'R') {
2402150Seric 		for (n = maxser(&gpkt); n > D_serial; n--) {
2412150Seric 			p = &gpkt.p_idel[n];
2422150Seric 			if (p->i_pred == D_serial)
2432150Seric 				fatal("not a 'leaf' delta (rc5)");
2442150Seric 		}
2452150Seric 
2462150Seric 		/*
2472150Seric 		   For 'rmdel' check that the sid requested is
2482150Seric 		   not contained in p-file, should a p-file
2492150Seric 		   exist.
2502150Seric 		*/
2512150Seric 
2522150Seric 		if (exists(auxf(gpkt.p_file,'p')))
2532150Seric 			rdpfile(&gpkt,&sid);
2542150Seric 
2552150Seric 		flushto(&gpkt,EUSERTXT,COPY);
2562150Seric 
2572150Seric 		keep = YES;
2582150Seric 		gpkt.p_chkeof = 1;		/* set EOF is ok */
2592150Seric 		while ((p = getline(&gpkt)) != NULL) {
2602150Seric 			if (*p++ == CTLCHAR) {
2612150Seric 				cp = p++;
2622150Seric 				NONBLANK(p);
2632150Seric 				/*
2642150Seric 				Convert serial number to binary.
2652150Seric 				*/
2662150Seric 				if (*(p = satoi(p,&n)) != '\n')
2672150Seric 					fmterr(&gpkt);
2682150Seric 				if (n == D_serial) {
2692150Seric 					gpkt.p_wrttn = 1;
2702150Seric 					if (*cp == INS)
2712150Seric 						keep = NO;
2722150Seric 					else
2732150Seric 						keep = YES;
2742150Seric 				}
2752150Seric 			}
2762150Seric 			else
2772150Seric 				if (keep == NO)
2782150Seric 					gpkt.p_wrttn = 1;
2792150Seric 		}
2802150Seric 	}
2812150Seric 	else {
2822150Seric 		/*
2832150Seric 		This is for invocation as 'chghist'.
2842150Seric 		Check MRs.
2852150Seric 		*/
2862150Seric 		if (Mrs) {
2872150Seric 			if (!(p = Sflags[VALFLAG - 'a']))
2882150Seric 				fatal("MRs not allowed (rc6)");
2892150Seric 			if (*p && valmrs(&gpkt,p))
2902150Seric 				fatal("inavlid MRs (rc7)");
2912150Seric 		}
2922150Seric 		else
2932150Seric 			if (Sflags[VALFLAG - 'a'])
2942150Seric 				fatal("MRs required (rc8)");
2952150Seric 
2962150Seric 		/*
2972150Seric 		Indicate that EOF at this point is ok, and
2982150Seric 		flush rest of s-file to x-file.
2992150Seric 		*/
3002150Seric 		gpkt.p_chkeof = 1;
3012150Seric 		while (getline(&gpkt))
3022150Seric 			;
3032150Seric 	}
3042150Seric 
3052150Seric 	flushline(&gpkt,0);
3062150Seric 
3072150Seric 	/*
3082150Seric 	Delete old s-file, change x-file name to s-file.
3092150Seric 	*/
3102150Seric 	rename(auxf(&gpkt,'x'),&gpkt);
3112150Seric 
3122150Seric 	clean_up();
3132150Seric }
3142150Seric 
3152150Seric 
3162150Seric escdodelt(pkt)
3172150Seric struct packet *pkt;
3182150Seric {
3192150Seric 	extern int First_esc;
3202150Seric 	char *p;
3212150Seric 	extern long Timenow;
3222150Seric 
3232150Seric 	if (D_type == 'D' && First_esc) {	/* chghist, first time */
3242150Seric 		First_esc = 0;
3252150Seric 		if (Mrs)
3262150Seric 			putmrs(pkt);
3272150Seric 
328*33423Sbostic 		sprintf(line,"%c%c ",CTLCHAR,COMMENTS);
329*33423Sbostic 		putline(pkt,line);
3302150Seric 		putline(pkt,Comments);
3312150Seric 		putline(pkt,"\n");
332*33423Sbostic 		sprintf(line,"%c%c ",CTLCHAR,COMMENTS);
333*33423Sbostic 		putline(pkt,line);
3342150Seric 		putline(pkt,"*** CHANGED *** ");
3352150Seric 		date_ba(&Timenow,line);		/* get date and time */
3362150Seric 		putline(pkt,line);
337*33423Sbostic 		sprintf(line," %s\n",logname());
338*33423Sbostic 		putline(pkt,line);
3392150Seric 	}
3402150Seric 
3412150Seric 	if (pkt->p_line[1] == MRNUM) {
3422150Seric 		p = &pkt->p_line;
3432150Seric 		while (*p)
3442150Seric 			p++;
3452150Seric 		if (*(p - 2) == DELIVER)
3462150Seric 			fatal("delta specified has delivered MR (rc9)");
3472150Seric 
3482150Seric 		if (D_type == 'D')		/* turn MRs into comments */
3492150Seric 			pkt->p_line[1] = COMMENTS;
3502150Seric 	}
3512150Seric }
3522150Seric 
3532150Seric 
3542150Seric putmrs(pkt)
3552150Seric struct packet *pkt;
3562150Seric {
3572150Seric 	register char **argv;
3582150Seric 	char str[64];
3592150Seric 	extern char *Varg[];
3602150Seric 
361*33423Sbostic 	for (argv = &Varg[VSTART]; *argv; argv++) {
362*33423Sbostic 		sprintf(str,"%c%c %s\n",CTLCHAR,MRNUM,*argv);
363*33423Sbostic 		putline(pkt,str);
364*33423Sbostic 	}
3652150Seric }
3662150Seric 
3672150Seric 
clean_up()3682150Seric clean_up()
3692150Seric {
3702150Seric 	xrm(&gpkt);
3712150Seric 	if (gpkt.p_file[0])
3722150Seric 		unlockit(auxf(gpkt.p_file,'z'),getpid());
3732150Seric 	if (exists(auxf(gpkt.p_file,'x')))
3742150Seric 		xunlink(auxf(gpkt.p_file,'x'));
3752150Seric 	xfreeall();
3762150Seric }
3772150Seric 
3782150Seric 
rdpfile(pkt,sp)3792150Seric rdpfile(pkt,sp)
3802150Seric register struct packet *pkt;
3812150Seric struct sid *sp;
3822150Seric {
3832150Seric 	struct pfile pf;
3842150Seric 	char line[BUFSIZ];
3852150Seric 	FILE *in;
3862150Seric 
3872150Seric 	in = xfopen(auxf(pkt->p_file,'p'),0);
3882150Seric 	while (fgets(line,sizeof(line),in) != NULL) {
3892150Seric 		pf_ab(line,&pf,1);
3902150Seric 		if (sp->s_rel == pf.pf_gsid.s_rel &&
3912150Seric 			sp->s_lev == pf.pf_gsid.s_lev &&
3922150Seric 			sp->s_br == pf.pf_gsid.s_br &&
3932150Seric 			sp->s_seq == pf.pf_gsid.s_seq) {
3942150Seric 				fclose(in);
3952150Seric 				fatal("being edited -- sid is in p-file (rc12)");
3962150Seric 		}
3972150Seric 	}
3982150Seric 	fclose(in);
3992150Seric 	return;
4002150Seric }
401