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