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) 1984, 1986, 1987, 1988, 1989 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  * @(#)prt.c	1.47 20/08/23 J. Schilling
33  */
34 #if defined(sun)
35 #pragma ident "@(#)prt.c 1.47 20/08/23 J. Schilling"
36 #endif
37 /*
38  * @(#)prt.c 1.22 06/12/12
39  */
40 
41 /*
42  * Copyright (c) 1980 Regents of the University of California.
43  * All rights reserved. The Berkeley software License Agreement
44  * specifies the terms and conditions for redistribution.
45  */
46 
47 #if defined(sun)
48 #pragma ident	"@(#)ucbprt:prt.c"
49 #endif
50 
51 /*
52 	Program to print parts or all of an SCCS file.
53 	Arguments to the program may appear in any order
54 	and consist of keyletters, which begin with '-',
55 	and named files.
56 
57 	If a directory is given as an argument, each
58 	SCCS file within the directory is processed as if
59 	it had been specifically named. If a name of '-'
60 	is given, the standard input is read for a list
61 	of names of SCCS files to be processed.
62 	Non-SCCS files are ignored.
63 */
64 
65 
66 #define		SCCS_MAIN			/* define global vars */
67 #include	<defines.h>
68 #include	<version.h>
69 #include	<had.h>
70 #include	<i18n.h>
71 #include	<schily/sysexits.h>
72 
73 #define	NOEOF	0
74 #define	BLANK(p)	while (!(*p == '\0' || *p == ' ' || *p == '\t')) p++;
75 
76 static Nparms	N;			/* Keep -N parameters		*/
77 static Xparms	X;			/* Keep -X parameters		*/
78 static FILE *iptr;
79 static char *line = NULL;
80 static size_t line_size = 0;
81 static char statistics[25];
82 static struct delent {
83 	char type;
84 	char *osid;
85 	char *datetime;
86 	char *pgmr;
87 	char *serial;
88 	char *pred;
89 } del;
90 static int num_files;
91 static int prefix;
92 static time_t	cutoff;
93 static time_t	revcut;
94 static int linenum;
95 static char *ysid;
96 static char *flagdesc[26] = {
97 			NOGETTEXT(""),
98 			NOGETTEXT("branch"),
99 			NOGETTEXT("ceiling"),
100 			NOGETTEXT("default SID"),
101 			NOGETTEXT("encoded"),
102 			NOGETTEXT("floor"),
103 			NOGETTEXT(""),
104 			NOGETTEXT(""),
105 			NOGETTEXT("id keywd err/warn"),
106 			NOGETTEXT("joint edit"),
107 			NOGETTEXT(""),
108 			NOGETTEXT("locked releases"),
109 			NOGETTEXT("module"),
110 			NOGETTEXT("null delta"),
111 			NOGETTEXT(""),
112 			NOGETTEXT(""),
113 			NOGETTEXT("csect name"),
114 			NOGETTEXT(""),
115 			NOGETTEXT("keywd scan lines"),
116 			NOGETTEXT("type"),
117 			NOGETTEXT(""),
118 			NOGETTEXT("validate MRs"),
119 			NOGETTEXT(""),
120 			NOGETTEXT("extensions"),
121 			NOGETTEXT("expand keywds"),
122 			NOGETTEXT("")
123 };
124 
125 	int	main __PR((int argc, char **argv));
126 static void 	prt __PR((char *file));
127 static void	getdel __PR((register struct delent *delp, register char *lp));
128 static char	*read_to __PR((register int ch));
129 static char	*lineread __PR((register int eof));
130 static void	printdel __PR((register char *file, register struct delent *delp));
131 static void	printit __PR((register char *file, register char *str, register char *cp));
132 
133 int
main(argc,argv)134 main(argc, argv)
135 int argc;
136 char *argv[];
137 {
138 	register int j;
139 	register char *p;
140 	int  c;
141 	int testklt;
142 	extern int Fcnt;
143 	int current_optind;
144 	int no_arg;
145 
146 	/*
147 	 * Set locale for all categories.
148 	 */
149 	setlocale(LC_ALL, NOGETTEXT(""));
150 
151 	sccs_setinsbase(INS_BASE);
152 
153 	/*
154 	 * Set directory to search for general l10n SCCS messages.
155 	 */
156 #ifdef	PROTOTYPES
157 	(void) bindtextdomain(NOGETTEXT("SUNW_SPRO_SCCS"),
158 	    NOGETTEXT(INS_BASE "/" SCCS_BIN_PRE "lib/locale/"));
159 #else
160 	(void) bindtextdomain(NOGETTEXT("SUNW_SPRO_SCCS"),
161 	    NOGETTEXT("/usr/ccs/lib/locale/"));
162 #endif
163 
164 	(void) textdomain(NOGETTEXT("SUNW_SPRO_SCCS"));
165 
166 	tzset();	/* Set up timezome related vars */
167 
168 #ifdef	SCHILY_BUILD
169 	save_args(argc, argv);
170 #endif
171 	/*
172 	Set flags for 'fatal' to issue message, call clean-up
173 	routine, and terminate processing.
174 	*/
175 	Fflags = FTLMSG | FTLCLN | FTLEXIT;
176 #ifdef	SCCS_FATALHELP
177 	Fflags |= FTLFUNC;
178 	Ffunc = sccsfatalhelp;
179 #endif
180 
181 	testklt = 1;
182 
183 	/*
184 	The following loop processes keyletters and arguments.
185 	Note that these are processed only once for each
186 	invocation of 'main'.
187 	*/
188 
189 	current_optind = 1;
190 	optind = 1;
191 	opterr = 0;
192 	no_arg = 0;
193 	j = 1;
194 	/*CONSTCOND*/
195 	while (1) {
196 			if (current_optind < optind) {
197 			    if (optind > j+1) {
198 				if ((argv[j+1][0] != '-') && (no_arg == 0) &&
199 				    ((argv[j][0] == 'c' && argv[j+1][0] <= '9') ||
200 				    (argv[j][0] == 'r' && argv[j+1][0] <= '9') ||
201 				    (argv[j][0] == 'y' && argv[j+1][0] <= '9'))) {
202 					argv[j+1] = NULL;
203 				} else {
204 					optind = j+1;
205 					current_optind = optind;
206 				}
207 			    }
208 			    if (argv[j][0] == '-') {
209 				argv[j] = 0;
210 			    }
211 			    current_optind = optind;
212 			}
213 			no_arg = 0;
214 			j = current_optind;
215 			c = getopt(argc, argv, "()-r:c:y:esdaiuftbtN:X:V(version)");
216 
217 				/* this takes care of options given after
218 				** file names.
219 				*/
220 			if (c == EOF) {
221 			    if (optind < argc) {
222 				/* if it's due to -- then break; */
223 				if (argv[j][0] == '-' &&
224 				    argv[j][1] == '-') {
225 					argv[j] = 0;
226 					break;
227 				}
228 				optind++;
229 				current_optind = optind;
230 				continue;
231 			    } else {
232 				break;
233 			    }
234 			}
235 			p = optarg;
236 			switch (c) {
237 			case 'e':	/* print everything but body */
238 			case 's':	/* print only delta desc. and stats */
239 			case 'd':	/* print whole delta table */
240 			case 'a':	/* print all deltas */
241 			case 'i':	/* print inc, exc, and ignore info */
242 			case 'u':	/* print users allowed to do deltas */
243 			case 'f':	/* print flags */
244 			case 't':	/* print descriptive user-text */
245 			case 'b':	/* print body */
246 				break;
247 			case 'y':	/* delta cutoff */
248 				ysid = p;
249 				if (p[0] < '0' || p[0] > '9') {
250 					ysid = "";
251 				}
252 				prefix++;
253 				break;
254 
255 			case 'c':	/* time cutoff */
256 				if (HADR) {
257 				    fatal(gettext("both 'c' and 'r' keyletters specified (pr2)"));
258 				}
259 				if (p[0] ==  '-') {
260 				    /* no cutoff date given */
261 				    prefix++;
262 				    break;
263 				}
264 				if (p[0] > '9') {
265 				    prefix++;
266 				    break;
267 				}
268 				if (*p && parse_date(p, &cutoff, 0))
269 					fatal(gettext("bad date/time (cm5)"));
270 				prefix++;
271 				break;
272 
273 			case 'r':	/* reverse time cutoff */
274 				if (HADC) {
275 				    fatal(gettext("both 'c' and 'r' keyletters specified (pr2)"));
276 				}
277 				if (p[0] ==  '-') {
278 				    /* no cutoff date given */
279 				    prefix++;
280 				    break;
281 				}
282 				if (p[0] > '9') {
283 				    prefix++;
284 				    break;
285 				}
286 				if (*p && parse_date(p, &revcut, 0))
287 					fatal(gettext("bad date/time (cm5)"));
288 				prefix++;
289 				break;
290 
291 			case 'N':	/* Bulk names */
292 				initN(&N);
293 				if (optarg == argv[j+1]) {
294 				   no_arg = 1;
295 				   break;
296 				}
297 				N.n_parm = p;
298 				break;
299 
300 			case 'X':	/* -Xtended options */
301 				X.x_parm = optarg;
302 				X.x_flags = XO_NULLPATH;
303 				if (!parseX(&X))
304 					goto err;
305 				had[NLOWER+c-'A'] = 0;	/* Allow mult -X */
306 				break;
307 
308 			case 'V':		/* version */
309 				printf(gettext(
310 				    "prt %s-SCCS version %s %s (%s-%s-%s)\n"),
311 					PROVIDER,
312 					VERSION,
313 					VDATE,
314 					HOST_CPU, HOST_VENDOR, HOST_OS);
315 				exit(EX_OK);
316 
317 			default:
318 			err:
319 				fatal(gettext("Usage: prt [ -abdefistu ][ -c date-time ]\n\t[ -r date-time ][ -ySID ][ -N[bulk-spec]][ -Xxopts ] s.filename..."));
320 			}
321 
322 			/*
323 			 * Make sure that we only collect option letters from
324 			 * the range 'a'..'z' and 'A'..'Z'.
325 			 */
326 			if (ALPHA(c) &&
327 			    (had[LOWER(c)? c-'a' : NLOWER+c-'A']++ && testklt++)) {
328 				if (c != 'X')
329 					fatal(gettext("key letter twice (cm2)"));
330 			}
331 #if 0
332 			if (((c == 'c') || (c == 'r')||(c == 'y')) && (p[0] > '9')) {
333 			    argv[j] = NULL;
334 			    break;
335 			}
336 #endif
337 	}
338 
339 	for (j = 1; j < argc; j++) {
340 		if (argv[j]) {
341 			num_files++;
342 		}
343 	}
344 
345 	if (num_files == 0)
346 		fatal(gettext("missing file arg (cm3)"));
347 
348 	if (HADC && HADR)
349 		fatal(gettext("both 'c' and 'r' keyletters specified (pr2)"));
350 
351 	setsig();
352 	xsethome(NULL);
353 	if (HADUCN) {					/* Parse -N args  */
354 		parseN(&N);
355 
356 		if (N.n_sdot && (sethomestat & SETHOME_OFFTREE))
357 			fatal(gettext("-Ns. not supported in off-tree project mode"));
358 	}
359 
360 	/*
361 	Change flags for 'fatal' so that it will return to this
362 	routine (main) instead of terminating processing.
363 	*/
364 	Fflags &= ~FTLEXIT;
365 	Fflags |= FTLJMP;
366 
367 	/*
368 	Call 'prt' routine for each file argument.
369 	*/
370 	for (j = 1; j < argc; j++)
371 		if ((p = argv[j]) != NULL)
372 			do_file(p, prt, 1, N.n_sdot, &X);
373 
374 	return (Fcnt ? 1 : 0);
375 }
376 
377 
378 /*
379 	Routine that actually performs the 'prt' functions.
380 */
381 
382 static void
prt(file)383 prt(file)
384 char *file;
385 {
386 	int stopdel;
387 	int user, flag, text;
388 	char *p;
389 	time_t	bindate;
390 #if defined(BUG_1205145) || defined(GMT_TIME)
391 	time_t	tim;
392 	struct tm *tmp;
393 #endif	/* defined(BUG_1205145) || defined(GMT_TIME) */
394 
395 	if (setjmp(Fjmp))	/* set up to return here from 'fatal' */
396 		return;		/* and return to caller of prt */
397 	if (HADUCN) {
398 #ifdef	__needed__
399 		char	*ofile = file;
400 #endif
401 
402 		file = bulkprepare(&N, file);
403 		if (file == NULL) {
404 #ifdef	__needed__
405 			if (N.n_ifile)
406 				ofile = N.n_ifile;
407 #endif
408 			/*
409 			 * The error is typically
410 			 * "directory specified as s-file (cm14)"
411 			 */
412 			fatal(gettext(bulkerror(&N)));
413 		}
414 	}
415 
416 	if (HADE)
417 		HADD = HADI = HADU = HADF = HADT = 1;
418 
419 	if (!HADU && !HADF && !HADT && !HADB)
420 		HADD = 1;
421 
422 	if (!HADD)
423 		HADR = HADS = HADA = HADI = HADY = HADC = 0;
424 
425 	if (HADS && HADI)
426 		fatal(gettext("s and i conflict (pr1)"));
427 
428 	iptr = xfopen(file, O_RDONLY|O_BINARY);
429 	linenum = 0;
430 
431 	p = lineread(NOEOF);
432 	if (*p++ != CTLCHAR || *p != HEAD)
433 		fatal(gettext("not an sccs file (co2)"));
434 
435 	stopdel = 0;
436 
437 	if (!prefix) {
438 		printf("\n%s:\n", file);
439 	}
440 	if (HADD) {
441 		while (((p = lineread(NOEOF)) != NULL) && *p++ == CTLCHAR &&
442 				*p++ == STATS && !stopdel) {
443 			NONBLANK(p);
444 			copy(p, statistics);
445 
446 			p = lineread(NOEOF);
447 			getdel(&del, p);
448 
449 #if defined(BUG_1205145) || defined(GMT_TIME)
450 			date_ab(del.datetime, &bindate, 0);
451 			/*
452 			Local time corrections before date_ba() call.
453 			Because this function uses gmtime() instead of localtime().
454 			*/
455 			tmp = localtime(&bindate);
456 			tim = mklgmtime(tmp);
457 			/*
458 			 * Avoid to use more space as expectecd in del.datetime
459 			 */
460 #if SIZEOF_TIME_T == 4
461 			if (tim < Y1969)
462 #else
463 			if ((tim < Y1969) ||
464 			    (tim >= Y2069))
465 #endif
466 				date_bal(&tim, del.datetime, 0); /* 4 digit year */
467 			else
468 				date_ba(&tim, del.datetime, 0);	/* 2 digit year */
469 
470 #endif	/* defined(BUG_1205145) || defined(GMT_TIME) */
471 
472 			if (!HADA && (del.type != 'D' && del.type != 'U')) {
473 				(void) read_to(EDELTAB);
474 				continue;
475 			}
476 			if (HADC) {
477 #if !(defined(BUG_1205145) || defined(GMT_TIME))
478 				date_ab(del.datetime, &bindate, 0);
479 #endif
480 				if (bindate < cutoff) {
481 					stopdel = 1;
482 					break;
483 				}
484 			}
485 			if (HADR) {
486 #if !(defined(BUG_1205145) || defined(GMT_TIME))
487 				date_ab(del.datetime, &bindate, 0);
488 #endif
489 				if (bindate >= revcut) {
490 					(void) read_to(EDELTAB);
491 					continue;
492 				}
493 			}
494 			if (HADY && (equal(del.osid, ysid) || !(*ysid)))
495 				stopdel = 1;
496 
497 			printdel(file, &del);
498 
499 			while (((p = lineread(NOEOF)) != NULL) && *p++ == CTLCHAR) {
500 				if (*p == EDELTAB)
501 					break;
502 				switch (*p) {
503 				case INCLUDE:
504 					if (HADI)
505 						printit(file, gettext("Included:\t"), p);
506 					break;
507 
508 				case EXCLUDE:
509 					if (HADI)
510 						printit(file, gettext("Excluded:\t"), p);
511 					break;
512 
513 				case IGNORE:
514 					if (HADI)
515 						printit(file, gettext("Ignored:\t"), p);
516 					break;
517 
518 				case MRNUM:
519 					if (!HADS)
520 						printit(file, gettext("MRs:\t"), p);
521 					break;
522 
523 				case SIDEXTENS:
524 					if (!HADS)
525 						printit(file, gettext("SIDext:\t"), p);
526 					break;
527 
528 				case COMMENTS:
529 					if (!HADS)
530 						printit(file, "", p);
531 					break;
532 
533 				default:
534 					sprintf(SccsError, gettext("format error at line %d (co4)"), linenum);
535 					fatal(SccsError);
536 				}
537 			}
538 		}
539 		if (prefix)
540 			printf("\n");
541 
542 		if (stopdel && !(line[0] == CTLCHAR && line[1] == BUSERNAM))
543 			(void) read_to(BUSERNAM);
544 	}
545 	else
546 		(void) read_to(BUSERNAM);
547 
548 	if (HADU) {
549 		user = 0;
550 		printf(gettext("\n Users allowed to make deltas -- \n"));
551 		while (((p = lineread(NOEOF)) != NULL) && *p != CTLCHAR) {
552 			user = 1;
553 			printf("\t%s", p);
554 		}
555 		if (!user)
556 			printf(gettext("\teveryone\n"));
557 	}
558 	else
559 		(void) read_to(EUSERNAM);
560 
561 	if (HADF) {
562 		flag = 0;
563 		printf("\n%s\n", "Flags --");
564 		while (((p = lineread(NOEOF)) != NULL) && *p++ == CTLCHAR &&
565 				*p++ == FLAG) {
566 			flag = 1;
567 			NONBLANK(p);
568 			/*
569 			 * The 'e' flag (file in encoded form) requires
570 			 * special treatment, as some versions of admin
571 			 * force the flag to be present (with operand 0)
572 			 * even when the user didn't explicitly specify
573 			 * it.  Stated differently, this flag has somewhat
574 			 * different semantics than the other binary-
575 			 * valued flags.
576 			 */
577 			if (*p == ENCODEFLAG) {
578 				/*
579 				 * Look for operand value; print description
580 				 * only if the operand value exists and is '1'.
581 				 */
582 				if (*++p) {
583 					int	i;
584 
585 					NONBLANK(p);
586 					p = satoi(p, &i);
587 					if (*p == '\n' && (i & EF_UUENCODE))
588 						printf("\t%s\n",
589 						    flagdesc[ENCODEFLAG - 'a']);
590 				}
591 			} else if (*p - 'a' < 0 || *p - 'a' >= NFLAGS) {
592 				printf(gettext("\tUnknown flag '%c'\t"), *p);
593 				if (*++p) {
594 					NONBLANK(p);
595 					printf("\t%s", p);
596 				}
597 			} else {
598 				/*
599 				 * Standard flag: print description and
600 				 * operand value if present.
601 				 * The newline is included in the operand.
602 				 */
603 				printf("\t%s", flagdesc[*p - 'a']);
604 
605 				if (*++p) {
606 					NONBLANK(p);
607 					printf("\t%s", p);
608 				}
609 			}
610 		}
611 		if (!flag)
612 			printf(gettext("\tnone\n"));
613 	}
614 	else
615 		(void) read_to(BUSERTXT);
616 
617 	if (HADT) {
618 		text = 0;
619 		printf(gettext("\nDescription --\n"));
620 		while (((p = lineread(NOEOF)) != NULL) && *p != CTLCHAR) {
621 			text = 1;
622 			printf("\t%s", p);
623 		}
624 		if (!text)
625 			printf(gettext("\tnone\n"));
626 	}
627 	else
628 		(void) read_to(EUSERTXT);
629 
630 	if (HADB) {
631 		printf("\n");
632 		while ((p = lineread(EOF)) != NULL)
633 			if (*p == CTLCHAR)
634 				printf("*** %s", ++p);
635 			else
636 				printf("\t%s", p);
637 	}
638 
639 	fclose(iptr);
640 }
641 
642 
643 static void
getdel(delp,lp)644 getdel(delp, lp)
645 register struct delent *delp;
646 register char *lp;
647 {
648 	lp += 2;
649 	NONBLANK(lp);
650 	delp->type = *lp++;
651 	NONBLANK(lp);
652 	delp->osid = lp;
653 	BLANK(lp);
654 	*lp++ = '\0';
655 	NONBLANK(lp);
656 	delp->datetime = lp;
657 	BLANK(lp);
658 	NONBLANK(lp);
659 	BLANK(lp);
660 	*lp++ = '\0';
661 	NONBLANK(lp);
662 	delp->pgmr = lp;
663 	BLANK(lp);
664 	*lp++ = '\0';
665 	NONBLANK(lp);
666 	delp->serial = lp;
667 	BLANK(lp);
668 	*lp++ = '\0';
669 	NONBLANK(lp);
670 	delp->pred = lp;
671 	repl(lp, '\n', '\0');
672 }
673 
674 
675 static char *
read_to(ch)676 read_to(ch)
677 register char ch;
678 {
679 	char *n;
680 
681 	while (((n = lineread(NOEOF)) != NULL) &&
682 			!(*n++ == CTLCHAR && *n == ch))
683 		continue;
684 
685 	return (n);
686 }
687 
688 
689 static char *
lineread(eof)690 lineread(eof)
691 register int eof;
692 {
693 	char	buf[512];
694 	size_t	nread, used = 0;
695 
696 	do {
697 		if (fgets(buf, sizeof (buf), iptr) == NULL) {
698 			if (!eof) {
699 				fatal(gettext("premature eof (co5)"));
700 			}
701 			return ((used) ? line : NULL);
702 		}
703 
704 		nread = strlen(buf);
705 
706 		if ((used + nread) >= line_size) {
707 			line_size += sizeof (buf);
708 			line = (char *) realloc(line, line_size);
709 			if (line == NULL) {
710 				efatal(gettext("OUT OF SPACE (ut9)"));
711 			}
712 		}
713 
714 		strcpy(line + used, buf);
715 		used += nread;
716 	} while (line[used - 1] != '\n');
717 
718 	linenum++;
719 
720 	return (line);
721 }
722 
723 
724 static void
printdel(file,delp)725 printdel(file, delp)
726 register char *file;
727 register struct delent *delp;
728 {
729 	printf("\n");
730 
731 	if (prefix) {
732 		statistics[length(statistics) - 1] = '\0';
733 		printf("%s:\t", file);
734 	}
735 
736 	printf("%c %s\t%s %s\t%s %s\t%s", delp->type, delp->osid,
737 		delp->datetime, delp->pgmr, delp->serial, delp->pred, statistics);
738 }
739 
740 
741 /*ARGSUSED*/
742 static void
printit(file,str,cp)743 printit(file, str, cp)
744 register char *file;
745 register char *str, *cp;
746 {
747 	cp++;
748 	NONBLANK(cp);
749 
750 	if (prefix) {
751 		cp[length(cp) - 1] = '\0';
752 		printf(" ");
753 	}
754 
755 	printf("%s%s", str, cp);
756 }
757