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