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