1 # include "../hdr/defines.h"
2 # include "../hdr/had.h"
3 
4 SCCSID(@(#)rmchg.c	4.1);
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 		fatal(sprintf(Error,"file %s does not exist (rc2)",file));
156 
157 	/*
158 	Lock out any other user who may be trying to process
159 	the same file.
160 	*/
161 	if (lockit(auxf(file,'z'),2,getpid()))
162 		fatal("cannot create lock file (cm4)");
163 
164 	sinit(&gpkt,file,1);	/* initialize packet and open s-file */
165 
166 	/*
167 	Flag for 'putline' routine to tell it to open x-file
168 	and allow writing on it.
169 	*/
170 	gpkt.p_upd = 1;
171 
172 	/*
173 	Save requested SID for later checking of
174 	permissions (by 'permiss').
175 	*/
176 	move(&sid,&gpkt.p_reqsid,sizeof(gpkt.p_reqsid));
177 
178 	/*
179 	Now read-in delta table. The 'dodelt' routine
180 	will read the table and change the delta entry of the
181 	requested SID to be of type 'R' if this is
182 	being executed as 'rmdel'; otherwise, for 'chghist', only
183 	the MR and comments sections will be changed
184 	(by 'escdodelt', called by 'dodelt').
185 	*/
186 	if (dodelt(&gpkt,&stats,&sid,D_type) == 0)
187 		fmterr(&gpkt);
188 
189 	/*
190 	Get serial number of requested SID from
191 	delta table just processed.
192 	*/
193 	D_serial = sidtoser(&gpkt.p_reqsid,&gpkt);
194 
195 	/*
196 	If SID has not been zeroed (by 'dodelt'),
197 	SID was not found in file.
198 	*/
199 	if (sid.s_rel != 0)
200 		fatal("nonexistent sid (rc3)");
201 	/*
202 	Replace 'sid' with original 'sid'
203 	requested.
204 	*/
205 	move(&gpkt.p_reqsid,&sid,sizeof(gpkt.p_reqsid));
206 
207 	/*
208 	Now check permissions.
209 	*/
210 	finduser(&gpkt);
211 	doflags(&gpkt);
212 	permiss(&gpkt);
213 
214 	/*
215 	Check that user is either owner of file or
216 	directory, or is one who made the delta.
217 	*/
218 	fstat(fileno(gpkt.p_iop),&Statbuf);
219 	fowner = Statbuf.st_uid & 0377;
220 	copy(gpkt.p_file,line);		/* temporary for dname() */
221 	if (stat(dname(line),&Statbuf))
222 		downer = -1;
223 	else
224 		downer = Statbuf.st_uid & 0377;
225 	user = getuid() & 0377;
226 	if (user != fowner || user != downer)
227 		if (!equal(Pgmr,logname()))
228 			fatal(sprintf(Error,
229 				"you are neither owner nor '%s' (rc4)",Pgmr));
230 
231 	/*
232 	For 'rmdel', check that delta being removed is a
233 	'leaf' delta, and if ok,
234 	process the body.
235 	*/
236 	if (D_type == 'R') {
237 		for (n = maxser(&gpkt); n > D_serial; n--) {
238 			p = &gpkt.p_idel[n];
239 			if (p->i_pred == D_serial)
240 				fatal("not a 'leaf' delta (rc5)");
241 		}
242 
243 		/*
244 		   For 'rmdel' check that the sid requested is
245 		   not contained in p-file, should a p-file
246 		   exist.
247 		*/
248 
249 		if (exists(auxf(gpkt.p_file,'p')))
250 			rdpfile(&gpkt,&sid);
251 
252 		flushto(&gpkt,EUSERTXT,COPY);
253 
254 		keep = YES;
255 		gpkt.p_chkeof = 1;		/* set EOF is ok */
256 		while ((p = getline(&gpkt)) != NULL) {
257 			if (*p++ == CTLCHAR) {
258 				cp = p++;
259 				NONBLANK(p);
260 				/*
261 				Convert serial number to binary.
262 				*/
263 				if (*(p = satoi(p,&n)) != '\n')
264 					fmterr(&gpkt);
265 				if (n == D_serial) {
266 					gpkt.p_wrttn = 1;
267 					if (*cp == INS)
268 						keep = NO;
269 					else
270 						keep = YES;
271 				}
272 			}
273 			else
274 				if (keep == NO)
275 					gpkt.p_wrttn = 1;
276 		}
277 	}
278 	else {
279 		/*
280 		This is for invocation as 'chghist'.
281 		Check MRs.
282 		*/
283 		if (Mrs) {
284 			if (!(p = Sflags[VALFLAG - 'a']))
285 				fatal("MRs not allowed (rc6)");
286 			if (*p && valmrs(&gpkt,p))
287 				fatal("inavlid MRs (rc7)");
288 		}
289 		else
290 			if (Sflags[VALFLAG - 'a'])
291 				fatal("MRs required (rc8)");
292 
293 		/*
294 		Indicate that EOF at this point is ok, and
295 		flush rest of s-file to x-file.
296 		*/
297 		gpkt.p_chkeof = 1;
298 		while (getline(&gpkt))
299 			;
300 	}
301 
302 	flushline(&gpkt,0);
303 
304 	/*
305 	Delete old s-file, change x-file name to s-file.
306 	*/
307 	rename(auxf(&gpkt,'x'),&gpkt);
308 
309 	clean_up();
310 }
311 
312 
313 escdodelt(pkt)
314 struct packet *pkt;
315 {
316 	extern int First_esc;
317 	char *p;
318 	extern long Timenow;
319 
320 	if (D_type == 'D' && First_esc) {	/* chghist, first time */
321 		First_esc = 0;
322 		if (Mrs)
323 			putmrs(pkt);
324 
325 		putline(pkt,sprintf(line,"%c%c ",CTLCHAR,COMMENTS));
326 		putline(pkt,Comments);
327 		putline(pkt,"\n");
328 		putline(pkt,sprintf(line,"%c%c ",CTLCHAR,COMMENTS));
329 		putline(pkt,"*** CHANGED *** ");
330 		date_ba(&Timenow,line);		/* get date and time */
331 		putline(pkt,line);
332 		putline(pkt,sprintf(line," %s\n",logname()));
333 	}
334 
335 	if (pkt->p_line[1] == MRNUM) {
336 		p = &pkt->p_line;
337 		while (*p)
338 			p++;
339 		if (*(p - 2) == DELIVER)
340 			fatal("delta specified has delivered MR (rc9)");
341 
342 		if (D_type == 'D')		/* turn MRs into comments */
343 			pkt->p_line[1] = COMMENTS;
344 	}
345 }
346 
347 
348 putmrs(pkt)
349 struct packet *pkt;
350 {
351 	register char **argv;
352 	char str[64];
353 	extern char *Varg[];
354 
355 	for (argv = &Varg[VSTART]; *argv; argv++)
356 		putline(pkt,sprintf(str,"%c%c %s\n",CTLCHAR,MRNUM,*argv));
357 }
358 
359 
360 clean_up()
361 {
362 	xrm(&gpkt);
363 	if (gpkt.p_file[0])
364 		unlockit(auxf(gpkt.p_file,'z'),getpid());
365 	if (exists(auxf(gpkt.p_file,'x')))
366 		xunlink(auxf(gpkt.p_file,'x'));
367 	xfreeall();
368 }
369 
370 
371 rdpfile(pkt,sp)
372 register struct packet *pkt;
373 struct sid *sp;
374 {
375 	struct pfile pf;
376 	char line[BUFSIZ];
377 	FILE *in;
378 
379 	in = xfopen(auxf(pkt->p_file,'p'),0);
380 	while (fgets(line,sizeof(line),in) != NULL) {
381 		pf_ab(line,&pf,1);
382 		if (sp->s_rel == pf.pf_gsid.s_rel &&
383 			sp->s_lev == pf.pf_gsid.s_lev &&
384 			sp->s_br == pf.pf_gsid.s_br &&
385 			sp->s_seq == pf.pf_gsid.s_seq) {
386 				fclose(in);
387 				fatal("being edited -- sid is in p-file (rc12)");
388 		}
389 	}
390 	fclose(in);
391 	return;
392 }
393