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