12143Seric # include "../hdr/defines.h"
22143Seric # include "../hdr/had.h"
32143Seric 
4*30503Slepreau static char Sccsid[] = "@(#)admin.c	4.2	02/15/87";
52143Seric 
62143Seric /*
72143Seric 	Program to create new SCCS files and change parameters
82143Seric 	of existing ones. Arguments to the program may appear in
92143Seric 	any order and consist of keyletters, which begin with '-',
102143Seric 	and named files. Named files which do not exist are created
112143Seric 	and their parameters are initialized according to the given
122143Seric 	keyletter arguments, or are given default values if the
132143Seric 	corresponding keyletters were not supplied. Named files which
142143Seric 	do exist have those parameters corresponding to given key-letter
152143Seric 	arguments changed and other parameters are left as is.
162143Seric 
172143Seric 	If a directory is given as an argument, each SCCS file within
182143Seric 	the directory is processed as if it had been specifically named.
192143Seric 	If a name of '-' is given, the standard input is read for a list
202143Seric 	of names of SCCS files to be processed.
212143Seric 	Non-SCCS files are ignored.
222143Seric 
232143Seric 	Files created are given mode 444.
242143Seric */
252143Seric 
262143Seric # define MINR 1		/* minimum release number */
272143Seric # define MAXR 9999	/* maximum release number */
282143Seric # define MAXNAMES 9
292143Seric # define COPY 0
302143Seric # define NOCOPY 1
312143Seric 
322143Seric char *ifile, *tfile;
332143Seric char *z;	/* for validation program name */
342143Seric char had[26], had_flag[26], rm_flag[26];
352143Seric char	*Comments, *Mrs;
36*30503Slepreau char Valpgm[] = "/usr/local/val";
372143Seric int irel, fexists, num_files;
38*30503Slepreau int	VFLAG = 0;
392143Seric int	Domrs;
402143Seric char *Sflags[];
412143Seric char *anames[MAXNAMES], *enames[MAXNAMES];
422143Seric char *flag_p[26];
432143Seric int asub, esub;
442143Seric int check_id;
452143Seric int Did_id;
462143Seric 
472143Seric main(argc,argv)
482143Seric int argc;
492143Seric char *argv[];
502143Seric {
512143Seric 	register int j;
522143Seric 	register char *p;
532143Seric 	char c, f;
542143Seric 	int i, testklt;
552143Seric 	extern admin();
562143Seric 	extern int Fcnt;
572143Seric 	struct sid sid;
582143Seric 
592143Seric 	/*
602143Seric 	Set flags for 'fatal' to issue message, call clean-up
612143Seric 	routine and terminate processing.
622143Seric 	*/
632143Seric 	Fflags = FTLMSG | FTLCLN | FTLEXIT;
642143Seric 
652143Seric 	testklt = 1;
662143Seric 
672143Seric 	/*
682143Seric 	The following loop processes keyletters and arguments.
692143Seric 	Note that these are processed only once for each
702143Seric 	invocation of 'main'.
712143Seric 	*/
722143Seric 	for(j=1; j<argc; j++)
732143Seric 		if(argv[j][0] == '-' && (c = argv[j][1])) {
742143Seric 			p = &argv[j][2];
752143Seric 			switch (c) {
762143Seric 
772143Seric 			case 'i':	/* name of file of body */
782143Seric 				ifile = p;
792143Seric 				break;
802143Seric 
812143Seric 			case 't':	/* name of file of descriptive text */
822143Seric 				tfile = p;
832143Seric 				break;
842143Seric 			case 'm':	/* mr flag */
852143Seric 				Mrs = p;
862143Seric 				break;
872143Seric 			case 'y':	/* comments flag for entry */
882143Seric 				Comments = p;
892143Seric 				break;
902143Seric 
912143Seric 			case 'd':	/* flags to be deleted */
922143Seric 				testklt = 0;
932143Seric 				if (!(f = *p))
942143Seric 					fatal("d has no argument (ad1)");
952143Seric 				p = &argv[j][3];
962143Seric 
972143Seric 				switch (f) {
982143Seric 
992143Seric 				case IDFLAG:	/* see 'f' keyletter */
1002143Seric 				case BRCHFLAG:	/* for meanings of flags */
1012143Seric 				case VALFLAG:
1022143Seric 				case TYPEFLAG:
1032143Seric 				case MODFLAG:
1042143Seric 				case NULLFLAG:
1052143Seric 				case FLORFLAG:
1062143Seric 				case CEILFLAG:
1072143Seric 				case DEFTFLAG:
1082143Seric 					if (*p)
1092143Seric 						fatal(sprintf(Error,
1102143Seric 						"value after %c flag (ad12)",f));
1112143Seric 					break;
1122143Seric 
1132143Seric 				default:
1142143Seric 					fatal("unknown flag (ad3)");
1152143Seric 				}
1162143Seric 
1172143Seric 				if (rm_flag[f - 'a']++)
1182143Seric 					fatal("flag twice (ad4)");
1192143Seric 				break;
1202143Seric 
1212143Seric 			case 'f':	/* flags to be added */
1222143Seric 				testklt = 0;
1232143Seric 				if (!(f = *p))
1242143Seric 					fatal("f has no argument (ad5)");
1252143Seric 				p = &argv[j][3];
1262143Seric 
1272143Seric 				switch (f) {
1282143Seric 
1292143Seric 				case IDFLAG:	/* id-kwd message (err/warn) */
1302143Seric 				case BRCHFLAG:	/* branch */
1312143Seric 				case NULLFLAG:	/* null deltas */
1322143Seric 					if (*p)
1332143Seric 						fatal(sprintf(Error,
1342143Seric 						"value after %c flag (ad13)",f));
1352143Seric 					break;
1362143Seric 
1372143Seric 				case VALFLAG:	/* mr validation */
1382143Seric 					VFLAG++;
1392143Seric 					if (*p)
1402143Seric 						z = p;
1412143Seric 					break;
1422143Seric 
1432143Seric 				case FLORFLAG:	/* floor */
1442143Seric 					if ((i = patoi(p)) == -1)
1452143Seric 						fatal("floor not numeric (ad22)");
1462143Seric 					if ((size(p) > 5) || (i < MINR) ||
1472143Seric 							(i > MAXR))
1482143Seric 						fatal("floor out of range (ad23)");
1492143Seric 					break;
1502143Seric 
1512143Seric 				case CEILFLAG:	/* ceiling */
1522143Seric 					if ((i = patoi(p)) == -1)
1532143Seric 						fatal("ceiling not numeric (ad24)");
1542143Seric 					if ((size(p) > 5) || (i < MINR) ||
1552143Seric 							(i > MAXR))
1562143Seric 						fatal("ceiling out of range (ad25)");
1572143Seric 					break;
1582143Seric 
1592143Seric 				case DEFTFLAG:	/* default sid */
1602143Seric 					if (!(*p))
1612143Seric 						fatal("no default sid (ad14)");
1622143Seric 					chksid(sid_ab(p,&sid),&sid);
1632143Seric 					break;
1642143Seric 
1652143Seric 				case TYPEFLAG:	/* type */
1662143Seric 				case MODFLAG:	/* module name */
1672143Seric 					if (!(*p))
1682143Seric 						fatal(sprintf(Error,
1692143Seric 						"flag %c has no value (ad2)",f));
1702143Seric 					break;
1712143Seric 
1722143Seric 				default:
1732143Seric 					fatal("unknown flag (ad3)");
1742143Seric 				}
1752143Seric 
1762143Seric 				if (had_flag[f - 'a']++)
1772143Seric 					fatal("flag twice (ad4)");
1782143Seric 				flag_p[f - 'a'] = p;
1792143Seric 				break;
1802143Seric 
1812143Seric 			case 'r':	/* initial release number supplied */
1822143Seric 				if ((irel = patoi(p)) == -1)
1832143Seric 					fatal("r arg not numeric (ad6)");
1842143Seric 				if ((size(p) > 5) || (irel < MINR) ||
1852143Seric 						(irel > MAXR))
1862143Seric 					fatal("r out of range (ad7)");
1872143Seric 				break;
1882143Seric 
1892143Seric 			case 'n':	/* creating new SCCS file */
1902143Seric 			case 'h':	/* only check hash of file */
1912143Seric 			case 'z':	/* zero the input hash */
1922143Seric 				break;
1932143Seric 
1942143Seric 			case 'a':	/* user-name allowed to make deltas */
1952143Seric 				testklt = 0;
1962143Seric 				if (!(*p))
1972143Seric 					fatal("bad a argument (ad8)");
1982143Seric 				if (asub > MAXNAMES)
1992143Seric 					fatal("too many 'a' keyletters (ad9)");
2002143Seric 				anames[asub++] = p;
2012143Seric 				break;
2022143Seric 
2032143Seric 			case 'e':	/* user-name to be removed */
2042143Seric 				testklt = 0;
2052143Seric 				if (!(*p))
2062143Seric 					fatal("bad e argument (ad10)");
2072143Seric 				if (esub > MAXNAMES)
2082143Seric 					fatal("too many 'e' keyletters (ad11)");
2092143Seric 				enames[esub++] = p;
2102143Seric 				break;
2112143Seric 
2122143Seric 			default:
2132143Seric 				fatal("unknown key letter (cm1)");
2142143Seric 			}
2152143Seric 
2162143Seric 			if (had[c - 'a']++ && testklt++)
2172143Seric 				fatal("key letter twice (cm2)");
2182143Seric 			argv[j] = 0;
2192143Seric 		}
2202143Seric 		else
2212143Seric 			num_files++;
2222143Seric 
2232143Seric 	if (num_files == 0)
2242143Seric 		fatal("missing file arg (cm3)");
2252143Seric 
2262143Seric 	if (HADI && num_files > 1) /* only one file allowed with `i' */
2272143Seric 		fatal("more than one file (ad15)");
2282143Seric 
2292143Seric 	setsig();
2302143Seric 
2312143Seric 	/*
2322143Seric 	Change flags for 'fatal' so that it will return to this
2332143Seric 	routine (main) instead of terminating processing.
2342143Seric 	*/
235*30503Slepreau 	Fflags &= ~FTLEXIT;
236*30503Slepreau 	Fflags |= FTLJMP;
2372143Seric 
2382143Seric 	/*
2392143Seric 	Call 'admin' routine for each file argument.
2402143Seric 	*/
2412143Seric 	for (j=1; j<argc; j++)
2422143Seric 		if (p = argv[j])
2432143Seric 			do_file(p,admin);
2442143Seric 
2452143Seric 	exit(Fcnt ? 1 : 0);
2462143Seric }
2472143Seric 
2482143Seric 
2492143Seric /*
2502143Seric 	Routine that actually does admin's work on SCCS files.
2512143Seric 	Existing s-files are copied, with changes being made, to a
2522143Seric 	temporary file (x-file). The name of the x-file is the same as the
2532143Seric 	name of the s-file, with the 's.' replaced by 'x.'.
2542143Seric 	s-files which are to be created are processed in a similar
2552143Seric 	manner, except that a dummy s-file is first created with
2562143Seric 	mode 444.
2572143Seric 	At end of processing, the x-file is renamed with the name of s-file
2582143Seric 	and the old s-file is removed.
2592143Seric */
2602143Seric 
2612143Seric struct packet gpkt;	/* see file defines.h */
2622143Seric char	Zhold[BUFSIZ];	/* temporary z-file name */
2632143Seric 
2642143Seric USXALLOC();		/* defines alloc() and free() */
2652143Seric 
2662143Seric admin(afile)
2672143Seric char *afile;
2682143Seric {
2692143Seric 	struct deltab dt;	/* see file defines.h */
2702143Seric 	struct stats stats;	/* see file defines.h */
2712143Seric 	FILE *iptr;
2722143Seric 	register int k;
2732143Seric 	register char *cp, *q;
2742143Seric 	char command[80];
2752143Seric 	char line[512];
2762143Seric 	int i;			/* used in forking procedure */
2772143Seric 	int status;
2782143Seric 	extern nfiles;
2792143Seric 	extern had_dir;
2802143Seric 
2812143Seric 	if (setjmp(Fjmp))	/* set up to return here from 'fatal' */
2822143Seric 		return;		/* and return to caller of admin */
2832143Seric 
2842143Seric 	if (HADI && had_dir) /* directory not allowed with `i' keyletter */
2852143Seric 		fatal("directory named with `i' keyletter (ad26)");
2862143Seric 
2872143Seric 	fexists = exists(afile);
2882143Seric 
2892143Seric 	if (HADI)
2902143Seric 		HADN = 1;
2912143Seric 	if (HADI || HADN) {
2922143Seric 		if (HADM && !VFLAG)
2932143Seric 			fatal("MRs not allowed (de8)");
2942143Seric 
2952143Seric 		if (VFLAG && !HADM)
2962143Seric 			fatal("MRs required (de10)");
2972143Seric 
2982143Seric 	}
2992143Seric 
3002143Seric 	if (!HADI && HADR)
3012143Seric 		fatal("r only allowed with i (ad16)");
3022143Seric 
3032143Seric 	if (HADN && HADT && !(*tfile))
3042143Seric 		fatal("t has no argument (ad17)");
3052143Seric 
3062143Seric 	if (HADN && HADD)
3072143Seric 		fatal("d not allowed with n (ad18)");
3082143Seric 
3092143Seric 	if (HADN && fexists)
3102143Seric 		fatal(sprintf(Error,"file %s exists (ad19)",afile));
3112143Seric 
3122143Seric 	if (!HADN && !fexists)
3132143Seric 		fatal(sprintf(Error,"file %s does not exist (ad20)",afile));
3142143Seric 	/*
3152143Seric 	   Check for '-h' flag.  If set, create child process and
3162143Seric 	   invoke 'get' to examine format of SCCS file.
3172143Seric 	*/
3182143Seric 
3192143Seric 	if (HADH) {
3202143Seric 		/*
3212143Seric 		   fork here so 'admin' can execute 'val' to
3222143Seric 		   check for a corrupted file.
3232143Seric 		*/
3242143Seric 		if ((i = fork()) < 0)
3252143Seric 			fatal("cannot fork, try again");
3262143Seric 		if (i == 0) {		/* child */
3272143Seric 			/*
3282143Seric 			   perform 'val' with appropriate keyletters
3292143Seric 			*/
3302143Seric 			execl("/bin/sh","/bin/sh","-c",
3312143Seric 				sprintf(command,
3322143Seric 					"/usr/local/val -s %s",
3332143Seric 						afile),0);
3342143Seric 			fatal(sprintf(Error,"cannot execute '%s'",Valpgm));
3352143Seric 		}
3362143Seric 		else {
3372143Seric 			wait(&status);	   /* wait on status from 'execl' */
3382143Seric 			if (status)
3392143Seric 				fatal("corrupted file (co6)");
3402143Seric 			return;		/* return to caller of 'admin' */
3412143Seric 		}
3422143Seric 	}
3432143Seric 
3442143Seric 	/*
3452143Seric 	Lock out any other user who may be trying to process
3462143Seric 	the same file.
3472143Seric 	*/
3482143Seric 	if (!HADH && lockit(copy(auxf(afile,'z'),Zhold),2,getpid()))
3492143Seric 		fatal("cannot create lock file (cm4)");
3502143Seric 
3512143Seric 	if (fexists)
3522143Seric 		sinit(&gpkt,afile,1);	/* init pkt & open s-file */
3532143Seric 	else {
3542143Seric 		xfcreat(afile,0444);	/* create dummy s-file */
3552143Seric 		sinit(&gpkt,afile,0);	/* and init pkt */
3562143Seric 	}
3572143Seric 
3582143Seric 	if (!HADH)
3592143Seric 		/*
3602143Seric 		   set the flag for 'putline' routine to open
3612143Seric 		   the 'x-file' and allow writing on it.
3622143Seric 		*/
3632143Seric 		gpkt.p_upd = 1;
3642143Seric 
3652143Seric 	if (HADZ) {
3662143Seric 		gpkt.do_chksum = 0;	/* ignore checksum processing */
3672143Seric 		gpkt.p_ihash = 0;
3682143Seric 	}
3692143Seric 
3702143Seric 	/*
3712143Seric 	Get statistics of latest delta in old file.
3722143Seric 	*/
3732143Seric 	if (!HADN) {
3742143Seric 		stats_ab(&gpkt,&stats);
3752143Seric 		gpkt.p_wrttn++;
3762143Seric 		newstats(&gpkt,line,"0");
3772143Seric 	}
3782143Seric 
3792143Seric 	if (HADN) {		/*   N E W   F I L E   */
3802143Seric 
3812143Seric 		/*
3822143Seric 		Beginning of SCCS file.
3832143Seric 		*/
3842143Seric 		putline(&gpkt,sprintf(line,"%c%c%s\n",CTLCHAR,HEAD,"00000"));
3852143Seric 
3862143Seric 		/*
3872143Seric 		Statistics.
3882143Seric 		*/
3892143Seric 		newstats(&gpkt,line,"0");
3902143Seric 
3912143Seric 		dt.d_type = 'D';	/* type of delta */
3922143Seric 
3932143Seric 		/*
3942143Seric 		Set initial release, level, branch and
3952143Seric 		sequence values.
3962143Seric 		*/
3972143Seric 		if (HADR)
3982143Seric 			dt.d_sid.s_rel = irel;
3992143Seric 		else
4002143Seric 			dt.d_sid.s_rel = 1;
4012143Seric 		dt.d_sid.s_lev = 1;
4022143Seric 		dt.d_sid.s_br = dt.d_sid.s_seq = 0;
4032143Seric 
4042143Seric 		time(&dt.d_datetime);		/* get time and date */
4052143Seric 
4062143Seric 		copy(logname(),dt.d_pgmr);	/* get user's name */
4072143Seric 
4082143Seric 		dt.d_serial = 1;
4092143Seric 		dt.d_pred = 0;
4102143Seric 
4112143Seric 		del_ba(&dt,line);	/* form and write */
4122143Seric 		putline(&gpkt,line);	/* delta-table entry */
4132143Seric 
4142143Seric 		/*
4152143Seric 		If -m flag, enter MR numbers
4162143Seric 		*/
4172143Seric 
4182143Seric 		if (Mrs) {
4192143Seric 			mrfixup();
4202143Seric 			if (z && valmrs(&gpkt,z))
4212143Seric 				fatal("invalid MRs (de9)");
4222143Seric 			putmrs(&gpkt);
4232143Seric 		}
4242143Seric 
4252143Seric 		/*
4262143Seric 		Enter comment line for `chghist'
4272143Seric 		*/
4282143Seric 
4292143Seric 		if (HADY) {
4302143Seric 			putline(&gpkt,sprintf(line,"%c%c ",CTLCHAR,COMMENTS));
4312143Seric 			putline(&gpkt,Comments);
4322143Seric 			putline(&gpkt,"\n");
4332143Seric 		}
4342143Seric 		else {
4352143Seric 			/*
4362143Seric 			insert date/time and pgmr into comment line
4372143Seric 			*/
4382143Seric 			cmt_ba(&dt,line);
4392143Seric 			putline(&gpkt,line);
4402143Seric 		}
4412143Seric 		/*
4422143Seric 		End of delta-table.
4432143Seric 		*/
4442143Seric 		putline(&gpkt,sprintf(line,CTLSTR,CTLCHAR,EDELTAB));
4452143Seric 
4462143Seric 		/*
4472143Seric 		Beginning of user-name section.
4482143Seric 		*/
4492143Seric 		putline(&gpkt,sprintf(line,CTLSTR,CTLCHAR,BUSERNAM));
4502143Seric 	}
4512143Seric 	else
4522143Seric 		/*
4532143Seric 		For old file, copy to x-file until user-name section
4542143Seric 		is found.
4552143Seric 		*/
4562143Seric 		flushto(&gpkt,BUSERNAM,COPY);
4572143Seric 
4582143Seric 	/*
4592143Seric 	Write user-names to be added to list of those
4602143Seric 	allowed to make deltas.
4612143Seric 	*/
4622143Seric 	if (HADA)
4632143Seric 		for (k = 0; k < asub; k++)
4642143Seric 			putline(&gpkt,sprintf(line,"%s\n",anames[k]));
4652143Seric 
4662143Seric 	/*
4672143Seric 	Do not copy those user-names which are to be erased.
4682143Seric 	*/
4692143Seric 	if (HADE && !HADN)
4702143Seric 		while ((cp = getline(&gpkt)) &&
4712143Seric 				!(*cp++ == CTLCHAR && *cp == EUSERNAM)) {
4722143Seric 			for (k = 0; k < esub; k++) {
4732143Seric 				cp = &gpkt.p_line;
4742143Seric 				while (*cp)	/* find and */
4752143Seric 					cp++;	/* zero newline */
4762143Seric 				*--cp = '\0';	/* character */
4772143Seric 
4782143Seric 				if (equal(enames[k],&gpkt.p_line)) {
4792143Seric 					/*
4802143Seric 					Tell getline not to output
4812143Seric 					previously read line.
4822143Seric 					*/
4832143Seric 					gpkt.p_wrttn = 1;
4842143Seric 					break;
4852143Seric 				}
4862143Seric 				else
4872143Seric 					*cp = '\n';	/* restore newline */
4882143Seric 			}
4892143Seric 		}
4902143Seric 
4912143Seric 	if (HADN) {		/*   N E W  F I L E   */
4922143Seric 
4932143Seric 		/*
4942143Seric 		End of user-name section.
4952143Seric 		*/
4962143Seric 		putline(&gpkt,sprintf(line,CTLSTR,CTLCHAR,EUSERNAM));
4972143Seric 	}
4982143Seric 	else
4992143Seric 		/*
5002143Seric 		For old file, copy to x-file until end of
5012143Seric 		user-names section is found.
5022143Seric 		*/
5032143Seric 		if (!HADE)
5042143Seric 			flushto(&gpkt,EUSERNAM,COPY);
5052143Seric 
5062143Seric 	/*
5072143Seric 	For old file, read flags and their values (if any), and
5082143Seric 	store them. Check to see if the flag read is one that
5092143Seric 	should be deleted.
5102143Seric 	*/
5112143Seric 	if (!HADN)
5122143Seric 		while ((cp = getline(&gpkt)) &&
5132143Seric 				(*cp++ == CTLCHAR && *cp == FLAG)) {
5142143Seric 
5152143Seric 			gpkt.p_wrttn = 1;	/* don't write previous line */
5162143Seric 
517*30503Slepreau 			cp += 2;	/* point to flag character */
5182143Seric 			k = *cp - 'a';
5192143Seric 
5202143Seric 			if (!had_flag[k] && !rm_flag[k]) {
5212143Seric 				had_flag[k] = 2;	/* indicate flag is */
5222143Seric 							/* from file, not */
5232143Seric 							/* from arg list */
5242143Seric 
5252143Seric 				if (*++cp != '\n') {	/* get flag value */
5262143Seric 					q = alloc(size(gpkt.p_line)-5);
5272143Seric 					copy(++cp,q);
5282143Seric 					flag_p[k] = q;
5292143Seric 					while (*q)	/* find and */
5302143Seric 						q++;	/* zero newline */
5312143Seric 					*--q = '\0';	/* character */
5322143Seric 				}
5332143Seric 			}
5342143Seric 			else
5352143Seric 				if (rm_flag[k])
5362143Seric 					had_flag[k] = 0;
5372143Seric 		}
5382143Seric 
5392143Seric 
5402143Seric 	/*
5412143Seric 	Write out flags.
5422143Seric 	*/
5432143Seric 	for (k = 0; k < 26; k++)
5442143Seric 		if (had_flag[k]) {
5452143Seric 			if (flag_p[k])
5462143Seric 				sprintf(line,"%c%c %c %s\n",
5472143Seric 					CTLCHAR,FLAG,'a'+k,flag_p[k]);
5482143Seric 			else
5492143Seric 				sprintf(line,"%c%c %c\n",
5502143Seric 					CTLCHAR,FLAG,'a'+k);
5512143Seric 
5522143Seric 			putline(&gpkt,line);
5532143Seric 
5542143Seric 			if (had_flag[k] == 2) {	/* flag was taken from file */
5552143Seric 				had_flag[k] = 0;
5562143Seric 				if (flag_p[k]) {
5572143Seric 					free(flag_p[k]);
5582143Seric 					flag_p[k] = 0;
5592143Seric 				}
5602143Seric 			}
5612143Seric 		}
5622143Seric 
5632143Seric 	if (HADN)
5642143Seric 		/*
5652143Seric 		Beginning of descriptive (user) text.
5662143Seric 		*/
5672143Seric 		putline(&gpkt,sprintf(line,CTLSTR,CTLCHAR,BUSERTXT));
5682143Seric 	else
5692143Seric 		/*
5702143Seric 		Write out BUSERTXT record which was read in
5712143Seric 		above loop that processes flags.
5722143Seric 		*/
5732143Seric 		gpkt.p_wrttn = 0;
5742143Seric 		putline(&gpkt,0);
5752143Seric 
5762143Seric 	/*
5772143Seric 	Get user description, copy to x-file.
5782143Seric 	*/
5792143Seric 	if (HADT) {
5802143Seric 		if (*tfile) {
5812143Seric 			iptr = xfopen(tfile,0);
5822143Seric 			fgetchk(line,512,iptr,tfile,&gpkt);
5832143Seric 			fclose(iptr);
5842143Seric 		}
5852143Seric 
5862143Seric 		/*
5872143Seric 		If old file, ignore any previously supplied
5882143Seric 		commentary. (i.e., don't copy it to x-file.)
5892143Seric 		*/
5902143Seric 		if (!HADN)
5912143Seric 			flushto(&gpkt,EUSERTXT,NOCOPY);
5922143Seric 	}
5932143Seric 
5942143Seric 	if (HADN) {		/*   N E W  F I L E   */
5952143Seric 
5962143Seric 		/*
5972143Seric 		End of user description.
5982143Seric 		*/
5992143Seric 		putline(&gpkt,sprintf(line,CTLSTR,CTLCHAR,EUSERTXT));
6002143Seric 
6012143Seric 		/*
6022143Seric 		Beginning of body (text) of first delta.
6032143Seric 		*/
6042143Seric 		putline(&gpkt,sprintf(line,"%c%c %u\n",CTLCHAR,INS,1));
6052143Seric 
6062143Seric 		if (HADI) {		/* get body */
6072143Seric 
6082143Seric 			/*
6092143Seric 			Set indicator to check lines of body of file for
6102143Seric 			keyword definitions.
6112143Seric 			If no keywords are found, a warning
6122143Seric 			will be produced.
6132143Seric 			*/
6142143Seric 			check_id = 1;
6152143Seric 			/*
6162143Seric 			Set indicator that tells whether there
6172143Seric 			were any keywords to 'no'.
6182143Seric 			*/
6192143Seric 			Did_id = 0;
6202143Seric 			if (*ifile)
6212143Seric 				iptr = xfopen(ifile,0);	/* from a file */
6222143Seric 			else
6232143Seric 				iptr = stdin;	/* from standard input */
6242143Seric 
6252143Seric 			/*
6262143Seric 			Read and copy to x-file, while checking
6272143Seric 			first character of each line to see that it
6282143Seric 			is not the control character (octal 1).
6292143Seric 			Also, count lines read, and set statistics'
6302143Seric 			structure appropriately.
6312143Seric 			The 'fgetchk' routine will check for keywords.
6322143Seric 			*/
6332143Seric 			stats.s_ins = fgetchk(line,512,iptr,ifile,&gpkt);
6342143Seric 			stats.s_del = stats.s_unc = 0;
6352143Seric 
6362143Seric 			/*
6372143Seric 			If no keywords were found, issue warning.
6382143Seric 			*/
6392143Seric 			if (!Did_id) {
6402143Seric 				if (had_flag[IDFLAG - 'a'])
6412143Seric 					fatal("no id keywords (cm6)");
6422143Seric 				else
6432143Seric 					fprintf(stderr,"%s\n","No id keywords (cm7)");
6442143Seric 			}
6452143Seric 
6462143Seric 			check_id = 0;
6472143Seric 			Did_id = 0;
6482143Seric 		}
6492143Seric 
6502143Seric 		/*
6512143Seric 		End of body of first delta.
6522143Seric 		*/
6532143Seric 		putline(&gpkt,sprintf(line,"%c%c %u\n",CTLCHAR,END,1));
6542143Seric 	}
6552143Seric 	else {
6562143Seric 		/*
6572143Seric 		Indicate that EOF at this point is ok, and
6582143Seric 		flush rest of (old) s-file to x-file.
6592143Seric 		*/
6602143Seric 		gpkt.p_chkeof = 1;
6612143Seric 		while (getline(&gpkt)) ;
6622143Seric 	}
6632143Seric 
6642143Seric 	/*
6652143Seric 	Flush the buffer, take care of rewinding to insert
6662143Seric 	checksum and statistics in file, and close.
6672143Seric 	*/
6682143Seric 	flushline(&gpkt,&stats);
6692143Seric 
6702143Seric 	/*
6712143Seric 	Change x-file name to s-file, and delete old file.
6722143Seric 	Unlock file before returning.
6732143Seric 	*/
6742143Seric 	if (!HADH) {
6752143Seric 		rename(auxf(&gpkt,'x'),&gpkt);
6762143Seric 		xrm(&gpkt);
6772143Seric 		unlockit(auxf(afile,'z'),getpid());
6782143Seric 	}
6792143Seric }
6802143Seric 
6812143Seric 
6822143Seric fgetchk(strp,len,inptr,file,pkt)
6832143Seric register char *strp;
6842143Seric register int len;
6852143Seric FILE *inptr;
6862143Seric register char *file;
6872143Seric register struct packet *pkt;
6882143Seric {
6892143Seric 	register int k;
6902143Seric 
6912143Seric 	for (k = 1; fgets(strp,len,inptr); k++) {
6922143Seric 		if (*strp == CTLCHAR) fatal(
6932143Seric 			sprintf(Error,"%s illegal data on line %d (ad21)",
6942143Seric 				file,k));
6952143Seric 
6962143Seric 		if (check_id)
6972143Seric 			chkid(strp);
6982143Seric 
6992143Seric 		putline(pkt,strp);
7002143Seric 	}
7012143Seric 	return(k - 1);
7022143Seric }
7032143Seric 
7042143Seric 
7052143Seric clean_up()
7062143Seric {
7072143Seric 	xrm(&gpkt);
7082143Seric 	if (!HADH)
7092143Seric 		unlockit(Zhold,getpid());
7102143Seric 	if (HADN)
7112143Seric 		unlink(&gpkt);
7122143Seric }
7132143Seric 
7142143Seric 
7152143Seric cmt_ba(dt,str)
7162143Seric register struct deltab *dt;
7172143Seric char *str;
7182143Seric {
7192143Seric 	register char *p;
7202143Seric 
7212143Seric 	p = str;
7222143Seric 	*p++ = CTLCHAR;
7232143Seric 	*p++ = COMMENTS;
7242143Seric 	*p++ = ' ';
7252143Seric 	copy("date and time created",p);
7262143Seric 	while (*p++)
7272143Seric 		;
7282143Seric 	--p;
7292143Seric 	*p++ = ' ';
7302143Seric 	date_ba(&dt->d_datetime,p);
7312143Seric 	while (*p++)
7322143Seric 		;
7332143Seric 	--p;
7342143Seric 	*p++ = ' ';
7352143Seric 	copy("by",p);
7362143Seric 	while (*p++)
7372143Seric 		;
7382143Seric 	--p;
7392143Seric 	*p++ = ' ';
7402143Seric 	copy(dt->d_pgmr,p);
7412143Seric 	while (*p++)
7422143Seric 		;
7432143Seric 	--p;
7442143Seric 	*p++ = '\n';
7452143Seric 	*p = 0;
7462143Seric 	return(str);
7472143Seric }
7482143Seric 
7492143Seric 
7502143Seric putmrs(pkt)
7512143Seric struct packet *pkt;
7522143Seric {
7532143Seric 	register char **argv;
7542143Seric 	char str[64];
7552143Seric 	extern char *Varg[];
7562143Seric 
7572143Seric 	for (argv = &Varg[VSTART]; *argv; argv++)
7582143Seric 		putline(pkt,sprintf(str,"%c%c %s\n",CTLCHAR,MRNUM,*argv));
7592143Seric }
760