12150Seric # include "../hdr/defines.h"
22150Seric # include "../hdr/had.h"
32150Seric 
4*19943Ssam SCCSID(@(#)rmchg.c	4.2);
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 
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 	*/
952150Seric 	Fflags =& ~FTLEXIT;
962150Seric 	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 
1302150Seric rmchg(file)
1312150Seric char *file;
1322150Seric {
1332150Seric 	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 
1542150Seric 	if (!exists(file))
1552150Seric 		fatal(sprintf(Error,"file %s does not exist (rc2)",file));
1562150Seric 
1572150Seric 	/*
1582150Seric 	Lock out any other user who may be trying to process
1592150Seric 	the same file.
1602150Seric 	*/
1612150Seric 	if (lockit(auxf(file,'z'),2,getpid()))
1622150Seric 		fatal("cannot create lock file (cm4)");
1632150Seric 
1642150Seric 	sinit(&gpkt,file,1);	/* initialize packet and open s-file */
1652150Seric 
1662150Seric 	/*
1672150Seric 	Flag for 'putline' routine to tell it to open x-file
1682150Seric 	and allow writing on it.
1692150Seric 	*/
1702150Seric 	gpkt.p_upd = 1;
1712150Seric 
1722150Seric 	/*
1732150Seric 	Save requested SID for later checking of
1742150Seric 	permissions (by 'permiss').
1752150Seric 	*/
176*19943Ssam 	bcopy(&sid,&gpkt.p_reqsid,sizeof(gpkt.p_reqsid));
1772150Seric 
1782150Seric 	/*
1792150Seric 	Now read-in delta table. The 'dodelt' routine
1802150Seric 	will read the table and change the delta entry of the
1812150Seric 	requested SID to be of type 'R' if this is
1822150Seric 	being executed as 'rmdel'; otherwise, for 'chghist', only
1832150Seric 	the MR and comments sections will be changed
1842150Seric 	(by 'escdodelt', called by 'dodelt').
1852150Seric 	*/
1862150Seric 	if (dodelt(&gpkt,&stats,&sid,D_type) == 0)
1872150Seric 		fmterr(&gpkt);
1882150Seric 
1892150Seric 	/*
1902150Seric 	Get serial number of requested SID from
1912150Seric 	delta table just processed.
1922150Seric 	*/
1932150Seric 	D_serial = sidtoser(&gpkt.p_reqsid,&gpkt);
1942150Seric 
1952150Seric 	/*
1962150Seric 	If SID has not been zeroed (by 'dodelt'),
1972150Seric 	SID was not found in file.
1982150Seric 	*/
1992150Seric 	if (sid.s_rel != 0)
2002150Seric 		fatal("nonexistent sid (rc3)");
2012150Seric 	/*
2022150Seric 	Replace 'sid' with original 'sid'
2032150Seric 	requested.
2042150Seric 	*/
205*19943Ssam 	bcopy(&gpkt.p_reqsid,&sid,sizeof(gpkt.p_reqsid));
2062150Seric 
2072150Seric 	/*
2082150Seric 	Now check permissions.
2092150Seric 	*/
2102150Seric 	finduser(&gpkt);
2112150Seric 	doflags(&gpkt);
2122150Seric 	permiss(&gpkt);
2132150Seric 
2142150Seric 	/*
2152150Seric 	Check that user is either owner of file or
2162150Seric 	directory, or is one who made the delta.
2172150Seric 	*/
2182150Seric 	fstat(fileno(gpkt.p_iop),&Statbuf);
2192150Seric 	fowner = Statbuf.st_uid & 0377;
2202150Seric 	copy(gpkt.p_file,line);		/* temporary for dname() */
2212150Seric 	if (stat(dname(line),&Statbuf))
2222150Seric 		downer = -1;
2232150Seric 	else
2242150Seric 		downer = Statbuf.st_uid & 0377;
2252150Seric 	user = getuid() & 0377;
2262150Seric 	if (user != fowner || user != downer)
2272150Seric 		if (!equal(Pgmr,logname()))
2282150Seric 			fatal(sprintf(Error,
2292150Seric 				"you are neither owner nor '%s' (rc4)",Pgmr));
2302150Seric 
2312150Seric 	/*
2322150Seric 	For 'rmdel', check that delta being removed is a
2332150Seric 	'leaf' delta, and if ok,
2342150Seric 	process the body.
2352150Seric 	*/
2362150Seric 	if (D_type == 'R') {
2372150Seric 		for (n = maxser(&gpkt); n > D_serial; n--) {
2382150Seric 			p = &gpkt.p_idel[n];
2392150Seric 			if (p->i_pred == D_serial)
2402150Seric 				fatal("not a 'leaf' delta (rc5)");
2412150Seric 		}
2422150Seric 
2432150Seric 		/*
2442150Seric 		   For 'rmdel' check that the sid requested is
2452150Seric 		   not contained in p-file, should a p-file
2462150Seric 		   exist.
2472150Seric 		*/
2482150Seric 
2492150Seric 		if (exists(auxf(gpkt.p_file,'p')))
2502150Seric 			rdpfile(&gpkt,&sid);
2512150Seric 
2522150Seric 		flushto(&gpkt,EUSERTXT,COPY);
2532150Seric 
2542150Seric 		keep = YES;
2552150Seric 		gpkt.p_chkeof = 1;		/* set EOF is ok */
2562150Seric 		while ((p = getline(&gpkt)) != NULL) {
2572150Seric 			if (*p++ == CTLCHAR) {
2582150Seric 				cp = p++;
2592150Seric 				NONBLANK(p);
2602150Seric 				/*
2612150Seric 				Convert serial number to binary.
2622150Seric 				*/
2632150Seric 				if (*(p = satoi(p,&n)) != '\n')
2642150Seric 					fmterr(&gpkt);
2652150Seric 				if (n == D_serial) {
2662150Seric 					gpkt.p_wrttn = 1;
2672150Seric 					if (*cp == INS)
2682150Seric 						keep = NO;
2692150Seric 					else
2702150Seric 						keep = YES;
2712150Seric 				}
2722150Seric 			}
2732150Seric 			else
2742150Seric 				if (keep == NO)
2752150Seric 					gpkt.p_wrttn = 1;
2762150Seric 		}
2772150Seric 	}
2782150Seric 	else {
2792150Seric 		/*
2802150Seric 		This is for invocation as 'chghist'.
2812150Seric 		Check MRs.
2822150Seric 		*/
2832150Seric 		if (Mrs) {
2842150Seric 			if (!(p = Sflags[VALFLAG - 'a']))
2852150Seric 				fatal("MRs not allowed (rc6)");
2862150Seric 			if (*p && valmrs(&gpkt,p))
2872150Seric 				fatal("inavlid MRs (rc7)");
2882150Seric 		}
2892150Seric 		else
2902150Seric 			if (Sflags[VALFLAG - 'a'])
2912150Seric 				fatal("MRs required (rc8)");
2922150Seric 
2932150Seric 		/*
2942150Seric 		Indicate that EOF at this point is ok, and
2952150Seric 		flush rest of s-file to x-file.
2962150Seric 		*/
2972150Seric 		gpkt.p_chkeof = 1;
2982150Seric 		while (getline(&gpkt))
2992150Seric 			;
3002150Seric 	}
3012150Seric 
3022150Seric 	flushline(&gpkt,0);
3032150Seric 
3042150Seric 	/*
3052150Seric 	Delete old s-file, change x-file name to s-file.
3062150Seric 	*/
3072150Seric 	rename(auxf(&gpkt,'x'),&gpkt);
3082150Seric 
3092150Seric 	clean_up();
3102150Seric }
3112150Seric 
3122150Seric 
3132150Seric escdodelt(pkt)
3142150Seric struct packet *pkt;
3152150Seric {
3162150Seric 	extern int First_esc;
3172150Seric 	char *p;
3182150Seric 	extern long Timenow;
3192150Seric 
3202150Seric 	if (D_type == 'D' && First_esc) {	/* chghist, first time */
3212150Seric 		First_esc = 0;
3222150Seric 		if (Mrs)
3232150Seric 			putmrs(pkt);
3242150Seric 
3252150Seric 		putline(pkt,sprintf(line,"%c%c ",CTLCHAR,COMMENTS));
3262150Seric 		putline(pkt,Comments);
3272150Seric 		putline(pkt,"\n");
3282150Seric 		putline(pkt,sprintf(line,"%c%c ",CTLCHAR,COMMENTS));
3292150Seric 		putline(pkt,"*** CHANGED *** ");
3302150Seric 		date_ba(&Timenow,line);		/* get date and time */
3312150Seric 		putline(pkt,line);
3322150Seric 		putline(pkt,sprintf(line," %s\n",logname()));
3332150Seric 	}
3342150Seric 
3352150Seric 	if (pkt->p_line[1] == MRNUM) {
3362150Seric 		p = &pkt->p_line;
3372150Seric 		while (*p)
3382150Seric 			p++;
3392150Seric 		if (*(p - 2) == DELIVER)
3402150Seric 			fatal("delta specified has delivered MR (rc9)");
3412150Seric 
3422150Seric 		if (D_type == 'D')		/* turn MRs into comments */
3432150Seric 			pkt->p_line[1] = COMMENTS;
3442150Seric 	}
3452150Seric }
3462150Seric 
3472150Seric 
3482150Seric putmrs(pkt)
3492150Seric struct packet *pkt;
3502150Seric {
3512150Seric 	register char **argv;
3522150Seric 	char str[64];
3532150Seric 	extern char *Varg[];
3542150Seric 
3552150Seric 	for (argv = &Varg[VSTART]; *argv; argv++)
3562150Seric 		putline(pkt,sprintf(str,"%c%c %s\n",CTLCHAR,MRNUM,*argv));
3572150Seric }
3582150Seric 
3592150Seric 
3602150Seric clean_up()
3612150Seric {
3622150Seric 	xrm(&gpkt);
3632150Seric 	if (gpkt.p_file[0])
3642150Seric 		unlockit(auxf(gpkt.p_file,'z'),getpid());
3652150Seric 	if (exists(auxf(gpkt.p_file,'x')))
3662150Seric 		xunlink(auxf(gpkt.p_file,'x'));
3672150Seric 	xfreeall();
3682150Seric }
3692150Seric 
3702150Seric 
3712150Seric rdpfile(pkt,sp)
3722150Seric register struct packet *pkt;
3732150Seric struct sid *sp;
3742150Seric {
3752150Seric 	struct pfile pf;
3762150Seric 	char line[BUFSIZ];
3772150Seric 	FILE *in;
3782150Seric 
3792150Seric 	in = xfopen(auxf(pkt->p_file,'p'),0);
3802150Seric 	while (fgets(line,sizeof(line),in) != NULL) {
3812150Seric 		pf_ab(line,&pf,1);
3822150Seric 		if (sp->s_rel == pf.pf_gsid.s_rel &&
3832150Seric 			sp->s_lev == pf.pf_gsid.s_lev &&
3842150Seric 			sp->s_br == pf.pf_gsid.s_br &&
3852150Seric 			sp->s_seq == pf.pf_gsid.s_seq) {
3862150Seric 				fclose(in);
3872150Seric 				fatal("being edited -- sid is in p-file (rc12)");
3882150Seric 		}
3892150Seric 	}
3902150Seric 	fclose(in);
3912150Seric 	return;
3922150Seric }
393