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 2003 Sun Microsystems, Inc. All rights reserved.
27  * Use is subject to license terms.
28  */
29 /*
30  * Copyright 2006-2020 J. Schilling
31  *
32  * @(#)prs.c	1.68 20/09/11 J. Schilling
33  */
34 #if defined(sun)
35 #pragma ident "@(#)prs.c 1.68 20/09/11 J. Schilling"
36 #endif
37 /*
38  * @(#)prs.c 1.33 06/12/12
39  */
40 
41 /*
42  * Copyright 1990-2003 Sun Microsystems, Inc.  All rights reserved.
43  * Use is subject to license terms.
44  */
45 
46 #if defined(sun)
47 #pragma ident	"@(#)prs.c"
48 #pragma ident	"@(#)sccs:cmd/prs.c"
49 #endif
50 /*************************************************************************/
51 /*									 */
52 /*	prs [-d<dataspec>] [-r<sid>] [-l] [-e] [-a] file ...		 */
53 /*									 */
54 /*************************************************************************/
55 
56 /*
57 	Program to print parts or all of an SCCS file
58 	in user supplied format.
59 	Arguments to the program may appear in any order
60 	and consist of keyletters, which begin with '-',
61 	and named files.
62 
63 	If a direcory is given as an argument, each
64 	SCCS file within the directory is processed as if
65 	it had been specifically named. If a name of '-'
66 	is given, the standard input is read for a list
67 	of names of SCCS files to be processed.
68 	Non-SCCS files are ignored.
69 */
70 
71 #define		SCCS_MAIN			/* define global vars */
72 #include 	<defines.h>
73 #include	<version.h>
74 #include	<had.h>
75 #include	<i18n.h>
76 #include	<schily/wait.h>
77 #define		VMS_VFORK_OK
78 #include	<schily/vfork.h>
79 #include	<schily/sysexits.h>
80 
81 #if	defined(PROTOTYPES) && defined(INS_BASE)
82 static char	Getpgmp[]   =   NOGETTEXT(INS_BASE "/" SCCS_BIN_PRE "bin/" "get");
83 #endif
84 static char	Getpgm[]   =   NOGETTEXT("get");
85 static char defline[] = NOGETTEXT(":Dt:\t:DL:\nMRs:\n:MR:COMMENTS:\n:C:");
86 static char	Sid[SID_STRSIZE];
87 static char	Mod[FILESIZE];
88 static char	Olddir[BUFSIZ];
89 static char	Pname[BUFSIZ];
90 static char	Dir[BUFSIZ];
91 static char	*Type;
92 static char	*Qsect;
93 static char	Deltadate[DT_ZSTRSIZE];
94 static char	Deltadatel[DT_ZSTRSIZE];
95 static char	*Deltatime;
96 static char	tempskel[] = NOGETTEXT("/tmp/prXXXXXX"); /* used to generate */
97 							/* temp file names */
98 static Nparms	N;			/* Keep -N parameters		*/
99 static Xparms	X;			/* Keep -X parameters		*/
100 static struct	sid	sid;
101 
102 static char	untmp[32], uttmp[32], cmtmp[32];
103 static char	mrtmp[32], sxtmp[32], bdtmp[32];
104 static FILE	*UNiop;
105 static FILE	*UTiop;
106 static FILE	*CMiop;
107 static FILE	*MRiop;
108 static FILE	*SXiop;
109 static FILE	*BDiop;
110 static char	line[BUFSIZ];
111 static int	num_files;
112 static int	HAD_CM, HAD_MR, HAD_SX, HAD_FD, HAD_BD, HAD_UN;
113 static char	dt_line[BUFSIZ];
114 static char	*dataspec = &defline[0];
115 static char	iline[BUFSIZ], xline[BUFSIZ], gline[BUFSIZ];
116 static struct	packet	gpkt;
117 static struct	tm	*Dtime;
118 static struct tm 	Date_time;
119 static struct tm 	Del_Date_time;
120 
121 static void	clean_up __PR((void));
122 
123 	int	main __PR((int argc, char **argv));
124 static void	process __PR((char *file));
125 static void	dodeltbl __PR((struct packet *pkt));
126 static void	scanspec __PR((char *spec, struct deltab *dtp, struct stats *statp));
127 static void	deltblchk __PR((struct packet *pkt));
128 static int	getstats __PR((struct packet *pkt, struct stats *statp));
129 static int	sidcmp __PR((struct sid *sid1, struct sid *sid2));
130 static int	getadel __PR((struct packet *pkt, struct deltab *dt));
131 static FILE *	maket __PR((char *file));
132 static void	printfile __PR((char *file));
133 static int	read_mod __PR((struct packet *pkt));
134 static void	getbody __PR((struct sid *gsid, struct packet *pkt));
135 static void	getit __PR((char *str, char *cp));
136 static void	aux_create __PR((FILE *iop, char *file, int delchar));
137 #if defined(BUG_1205145) || defined(GMT_TIME)
138 static void	prs_idsetup __PR((struct sid *gsid, struct packet *pkt, struct deltab *dt));
139 #else
140 static void	prs_idsetup __PR((struct sid *gsid, struct packet *pkt, time_t *bdate));
141 #endif
142 static void	putmr __PR((char *cp));
143 static void	putsx __PR((char *cp));
144 static void	putcom __PR((char *cp));
145 static void	read_to __PR((int ch, struct packet *pkt));
146 static void	printflags __PR((void));
147 static void	ck_spec __PR((char *p));
148 
149 int
main(argc,argv)150 main(argc, argv)
151 int argc;
152 char *argv[];
153 {
154 	register int j;
155 	register char *p;
156 	int  c;
157 	int strlength = 0;
158 	extern int Fcnt;
159 	int current_optind;
160 	int no_arg;
161 	/*
162 	 * Set locale for all categories.
163 	 */
164 	setlocale(LC_ALL, NOGETTEXT(""));
165 
166 	sccs_setinsbase(INS_BASE);
167 
168 	/*
169 	 * Set directory to search for general l10n SCCS messages.
170 	 */
171 #ifdef	PROTOTYPES
172 	(void) bindtextdomain(NOGETTEXT("SUNW_SPRO_SCCS"),
173 	    NOGETTEXT(INS_BASE "/" SCCS_BIN_PRE "lib/locale/"));
174 #else
175 	(void) bindtextdomain(NOGETTEXT("SUNW_SPRO_SCCS"),
176 	    NOGETTEXT("/usr/ccs/lib/locale/"));
177 #endif
178 
179 	(void) textdomain(NOGETTEXT("SUNW_SPRO_SCCS"));
180 
181 	tzset();	/* Set up timezome related vars */
182 
183 #ifdef	SCHILY_BUILD
184 	save_args(argc, argv);
185 #endif
186 	/*
187 	Set flags for 'fatal' to issue message, call clean-up
188 	routine, and terminate processing.
189 	*/
190 	set_clean_up(clean_up);
191 	Fflags = FTLMSG | FTLCLN | FTLEXIT;
192 #ifdef	SCCS_FATALHELP
193 	Fflags |= FTLFUNC;
194 	Ffunc = sccsfatalhelp;
195 #endif
196 
197 	/*
198 	The following loop processes keyletters and arguments.
199 	Note that these are processed only once for each
200 	invocation of 'main'.
201 	*/
202 
203 	current_optind = 1;
204 	optind = 1;
205 	opterr = 0;
206 	no_arg = 0;
207 	j = 1;
208 	/*CONSTCOND*/
209 	while (1) {
210 			if (current_optind < optind) {
211 			    current_optind = optind;
212 			    argv[j] = 0;
213 			    if (optind > j+1) {
214 				if ((argv[j+1][0] != '-') && (no_arg == 0)) {
215 					argv[j+1] = NULL;
216 				} else {
217 					optind = j+1;
218 					current_optind = optind;
219 				}
220 			    }
221 			}
222 			no_arg = 0;
223 			j = current_optind;
224 			c = getopt(argc, argv, "()-d:r:c:ealqN:X:V(version)");
225 
226 				/* this takes care of options given after
227 				** file names.
228 				*/
229 			if (c == EOF) {
230 			    if (optind < argc) {
231 				/* if it's due to -- then break; */
232 				if (argv[j][0] == '-' &&
233 				    argv[j][1] == '-') {
234 					argv[j] = 0;
235 					break;
236 				}
237 				optind++;
238 				current_optind = optind;
239 				continue;
240 			    } else {
241 				break;
242 			    }
243 			}
244 			p = optarg;
245 			switch (c) {
246 			case 'r':	/* specified SID */
247 				if (argv[j][2] == '\0') {
248 					if (*(omit_sid(p)) != '\0') {
249 						no_arg = 1;
250 						continue;
251 					}
252 				}
253 				chksid(sid_ab(p, &sid), &sid);
254 				break;
255 			case 'c':	/* cutoff date[time] */
256 				if ((*p) && (mystrptime(p, &Date_time, 0) != -1)) {
257 					break;
258 				} else {
259 					fatal(gettext("invalid cutoff date (prs4)"));
260 				}
261 			/*FALLTHRU*/
262 			case 'l':	/* later than specified SID */
263 			case 'e':	/* earlier than specified SID */
264 			case 'a':	/* print all delta types (R or D) */
265 				if (p) {
266 				    if (*p) {
267 					sprintf(SccsError,
268 					    gettext("value after %c arg (cm7)"),
269 						c);
270 					fatal(SccsError);
271 				    }
272 				}
273 				break;
274 			case 'd':	/* dataspec line */
275 				if (*p) {
276 				    strlength = strlen(p);
277 				    if ((p[strlength-2] == '\\') &&
278 					(p[strlength-1] == 'n'))
279 						p[strlength-2] = '\0';
280 				    dataspec = p;
281 				}
282 				break;
283 			case 'q': /* enable NSE mode */
284 				if (p) {
285 					if (*p) {
286 						nsedelim = p;
287 					}
288 				} else {
289 					nsedelim = (char *) 0;
290 				}
291 				break;
292 
293 			case 'N':	/* Bulk names */
294 				initN(&N);
295 				if (optarg == argv[j+1]) {
296 				   no_arg = 1;
297 				   break;
298 				}
299 				N.n_parm = p;
300 				break;
301 
302 			case 'X':	/* -Xtended options */
303 				X.x_parm = optarg;
304 				X.x_flags = XO_NULLPATH;
305 				if (!parseX(&X))
306 					goto err;
307 				had[NLOWER+c-'A'] = 0;	/* Allow mult -X */
308 				break;
309 
310 			case 'V':		/* version */
311 				printf(gettext(
312 				    "prs %s-SCCS version %s %s (%s-%s-%s)\n"),
313 					PROVIDER,
314 					VERSION,
315 					VDATE,
316 					HOST_CPU, HOST_VENDOR, HOST_OS);
317 				exit(EX_OK);
318 
319 			default:
320 			err:
321 				fatal(gettext("Usage: prs [ -ael ][ -c date-time ][ -d dataspec ]\n\t[ -r SID ][ -N[bulk-spec]][ -Xxopts ] s.filename ..."));
322 			}
323 
324 			/*
325 			 * Make sure that we only collect option letters from
326 			 * the range 'a'..'z' and 'A'..'Z'.
327 			 */
328 			if (ALPHA(c) &&
329 			    (had[LOWER(c)? c-'a' : NLOWER+c-'A']++)) {
330 				if (c != 'X')
331 					fatal(gettext("key letter twice (cm2)"));
332 			}
333 	}
334 
335 	for (j = 1; j < argc; j++) {
336 		if (argv[j]) {
337 			num_files++;
338 		}
339 	}
340 
341 	if (num_files == 0)
342 		fatal(gettext("missing file arg (cm3)"));
343 
344 	if (HADR && HADC)
345 		fatal(gettext("can't specify cutoff date and SID (prs5)"));
346 	if (HADC && (!HADL) && (!HADE))
347 		fatal(gettext("must specify -e or -l with -c (prs6)"));
348 	/*
349 	if (!HADD)
350 		HADD = 1;
351 	*/
352 
353 	setsig();
354 	xsethome(NULL);
355 	if (HADUCN) {					/* Parse -N args  */
356 		parseN(&N);
357 
358 		if (N.n_sdot && (sethomestat & SETHOME_OFFTREE))
359 			fatal(gettext("-Ns. not supported in off-tree project mode"));
360 	}
361 
362 	/*
363 	check the dataspec line and determine if any tmp files
364 	need be created
365 	*/
366 	ck_spec(dataspec);
367 
368 	/*
369 	Change flags for 'fatal' so that it will return to this
370 	routine (main) instead of terminating processing.
371 	*/
372 	Fflags &= ~FTLEXIT;
373 	Fflags |= FTLJMP;
374 
375 	/*
376 	Call 'process' routine for each file argument.
377 	*/
378 	for (j = 1; j < argc; j++)
379 		if ((p = argv[j]) != NULL)
380 			do_file(p, process, 1, N.n_sdot, &X);
381 
382 	return (Fcnt ? 1 : 0);
383 }
384 
385 
386 /*
387  * This procedure opens the SCCS file and calls all subsequent
388  * modules to perform 'prs'.  Once the file is finished, process
389  * returns to 'main' to process any other possible files.
390 */
391 
392 static void
process(file)393 process(file)
394 register	char	*file;
395 {
396 	if (setjmp(Fjmp))	/* set up to return here from 'fatal' */
397 		return;		/* and return to caller of 'process' */
398 	if (HADUCN) {
399 #ifdef	__needed__
400 		char	*ofile = file;
401 #endif
402 
403 		file = bulkprepare(&N, file);
404 		if (file == NULL) {
405 #ifdef	__needed__
406 			if (N.n_ifile)
407 				ofile = N.n_ifile;
408 #endif
409 			/*
410 			 * The error is typically
411 			 * "directory specified as s-file (cm14)"
412 			 */
413 			fatal(gettext(bulkerror(&N)));
414 		}
415 		if (sid.s_rel == 0 && N.n_sid.s_rel != 0) {
416 			sid.s_rel = N.n_sid.s_rel;
417 			sid.s_lev = N.n_sid.s_lev;
418 			sid.s_br  = N.n_sid.s_br;
419 			sid.s_seq = N.n_sid.s_seq;
420 		}
421 	}
422 
423 	sinit(&gpkt, file, SI_OPEN);	/* init packet and open SCCS file */
424 
425 	/*
426 	move value of global sid into gpkt.p_reqsid for
427 	later comparision.
428 	*/
429 
430 	gpkt.p_reqsid = sid;
431 
432 	gpkt.p_reopen = 1;	/* set reopen flag to 1 for 'getline' */
433 
434 	/*
435 	Read delta table entries checking for format error and
436 	setting the value for the SID if none was specified.
437 	Also check to see if SID specified does in fact exists.
438 	*/
439 
440 	deltblchk(&gpkt);
441 	/*
442 	create auxiliary file for User Name Section
443 	*/
444 
445 	if (HAD_UN)
446 		aux_create(UNiop, untmp, EUSERNAM);
447 	else read_to(EUSERNAM, &gpkt);
448 
449 	/*
450 	store flags (if any) into array called 'gpkt.p_sflags'
451 	*/
452 
453 	doflags(&gpkt);
454 
455 	/*
456 	 * If there are SCCS v6 flags or SCCS v6 global meta data,
457 	 * we need to skip this here.
458 	 * XXX In order to be able to print this data, we need to
459 	 * XXX parse this block instead of just skipping it.
460 	 */
461 	donamedflags(&gpkt);
462 	dometa(&gpkt);
463 	if (gpkt.p_line != NULL &&
464 	    gpkt.p_line[0] == CTLCHAR && gpkt.p_line[1] != BUSERTXT)
465 		read_to(BUSERTXT, &gpkt);
466 
467 	/*
468 	create auxiliary file for the User Text section
469 	*/
470 
471 	if (HAD_FD)
472 		aux_create(UTiop, uttmp, EUSERTXT);
473 	else read_to(EUSERTXT, &gpkt);
474 
475 	/*
476 	indicate to 'getline' that EOF is okay
477 	*/
478 
479 	gpkt.p_chkeof = 1;
480 
481 	/*
482 	read body of SCCS file and create temp file for it
483 	*/
484 
485 	while (read_mod(&gpkt))
486 		;
487 
488 	/*
489 	Here, file has already been re-opened (by 'getline' after
490 	EOF was encountered by 'read_mod' calling 'getline')
491 	*/
492 
493 	getline(&gpkt);		/* skip over header line */
494 
495 	if (!HADD && !HADR && !HADE && !HADL)
496 		HADE = 1;
497 	if (!HADD)
498 		printf("%s:\n\n", file);
499 
500 	/*
501 	call 'dodeltbl' to read delta table entries
502 	and determine which deltas are to be considered
503 	*/
504 
505 	dodeltbl(&gpkt);
506 
507 	/*
508 	call 'clean_up' to remove any temporary file created
509 	during processing of the SCCS file passed as an argument from
510 	'do_file'
511 	*/
512 
513 	clean_up();
514 
515 	/* return to caller of 'process' */
516 }
517 
518 
519 /*
520  * This procedure actually reads the delta table entries and
521  * substitutes pre-defined strings and pointers with the information
522  * needed during the scanning of the 'dataspec' line
523 */
524 
525 static void
dodeltbl(pkt)526 dodeltbl(pkt)
527 register struct packet *pkt;
528 {
529 	char	*n;
530 	int	stopdel;
531 	int	found;
532 	long dcomp;
533 	struct	deltab	dt;
534 	struct	stats	stats;
535 
536 	/*
537 	flags used during determination of deltas to be
538 	considered
539 	*/
540 
541 	found = stopdel = 0;
542 
543 	/*
544 	Read entire delta table.
545 	*/
546 	while (getstats(pkt, &stats) && !stopdel) {
547 		if (getadel(pkt, &dt) != BDELTAB)
548 			fmterr(pkt);
549 
550 		/*
551 		ignore 'removed' deltas if !HADA keyletter
552 		*/
553 
554 		if (!HADA && (dt.d_type != 'D' && dt.d_type != 'U')) {
555 			read_to(EDELTAB, pkt);
556 			continue;
557 		}
558 
559 		/*
560 		determine whether or not to consider current delta
561 		*/
562 
563 		if (HADC) {
564 			get_Del_Date_time(pkt->p_line, &dt, pkt, &Del_Date_time);
565 			dcomp = cmpdate(&Date_time, &Del_Date_time);
566 			if (HADE && !HADL && (dcomp < 0)) {
567 				read_to(EDELTAB, pkt);
568 				continue;
569 			} else if (HADL && !HADE && (dcomp > 0)) {
570 				stopdel = 1;
571 				continue;
572 			}
573 		} else if (HADE && HADL)
574 			stopdel = 0;
575 
576 		else if (!(eqsid(&gpkt.p_reqsid, &dt.d_sid)) && !found) {
577 			/*
578 			if !HADL keyletter skip delta entry
579 			*/
580 			if (!HADL) {
581 				read_to(EDELTAB, pkt);
582 				continue;
583 			}
584 		} else {
585 			found = 1;
586 			stopdel = 1;
587 		}
588 		/*
589 		if HADE keyletter read remainder of delta table entries
590 		*/
591 		if (HADE && stopdel)
592 			stopdel = 0;
593 		/*
594 		create temp file for MRs and comments
595 		*/
596 		if (HAD_MR)
597 			MRiop = maket(mrtmp);
598 		if (HAD_CM)
599 			CMiop = maket(cmtmp);
600 		if (HAD_SX)
601 			SXiop = maket(sxtmp);
602 		/*
603 		Read rest of delta entry.
604 		*/
605 		while ((n = getline(pkt)) != NULL)
606 			if (pkt->p_line[0] != CTLCHAR)
607 				break;
608 			else {
609 				switch (pkt->p_line[1]) {
610 				case INCLUDE:
611 					getit(iline, n);
612 					continue;
613 				case EXCLUDE:
614 					getit(xline, n);
615 					continue;
616 				case IGNORE:
617 					getit(gline, n);
618 					continue;
619 				case MRNUM:
620 					if (HAD_MR)
621 						putmr(n);
622 					continue;
623 				case SIDEXTENS:
624 					if (HAD_SX)
625 						putsx(n);
626 					continue;
627 				case COMMENTS:
628 					if (HAD_CM)
629 						putcom(n);
630 					continue;
631 				case EDELTAB:
632 					/*
633 					close temp files for MRs and comments
634 					*/
635 					if (HAD_MR) {
636 						if (MRiop)
637 							fclose(MRiop);
638 						MRiop = NULL;
639 					}
640 					if (HAD_CM) {
641 						if (CMiop)
642 							fclose(CMiop);
643 						CMiop = NULL;
644 					}
645 					if (HAD_SX) {
646 						if (SXiop)
647 							fclose(SXiop);
648 						SXiop = NULL;
649 					}
650 					scanspec(dataspec, &dt, &stats);
651 					/*
652 					remove temp files for MRs and comments
653 					*/
654 					unlink(mrtmp);
655 					unlink(cmtmp);
656 					unlink(sxtmp);
657 					break;
658 				default:
659 					fmterr(pkt);
660 				}
661 				break;
662 			}
663 		if (n == NULL || pkt->p_line[0] != CTLCHAR)
664 			fmterr(pkt);
665 	}
666 }
667 
668 
669 /*
670  * The scanspec procedure scans the dataspec searching for ID keywords.
671  * When a keyword is found the value is replaced and printed on the
672  * standard output. Any character that is not an ID keyword is printed
673  * immediately.
674 */
675 
676 static	char	Zkywd[5]   =   "@(#)";
677 
678 static void
scanspec(spec,dtp,statp)679 scanspec(spec, dtp, statp)
680 char spec[];
681 struct	deltab	*dtp;
682 struct	stats	*statp;
683 {
684 
685 	register char *lp;
686 	register char	*k;
687 
688 	int	istr;
689 	register	char	c;
690 
691 	istr = 0;
692 
693 	/*
694 	call 'idsetup' to set certain data keywords for
695 	'scanspec' substitution
696 	*/
697 #if defined(BUG_1205145) || defined(GMT_TIME)
698 	prs_idsetup(&dtp->d_sid, &gpkt, dtp);
699 #else
700 	prs_idsetup(&dtp->d_sid, &gpkt, &dtp->d_dtime.dt_sec);
701 #endif
702 
703 	/*
704 	scan 'dataspec' line
705 	*/
706 	for (lp = spec; *lp != 0; lp++) {
707 		if (lp[0] == ':' && lp[1] != 0 && lp[2] == ':') {
708 			c = *++lp;
709 			switch (c) {
710 			case 'I':	/* SID */
711 				printf("%s", Sid);
712 				break;
713 			case 'R':	/* Release number */
714 				printf("%d", dtp->d_sid.s_rel);
715 				break;
716 			case 'L':	/* Level number */
717 				printf("%d", dtp->d_sid.s_lev);
718 				break;
719 			case 'B':	/* Branch number */
720 				if (dtp->d_sid.s_br != 0)
721 					printf("%d", dtp->d_sid.s_br);
722 				break;
723 			case 'S':	/* Sequence number */
724 				if (dtp->d_sid.s_seq != 0)
725 					printf("%d", dtp->d_sid.s_seq);
726 				break;
727 			case 'D':	/* Date delta created */
728 				printf("%s", Deltadate);
729 				break;
730 			case 'd':	/* Date delta created (4 digit year) */
731 				printf("%s", Deltadatel);
732 				break;
733 			case 'T':	/* Time delta created */
734 				printf("%s", Deltatime);
735 				break;
736 			case 'P':	/* Programmer who created delta */
737 				printf("%s", dtp->d_pgmr);
738 				break;
739 			case 'C':	/* Comments */
740 				if (exists(cmtmp))
741 					printfile(cmtmp);
742 				break;
743 			case 'Y':	/* Type flag */
744 				printf("%s", Type);
745 				break;
746 			case 'Q':	/* csect flag */
747 				printf("%s", Qsect);
748 				break;
749 			case 'J':	/* joint edit flag */
750 				if (gpkt.p_sflags[JOINTFLAG - 'a'])
751 					printf(gettext("yes"));
752 				else printf(gettext("no"));
753 				break;
754 			case 'M':	/* Module name */
755 				printf("%s", Mod);
756 				break;
757 			case 'W':	/* Form of what string */
758 				printf("%s%s\t%s", Zkywd, Mod, Sid);
759 				break;
760 			case 'A':	/* Form of what string */
761 				printf("%s%s %s %s%s", Zkywd, Type, Mod, Sid, Zkywd);
762 				break;
763 			case 'Z':	/* what string constructor */
764 				printf("%s", Zkywd);
765 				break;
766 			case 'F':	/* File name */
767 				printf("%s", sname(gpkt.p_file));
768 				break;
769 			case 'G':	/* Basename of g-file */
770 				printf("%s", auxf(gpkt.p_file, 'g'));
771 				break;
772 			default:
773 				putchar(':');
774 				--lp;
775 				continue;
776 			}
777 			lp++;
778 		} else if (lp[0] == ':' && lp[1] != 0 && lp[2] != 0 && lp[3] == ':') {
779 			if (lp[1] == ':') {
780 				putchar(':');
781 				continue;
782 			}
783 			istr = *++lp;
784 			istr += 256 * (*++lp);
785 
786 			switch (istr) {
787 			case 256*'L'+'D':	/* :DL: Delta line statistics */
788 				printf("%.05d", statp->s_ins);
789 				putchar('/');
790 				printf("%.05d", statp->s_del);
791 				putchar('/');
792 				printf("%.05d", statp->s_unc);
793 				break;
794 			case 256*'i'+'L':	/* :Li: Lines inserted by delta */
795 				printf("%.05d", statp->s_ins);
796 				break;
797 			case 256*'d'+'L':	/* :Ld: Lines deleted by delta */
798 				printf("%.05d", statp->s_del);
799 				break;
800 			case 256*'u'+'L':	/* :Lu: Lines unchanged by delta */
801 				printf("%.05d", statp->s_unc);
802 				break;
803 			case 256*'T'+'D':	/* :DT: Delta type */
804 				printf("%c", dtp->d_type);
805 				break;
806 			case 256*'y'+'D':	/* :Dy: Year delta created */
807 				if (Dtime->tm_year >= 100) {
808 				    Dtime->tm_year %= 100;
809 				}
810 				printf("%02d", Dtime->tm_year);
811 				break;
812 			case 256*'Y'+'D':	/* :DY: Year delta created */
813 				Dtime->tm_year += 1900;
814 				printf("%4d", Dtime->tm_year); /* 4 digits */
815 				break;
816 			case 256*'_'+'D':	/* :D_: Date delta created y-m-d */
817 				Deltadatel[4] = '-';
818 				Deltadatel[7] = '-';
819 				printf("%s", Deltadatel);
820 				Deltadatel[4] = '/';
821 				Deltadatel[7] = '/';
822 				break;
823 			case 256*'m'+'D':	/* :Dm: Month delta created */
824 				printf("%02d", (Dtime->tm_mon + 1));
825 				break;
826 			case 256*'d'+'D':	/* :Dd: Day delta created */
827 				printf("%02d", Dtime->tm_mday);
828 				break;
829 			case 256*'h'+'T':	/* :Th: Hour delta created */
830 				printf("%02d", Dtime->tm_hour);
831 				break;
832 			case 256*'m'+'T':	/* :Tm: Minutes delta created */
833 				printf("%02d", Dtime->tm_min);
834 				break;
835 			case 256*'s'+'T':	/* :Ts: Seconds delta created */
836 				printf("%02d", Dtime->tm_sec);
837 				break;
838 			case 256*'S'+'D':	/* :DS: Delta sequence number */
839 				printf("%d", dtp->d_serial);
840 				break;
841 			case 256*'P'+'D':	/* :DP: Predecessor delta sequence number */
842 				printf("%d", dtp->d_pred);
843 				break;
844 			case 256*'I'+'D':	/* :DI: Deltas included,excluded,ignored */
845 #ifdef	ATT_BUG
846 				/*
847 				 * This has been introduced around 1984.
848 				 */
849 				printf("%s", iline);
850 				if (length(xline))
851 					printf("/%s", xline);
852 				if (length(gline))
853 					printf("/%s", gline);
854 #else
855 				/*
856 				 * This is the POSIX compliant behavior.
857 				 */
858 				printf("%s", iline);
859 				printf("/%s", xline);
860 				printf("/%s", gline);
861 #endif
862 				break;
863 			case 256*'n'+'D':	/* :Dn: Deltas included */
864 				printf("%s", iline);
865 				break;
866 			case 256*'x'+'D':	/* :Dx: Deltas excluded */
867 				printf("%s", xline);
868 				break;
869 			case 256*'g'+'D':	/* :Dg: Deltas ignored */
870 				printf("%s", gline);
871 				break;
872 			case 256*'K'+'L':	/* :LK: locked releases */
873 				if ((k = gpkt.p_sflags[LOCKFLAG - 'a']) != NULL)
874 					printf("%s", k);
875 				else printf(gettext("none"));
876 				break;
877 			case 256*'R'+'M':	/* :MR: MR numbers */
878 				if (exists(mrtmp))
879 					printfile(mrtmp);
880 				break;
881 			case 256*'C'+'M':  /* :MC: cmf flag yes or no */
882 				if (gpkt.p_sflags[CMFFLAG - 'a'])
883 					printf(gettext("yes"));
884 				else
885 					printf(gettext("no"));
886 				break;
887 			case 256*'X'+'S':	/* :SX: SID Extensions */
888 				if (exists(sxtmp))
889 					printfile(sxtmp);
890 				break;
891 			case 256*'C'+'A':  /* :AC: cmf application field */
892 				if ((k = gpkt.p_sflags[CMFFLAG - 'a']) == NULL)
893 					printf(gettext("none"));
894 				else
895 					printf("%s", k);
896 				break;
897 			case 256*'N'+'U':	/* :UN: User names */
898 				if (exists(untmp))
899 					printfile(untmp);
900 				break;
901 			case 256*'F'+'M':	/* :MF: MR validation flag */
902 				if (gpkt.p_sflags[VALFLAG - 'a'])
903 					printf(gettext("yes"));
904 				else printf(gettext("no"));
905 				break;
906 			case 256*'P'+'M':	/* :MP: MR validation program */
907 				if ((k = gpkt.p_sflags[VALFLAG - 'a']) == NULL)
908 					printf(gettext("none"));
909 				else printf("%s", k);
910 				break;
911 			case 256*'F'+'K':	/* :KF: Keyword err/warn flag */
912 				if (gpkt.p_sflags[IDFLAG - 'a'])
913 					printf(gettext("yes"));
914 				else printf(gettext("no"));
915 				break;
916 			case 256*'F'+'B':	/* :BF: Branch flag */
917 				if (gpkt.p_sflags[BRCHFLAG - 'a'])
918 					printf(gettext("yes"));
919 				else printf(gettext("no"));
920 				break;
921 			case 256*'B'+'F':	/* :FB: Floor Boundry */
922 				if ((k = gpkt.p_sflags[FLORFLAG - 'a']) != NULL)
923 					printf("%s", k);
924 				else printf(gettext("none"));
925 				break;
926 			case 256*'B'+'C':	/* :CB: Ceiling Boundry */
927 				if ((k = gpkt.p_sflags[CEILFLAG - 'a']) != NULL)
928 					printf("%s", k);
929 				else printf(gettext("none"));
930 				break;
931 			case 256*'s'+'D':	/* :Ds: Default SID */
932 				if ((k = gpkt.p_sflags[DEFTFLAG - 'a']) != NULL)
933 					printf("%s", k);
934 				else printf(gettext("none"));
935 				break;
936 			case 256*'D'+'N':	/* :ND: Null delta */
937 				if (gpkt.p_sflags[NULLFLAG - 'a'])
938 					printf(gettext("yes"));
939 				else printf(gettext("no"));
940 				break;
941 			case 256*'D'+'F':	/* :FD: File descriptive text */
942 				if (exists(uttmp))
943 					printfile(uttmp);
944 				break;
945 			case 256*'D'+'B':	/* :BD: Entire file body */
946 				if (exists(bdtmp))
947 					printfile(bdtmp);
948 				break;
949 			case 256*'B'+'G':	/* :GB: Gotten body from 'get' */
950 				getbody(&dtp->d_sid, &gpkt);
951 				break;
952 			case 256*'N'+'P':	/* :PN: Full pathname of File */
953 				copy(gpkt.p_file, Dir);
954 				dname(Dir);
955 				if (getcwd(Olddir, sizeof (Olddir)) == NULL)
956 					efatal(gettext("getcwd() failed (prs2)"));
957 				if (chdir(Dir) != 0)
958 					efatal(gettext("cannot change directory (prs3)"));
959 				if (getcwd(Pname, sizeof (Pname)) == NULL)
960 					efatal(gettext("getcwd() failed (prs2)"));
961 				if (chdir(Olddir) != 0)
962 					efatal(gettext("cannot change directory (prs3)"));
963 				printf("%s/", Pname);
964 				printf("%s", sname(gpkt.p_file));
965 				break;
966 			case 256*'L'+'F':	/* :FL: Flag descriptions (as in 'prt') */
967 				printflags();
968 				break;
969 			case 256*'t'+'D':	/* :Dt: Whole delta table line */
970 				/*
971 				replace newline with null char to make
972 				data keyword simple format
973 				*/
974 				repl(dt_line, '\n', '\0');
975 				k = dt_line;
976 				/*
977 				skip control char, line flag, and blank
978 				*/
979 				k += 3;
980 				printf("%s", k);
981 				break;
982 			case 256*'p'+'G':	/* :Gp: V6 Initial Path */
983 				if (gpkt.p_init_path)
984 					printf("%s", gpkt.p_init_path);
985 				break;
986 			case 256*'r'+'G': {	/* :Gr: V6 Unified Random */
987 				char	rbuf[64];
988 				urand_ba(&gpkt.p_rand, rbuf, sizeof (rbuf));
989 				printf("%s", rbuf);
990 				break;
991 				}
992 			default:
993 				putchar(':');
994 				lp -= 2;
995 				continue;
996 			}
997 			lp++;
998 		} else {
999 			c = *lp;
1000 			if (c == '\\') {
1001 				switch (*++lp) {
1002 				case 'n':	/* for newline */
1003 					putchar('\n');
1004 					break;
1005 				case ':':	/* for wanted colon */
1006 					putchar(':');
1007 					break;
1008 				case 't':	/* for tab */
1009 					putchar('\t');
1010 					break;
1011 				case 'b':	/* for backspace */
1012 					putchar('\b');
1013 					break;
1014 				case 'r':	/* for carriage return */
1015 					putchar('\r');
1016 					break;
1017 				case 'f':	/* for form feed */
1018 					putchar('\f');
1019 					break;
1020 				case '\\':	/* for backslash */
1021 					putchar('\\');
1022 					break;
1023 				case '\'':	/* for single quote */
1024 					putchar('\'');
1025 					break;
1026 				default:	/* unknown case */
1027 					putchar('\\');
1028 					putchar(*lp);
1029 					break;
1030 				}
1031 			} else
1032 				putchar(*lp);
1033 		}
1034 	}
1035 	/*
1036 	zero out first char of global string lines in case
1037 	a value is not gotten in next delta table entry
1038 	*/
1039 	iline[0] = xline[0] = gline[0] = 0;
1040 	putchar('\n');
1041 }
1042 
1043 
1044 /*
1045  * This procedure cleans up all temporary files created during
1046  * 'process' that are used for data keyword substitution
1047 */
1048 
1049 static void
clean_up()1050 clean_up()
1051 {
1052 	sclose(&gpkt);	/* if SCCS file is open, close it */
1053 	sfree(&gpkt);	/* free line buffer			*/
1054 	xrm(&gpkt);	/* remove the 'packet' used for this SCCS file */
1055 	unlink(mrtmp);	/* remove all temporary files from /tmp */
1056 	unlink(cmtmp);	/*			"		*/
1057 	unlink(sxtmp);	/*			"		*/
1058 	unlink(untmp);	/*			"		*/
1059 	unlink(uttmp);	/*			"		*/
1060 	unlink(bdtmp);	/*			"		*/
1061 	ffreeall();
1062 }
1063 
1064 
1065 /*
1066  * This procedure checks the delta table entries for correct format.
1067  * It also checks to see if the SID specified by the -r keyletter
1068  * is contained in the file.  If no SID was specified assumes the top
1069  * delta created (last in time).
1070 */
1071 
1072 static void
deltblchk(pkt)1073 deltblchk(pkt)
1074 register struct packet *pkt;
1075 {
1076 	char	*n;
1077 	int	found;
1078 	struct	deltab	dt;
1079 	struct	deltab	odt;	/* Old delta */
1080 	struct	stats	stats;
1081 
1082 	odt.d_sid.s_rel = 0;
1083 	odt.d_sid.s_lev = 0;
1084 	odt.d_sid.s_br = 0;
1085 	odt.d_sid.s_seq = 0;
1086 	found = 0;
1087 	/*
1088 	Read entire delta table.
1089 	*/
1090 	while (getstats(pkt, &stats)) {
1091 		if (getadel(pkt, &dt) != BDELTAB)
1092 			fmterr(pkt);
1093 
1094 		/*
1095 		ignore if "removed" delta
1096 		*/
1097 		if (!HADA && (dt.d_type != 'D' && dt.d_type != 'U')) {
1098 			read_to(EDELTAB, pkt);
1099 			continue;
1100 		}
1101 
1102 		if (!HADR && (pkt->p_reqsid.s_rel == 0)) {
1103 			if (!found)
1104 				odt.d_sid = dt.d_sid;
1105 			found = 1;
1106 		} else if (pkt->p_reqsid.s_rel == 0) {
1107 			if (sidcmp(&odt.d_sid, &dt.d_sid) < 0)
1108 				odt.d_sid = dt.d_sid;
1109 		} else if (pkt->p_reqsid.s_lev == 0) {
1110 			if ((pkt->p_reqsid.s_rel >= dt.d_sid.s_rel) &&
1111 				(sidcmp(&odt.d_sid, &dt.d_sid) < 0))
1112 					odt.d_sid = dt.d_sid;
1113 		} else if ((pkt->p_reqsid.s_br == 0) && !found) {
1114 			if (!(sidcmp(&pkt->p_reqsid, &dt.d_sid))) {
1115 				found = 1;
1116 				odt.d_sid = dt.d_sid;
1117 			}
1118 		} else if (pkt->p_reqsid.s_seq == 0) {
1119 			if ((pkt->p_reqsid.s_rel == dt.d_sid.s_rel) &&
1120 				(pkt->p_reqsid.s_lev == dt.d_sid.s_lev) &&
1121 				(pkt->p_reqsid.s_br  == dt.d_sid.s_br) &&
1122 				(sidcmp(&odt.d_sid, &dt.d_sid) < 0))
1123 					odt.d_sid = dt.d_sid;
1124 		} else if (!found) {
1125 			if (!(sidcmp(&pkt->p_reqsid, &dt.d_sid))) {
1126 				found = 1;
1127 				odt.d_sid = dt.d_sid;
1128 			}
1129 		}
1130 
1131 		/*
1132 		Read rest of delta entry.
1133 		*/
1134 		while ((n = getline(pkt)) != NULL)
1135 			if (pkt->p_line[0] != CTLCHAR)
1136 				break;
1137 			else {
1138 				switch (pkt->p_line[1]) {
1139 				case EDELTAB:
1140 					break;
1141 				case INCLUDE:
1142 				case EXCLUDE:
1143 				case IGNORE:
1144 				case MRNUM:
1145 				case SIDEXTENS:
1146 				case COMMENTS:
1147 					continue;
1148 				default:
1149 					fmterr(pkt);
1150 				}
1151 				break;
1152 			}
1153 		if (n == NULL || pkt->p_line[0] != CTLCHAR)
1154 			fmterr(pkt);
1155 	}
1156 	/*
1157 	if not at the beginning of the User Name section
1158 	there is an internal error
1159 	*/
1160 	if (pkt->p_line[1] != BUSERNAM)
1161 		fmterr(pkt);
1162 	/*
1163 	if SID did not exist (the one specified by -r keyletter)
1164 	then there exists an error
1165 	*/
1166 	if (odt.d_sid.s_rel == 0)
1167 		fatal(gettext("nonexistent SID (prs1)"));
1168 	else
1169 		gpkt.p_reqsid = odt.d_sid;
1170 }
1171 
1172 
1173 /*
1174  * This procedure reads the stats line from the delta table entry
1175  * and places the statisitics into a structure called "stats".
1176 */
1177 
1178 static int
getstats(pkt,statp)1179 getstats(pkt, statp)
1180 register struct packet *pkt;
1181 register struct stats *statp;
1182 {
1183 	register char *p = getline(pkt);
1184 
1185 	if (p == NULL || *p++ != CTLCHAR || *p++ != STATS)
1186 		return (0);
1187 	NONBLANK(p);
1188 	p = satoi(p, &statp->s_ins);
1189 	p = satoi(++p, &statp->s_del);
1190 	satoi(++p, &statp->s_unc);
1191 	return (1);
1192 }
1193 
1194 /*
1195  * This routine compares to SIDs numerically.
1196 */
1197 
1198 static int
sidcmp(sid1,sid2)1199 sidcmp(sid1, sid2)
1200 struct sid *sid1, *sid2;
1201 
1202 {
1203 int diff = 0;
1204 
1205 	if ((diff = (sid1->s_rel - sid2->s_rel)) != 0)
1206 		return (diff);
1207 	if ((diff = (sid1->s_lev - sid2->s_lev)) != 0)
1208 		return (diff);
1209 	if ((diff = (sid1->s_br  - sid2->s_br)) != 0)
1210 		return (diff);
1211 
1212 	return (sid1->s_seq - sid2->s_seq);
1213 }
1214 /*
1215  * This procedure reads a delta table entry line from the delta
1216  * table entry and places the contents of the line into a structure
1217  * called "deltab".
1218 */
1219 
1220 static int
getadel(pkt,dt)1221 getadel(pkt, dt)
1222 register struct packet *pkt;
1223 register struct deltab *dt;
1224 {
1225 	if (getline(pkt) == NULL)
1226 		fmterr(pkt);
1227 #if !(defined(BUG_1205145) || defined(GMT_TIME))
1228 	copy(pkt->p_line, dt_line);  /* copy delta table line for :Dt: keywd */
1229 #endif
1230 	return (del_ab(pkt->p_line, dt, pkt));
1231 }
1232 
1233 
1234 /*
1235  * This procedure creates the temporary file used during the
1236  * "process" subroutine.  The skeleton defined at the beginning
1237  * of the program is filled in in this function
1238 */
1239 static FILE *
maket(file)1240 maket(file)
1241 char	*file;
1242 {
1243 	FILE *iop;
1244 	mode_t cur_umask;
1245 
1246 	copy(tempskel, file);	/* copy file name into the skeleton */
1247 				/* umask 0133 -> create mode 0644 */
1248 	cur_umask = umask((mode_t)((S_IRWXU|S_IRWXG|S_IRWXO)&~(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)));
1249 #ifdef	HAVE_MKSTEMP
1250 	iop = fdfopen(mkstemp(file), O_WRONLY|O_BINARY);
1251 #else
1252 	iop = xfcreat(mktemp(file), 0644);
1253 #endif
1254 #ifdef	NEED_O_BINARY
1255 	if (iop != NULL)
1256 		setmode(fileno(iop), O_BINARY);
1257 #endif
1258 	(void) umask(cur_umask);
1259 
1260 	return (iop);
1261 }
1262 
1263 
1264 /*
1265  * This procedure prints (on the standard output) the contents of any
1266  * temporary file that may have been created during "process".
1267 */
1268 
1269 static void
printfile(file)1270 printfile(file)
1271 register	char	*file;
1272 {
1273 #ifdef	__historic__
1274 	register	char	*p;
1275 #else
1276 	register	size_t	amt;
1277 #endif
1278 	FILE	*iop;
1279 
1280 	iop = xfopen(file, O_RDONLY|O_BINARY);
1281 #ifdef	__historic__
1282 	while ((p = fgets(line, sizeof (line), iop)) != NULL)
1283 		printf("%s", p);
1284 #else
1285 	while ((amt= fread(line, 1, sizeof (line), iop)) > 0)
1286 		fwrite(line, 1, amt, stdout);
1287 #endif
1288 	fclose(iop);
1289 }
1290 
1291 
1292 /*
1293  * This procedure reads the body of the SCCS file from beginning to end.
1294  * It also creates the temporary file /tmp/prbdtmp which contains
1295  * the body of the SCCS file for data keyword substitution.
1296 */
1297 
1298 static int
read_mod(pkt)1299 read_mod(pkt)
1300 register struct packet *pkt;
1301 {
1302 	register char *p;
1303 	int ser;
1304 	int iod;
1305 
1306 	if (HAD_BD)
1307 		BDiop = maket(bdtmp);
1308 	while (getline(pkt) != NULL) {
1309 		p = pkt->p_line;
1310 		if (HAD_BD) {
1311 #ifdef	__historic__
1312 			if (fputs(p, BDiop) == EOF) {
1313 				xmsg(bdtmp, NOGETTEXT("read_mod"));
1314 #else
1315 			if (fwrite(p, 1, pkt->p_line_length, BDiop) == EOF) {
1316 				xmsg(bdtmp, NOGETTEXT("read_mod"));
1317 #endif
1318 			}
1319 		}
1320 		if (*p++ != CTLCHAR)
1321 			continue;
1322 		else {
1323 			if (!((iod = *p++) == INS || iod == DEL || iod == END)) {
1324 				if (iod == CTLCHAR)
1325 					continue;
1326 				if (iod == NONL)
1327 					continue;
1328 				fmterr(pkt);
1329 			}
1330 			NONBLANK(p);
1331 			satoi(p, &ser);
1332 			if (iod == END)
1333 				remq(pkt, ser);
1334 			else
1335 				addq(pkt, ser, iod == INS ? NO : 0, iod, USER);
1336 		}
1337 	}
1338 	if (HAD_BD) {
1339 		if (BDiop)
1340 			fclose(BDiop);
1341 		BDiop = NULL;
1342 	}
1343 	if (pkt->p_q)
1344 		fatal(gettext("premature eof (co5)"));
1345 	return (0);
1346 }
1347 
1348 
1349 /*
1350  * This procedure is only called if the :GB: data keyword is specified.
1351  * It forks and creates a child process to invoke 'get' with the '-p'
1352  * and '-s' options for the SID currently being processed.  Upon
1353  * completion, control of the program is returned to 'prs'.
1354 */
1355 
1356 static void
getbody(gsid,pkt)1357 getbody(gsid, pkt)
1358 struct	sid	*gsid;
1359 struct packet *pkt;
1360 {
1361 	int	i;
1362 	int	status;
1363 	char	str[SID_STRSIZE];
1364 	char	rarg[SID_STRSIZE+2];
1365 	char	filearg[80];
1366 
1367 	sid_ba(gsid, str);
1368 	sprintf(rarg, "-r%s", str);
1369 	sprintf(filearg, "%s", pkt->p_file);
1370 	/*
1371 	fork here so 'getbody' can execute 'get' to
1372 	print out gotten body :GB:
1373 	*/
1374 	if ((i = vfork()) < 0)
1375 		efatal(gettext("cannot fork, try again"));
1376 	if (i == 0) {
1377 		/*
1378 		perform 'get' and redirect output
1379 		to standard output
1380 		*/
1381 #if	defined(PROTOTYPES) && defined(INS_BASE)
1382 		execlp(Getpgmp, Getpgm, NOGETTEXT("-s"), NOGETTEXT("-p"), rarg, filearg, (char *)0);
1383 #endif
1384 		execlp(Getpgm, Getpgm, NOGETTEXT("-s"), NOGETTEXT("-p"), rarg, filearg, (char *)0);
1385 		sprintf(SccsError, gettext("cannot execute '%s'"),
1386 			Getpgm);
1387 #ifdef	HAVE_VFORK
1388 		Fflags |= FTLVFORK;
1389 #endif
1390 		efatal(SccsError);
1391 	} else {
1392 		wait(&status);
1393 		return;
1394 	}
1395 }
1396 
1397 
1398 /*
1399  * This procedure places the line read in "dodeltbl" into a global string
1400  * 'str'.  This procedure is only called for include, exclude or ignore
1401  * lines.
1402 */
1403 
1404 static void
getit(str,cp)1405 getit(str, cp)
1406 register	char	*str, *cp;
1407 {
1408 	cp += 2;
1409 	NONBLANK(cp);
1410 	cp[length(cp) - 1] = '\0';
1411 	sprintf(str, "%s", cp);
1412 }
1413 
1414 
1415 /*
1416  * This procedure creates an auxiliary file for the iop passed as an argument
1417  * for the file name also passed as an argument.  If no text exists for the
1418  * named file, an auxiliary file is still created with the text "(none)".
1419 */
1420 
1421 static void
aux_create(iop,file,delchar)1422 aux_create(iop, file, delchar)
1423 FILE	*iop;
1424 char	*file;
1425 char	delchar;
1426 {
1427 
1428 	char	*n;
1429 	int	text;
1430 	/*
1431 	create auxiliary file for the named section
1432 	*/
1433 
1434 	text = 0;
1435 	iop = maket(file);
1436 	while ((n = getline(&gpkt)) != NULL && gpkt.p_line[0] != CTLCHAR) {
1437 		text = 1;
1438 		if (fputs(n, iop) == EOF) {
1439 			xmsg(file, NOGETTEXT("aux_create"));
1440 		}
1441 	}
1442 	/*
1443 	check to see that delimiter found is correct
1444 	*/
1445 	if (n == NULL || gpkt.p_line[0] != CTLCHAR || gpkt.p_line[1] != delchar)
1446 		fmterr(&gpkt);
1447 	if (!text)
1448 		fprintf(iop, gettext("none\n"));
1449 	fclose(iop);
1450 }
1451 
1452 
1453 /*
1454  * This procedure sets the values for certain data keywords which are
1455  * either shared by more than one data keyword or because substitution
1456  * here would be easier than doing it in "scanspec" (more efficient etc.)
1457 */
1458 
1459 static void
1460 #if defined(BUG_1205145) || defined(GMT_TIME)
prs_idsetup(gsid,pkt,dt)1461 prs_idsetup(gsid, pkt, dt)
1462 #else
1463 prs_idsetup(gsid, pkt, bdate)
1464 #endif
1465 struct	sid	*gsid;
1466 struct	packet	*pkt;
1467 #if defined(BUG_1205145) || defined(GMT_TIME)
1468 struct	deltab	*dt;
1469 #else
1470 time_t	*bdate;
1471 #endif
1472 {
1473 
1474 	register	char	*p;
1475 
1476 #if defined(BUG_1205145) || defined(GMT_TIME)
1477 	Dtime = localtime(&dt->d_dtime.dt_sec);
1478 	/*
1479 	 * Local time corrections before del_ba() and date_ba() calls.
1480 	 * Because these functions use gmtime() instead of localtime().
1481 	 * This allows to print the local time for the :Dt: keywd instead
1482 	 * of the GMT based time from the s-file.
1483 	 */
1484 	dt->d_dtime.dt_sec = mklgmtime(Dtime);
1485 
1486 	del_ba(dt, dt_line, pkt->p_flags|PF_GMT);	/* create delta table line for :Dt: keywd */
1487 	date_ba(&dt->d_dtime.dt_sec, Deltadate, 0);
1488 	date_bal(&dt->d_dtime.dt_sec, Deltadatel, 0);
1489 #else
1490 	date_ba(bdate, Deltadate, 0);
1491 	date_bal(bdate, Deltadatel, 0);
1492 #endif
1493 	Deltatime = &Deltadate[9];
1494 	Deltadate[8] = 0;
1495 	Deltadatel[10] = 0;
1496 	sid_ba(gsid, Sid);
1497 #if !(defined(BUG_1205145) || defined(GMT_TIME))
1498 	Dtime = localtime(bdate);
1499 #endif
1500 	if ((p = pkt->p_sflags[MODFLAG - 'a']) != NULL)
1501 		copy(p, Mod);
1502 	else sprintf(Mod, "%s", auxf(pkt->p_file, 'g'));
1503 	if ((Type = pkt->p_sflags[TYPEFLAG - 'a']) == NULL)
1504 		Type = Null;
1505 	if ((Qsect = pkt->p_sflags[QSECTFLAG - 'a']) == NULL)
1506 		Qsect = Null;
1507 }
1508 
1509 
1510 /*
1511  * This procedure places any MRs that are found in the delta table entry
1512  * into the temporary file created for that express purpose (/tmp/prXXXXXX).
1513 */
1514 
1515 static void
putmr(cp)1516 putmr(cp)
1517 register char	*cp;
1518 {
1519 
1520 	cp += 3;
1521 
1522 	if (!(*cp) || (*cp == '\n')) {
1523 		if (MRiop)
1524 			fclose(MRiop);
1525 		MRiop = NULL;
1526 		return;
1527 	}
1528 
1529 	if (fputs(cp, MRiop) == EOF) {
1530 		xmsg(mrtmp, NOGETTEXT("putmr"));
1531 	}
1532 }
1533 
1534 
1535 /*
1536  * This procedure places any SID extensions that are found in the delta table
1537  * entry * into the temporary file created for that express purpose
1538  * (/tmp/prXXXXXX).
1539  */
1540 static void
putsx(cp)1541 putsx(cp)
1542 register char	*cp;
1543 {
1544 
1545 	cp += 3;
1546 
1547 	if (!(*cp) || (*cp == '\n')) {
1548 		if (SXiop)
1549 			fclose(SXiop);
1550 		SXiop = NULL;
1551 		return;
1552 	}
1553 
1554 	if (fputs(cp, SXiop) == EOF) {
1555 		xmsg(sxtmp, NOGETTEXT("putsx"));
1556 	}
1557 }
1558 
1559 
1560 /*
1561  * This procedure is the same as "putmr" except it is used for the comment
1562  * section of the delta table entries.
1563 */
1564 
1565 static void
putcom(cp)1566 putcom(cp)
1567 register char	*cp;
1568 {
1569 
1570 	cp += 3;
1571 
1572 	if (fputs(cp, CMiop) == EOF) {
1573 		xmsg(cmtmp, NOGETTEXT("putcom"));
1574 	}
1575 }
1576 
1577 
1578 /*
1579  * This procedure reads through the SCCS file until a line is found
1580  * containing the character passed as an argument in the 2nd position
1581  * of the line.
1582 */
1583 
1584 static void
read_to(ch,pkt)1585 read_to(ch, pkt)
1586 register char	ch;
1587 register struct packet *pkt;
1588 {
1589 	register char *p;
1590 	while (((p = getline(pkt)) != NULL) &&
1591 			!(*p++ == CTLCHAR && *p == ch))
1592 		;
1593 }
1594 
1595 
1596 /*
1597  * This procedure prints a list of all the flags that are present in the
1598  * SCCS file.  The format is the same as 'prt' except the flag description
1599  * is not preceeded by a "tab".
1600 */
1601 
1602 static void
printflags()1603 printflags()
1604 {
1605 	register	char	*k;
1606 	register	char	**sflags = gpkt.p_sflags;
1607 
1608 	if (sflags[BRCHFLAG - 'a'])			/* check for 'branch' flag */
1609 		printf(gettext("branch\n"));
1610 	if ((k = (sflags[CEILFLAG - 'a'])) != NULL)	/* check for 'ceiling flag */
1611 		printf(gettext("ceiling\t%s\n"), k);
1612 	if ((k = (sflags[DEFTFLAG - 'a'])) != NULL)	/* check for 'default SID' flag */
1613 		printf(gettext("default SID\t%s\n"), k);
1614 	if ((k = (sflags[ENCODEFLAG - 'a'])) != NULL) {	/* check for 'encoded' flag */
1615 		int	i;
1616 
1617 		NONBLANK(k);
1618 		k = satoi(k, &i);
1619 		/*
1620 		 * The semantics of this flag are unusual: report its
1621 		 * existence only if an operand with value '1' is present.
1622 		 * Yes, the test below is sloppy...
1623 		 */
1624 		if (*k == '\0' && (i & EF_UUENCODE))
1625 			printf(gettext("encoded\n"));
1626 	}
1627 	if ((k = (sflags[FLORFLAG - 'a'])) != NULL)	/* check for 'floor' flag */
1628 		printf(gettext("floor\t%s\n"), k);
1629 	if (sflags[IDFLAG - 'a'])			/* check for 'id err/warn' flag */
1630 		printf(gettext("id keywd err/warn\n"));
1631 	if (sflags[JOINTFLAG - 'a'])			/* check for joint edit flag */
1632 		printf(gettext("joint edit\n"));
1633 	if ((k = (sflags[LOCKFLAG - 'a'])) != NULL)	/* check for 'lock' flag */
1634 		printf(gettext("locked releases\t%s\n"), k);
1635 	if ((k = (sflags[MODFLAG - 'a'])) != NULL)	/* check for 'module' flag */
1636 		printf(gettext("module\t%s\n"), k);
1637 	if (sflags[NULLFLAG - 'a'])			/* check for 'null delta' flag */
1638 		printf(gettext("null delta\n"));
1639 	if ((k = (sflags[QSECTFLAG - 'a'])) != NULL)	/* check for 'qsect' flag */
1640 		printf(gettext("csect name\t%s\n"), k);
1641 	if ((k = (sflags[TYPEFLAG - 'a'])) != NULL)	/* check for 'type' flag */
1642 		printf(gettext("type\t%s\n"), k);
1643 	if (sflags[VALFLAG - 'a']) {			/* check for 'MR valid' flag */
1644 		printf(gettext("validate MRs\t"));
1645 		/*
1646 		check for MR validating program
1647 		(optional)
1648 		*/
1649 		if ((k = (sflags[VALFLAG - 'a'])) != NULL)
1650 			printf("%s\n", k);
1651 		else putchar('\n');
1652 	}
1653 	if ((k = (sflags[SCANFLAG - 'a'])) != NULL)	/* check for 'keywd scan lines' flag */
1654 		printf(gettext("keywd scan lines\t%s\n"), k);
1655 	if ((k = (sflags[EXTENSFLAG - 'a'])) != NULL)	/* check for 'extensions' flag */
1656 		printf(gettext("extensions\t%s\n"), k);
1657 	if ((k = (sflags[EXPANDFLAG - 'a'])) != NULL)	/* check for 'expand keywds' flag */
1658 		printf(gettext("expand keywds\t%s\n"), k);
1659 }
1660 
1661 
1662 /*
1663  * This procedure checks the `dataspec' (if user defined) and determines
1664  * if any temporary files need be created for future keyword replacement
1665 */
1666 
1667 static void
ck_spec(p)1668 ck_spec(p)
1669 register char *p;
1670 {
1671 	if (sccs_index(p, NOGETTEXT(":C:")) != -1)	/* check for Comment keyword */
1672 		HAD_CM = 1;
1673 	if (sccs_index(p, NOGETTEXT(":MR:")) != -1)	/* check for MR keyword */
1674 		HAD_MR = 1;
1675 	if (sccs_index(p, NOGETTEXT(":SX:")) != -1)	/* check for SX keyword */
1676 		HAD_SX = 1;
1677 	if (sccs_index(p, NOGETTEXT(":UN:")) != -1)	/* check for User name keyword */
1678 		HAD_UN = 1;
1679 	if (sccs_index(p, NOGETTEXT(":FD:")) != -1)	/* check for descriptive text kyword */
1680 		HAD_FD = 1;
1681 	if (sccs_index(p, NOGETTEXT(":BD:")) != -1)	/* check for body keyword */
1682 		HAD_BD = 1;
1683 }
1684