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