1e9989320Seric # include "../hdr/defines.h"
2e9989320Seric # include "../hdr/had.h"
3e9989320Seric 
4*a8a1d217Skarels static char Sccsid[] = "@(#)rmchg.c	4.5	08/23/90";
5e9989320Seric 
6e9989320Seric /*
7e9989320Seric 	Program to remove a specified delta from an SCCS file,
8e9989320Seric 	when invoked as 'rmdel',
9e9989320Seric 	or to change the MRs and/or comments of a specified delta,
10e9989320Seric 	when invoked as 'chghist'.
11e9989320Seric 	(The program has two links to it, one called 'rmdel', the
12e9989320Seric 	other 'chghist'.)
13e9989320Seric 
14e9989320Seric 	The delta to be removed (or whose MRs and/or comments
15e9989320Seric 	are to be changed) is specified via the
16e9989320Seric 	r argument, in the form of an SID.
17e9989320Seric 
18e9989320Seric 	If the delta is to be removed, it must be the most recent one
19e9989320Seric 	in its branch in the delta tree (a so-called 'leaf' delta).
20e9989320Seric 	For either function, the delta being processed must not
21e9989320Seric 	have any 'delivered' MRs, and the user must have basically
22e9989320Seric 	the same permissions as are required to make deltas.
23e9989320Seric 
24e9989320Seric 	If a directory is given as an argument, each SCCS file
25e9989320Seric 	within the directory will be processed as if it had been
26e9989320Seric 	specifically named. If a name of '-' is given, the standard
27e9989320Seric 	input will be read for a list of names of SCCS files to be
28e9989320Seric 	processed. Non SCCS files are ignored.
29e9989320Seric */
30e9989320Seric 
31e9989320Seric # define COPY 0
32e9989320Seric # define NOCOPY 1
33e9989320Seric 
34e9989320Seric struct sid sid;
35e9989320Seric int num_files;
36e9989320Seric char had[26];
37e9989320Seric char D_type;
38e9989320Seric int D_serial;
39e9989320Seric 
main(argc,argv)40e9989320Seric main(argc,argv)
41e9989320Seric int argc;
42e9989320Seric char *argv[];
43e9989320Seric {
44e9989320Seric 	register int i;
45e9989320Seric 	register char *p;
46e9989320Seric 	char c;
47e9989320Seric 	extern rmchg();
48e9989320Seric 	extern int Fcnt;
49e9989320Seric 
50e9989320Seric 	/*
51e9989320Seric 	Set flags for 'fatal' to issue message, call clean-up
52e9989320Seric 	routine, and terminate processing.
53e9989320Seric 	*/
54e9989320Seric 	Fflags = FTLMSG | FTLCLN | FTLEXIT;
55e9989320Seric 
56e9989320Seric 	for(i=1; i<argc; i++)
57e9989320Seric 		if(argv[i][0] == '-' && (c = argv[i][1])) {
58e9989320Seric 			p = &argv[i][2];
59e9989320Seric 			switch (c) {
60e9989320Seric 
61e9989320Seric 			case 'r':
62e9989320Seric 				if (!(*p))
63e9989320Seric 					fatal("r has no sid (rc11)");
64e9989320Seric 				chksid(sid_ab(p,&sid),&sid);
65e9989320Seric 				break;
66e9989320Seric 			default:
67e9989320Seric 				fatal("unknown key letter (cm1)");
68e9989320Seric 			}
69e9989320Seric 
70e9989320Seric 			if (had[c - 'a']++)
71e9989320Seric 				fatal("key letter twice (cm2)");
72e9989320Seric 			argv[i] = 0;
73e9989320Seric 		}
74e9989320Seric 		else
75e9989320Seric 			num_files++;
76e9989320Seric 
77e9989320Seric 	if(num_files == 0)
78e9989320Seric 		fatal("missing file arg (cm3)");
79e9989320Seric 
80e9989320Seric 	if (*(p = sname(argv[0])) == 'n')
81e9989320Seric 		p++;
82e9989320Seric 	if (equal(p,"rmdel"))
83e9989320Seric 		D_type = 'R';		/* invoked as 'rmdel' */
84e9989320Seric 	else if (equal(p,"chghist"))
85e9989320Seric 		D_type = 'D';		/* invoked as 'chghist' */
86e9989320Seric 	else
87e9989320Seric 		fatal("bad invocation (rc10)");
88e9989320Seric 
89e9989320Seric 	setsig();
90e9989320Seric 
91e9989320Seric 	/*
92e9989320Seric 	Change flags for 'fatal' so that it will return to this
93e9989320Seric 	routine (main) instead of terminating processing.
94e9989320Seric 	*/
958be0c981Slepreau 	Fflags &= ~FTLEXIT;
968be0c981Slepreau 	Fflags |= FTLJMP;
97e9989320Seric 
98e9989320Seric 	/*
99e9989320Seric 	Call 'rmchg' routine for each file argument.
100e9989320Seric 	*/
101e9989320Seric 	for (i=1; i<argc; i++)
102e9989320Seric 		if (p = argv[i])
103e9989320Seric 			do_file(p,rmchg);
104e9989320Seric 
105e9989320Seric 	exit(Fcnt ? 1 : 0);
106e9989320Seric }
107e9989320Seric 
108e9989320Seric 
109e9989320Seric /*
110e9989320Seric 	Routine that actually causes processing of the delta.
111e9989320Seric 	Processing on the file takes place on a
112e9989320Seric 	temporary copy of the SCCS file (the x-file).
113e9989320Seric 	The name of the x-file is the same as that of the
114e9989320Seric 	s-file (SCCS file) with the 's.' replaced by 'x.'.
115e9989320Seric 	At end of processing, the s-file is removed
116e9989320Seric 	and the x-file is renamed with the name of the old s-file.
117e9989320Seric 
118e9989320Seric 	This routine makes use of the z-file to lock out simultaneous
119e9989320Seric 	updates to the SCCS file by more than one user.
120e9989320Seric */
121e9989320Seric 
122e9989320Seric struct packet gpkt;	/* see file s.h */
123e9989320Seric char line[BUFSIZ];
124e9989320Seric char *Mrs;
125e9989320Seric char *Comments;
126e9989320Seric int Domrs;
127e9989320Seric 
128e9989320Seric USXALLOC();		/* defines alloc() and free() */
129e9989320Seric 
rmchg(file)130e9989320Seric rmchg(file)
131e9989320Seric char *file;
132e9989320Seric {
1338be0c981Slepreau 	static int first_time = 1;
134e9989320Seric 	struct deltab dt;	/* see file s.defines.h */
135e9989320Seric 	struct stats stats;	/* see file s.defines.h */
136e9989320Seric 	extern char *Sflags[];
137e9989320Seric 	int n;
138e9989320Seric 	char *p, *cp;
139e9989320Seric 	int keep;
140e9989320Seric 	extern char Pgmr[8];
141e9989320Seric 	int fowner, downer, user;
142e9989320Seric 
143e9989320Seric 	if (setjmp(Fjmp))	/* set up to return here from 'fatal' */
144e9989320Seric 		return;		/* and return to caller of rmchg */
145e9989320Seric 
146e9989320Seric 	if (!HADR)
147e9989320Seric 		fatal("missing r (rc1)");
148e9989320Seric 
149e9989320Seric 	if (D_type == 'D' && first_time) {
150e9989320Seric 		first_time = 0;
151e9989320Seric 		dohist(file);
152e9989320Seric 	}
153e9989320Seric 
154824dd99bSbostic 	if (!exists(file)) {
155824dd99bSbostic 		sprintf(Error,"file %s does not exist (rc2)",file);
156824dd99bSbostic 		fatal(Error);
157824dd99bSbostic 	}
158e9989320Seric 
159e9989320Seric 	/*
160e9989320Seric 	Lock out any other user who may be trying to process
161e9989320Seric 	the same file.
162e9989320Seric 	*/
163e9989320Seric 	if (lockit(auxf(file,'z'),2,getpid()))
164e9989320Seric 		fatal("cannot create lock file (cm4)");
165e9989320Seric 
166e9989320Seric 	sinit(&gpkt,file,1);	/* initialize packet and open s-file */
167e9989320Seric 
168e9989320Seric 	/*
169e9989320Seric 	Flag for 'putline' routine to tell it to open x-file
170e9989320Seric 	and allow writing on it.
171e9989320Seric 	*/
172e9989320Seric 	gpkt.p_upd = 1;
173e9989320Seric 
174e9989320Seric 	/*
175e9989320Seric 	Save requested SID for later checking of
176e9989320Seric 	permissions (by 'permiss').
177e9989320Seric 	*/
178f7460627Ssam 	bcopy(&sid,&gpkt.p_reqsid,sizeof(gpkt.p_reqsid));
179e9989320Seric 
180e9989320Seric 	/*
181e9989320Seric 	Now read-in delta table. The 'dodelt' routine
182e9989320Seric 	will read the table and change the delta entry of the
183e9989320Seric 	requested SID to be of type 'R' if this is
184e9989320Seric 	being executed as 'rmdel'; otherwise, for 'chghist', only
185e9989320Seric 	the MR and comments sections will be changed
186e9989320Seric 	(by 'escdodelt', called by 'dodelt').
187e9989320Seric 	*/
188e9989320Seric 	if (dodelt(&gpkt,&stats,&sid,D_type) == 0)
189e9989320Seric 		fmterr(&gpkt);
190e9989320Seric 
191e9989320Seric 	/*
192e9989320Seric 	Get serial number of requested SID from
193e9989320Seric 	delta table just processed.
194e9989320Seric 	*/
195e9989320Seric 	D_serial = sidtoser(&gpkt.p_reqsid,&gpkt);
196e9989320Seric 
197e9989320Seric 	/*
198e9989320Seric 	If SID has not been zeroed (by 'dodelt'),
199e9989320Seric 	SID was not found in file.
200e9989320Seric 	*/
201e9989320Seric 	if (sid.s_rel != 0)
202e9989320Seric 		fatal("nonexistent sid (rc3)");
203e9989320Seric 	/*
204e9989320Seric 	Replace 'sid' with original 'sid'
205e9989320Seric 	requested.
206e9989320Seric 	*/
207f7460627Ssam 	bcopy(&gpkt.p_reqsid,&sid,sizeof(gpkt.p_reqsid));
208e9989320Seric 
209e9989320Seric 	/*
210e9989320Seric 	Now check permissions.
211e9989320Seric 	*/
212e9989320Seric 	finduser(&gpkt);
213e9989320Seric 	doflags(&gpkt);
214e9989320Seric 	permiss(&gpkt);
215e9989320Seric 
216e9989320Seric 	/*
217e9989320Seric 	Check that user is either owner of file or
218e9989320Seric 	directory, or is one who made the delta.
219e9989320Seric 	*/
220e9989320Seric 	fstat(fileno(gpkt.p_iop),&Statbuf);
221e9989320Seric 	fowner = Statbuf.st_uid & 0377;
222e9989320Seric 	copy(gpkt.p_file,line);		/* temporary for dname() */
223e9989320Seric 	if (stat(dname(line),&Statbuf))
224e9989320Seric 		downer = -1;
225e9989320Seric 	else
226e9989320Seric 		downer = Statbuf.st_uid & 0377;
227e9989320Seric 	user = getuid() & 0377;
228e9989320Seric 	if (user != fowner || user != downer)
229824dd99bSbostic 		if (!equal(Pgmr,logname())) {
230824dd99bSbostic 			sprintf(Error, "you are neither owner nor '%s' (rc4)",Pgmr);
231824dd99bSbostic 			fatal(Error);
232824dd99bSbostic 		}
233e9989320Seric 
234e9989320Seric 	/*
235e9989320Seric 	For 'rmdel', check that delta being removed is a
236e9989320Seric 	'leaf' delta, and if ok,
237e9989320Seric 	process the body.
238e9989320Seric 	*/
239e9989320Seric 	if (D_type == 'R') {
240e9989320Seric 		for (n = maxser(&gpkt); n > D_serial; n--) {
241e9989320Seric 			p = &gpkt.p_idel[n];
242*a8a1d217Skarels 			if (((struct idel *)p)->i_pred == D_serial)
243e9989320Seric 				fatal("not a 'leaf' delta (rc5)");
244e9989320Seric 		}
245e9989320Seric 
246e9989320Seric 		/*
247e9989320Seric 		   For 'rmdel' check that the sid requested is
248e9989320Seric 		   not contained in p-file, should a p-file
249e9989320Seric 		   exist.
250e9989320Seric 		*/
251e9989320Seric 
252e9989320Seric 		if (exists(auxf(gpkt.p_file,'p')))
253e9989320Seric 			rdpfile(&gpkt,&sid);
254e9989320Seric 
255e9989320Seric 		flushto(&gpkt,EUSERTXT,COPY);
256e9989320Seric 
257e9989320Seric 		keep = YES;
258e9989320Seric 		gpkt.p_chkeof = 1;		/* set EOF is ok */
259e9989320Seric 		while ((p = getline(&gpkt)) != NULL) {
260e9989320Seric 			if (*p++ == CTLCHAR) {
261e9989320Seric 				cp = p++;
262e9989320Seric 				NONBLANK(p);
263e9989320Seric 				/*
264e9989320Seric 				Convert serial number to binary.
265e9989320Seric 				*/
266e9989320Seric 				if (*(p = satoi(p,&n)) != '\n')
267e9989320Seric 					fmterr(&gpkt);
268e9989320Seric 				if (n == D_serial) {
269e9989320Seric 					gpkt.p_wrttn = 1;
270e9989320Seric 					if (*cp == INS)
271e9989320Seric 						keep = NO;
272e9989320Seric 					else
273e9989320Seric 						keep = YES;
274e9989320Seric 				}
275e9989320Seric 			}
276e9989320Seric 			else
277e9989320Seric 				if (keep == NO)
278e9989320Seric 					gpkt.p_wrttn = 1;
279e9989320Seric 		}
280e9989320Seric 	}
281e9989320Seric 	else {
282e9989320Seric 		/*
283e9989320Seric 		This is for invocation as 'chghist'.
284e9989320Seric 		Check MRs.
285e9989320Seric 		*/
286e9989320Seric 		if (Mrs) {
287e9989320Seric 			if (!(p = Sflags[VALFLAG - 'a']))
288e9989320Seric 				fatal("MRs not allowed (rc6)");
289e9989320Seric 			if (*p && valmrs(&gpkt,p))
290e9989320Seric 				fatal("inavlid MRs (rc7)");
291e9989320Seric 		}
292e9989320Seric 		else
293e9989320Seric 			if (Sflags[VALFLAG - 'a'])
294e9989320Seric 				fatal("MRs required (rc8)");
295e9989320Seric 
296e9989320Seric 		/*
297e9989320Seric 		Indicate that EOF at this point is ok, and
298e9989320Seric 		flush rest of s-file to x-file.
299e9989320Seric 		*/
300e9989320Seric 		gpkt.p_chkeof = 1;
301e9989320Seric 		while (getline(&gpkt))
302e9989320Seric 			;
303e9989320Seric 	}
304e9989320Seric 
305e9989320Seric 	flushline(&gpkt,0);
306e9989320Seric 
307e9989320Seric 	/*
308e9989320Seric 	Delete old s-file, change x-file name to s-file.
309e9989320Seric 	*/
310e9989320Seric 	rename(auxf(&gpkt,'x'),&gpkt);
311e9989320Seric 
312e9989320Seric 	clean_up();
313e9989320Seric }
314e9989320Seric 
315e9989320Seric 
316e9989320Seric escdodelt(pkt)
317e9989320Seric struct packet *pkt;
318e9989320Seric {
319e9989320Seric 	extern int First_esc;
320e9989320Seric 	char *p;
321e9989320Seric 	extern long Timenow;
322e9989320Seric 
323e9989320Seric 	if (D_type == 'D' && First_esc) {	/* chghist, first time */
324e9989320Seric 		First_esc = 0;
325e9989320Seric 		if (Mrs)
326e9989320Seric 			putmrs(pkt);
327e9989320Seric 
328824dd99bSbostic 		sprintf(line,"%c%c ",CTLCHAR,COMMENTS);
329824dd99bSbostic 		putline(pkt,line);
330e9989320Seric 		putline(pkt,Comments);
331e9989320Seric 		putline(pkt,"\n");
332824dd99bSbostic 		sprintf(line,"%c%c ",CTLCHAR,COMMENTS);
333824dd99bSbostic 		putline(pkt,line);
334e9989320Seric 		putline(pkt,"*** CHANGED *** ");
335e9989320Seric 		date_ba(&Timenow,line);		/* get date and time */
336e9989320Seric 		putline(pkt,line);
337824dd99bSbostic 		sprintf(line," %s\n",logname());
338824dd99bSbostic 		putline(pkt,line);
339e9989320Seric 	}
340e9989320Seric 
341e9989320Seric 	if (pkt->p_line[1] == MRNUM) {
342e9989320Seric 		p = &pkt->p_line;
343e9989320Seric 		while (*p)
344e9989320Seric 			p++;
345e9989320Seric 		if (*(p - 2) == DELIVER)
346e9989320Seric 			fatal("delta specified has delivered MR (rc9)");
347e9989320Seric 
348e9989320Seric 		if (D_type == 'D')		/* turn MRs into comments */
349e9989320Seric 			pkt->p_line[1] = COMMENTS;
350e9989320Seric 	}
351e9989320Seric }
352e9989320Seric 
353e9989320Seric 
354e9989320Seric putmrs(pkt)
355e9989320Seric struct packet *pkt;
356e9989320Seric {
357e9989320Seric 	register char **argv;
358e9989320Seric 	char str[64];
359e9989320Seric 	extern char *Varg[];
360e9989320Seric 
361824dd99bSbostic 	for (argv = &Varg[VSTART]; *argv; argv++) {
362824dd99bSbostic 		sprintf(str,"%c%c %s\n",CTLCHAR,MRNUM,*argv);
363824dd99bSbostic 		putline(pkt,str);
364824dd99bSbostic 	}
365e9989320Seric }
366e9989320Seric 
367e9989320Seric 
clean_up()368e9989320Seric clean_up()
369e9989320Seric {
370e9989320Seric 	xrm(&gpkt);
371e9989320Seric 	if (gpkt.p_file[0])
372e9989320Seric 		unlockit(auxf(gpkt.p_file,'z'),getpid());
373e9989320Seric 	if (exists(auxf(gpkt.p_file,'x')))
374e9989320Seric 		xunlink(auxf(gpkt.p_file,'x'));
375e9989320Seric 	xfreeall();
376e9989320Seric }
377e9989320Seric 
378e9989320Seric 
rdpfile(pkt,sp)379e9989320Seric rdpfile(pkt,sp)
380e9989320Seric register struct packet *pkt;
381e9989320Seric struct sid *sp;
382e9989320Seric {
383e9989320Seric 	struct pfile pf;
384e9989320Seric 	char line[BUFSIZ];
385e9989320Seric 	FILE *in;
386e9989320Seric 
387e9989320Seric 	in = xfopen(auxf(pkt->p_file,'p'),0);
388e9989320Seric 	while (fgets(line,sizeof(line),in) != NULL) {
389e9989320Seric 		pf_ab(line,&pf,1);
390e9989320Seric 		if (sp->s_rel == pf.pf_gsid.s_rel &&
391e9989320Seric 			sp->s_lev == pf.pf_gsid.s_lev &&
392e9989320Seric 			sp->s_br == pf.pf_gsid.s_br &&
393e9989320Seric 			sp->s_seq == pf.pf_gsid.s_seq) {
394e9989320Seric 				fclose(in);
395e9989320Seric 				fatal("being edited -- sid is in p-file (rc12)");
396e9989320Seric 		}
397e9989320Seric 	}
398e9989320Seric 	fclose(in);
399e9989320Seric 	return;
400e9989320Seric }
401