1 # include "../hdr/defines.h"
2 # include "../hdr/had.h"
3 
4 static char Sccsid[] = "@(#)admin.c	4.3	02/02/88";
5 
6 /*
7 	Program to create new SCCS files and change parameters
8 	of existing ones. Arguments to the program may appear in
9 	any order and consist of keyletters, which begin with '-',
10 	and named files. Named files which do not exist are created
11 	and their parameters are initialized according to the given
12 	keyletter arguments, or are given default values if the
13 	corresponding keyletters were not supplied. Named files which
14 	do exist have those parameters corresponding to given key-letter
15 	arguments changed and other parameters are left as is.
16 
17 	If a directory is given as an argument, each SCCS file within
18 	the directory is processed as if it had been specifically named.
19 	If a name of '-' is given, the standard input is read for a list
20 	of names of SCCS files to be processed.
21 	Non-SCCS files are ignored.
22 
23 	Files created are given mode 444.
24 */
25 
26 # define MINR 1		/* minimum release number */
27 # define MAXR 9999	/* maximum release number */
28 # define MAXNAMES 9
29 # define COPY 0
30 # define NOCOPY 1
31 
32 char *ifile, *tfile;
33 char *z;	/* for validation program name */
34 char had[26], had_flag[26], rm_flag[26];
35 char	*Comments, *Mrs;
36 char Valpgm[] = "/usr/local/val";
37 int irel, fexists, num_files;
38 int	VFLAG = 0;
39 int	Domrs;
40 char *Sflags[];
41 char *anames[MAXNAMES], *enames[MAXNAMES];
42 char *flag_p[26];
43 int asub, esub;
44 int check_id;
45 int Did_id;
46 
47 main(argc,argv)
48 int argc;
49 char *argv[];
50 {
51 	register int j;
52 	register char *p;
53 	char c, f;
54 	int i, testklt;
55 	extern admin();
56 	extern int Fcnt;
57 	struct sid sid;
58 
59 	/*
60 	Set flags for 'fatal' to issue message, call clean-up
61 	routine and terminate processing.
62 	*/
63 	Fflags = FTLMSG | FTLCLN | FTLEXIT;
64 
65 	testklt = 1;
66 
67 	/*
68 	The following loop processes keyletters and arguments.
69 	Note that these are processed only once for each
70 	invocation of 'main'.
71 	*/
72 	for(j=1; j<argc; j++)
73 		if(argv[j][0] == '-' && (c = argv[j][1])) {
74 			p = &argv[j][2];
75 			switch (c) {
76 
77 			case 'i':	/* name of file of body */
78 				ifile = p;
79 				break;
80 
81 			case 't':	/* name of file of descriptive text */
82 				tfile = p;
83 				break;
84 			case 'm':	/* mr flag */
85 				Mrs = p;
86 				break;
87 			case 'y':	/* comments flag for entry */
88 				Comments = p;
89 				break;
90 
91 			case 'd':	/* flags to be deleted */
92 				testklt = 0;
93 				if (!(f = *p))
94 					fatal("d has no argument (ad1)");
95 				p = &argv[j][3];
96 
97 				switch (f) {
98 
99 				case IDFLAG:	/* see 'f' keyletter */
100 				case BRCHFLAG:	/* for meanings of flags */
101 				case VALFLAG:
102 				case TYPEFLAG:
103 				case MODFLAG:
104 				case NULLFLAG:
105 				case FLORFLAG:
106 				case CEILFLAG:
107 				case DEFTFLAG:
108 					if (*p) {
109 						sprintf(Error, "value after %c flag (ad12)",f);
110 						fatal(Error);
111 					}
112 					break;
113 
114 				default:
115 					fatal("unknown flag (ad3)");
116 				}
117 
118 				if (rm_flag[f - 'a']++)
119 					fatal("flag twice (ad4)");
120 				break;
121 
122 			case 'f':	/* flags to be added */
123 				testklt = 0;
124 				if (!(f = *p))
125 					fatal("f has no argument (ad5)");
126 				p = &argv[j][3];
127 
128 				switch (f) {
129 
130 				case IDFLAG:	/* id-kwd message (err/warn) */
131 				case BRCHFLAG:	/* branch */
132 				case NULLFLAG:	/* null deltas */
133 					if (*p) {
134 						sprintf(Error, "value after %c flag (ad13)",f);
135 						fatal(Error);
136 					}
137 					break;
138 
139 				case VALFLAG:	/* mr validation */
140 					VFLAG++;
141 					if (*p)
142 						z = p;
143 					break;
144 
145 				case FLORFLAG:	/* floor */
146 					if ((i = patoi(p)) == -1)
147 						fatal("floor not numeric (ad22)");
148 					if ((size(p) > 5) || (i < MINR) ||
149 							(i > MAXR))
150 						fatal("floor out of range (ad23)");
151 					break;
152 
153 				case CEILFLAG:	/* ceiling */
154 					if ((i = patoi(p)) == -1)
155 						fatal("ceiling not numeric (ad24)");
156 					if ((size(p) > 5) || (i < MINR) ||
157 							(i > MAXR))
158 						fatal("ceiling out of range (ad25)");
159 					break;
160 
161 				case DEFTFLAG:	/* default sid */
162 					if (!(*p))
163 						fatal("no default sid (ad14)");
164 					chksid(sid_ab(p,&sid),&sid);
165 					break;
166 
167 				case TYPEFLAG:	/* type */
168 				case MODFLAG:	/* module name */
169 					if (!(*p)) {
170 						sprintf(Error, "flag %c has no value (ad2)",f);
171 						fatal(Error);
172 					}
173 					break;
174 
175 				default:
176 					fatal("unknown flag (ad3)");
177 				}
178 
179 				if (had_flag[f - 'a']++)
180 					fatal("flag twice (ad4)");
181 				flag_p[f - 'a'] = p;
182 				break;
183 
184 			case 'r':	/* initial release number supplied */
185 				if ((irel = patoi(p)) == -1)
186 					fatal("r arg not numeric (ad6)");
187 				if ((size(p) > 5) || (irel < MINR) ||
188 						(irel > MAXR))
189 					fatal("r out of range (ad7)");
190 				break;
191 
192 			case 'n':	/* creating new SCCS file */
193 			case 'h':	/* only check hash of file */
194 			case 'z':	/* zero the input hash */
195 				break;
196 
197 			case 'a':	/* user-name allowed to make deltas */
198 				testklt = 0;
199 				if (!(*p))
200 					fatal("bad a argument (ad8)");
201 				if (asub > MAXNAMES)
202 					fatal("too many 'a' keyletters (ad9)");
203 				anames[asub++] = p;
204 				break;
205 
206 			case 'e':	/* user-name to be removed */
207 				testklt = 0;
208 				if (!(*p))
209 					fatal("bad e argument (ad10)");
210 				if (esub > MAXNAMES)
211 					fatal("too many 'e' keyletters (ad11)");
212 				enames[esub++] = p;
213 				break;
214 
215 			default:
216 				fatal("unknown key letter (cm1)");
217 			}
218 
219 			if (had[c - 'a']++ && testklt++)
220 				fatal("key letter twice (cm2)");
221 			argv[j] = 0;
222 		}
223 		else
224 			num_files++;
225 
226 	if (num_files == 0)
227 		fatal("missing file arg (cm3)");
228 
229 	if (HADI && num_files > 1) /* only one file allowed with `i' */
230 		fatal("more than one file (ad15)");
231 
232 	setsig();
233 
234 	/*
235 	Change flags for 'fatal' so that it will return to this
236 	routine (main) instead of terminating processing.
237 	*/
238 	Fflags &= ~FTLEXIT;
239 	Fflags |= FTLJMP;
240 
241 	/*
242 	Call 'admin' routine for each file argument.
243 	*/
244 	for (j=1; j<argc; j++)
245 		if (p = argv[j])
246 			do_file(p,admin);
247 
248 	exit(Fcnt ? 1 : 0);
249 }
250 
251 
252 /*
253 	Routine that actually does admin's work on SCCS files.
254 	Existing s-files are copied, with changes being made, to a
255 	temporary file (x-file). The name of the x-file is the same as the
256 	name of the s-file, with the 's.' replaced by 'x.'.
257 	s-files which are to be created are processed in a similar
258 	manner, except that a dummy s-file is first created with
259 	mode 444.
260 	At end of processing, the x-file is renamed with the name of s-file
261 	and the old s-file is removed.
262 */
263 
264 struct packet gpkt;	/* see file defines.h */
265 char	Zhold[BUFSIZ];	/* temporary z-file name */
266 
267 USXALLOC();		/* defines alloc() and free() */
268 
269 admin(afile)
270 char *afile;
271 {
272 	struct deltab dt;	/* see file defines.h */
273 	struct stats stats;	/* see file defines.h */
274 	FILE *iptr;
275 	register int k;
276 	register char *cp, *q;
277 	char command[80];
278 	char line[512];
279 	int i;			/* used in forking procedure */
280 	int status;
281 	extern nfiles;
282 	extern had_dir;
283 
284 	if (setjmp(Fjmp))	/* set up to return here from 'fatal' */
285 		return;		/* and return to caller of admin */
286 
287 	if (HADI && had_dir) /* directory not allowed with `i' keyletter */
288 		fatal("directory named with `i' keyletter (ad26)");
289 
290 	fexists = exists(afile);
291 
292 	if (HADI)
293 		HADN = 1;
294 	if (HADI || HADN) {
295 		if (HADM && !VFLAG)
296 			fatal("MRs not allowed (de8)");
297 
298 		if (VFLAG && !HADM)
299 			fatal("MRs required (de10)");
300 
301 	}
302 
303 	if (!HADI && HADR)
304 		fatal("r only allowed with i (ad16)");
305 
306 	if (HADN && HADT && !(*tfile))
307 		fatal("t has no argument (ad17)");
308 
309 	if (HADN && HADD)
310 		fatal("d not allowed with n (ad18)");
311 
312 	if (HADN && fexists) {
313 		sprintf(Error,"file %s exists (ad19)",afile);
314 		fatal(Error);
315 	}
316 
317 	if (!HADN && !fexists) {
318 		sprintf(Error,"file %s does not exist (ad20)",afile);
319 		fatal(Error);
320 	}
321 	/*
322 	   Check for '-h' flag.  If set, create child process and
323 	   invoke 'get' to examine format of SCCS file.
324 	*/
325 
326 	if (HADH) {
327 		/*
328 		   fork here so 'admin' can execute 'val' to
329 		   check for a corrupted file.
330 		*/
331 		if ((i = fork()) < 0)
332 			fatal("cannot fork, try again");
333 		if (i == 0) {		/* child */
334 			/*
335 			   perform 'val' with appropriate keyletters
336 			*/
337 			sprintf(command, "/usr/local/val -s %s", afile);
338 			execl("/bin/sh","/bin/sh","-c", command, 0);
339 			sprintf(Error,"cannot execute '%s'",Valpgm);
340 			fatal(Error);
341 		}
342 		else {
343 			wait(&status);	   /* wait on status from 'execl' */
344 			if (status)
345 				fatal("corrupted file (co6)");
346 			return;		/* return to caller of 'admin' */
347 		}
348 	}
349 
350 	/*
351 	Lock out any other user who may be trying to process
352 	the same file.
353 	*/
354 	if (!HADH && lockit(copy(auxf(afile,'z'),Zhold),2,getpid()))
355 		fatal("cannot create lock file (cm4)");
356 
357 	if (fexists)
358 		sinit(&gpkt,afile,1);	/* init pkt & open s-file */
359 	else {
360 		xfcreat(afile,0444);	/* create dummy s-file */
361 		sinit(&gpkt,afile,0);	/* and init pkt */
362 	}
363 
364 	if (!HADH)
365 		/*
366 		   set the flag for 'putline' routine to open
367 		   the 'x-file' and allow writing on it.
368 		*/
369 		gpkt.p_upd = 1;
370 
371 	if (HADZ) {
372 		gpkt.do_chksum = 0;	/* ignore checksum processing */
373 		gpkt.p_ihash = 0;
374 	}
375 
376 	/*
377 	Get statistics of latest delta in old file.
378 	*/
379 	if (!HADN) {
380 		stats_ab(&gpkt,&stats);
381 		gpkt.p_wrttn++;
382 		newstats(&gpkt,line,"0");
383 	}
384 
385 	if (HADN) {		/*   N E W   F I L E   */
386 
387 		/*
388 		Beginning of SCCS file.
389 		*/
390 		sprintf(line,"%c%c%s\n",CTLCHAR,HEAD,"00000");
391 		putline(&gpkt,line);
392 
393 		/*
394 		Statistics.
395 		*/
396 		newstats(&gpkt,line,"0");
397 
398 		dt.d_type = 'D';	/* type of delta */
399 
400 		/*
401 		Set initial release, level, branch and
402 		sequence values.
403 		*/
404 		if (HADR)
405 			dt.d_sid.s_rel = irel;
406 		else
407 			dt.d_sid.s_rel = 1;
408 		dt.d_sid.s_lev = 1;
409 		dt.d_sid.s_br = dt.d_sid.s_seq = 0;
410 
411 		time(&dt.d_datetime);		/* get time and date */
412 
413 		copy(logname(),dt.d_pgmr);	/* get user's name */
414 
415 		dt.d_serial = 1;
416 		dt.d_pred = 0;
417 
418 		del_ba(&dt,line);	/* form and write */
419 		putline(&gpkt,line);	/* delta-table entry */
420 
421 		/*
422 		If -m flag, enter MR numbers
423 		*/
424 
425 		if (Mrs) {
426 			mrfixup();
427 			if (z && valmrs(&gpkt,z))
428 				fatal("invalid MRs (de9)");
429 			putmrs(&gpkt);
430 		}
431 
432 		/*
433 		Enter comment line for `chghist'
434 		*/
435 
436 		if (HADY) {
437 			sprintf(line,"%c%c ",CTLCHAR,COMMENTS);
438 			putline(&gpkt,line);
439 			putline(&gpkt,Comments);
440 			putline(&gpkt,"\n");
441 		}
442 		else {
443 			/*
444 			insert date/time and pgmr into comment line
445 			*/
446 			cmt_ba(&dt,line);
447 			putline(&gpkt,line);
448 		}
449 		/*
450 		End of delta-table.
451 		*/
452 		sprintf(line,CTLSTR,CTLCHAR,EDELTAB);
453 		putline(&gpkt,line);
454 
455 		/*
456 		Beginning of user-name section.
457 		*/
458 		sprintf(line,CTLSTR,CTLCHAR,BUSERNAM);
459 		putline(&gpkt,line);
460 	}
461 	else
462 		/*
463 		For old file, copy to x-file until user-name section
464 		is found.
465 		*/
466 		flushto(&gpkt,BUSERNAM,COPY);
467 
468 	/*
469 	Write user-names to be added to list of those
470 	allowed to make deltas.
471 	*/
472 	if (HADA)
473 		for (k = 0; k < asub; k++) {
474 			sprintf(line,"%s\n",anames[k]);
475 			putline(&gpkt,line);
476 		}
477 
478 	/*
479 	Do not copy those user-names which are to be erased.
480 	*/
481 	if (HADE && !HADN)
482 		while ((cp = getline(&gpkt)) &&
483 				!(*cp++ == CTLCHAR && *cp == EUSERNAM)) {
484 			for (k = 0; k < esub; k++) {
485 				cp = &gpkt.p_line;
486 				while (*cp)	/* find and */
487 					cp++;	/* zero newline */
488 				*--cp = '\0';	/* character */
489 
490 				if (equal(enames[k],&gpkt.p_line)) {
491 					/*
492 					Tell getline not to output
493 					previously read line.
494 					*/
495 					gpkt.p_wrttn = 1;
496 					break;
497 				}
498 				else
499 					*cp = '\n';	/* restore newline */
500 			}
501 		}
502 
503 	if (HADN) {		/*   N E W  F I L E   */
504 
505 		/*
506 		End of user-name section.
507 		*/
508 		sprintf(line,CTLSTR,CTLCHAR,EUSERNAM);
509 		putline(&gpkt,line);
510 	}
511 	else
512 		/*
513 		For old file, copy to x-file until end of
514 		user-names section is found.
515 		*/
516 		if (!HADE)
517 			flushto(&gpkt,EUSERNAM,COPY);
518 
519 	/*
520 	For old file, read flags and their values (if any), and
521 	store them. Check to see if the flag read is one that
522 	should be deleted.
523 	*/
524 	if (!HADN)
525 		while ((cp = getline(&gpkt)) &&
526 				(*cp++ == CTLCHAR && *cp == FLAG)) {
527 
528 			gpkt.p_wrttn = 1;	/* don't write previous line */
529 
530 			cp += 2;	/* point to flag character */
531 			k = *cp - 'a';
532 
533 			if (!had_flag[k] && !rm_flag[k]) {
534 				had_flag[k] = 2;	/* indicate flag is */
535 							/* from file, not */
536 							/* from arg list */
537 
538 				if (*++cp != '\n') {	/* get flag value */
539 					q = alloc(size(gpkt.p_line)-5);
540 					copy(++cp,q);
541 					flag_p[k] = q;
542 					while (*q)	/* find and */
543 						q++;	/* zero newline */
544 					*--q = '\0';	/* character */
545 				}
546 			}
547 			else
548 				if (rm_flag[k])
549 					had_flag[k] = 0;
550 		}
551 
552 
553 	/*
554 	Write out flags.
555 	*/
556 	for (k = 0; k < 26; k++)
557 		if (had_flag[k]) {
558 			if (flag_p[k])
559 				sprintf(line,"%c%c %c %s\n",
560 					CTLCHAR,FLAG,'a'+k,flag_p[k]);
561 			else
562 				sprintf(line,"%c%c %c\n",
563 					CTLCHAR,FLAG,'a'+k);
564 
565 			putline(&gpkt,line);
566 
567 			if (had_flag[k] == 2) {	/* flag was taken from file */
568 				had_flag[k] = 0;
569 				if (flag_p[k]) {
570 					free(flag_p[k]);
571 					flag_p[k] = 0;
572 				}
573 			}
574 		}
575 
576 	if (HADN) {
577 		/*
578 		Beginning of descriptive (user) text.
579 		*/
580 		sprintf(line,CTLSTR,CTLCHAR,BUSERTXT);
581 		putline(&gpkt,line);
582 	}
583 	else
584 		/*
585 		Write out BUSERTXT record which was read in
586 		above loop that processes flags.
587 		*/
588 		gpkt.p_wrttn = 0;
589 		putline(&gpkt,0);
590 
591 	/*
592 	Get user description, copy to x-file.
593 	*/
594 	if (HADT) {
595 		if (*tfile) {
596 			iptr = xfopen(tfile,0);
597 			fgetchk(line,512,iptr,tfile,&gpkt);
598 			fclose(iptr);
599 		}
600 
601 		/*
602 		If old file, ignore any previously supplied
603 		commentary. (i.e., don't copy it to x-file.)
604 		*/
605 		if (!HADN)
606 			flushto(&gpkt,EUSERTXT,NOCOPY);
607 	}
608 
609 	if (HADN) {		/*   N E W  F I L E   */
610 
611 		/*
612 		End of user description.
613 		*/
614 		sprintf(line,CTLSTR,CTLCHAR,EUSERTXT);
615 		putline(&gpkt,line);
616 
617 		/*
618 		Beginning of body (text) of first delta.
619 		*/
620 		sprintf(line,"%c%c %u\n",CTLCHAR,INS,1);
621 		putline(&gpkt,line);
622 
623 		if (HADI) {		/* get body */
624 
625 			/*
626 			Set indicator to check lines of body of file for
627 			keyword definitions.
628 			If no keywords are found, a warning
629 			will be produced.
630 			*/
631 			check_id = 1;
632 			/*
633 			Set indicator that tells whether there
634 			were any keywords to 'no'.
635 			*/
636 			Did_id = 0;
637 			if (*ifile)
638 				iptr = xfopen(ifile,0);	/* from a file */
639 			else
640 				iptr = stdin;	/* from standard input */
641 
642 			/*
643 			Read and copy to x-file, while checking
644 			first character of each line to see that it
645 			is not the control character (octal 1).
646 			Also, count lines read, and set statistics'
647 			structure appropriately.
648 			The 'fgetchk' routine will check for keywords.
649 			*/
650 			stats.s_ins = fgetchk(line,512,iptr,ifile,&gpkt);
651 			stats.s_del = stats.s_unc = 0;
652 
653 			/*
654 			If no keywords were found, issue warning.
655 			*/
656 			if (!Did_id) {
657 				if (had_flag[IDFLAG - 'a'])
658 					fatal("no id keywords (cm6)");
659 				else
660 					fprintf(stderr,"%s\n","No id keywords (cm7)");
661 			}
662 
663 			check_id = 0;
664 			Did_id = 0;
665 		}
666 
667 		/*
668 		End of body of first delta.
669 		*/
670 		sprintf(line,"%c%c %u\n",CTLCHAR,END,1);
671 		putline(&gpkt,line);
672 	}
673 	else {
674 		/*
675 		Indicate that EOF at this point is ok, and
676 		flush rest of (old) s-file to x-file.
677 		*/
678 		gpkt.p_chkeof = 1;
679 		while (getline(&gpkt)) ;
680 	}
681 
682 	/*
683 	Flush the buffer, take care of rewinding to insert
684 	checksum and statistics in file, and close.
685 	*/
686 	flushline(&gpkt,&stats);
687 
688 	/*
689 	Change x-file name to s-file, and delete old file.
690 	Unlock file before returning.
691 	*/
692 	if (!HADH) {
693 		rename(auxf(&gpkt,'x'),&gpkt);
694 		xrm(&gpkt);
695 		unlockit(auxf(afile,'z'),getpid());
696 	}
697 }
698 
699 
700 fgetchk(strp,len,inptr,file,pkt)
701 register char *strp;
702 register int len;
703 FILE *inptr;
704 register char *file;
705 register struct packet *pkt;
706 {
707 	register int k;
708 
709 	for (k = 1; fgets(strp,len,inptr); k++) {
710 		if (*strp == CTLCHAR) {
711 			sprintf(Error,"%s illegal data on line %d (ad21)",
712 				file,k);
713 			fatal(Error);
714 		}
715 
716 		if (check_id)
717 			chkid(strp);
718 
719 		putline(pkt,strp);
720 	}
721 	return(k - 1);
722 }
723 
724 
725 clean_up()
726 {
727 	xrm(&gpkt);
728 	if (!HADH)
729 		unlockit(Zhold,getpid());
730 	if (HADN)
731 		unlink(&gpkt);
732 }
733 
734 
735 cmt_ba(dt,str)
736 register struct deltab *dt;
737 char *str;
738 {
739 	register char *p;
740 
741 	p = str;
742 	*p++ = CTLCHAR;
743 	*p++ = COMMENTS;
744 	*p++ = ' ';
745 	copy("date and time created",p);
746 	while (*p++)
747 		;
748 	--p;
749 	*p++ = ' ';
750 	date_ba(&dt->d_datetime,p);
751 	while (*p++)
752 		;
753 	--p;
754 	*p++ = ' ';
755 	copy("by",p);
756 	while (*p++)
757 		;
758 	--p;
759 	*p++ = ' ';
760 	copy(dt->d_pgmr,p);
761 	while (*p++)
762 		;
763 	--p;
764 	*p++ = '\n';
765 	*p = 0;
766 	return(str);
767 }
768 
769 
770 putmrs(pkt)
771 struct packet *pkt;
772 {
773 	register char **argv;
774 	char str[64];
775 	extern char *Varg[];
776 
777 	for (argv = &Varg[VSTART]; *argv; argv++)
778 		sprintf(str,"%c%c %s\n",CTLCHAR,MRNUM,*argv);
779 		putline(pkt,str);
780 }
781