1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License ("CDDL"), version 1.0.
6  * You may use this file only in accordance with the terms of version
7  * 1.0 of the CDDL.
8  *
9  * A full copy of the text of the CDDL should have accompanied this
10  * source.  A copy of the CDDL is also available via the Internet at
11  * http://www.opensource.org/licenses/cddl1.txt
12  * See the License for the specific language governing permissions
13  * and limitations under the License.
14  *
15  * When distributing Covered Code, include this CDDL HEADER in each
16  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17  * If applicable, add the following below this CDDL HEADER, with the
18  * fields enclosed by brackets "[]" replaced with your own identifying
19  * information: Portions Copyright [yyyy] [name of copyright owner]
20  *
21  * CDDL HEADER END
22  */
23 /* Copyright (c) 1988 AT&T */
24 /* All Rights Reserved */
25 /*
26  * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
27  * Use is subject to license terms.
28  */
29 /*
30  * Copyright 2006-2020 J. Schilling
31  *
32  * @(#)rmchg.c	1.59 20/08/23 J. Schilling
33  */
34 #if defined(sun)
35 #pragma ident "@(#)rmchg.c 1.59 20/08/23 J. Schilling"
36 #endif
37 /*
38  * @(#)rmchg.c 1.19 06/12/12
39  */
40 
41 #if defined(sun)
42 #pragma ident	"@(#)rmchg.c"
43 #pragma ident	"@(#)sccs:cmd/rmchg.c"
44 #endif
45 # define	SCCS_MAIN			/* define global vars */
46 # include	<defines.h>
47 # include	<version.h>
48 # include	<had.h>
49 # include	<filehand.h>
50 # include	<i18n.h>
51 # include	<schily/utsname.h>
52 # include	<schily/sysexits.h>
53 
54 /*
55 	Program to remove a specified delta from an SCCS file,
56 	when invoked as 'rmdel',
57 	or to change the MRs and/or comments of a specified delta,
58 	when invoked as 'cdc'.
59 	(The program has two links to it, one called 'rmdel', the
60 	other 'cdc'.)
61 
62 	The delta to be removed (or whose MRs and/or comments
63 	are to be changed) is specified via the
64 	r argument, in the form of an SID.
65 
66 	If the delta is to be removed, it must be the most recent one
67 	in its branch in the delta tree (a so-called 'leaf' delta).
68 	For either function, the delta being processed must not
69 	have any 'delivered' MRs, and the user must have basically
70 	the same permissions as are required to make deltas.
71 
72 	If a directory is given as an argument, each SCCS file
73 	within the directory will be processed as if it had been
74 	specifically named. If a name of '-' is given, the standard
75 	input will be read for a list of names of SCCS files to be
76 	processed. Non SCCS files are ignored.
77 	If the file is CASSI controlled special processing is performed:
78 	if the cdc command is being executed then the cmrs for the delta are
79 	list with the choice given about which to delete. This choice is checked
80 	against the fred file for validity. If the rmdel command is being performed
81 	then all cmrs for the delta are checked against fred, if there are any
82 	non editable cmrs then the change is rejected.
83 */
84 
85 static struct sid sid;
86 static Nparms	N;			/* Keep -N parameters		*/
87 static Xparms	X;			/* Keep -X parameters		*/
88 static int num_files;
89 static char D_type;
90 static char 	*Sidhold;
91 static char	*Darg[NVARGS];
92 static char	*testcmr[25];
93 static char	*Earg[NVARGS];
94 static char	*NVarg[NVARGS];
95 static int D_serial;
96 static struct utsname un;
97 static char *uuname;
98 
99 	int	main __PR((int argc, char **argv));
100 static void	rmchg __PR((char *file));
101 static	void	escdodelt __PR((struct packet *pkt));
102 static int	esccmfdelt __PR((struct packet *pkt));
103 static void	fredck	__PR((struct packet *pkt));
104 static int	verif __PR((char **test, struct packet *pkt));
105 static int	msg __PR((char *app, char *name, char *cmrs, char *stats, char *sids, char *fred, char *sflags[NFLAGS]));
106 static int	testfred __PR((char *cmr, char *fredfile));
107 static void	split_mrs __PR((void));
108 static void	putmrs __PR((struct packet *pkt));
109 static void	clean_up __PR((void));
110 static void	rdpfile __PR((struct packet *pkt, struct sid *sp));
111 static void	ckmrs __PR((struct packet *pkt, char *p));
112 static void	put_delmrs __PR((struct packet *pkt));
113 
114 
115 int
main(argc,argv)116 main(argc,argv)
117 int argc;
118 char *argv[];
119 {
120 	register int i;
121 	register char *p;
122 	int  c, no_arg;
123 	extern int Fcnt;
124 	int current_optind;
125 	/*
126 	 * Set locale for all categories.
127 	 */
128 	setlocale(LC_ALL, NOGETTEXT(""));
129 
130 	sccs_setinsbase(INS_BASE);
131 
132 	/*
133 	 * Set directory to search for general l10n SCCS messages.
134 	 */
135 #ifdef	PROTOTYPES
136 	(void) bindtextdomain(NOGETTEXT("SUNW_SPRO_SCCS"),
137 	   NOGETTEXT(INS_BASE "/" SCCS_BIN_PRE "lib/locale/"));
138 #else
139 	(void) bindtextdomain(NOGETTEXT("SUNW_SPRO_SCCS"),
140 	   NOGETTEXT("/usr/ccs/lib/locale/"));
141 #endif
142 
143 	(void) textdomain(NOGETTEXT("SUNW_SPRO_SCCS"));
144 
145 	tzset();	/* Set up timezome related vars */
146 
147 #ifdef	SCHILY_BUILD
148 	save_args(argc, argv);
149 #endif
150 	/*
151 	Set flags for 'fatal' to issue message, call clean-up
152 	routine, and terminate processing.
153 	*/
154 	set_clean_up(clean_up);
155 	Fflags = FTLMSG | FTLCLN | FTLEXIT;
156 #ifdef	SCCS_FATALHELP
157 	Fflags |= FTLFUNC;
158 	Ffunc = sccsfatalhelp;
159 #endif
160 
161 	current_optind = 1;
162 	optind = 1;
163 	opterr = no_arg = 0;
164 	i = 1;
165 	/*CONSTCOND*/
166 	while (1) {
167 			if (current_optind < optind) {
168 			   current_optind = optind;
169 			   argv[i] = 0;
170 			   if (optind > i+1) {
171 			      if ((argv[i+1][0]!='-') && (no_arg==0)) {
172 				 argv[i+1] = NULL;
173 			      } else {
174 			         optind = i+1;
175 			         current_optind = optind;
176 			      }
177 			   }
178 			}
179 			no_arg = 0;
180 			i = current_optind;
181 		        c = getopt(argc, argv, "()-r:m:y:dzqN:X:V(version)");
182 
183 				/* this takes care of options given after
184 				** file names.
185 				*/
186 			if (c == EOF) {
187 			   if (optind < argc) {
188 				/* if it's due to -- then break; */
189 			       if(argv[i][0] == '-' &&
190 				      argv[i][1] == '-') {
191 			          argv[i] = 0;
192 			          break;
193 			       }
194 			       optind++;
195 			       current_optind = optind;
196 			       continue;
197 			   } else {
198 			       break;
199 			   }
200 			}
201 			p = optarg;
202 			switch (c) {
203 
204 			case 'r':
205 				if (!(*p))
206 					fatal(gettext("r has no sid (rc11)"));
207 				Sidhold=p;
208 				chksid(sid_ab(p,&sid),&sid);
209 				break;
210 			case 'd':	/* rmdel -d -> fully discard delta */
211 				break;
212 			case 'm':	/* MR entry */
213 				Mrs = p;
214 				repl(Mrs,'\n',' ');
215 				break;
216 			case 'y':	/* comment line */
217 				if (optarg == argv[i+1]) {
218 				   Comments = "";
219 				   no_arg = 1;
220 				}
221 				else {
222 				   Comments = p;
223 				}
224 				break;
225                         case 'q': /* enable NSE mode */
226                                 if (p) {
227                                    if (*p) {
228                                         nsedelim = p;
229                                    }
230                                 } else {
231                                         nsedelim = (char *) 0;
232                                 }
233                                 break;
234 			case 'z':
235 				break;
236 
237 			case 'N':	/* Bulk names */
238 				initN(&N);
239 				if (optarg == argv[i+1]) {
240 				   no_arg = 1;
241 				   break;
242 				}
243 				N.n_parm = p;
244 				break;
245 
246 			case 'X':	/* -Xtended options */
247 				X.x_parm = optarg;
248 				X.x_flags = XO_NULLPATH;
249 				if (!parseX(&X))
250 					goto err;
251 				had[NLOWER+c-'A'] = 0;	/* Allow mult -X */
252 				break;
253 
254 			case 'V':		/* version */
255 				p = sname(argv[0]);
256 				printf(gettext(
257 				    "%s %s-SCCS version %s %s (%s-%s-%s)\n"),
258 					equal(p, "cdc") ? "cdc": "rmdel",
259 					PROVIDER,
260 					VERSION,
261 					VDATE,
262 					HOST_CPU, HOST_VENDOR, HOST_OS);
263 				exit(EX_OK);
264 
265 			default:
266 			err:
267 				p = sname(argv[0]);
268 				if (equal(p,"cdc"))
269 					fatal(gettext(
270 					"Usage: cdc -r SID [-mmr-list] [-y [comment]] [-N[bulk-spec]][ -Xxopts ] s.filename ..."));
271 				else
272 					fatal(gettext(
273 					"Usage: rmdel -r SID [-N[bulk-spec]] s.filename ..."));
274 			}
275 
276 			/*
277 			 * Make sure that we only collect option letters from
278 			 * the range 'a'..'z' and 'A'..'Z'.
279 			 */
280 			if (ALPHA(c) &&
281 			    (had[LOWER(c)? c-'a' : NLOWER+c-'A']++)) {
282 				if (c != 'X')
283 					fatal(gettext("key letter twice (cm2)"));
284 			}
285 	}
286 
287 	for(i=1; i<argc; i++){
288 		if(argv[i]) {
289 		       num_files++;
290 		}
291 	}
292 	if(HADM && HADZ)
293 	{
294 		fatal(gettext("Cassi not compatable with cmrs on command line"));
295 	}
296 	if(num_files == 0)
297 		fatal(gettext("missing file arg (cm3)"));
298 
299 	setsig();
300 	xsethome(NULL);
301 	if (HADUCN) {					/* Parse -N args  */
302 		parseN(&N);
303 
304 		if (N.n_sdot && (sethomestat & SETHOME_OFFTREE))
305 			fatal(gettext("-Ns. not supported in off-tree project mode"));
306 	}
307 
308 	if (*(p = sname(argv[0])) == 'n')
309 		p++;
310 	if (equal(p,NOGETTEXT("rmdel"))) {
311 		D_type = 'R';		/* invoked as 'rmdel' */
312 		if (HADD)
313 			D_type = '\0';	/* invoked as 'rmdel -d' */
314 	} else if (equal(p,"cdc")) {
315 		D_type = 'D';		/* invoked as 'cdc' */
316 	} else {
317 		fatal(gettext("bad invocation (rc10)"));
318 	}
319 	if (! logname())
320 		fatal(gettext("User ID not in password file (cm9)"));
321 
322 	/*
323 	 * Get the name of our machine to be used for the lockfile.
324 	 */
325 	uname(&un);
326 	uuname = un.nodename;
327 
328 	/*
329 	 * Set up a project global lock on the changeset file.
330 	 * Since we set FTLJMP, we do not need to unlockchset() from clean_up().
331 	 */
332 	if (SETHOME_CHSET())
333 		lockchset(getppid(), getpid(), uuname);
334 	timerchsetlock();
335 
336 	/*
337 	Change flags for 'fatal' so that it will return to this
338 	routine (main) instead of terminating processing.
339 	*/
340 	Fflags &= ~FTLEXIT;
341 	Fflags |= FTLJMP;
342 
343 	/*
344 	Call 'rmchg' routine for each file argument.
345 	*/
346 	for (i=1; i<argc; i++)
347 		if ((p = argv[i]) != NULL)
348 			do_file(p, rmchg, 1, N.n_sdot, &X);
349 
350 	/*
351 	 * Only remove the global lock it it was created by us and not by
352 	 * our parent.
353 	 */
354 	if (SETHOME_CHSET()) {
355 		if (HADUCN)
356 			bulkchdir(&N);
357 		unlockchset(getpid(), uuname);
358 	}
359 
360 	return (Fcnt ? 1 : 0);
361 }
362 
363 
364 /*
365 	Routine that actually causes processing of the delta.
366 	Processing on the file takes place on a
367 	temporary copy of the SCCS file (the x-file).
368 	The name of the x-file is the same as that of the
369 	s-file (SCCS file) with the 's.' replaced by 'x.'.
370 	At end of processing, the s-file is removed
371 	and the x-file is renamed with the name of the old s-file.
372 
373 	This routine makes use of the z-file to lock out simultaneous
374 	updates to the SCCS file by more than one user.
375 */
376 
377 static struct packet gpkt;	/* see file s.h */
378 static char Zhold[MAXPATHLEN];	/* temporary z-file name */
379 static char line[BUFSIZ];
380 
381 static void
rmchg(file)382 rmchg(file)
383 char *file;
384 {
385 	static int first_time = 1;
386 	struct stats stats;	/* see file s.defines.h */
387 	struct stat sbuf;
388 	int n, numdelts;
389 	char *p, *cp;
390 	int keep;
391 	int found;
392 	int fowner, downer, user;
393 
394 	if (setjmp(Fjmp))	/* set up to return here from 'fatal' */
395 		return;		/* and return to caller of rmchg */
396 
397 	/*
398 	 * In order to make the global lock with a potentially long duration
399 	 * not look as if it was expired, we refresh it for every file in our
400 	 * task list. This is needed since another SCCS instance on a different
401 	 * NFS machine cannot use kill() to check for a still active process.
402 	 */
403 	if (SETHOME_CHSET()) {
404 		if (HADUCN)
405 			bulkchdir(&N);	/* Done by bulkprepare() anyway */
406 		refreshchsetlock();
407 	}
408 
409 	if (HADUCN) {
410 #ifdef	__needed__
411 		char	*ofile = file;
412 #endif
413 
414 		file = bulkprepare(&N, file);
415 		if (file == NULL) {
416 #ifdef	__needed__
417 			if (N.n_ifile)
418 				ofile = N.n_ifile;
419 #endif
420 			/*
421 			 * The error is typically
422 			 * "directory specified as s-file (cm14)"
423 			 */
424 			fatal(gettext(bulkerror(&N)));
425 		}
426 		if (sid.s_rel == 0 && N.n_sid.s_rel != 0) {
427 			sid.s_rel = N.n_sid.s_rel;
428 			sid.s_lev = N.n_sid.s_lev;
429 			sid.s_br  = N.n_sid.s_br;
430 			sid.s_seq = N.n_sid.s_seq;
431 		}
432 	}
433 
434 	/*
435 	 * Initialize here to avoid setjmp() clobbering warnings
436 	 */
437 	found = NO;
438 
439 	if (!HADR)
440 		fatal(gettext("missing r (rc1)"));
441 
442 	if (D_type == 'D' && first_time) {
443 		first_time = 0;
444 		dohist(file);
445 	}
446 
447 	if (!exists(file)) {
448 		sprintf(SccsError,gettext("file %s does not exist (rc2)"),
449 			file);
450 		fatal(SccsError);
451 	}
452 
453 	/*
454 	 * Init and check for validity of file name but do not open the file.
455 	 * This prevents us from potentially damaging files with lockit().
456 	 */
457 	sinit(&gpkt, file, SI_INIT);
458 
459 	/*
460 	 * Lock out any other user who may be trying to process
461 	 * the same file.
462 	 */
463 	if (!islockchset(copy(auxf(file, 'z'), Zhold)) &&
464 	    lockit(Zhold, SCCS_LOCK_ATTEMPTS, getpid(), uuname)) {
465 		lockfatal(Zhold, getpid(), uuname);
466 	} else {
467 		timersetlockfile(Zhold);
468 	}
469 
470 	sinit(&gpkt, file, SI_OPEN);	/* initialize packet and open s-file */
471 
472 	gpkt.p_escdodelt = escdodelt;
473 	gpkt.p_fredck = fredck;
474 	/*
475 	Flag for 'putline' routine to tell it to open x-file
476 	and allow writing on it.
477 	*/
478 	gpkt.p_upd = 1;
479 
480 	/*
481 	Save requested SID for later checking of
482 	permissions (by 'permiss').
483 	*/
484 	gpkt.p_reqsid = sid;
485 
486 	/*
487 	Now read-in delta table. The 'dodelt' routine
488 	will read the table and change the delta entry of the
489 	requested SID to be of type 'R' if this is
490 	being executed as 'rmdel'; otherwise, for 'cdc', only
491 	the MR and comments sections will be changed
492 	(by 'escdodelt', called by 'dodelt').
493 	*/
494 	if (dodelt(&gpkt,&stats,&sid,D_type) == 0)
495 		fmterr(&gpkt);
496 
497 	/*
498 	Get serial number of requested SID from
499 	delta table just processed.
500 	*/
501 	D_serial = sidtoser(&gpkt.p_reqsid,&gpkt);
502 
503 	/*
504 	If SID has not been zeroed (by 'dodelt'),
505 	SID was not found in file.
506 	*/
507 	if (sid.s_rel != 0)
508 		fatal(gettext("nonexistent sid (rc3)"));
509 	/*
510 	Replace 'sid' with original 'sid'
511 	requested.
512 	*/
513 	sid = gpkt.p_reqsid;
514 
515 	/*
516 	Now check permissions.
517 	*/
518 	finduser(&gpkt);
519 	doflags(&gpkt);
520 	permiss(&gpkt);
521 
522 	donamedflags(&gpkt);
523 	dometa(&gpkt);
524 
525 	/*
526 	Check that user is either owner of file or
527 	directory, or is one who made the delta.
528 	*/
529 	fstat(fileno(gpkt.p_iop),&Statbuf);
530 	fowner = Statbuf.st_uid;
531 	copy(gpkt.p_file,line);		/* temporary for dname() */
532 	if (stat(dname(line),&Statbuf))
533 		downer = -1;
534 	else
535 		downer = Statbuf.st_uid;
536 	user = getuid();
537 	if (user != fowner && user != downer)
538 		if (!equal(gpkt.p_pgmr, logname())) {
539 			sprintf(SccsError, gettext("you are neither owner nor '%s' (rc4)"),
540 				gpkt.p_pgmr);
541 			fatal(SccsError);
542 		}
543 
544 	/*
545 	For 'rmdel', check that delta being removed is a
546 	'leaf' delta, and if ok,
547 	process the body.
548 	*/
549 	if (D_type == 'R' || D_type == '\0') {
550 		struct idel *ptr;
551 		for (n = maxser(&gpkt); n > D_serial; n--) {
552 			ptr = &gpkt.p_idel[n];
553 			if (ptr->i_pred == D_serial)
554 				fatal(gettext("not a 'leaf' delta (rc5)"));
555 		}
556 
557 		/*
558 		   For 'rmdel' check that the sid requested is
559 		   not contained in p-file, should a p-file
560 		   exist.
561 		*/
562 
563 		if (exists(auxf(gpkt.p_file,'p')))
564 			rdpfile(&gpkt,&sid);
565 
566 		flushto(&gpkt, EUSERTXT, FLUSH_COPY);
567 
568 		keep = YES;
569 		found = NO;
570 		numdelts = 0;			/* keeps count of numbers of deltas */
571 		gpkt.p_chkeof = 1;		/* set EOF is ok */
572 		while ((p = getline(&gpkt)) != NULL) {
573 			if (*p++ == CTLCHAR) {
574 				cp = p++;
575 				NONBLANK(p);
576 				/*
577 				Convert serial number to binary.
578 				*/
579 				if (*(p = satoi(p,&n)) != '\n')
580 					fmterr(&gpkt);
581 				if (n == D_serial) {
582 					gpkt.p_wrttn = 1;
583 					if (*cp == INS) {
584 						found = YES;
585 						keep = NO;
586 					}
587 					else
588 						keep = YES;
589 				}
590 				if (*cp == INS)
591 					/*
592 					   keep track of number of deltas  -
593 					   do not remove if it's the last one
594 					*/
595 					numdelts++;
596 			}
597 			else
598 				if (keep == NO)
599 					gpkt.p_wrttn = 1;
600 		}
601 	}
602 	else {
603 		numdelts = 2;    /* dummy - needed for rmdel, not cdc */
604 		/*
605 		This is for invocation as 'cdc'.
606 		Check MRs if not CASSI file.
607 		*/
608 		if ((Mrs != NULL) && (*Mrs != '\0')) {
609 			if ((p = gpkt.p_sflags[VALFLAG - 'a']) == NULL)
610 				fatal(gettext("MRs not allowed (rc6)"));
611 			if (*p && valmrs(&gpkt,p))
612 				fatal(gettext("invalid MRs (rc7)"));
613 		}
614 
615 		/*
616 		Indicate that EOF at this point is ok, and
617 		flush rest of s-file to x-file.
618 		*/
619 		gpkt.p_chkeof = 1;
620 		while (getline(&gpkt))
621 			;
622 	}
623 
624 	flushline(&gpkt,(struct stats *) 0);
625 
626 	/* if z flag on and -fz exists on the sccs s.file
627 		verify the deleted cmrs and send message
628 	*/
629 	if((HADZ && !gpkt.p_sflags[CMFFLAG - 'a']) ||
630 		(!(HADZ) && gpkt.p_sflags[CMFFLAG - 'a']))
631 		{
632 			fatal(gettext("invalid use of 'z' flag \n CASSI incompatablity\n"));
633 		}
634 	else
635 		{
636 		if(HADZ)
637 			{
638 			/*now process the testcmr table and send messages*/
639 				if(!verif(testcmr,&gpkt))
640 				{
641 					fatal(gettext("uneditable cmr for CASSI controlled file- Change not allowed"));
642 				}
643 			}
644 		}
645 
646 	if (numdelts < 2 && found == YES)
647 		/* they tried to delete the last remaining delta */
648 		fatal(gettext("may not remove the last delta (rc13)"));
649 
650 	/*
651 	Delete old s-file, change x-file name to s-file.
652 	*/
653 	stat(gpkt.p_file,&sbuf);
654 	rename(auxf(gpkt.p_file,'x'), gpkt.p_file);
655 	chmod(gpkt.p_file,sbuf.st_mode);
656 	chown(gpkt.p_file,sbuf.st_uid,sbuf.st_gid);
657 	clean_up();
658 }
659 
660 static void
escdodelt(pkt)661 escdodelt(pkt)
662 struct packet *pkt;
663 {
664 	static int first_time = 1;
665 	char	*p;
666 	extern dtime_t Timenow;
667 
668 	static short int doneesc;
669 	/* the CMF -z option is on and MR numb being processed*/
670 	if((HADZ) && (pkt->p_line[1] == MRNUM) && (D_type == 'D') && (!doneesc))
671 		{
672 		 doneesc = 1;
673 		 if(!esccmfdelt(pkt))
674 			{
675 			 fatal(gettext("bad cdc replace of cmr's (CASSI)"));
676 			}
677 		 else
678 			return;
679 		}
680 	/* non cassi processing begins*/
681 	if (first_time) {
682 		/*
683 		 * if first time calling `escdodelt'
684 		 * then split the MR list from mrfixup.
685 		*/
686 
687 		split_mrs();
688 		first_time = 0;
689 	}
690 	if (D_type == 'D' && pkt->p_first_esc) { /* cdc, first time */
691 		pkt->p_first_esc = 0;
692 		if ((Mrs != NULL) && (*Mrs != '\0')) {
693 			/*
694 			 * if adding MRs then put out the new list
695 			 * from `split_mrs' (if any)
696 			*/
697 
698 			putmrs(pkt);
699 			pkt->p_cdid_mrs = 1;	/* indicate that some MRs were read */
700 		}
701 	}
702 
703 	if (pkt->p_line[1] == MRNUM) {		/* line read is MR line */
704 		p = pkt->p_line;
705 		while (*p)
706 			p++;
707 		if (*(p - 2) == DELIVER)
708 			fatal(gettext("delta specified has delivered MR (rc9)"));
709 		if (!pkt->p_cdid_mrs)	/* if no MRs list from `dohist' then return */
710 			return;
711 		else
712 			/*
713 			 * check to see if this MR should be removed
714 			*/
715 
716 			ckmrs(pkt,pkt->p_line);
717 	}
718 	else if (D_type == 'D') {		/* this line is a comment */
719 		if (pkt->p_first_cmt) {		/* first comment encountered */
720 			pkt->p_first_cmt = 0;
721 			/*
722 			 * if comments were read by `dohist' then
723 			 * put them out.
724 			*/
725 
726 			if (*Comments) {
727 			  char *comment;
728 				sprintf(line,"%c%c ",CTLCHAR,
729 					COMMENTS);
730 				putline(pkt,line);
731 				comment = savecmt(Comments);
732 			        putline(pkt, comment);
733 			        putline(pkt, "\n");
734 			}
735 
736 			/*
737 			 * if any MRs were deleted, print them out
738 			 * as comments for this invocation of `cdc'
739 			*/
740 
741 			put_delmrs(pkt);
742 			/*
743 			 * if comments were read by `dohist' then
744 			 * notify user that comments were CHANGED.
745 			*/
746 
747 			if (*Comments) {
748 				sprintf(line,"%c%c ",CTLCHAR,COMMENTS);
749 				putline(pkt,line);
750 				putline(pkt,"*** CHANGED *** ");
751 				/* get date and time */
752 				/*
753 				 * The s-file is not part of the POSIX standard.
754 				 * For this reason, we are free to switch to a
755 				 * 4-digit year for the initial comment.
756 				 */
757 				if ((Timenow.dt_sec < Y1969) ||
758 				    (Timenow.dt_sec >= Y2038))			/* comment only */
759 					date_bal(&Timenow.dt_sec, line, 0);	/* 4 digit year */
760 				else
761 					date_ba(&Timenow.dt_sec, line, 0);	/* 2 digit year */
762 				putline(pkt,line);
763 				sprintf(line," %s\n",logname());
764 				putline(pkt,line);
765 			}
766 		}
767 		else return;
768 	}
769 }
770 
771 
772 /* esccmfdelt(pkt) takes a packet line of cassi cmrs
773 *  prompts for those which will be deleted from the delta
774 *  checks the ones to be deleted against fred
775 *  it turns the old line into a comment line
776 *  and makes a new line containing the non deleted cmrs
777 *  a chpost message is sent for the deleted cmrs
778 *  whole function
779 */
780 	static int
esccmfdelt(pkt)781 	esccmfdelt(pkt)
782 		struct packet *pkt;
783 		{
784 		 char *p,*pp,*pend,holder[100],outhold[110],answ[80],*holdptr[25],str[80];
785 		 int numcmrs,i,n,j,changed=0;
786 		 /* move the cmr data to a holder*/
787 		 p = pkt->p_line;
788 		 p += 3;
789 		 strlcpy(holder, p, sizeof (holder));
790 		 i=0;
791 		 outhold[0]= '\0';
792 		 holdptr[0]= strtok(holder,",\n");
793 		 while((holdptr[++i] = strtok(0,",\n")) != NULL)
794 			;
795 		 if(!holdptr[1])
796 			{
797 			 /* only one mr on the list */
798 			 printf(gettext("only one cmr left for this delta no change possible \n"));
799 			 return(1);
800 			}
801 		 numcmrs = i;
802 		 n=0;
803  		 j=0;
804 		 while (holdptr[j])
805 			{
806 			 printf(gettext("do you want to delete %s\n"),
807 				holdptr[j]);
808 			 fgets(answ, sizeof (answ), stdin);
809 			 if(imatch(NOGETTEXT("y"),answ))
810 				{
811 				 if(numcmrs <= 1)
812 					{
813 						printf(gettext("%s is not editable now\n"),
814 						       holdptr[j]);
815 						 cat(outhold,outhold,holdptr[j],",", (char *)0);
816 						 break;
817 						}
818 					else
819 				 		{
820 						 /* store the cmrnumber in a test table*/
821 						 testcmr[n]=(char *)malloc((unsigned)strlen(holdptr[j]) +1);
822 						 strcpy(testcmr[n++],holdptr[j]);
823 						 numcmrs--;
824 						 changed=1;
825 						}
826 				}
827 			 else
828 				{
829 				 cat(outhold,outhold,holdptr[j],",", (char *)0);
830 				}
831 			j++;
832 			}
833 			if(!changed)
834 				{
835 				 return(0);
836 				}
837 			/* turn old line into comment */
838 			p -= 2;
839 			*p = 'c';
840 			pend=strend(p);
841 			*--pend='\n';
842 			putline(pkt,0);
843 			/* place new line */
844 			pp=strend(outhold);
845 			*pp= '\0';
846 			*--pp= '\0';
847 			sprintf(str,"%c%c %s\n",CTLCHAR,MRNUM,outhold);
848 			putline(pkt,str);
849 			return(0);
850 		}
851 /*the fredchk routine verifies that the cmrs for the delta are all editableor
852 *		 degenerate  before changing the delta*/
853 static void
fredck(pkt)854 fredck(pkt)
855 struct packet *pkt;
856 {
857 	char *mrhold[20],*p,holder[80];
858 	int i,j;
859 	p = pkt->p_line;
860 	p += 3;
861 	strlcpy(holder, p, sizeof (holder));
862 	mrhold[0]=strtok(holder,"\n,");
863 	i=0;
864 	while((mrhold[++i] = strtok(0,"\n,")) != NULL)
865 		;
866 	for(j=0;j<i;j++)
867 	{
868 		testcmr[j]=(char *)malloc((unsigned)strlen(mrhold[j]) + 1);
869 		strcpy(testcmr[j],mrhold[j]);
870 	}
871 }
872 /*verif takes the list of deleted cmrs and checks them agains the fredfile
873   if any are invalid the function returns 0
874 */
875 static int
verif(test,pkt)876 verif(test,pkt)
877 char *test[];
878 struct packet *pkt;
879 {
880 	char *fred;
881 	int i;
882 	/* get the fred file name*/
883 	if (!(fred=(char *)gf(pkt->p_sflags[CMFFLAG - 'a'])))
884 		fatal(gettext("No fred file found- see sccs administrator"));
885 	/* check validity of values in test */
886 	for(i=0;test[i];i++)
887 		{
888 			if(!(testfred(test[i],fred)))
889 				{
890 					sprintf(SccsError, gettext("%s is not editable"),
891 					      test[i]);
892 					fatal(SccsError);
893 					return(0);
894 				}
895 		}
896 	/* write the messages for the valid cmrs */
897 	for(i=0;test[i];i++)
898 		{
899 			msg(pkt->p_sflags[CMFFLAG - 'a'],pkt->p_file,test[i],"nc",Sidhold,fred, pkt->p_sflags);
900 		}
901 	return(1);
902 }
903 
904 /*the  msg subroutine creates the command line to go to the mr subsystem*/
905 static int
msg(app,name,cmrs,stats,sids,fred,sflags)906 msg(app, name, cmrs, stats, sids, fred, sflags)
907 	char *app,*name,*cmrs,*stats,*sids,*fred;
908 	char *sflags[NFLAGS];
909 	{
910  	FILE *fd;
911 	 char pname[FILESIZE],*ptr,holdfred[100],dir[100],path[FILESIZE];
912 	 struct stat stbuf;
913 	 int noexist = 0;
914 	 char *k;
915 
916 	/*if -fm its value is made the file name */
917 	if ((k = sflags[MODFLAG -'a']) != NULL)
918 	{
919 		name = k;
920 	}
921 	if( *name != '/' )   /* NOT full path name */
922 	{
923 		if(getcwd(path,sizeof(path)) == NULL)
924                  	efatal(gettext("getcwd() failed (ge20)"));
925 		cat(pname,path,"/",name, (char *)0);
926 	}
927 	else
928 	{
929 		strlcpy(pname, name, sizeof (pname));
930 	}
931 	strlcpy(holdfred, fred, sizeof (holdfred));
932 	ptr=(char *)strchr(holdfred,'.');
933 	*ptr = '\0';
934 	 strlcat(holdfred, NOGETTEXT("source"), sizeof (holdfred));
935 	 strlcpy(dir, holdfred, sizeof (dir));
936 	 strlcat(holdfred, NOGETTEXT("/termLOG"), sizeof (holdfred));
937 	 if(stat(holdfred,&stbuf) == -1)
938 		noexist = 1; /*new termLOG */
939 	if(!(fd=fopen(holdfred, NOGETTEXT("ab"))))
940 		{
941 			efatal(gettext("CASSI msg not writable \n"));
942 			return(0);
943 		}
944 	fprintf(fd,"%s chpost %s q %s sw MID=%s MFS=%s MPA=%s q q\n",app,cmrs,pname,sids,stats,logname());
945 	fclose(fd);
946 	 if(noexist) /*new termLOG make owner of /BD/source owner of file */
947 	 {
948 		if(stat(dir,&stbuf) == -1)
949 		{
950 			fatal(gettext("Cassi BD/source not writeable\n"));
951 		}
952 		chmod(holdfred,0666);
953 		chown(holdfred,(int)stbuf.st_uid,(int)stbuf.st_gid);
954 	}
955 	return(1);
956 }
957 /* testfred takes the fredfile and the cmr and checks to see if the cmr is in
958 *the fred  file
959 */
960 static int
testfred(cmr,fredfile)961 testfred(cmr,fredfile)
962 	char *cmr,*fredfile;
963 	{
964 		char dcmr[50];
965 		char *cmrh[2],*dcmrh[2];
966 		cmrh[1] = (char *) NULL;
967 		dcmrh[1] = (char *) NULL;
968 		cat(dcmr,NOGETTEXT("D"),cmr, (char *)0);
969 		cmrh[0]=cmr;
970 		dcmrh[0]=dcmr;
971 		return(sweep(SEQVERIFY,fredfile,NULL,'\n',WHITE,80,cmrh,NULL,NULL,
972 		(int(*)__PR((char *, int, char **)))NULL,
973 		(int(*)__PR((char **, char **, int)))NULL) == FOUND || sweep(SEQVERIFY,fredfile
974 		,NULL,'\n',WHITE,80,dcmrh,NULL,NULL,
975 		(int(*)__PR((char *, int, char **)))NULL,
976 		(int(*)__PR((char **, char **, int)))NULL)
977 		== FOUND);
978 	}
979 
980 
981 extern char **Varg;
982 
983 static void
split_mrs()984 split_mrs()
985 {
986 	register char **argv;
987 	char	**dargv;
988 	char	**nargv;
989 	char	*ap;
990 
991 	dargv = &Darg[VSTART];
992 	nargv = &NVarg[VSTART];
993 	for (argv = &Varg[VSTART]; *argv; argv++)
994 		if (*argv[0] == '!') {
995 			*argv += 1;
996 			ap = *argv;
997 			copy(ap,*dargv++ = stalloc(size(ap)));
998 			*dargv = 0;
999 			continue;
1000 		}
1001 		else {
1002 			copy(*argv,*nargv++ = stalloc(size(*argv)));
1003 			*nargv = 0;
1004 		}
1005 	Varg = NVarg;
1006 }
1007 
1008 static void
putmrs(pkt)1009 putmrs(pkt)
1010 struct packet *pkt;
1011 {
1012 	register char **argv;
1013 	char	str[64];
1014 
1015 	for(argv = &Varg[VSTART]; *argv; argv++) {
1016 		sprintf(str,"%c%c %s\n",CTLCHAR,MRNUM,*argv);
1017 		putline(pkt,str);
1018 	}
1019 }
1020 
1021 static void
clean_up()1022 clean_up()
1023 {
1024 	sclose(&gpkt);
1025 	sfree(&gpkt);
1026 	xrm(&gpkt);
1027 	if (gpkt.p_file[0]) {
1028 		if (exists(auxf(gpkt.p_file,'x')))
1029 			xunlink(auxf(gpkt.p_file,'x'));
1030 	}
1031 	ffreeall();
1032 	if (gpkt.p_file[0]) {
1033 		uname(&un);
1034 		uuname = un.nodename;
1035 		timersetlockfile(NULL);
1036 		if (!islockchset(Zhold))
1037 			unlockit(Zhold, getpid(), uuname);
1038 	}
1039 }
1040 
1041 static void
rdpfile(pkt,sp)1042 rdpfile(pkt,sp)
1043 register struct packet *pkt;
1044 struct sid *sp;
1045 {
1046 	struct pfile pf;
1047 	char rline[BUFSIZ];
1048 	FILE *in;
1049 
1050 	in = xfopen(auxf(pkt->p_file,'p'), O_RDONLY|O_BINARY);
1051 	while (fgets(rline,sizeof(line),in) != NULL) {
1052 		pf_ab(rline,&pf,1);
1053 		if (sp->s_rel == pf.pf_gsid.s_rel &&
1054 			sp->s_lev == pf.pf_gsid.s_lev &&
1055 			sp->s_br == pf.pf_gsid.s_br &&
1056 			sp->s_seq == pf.pf_gsid.s_seq) {
1057 				fclose(in);
1058 				fatal(gettext("being edited -- sid is in p-file (rc12)"));
1059 		}
1060 	}
1061 	fclose(in);
1062 	return;
1063 }
1064 
1065 static void
ckmrs(pkt,p)1066 ckmrs(pkt,p)
1067 struct packet *pkt;
1068 char *p;
1069 {
1070 	register char **argv, **eargv;
1071 	char mr_no[BUFSIZ];
1072 	char *mr_ptr;
1073 
1074 	eargv = &Earg[VSTART];
1075 	copy(p,mr_no);
1076 	mr_ptr = mr_no;
1077 	repl(mr_ptr,'\n','\0');
1078 	mr_ptr += 3;
1079 	for (argv = &Darg[VSTART]; *argv; argv++)
1080 		if (equal(mr_ptr,*argv)) {
1081 			pkt->p_wrttn = 1;
1082 			copy(mr_ptr,*eargv++ = stalloc(size(mr_ptr)));
1083 			*eargv = 0;
1084 		}
1085 }
1086 
1087 static void
put_delmrs(pkt)1088 put_delmrs(pkt)
1089 struct	packet	*pkt;
1090 {
1091 
1092 	register char	**argv;
1093 	register int first;
1094 	char	str[64];
1095 
1096 	first = 1;
1097 	for(argv = &Earg[VSTART]; *argv; argv++) {
1098 		if (first) {
1099 			putline(pkt,"c *** LIST OF DELETED MRS ***\n");
1100 			first = 0;
1101 		}
1102 		sprintf(str,"%c%c %s\n",CTLCHAR,COMMENTS,*argv);
1103 		putline(pkt,str);
1104 	}
1105 }
1106