xref: /original-bsd/local/local.cmd/showtc.c (revision f1fb777f)
1 #ifndef lint
2 static char *sccsid="@(#)showtc.c	1.9	(Berkeley) 11/01/85";
3 #endif
4 
5 /*
6 ** show termcap entries
7 **
8 ** where:
9 **	-D	look for duplicate names and print termcap file
10 **	-S	sort entries before display
11 **	-T	trace (-DDEBUG only)
12 **	-U	print unknown capabilities
13 **	-b	show bare entries
14 **	-d	-D and stop
15 **	-f	following arg is FULL PATHNAME of termcap file
16 **	-g	sort on generic names
17 **	-s	don't print two char name at the front of every line
18 **	-x	expand tc= capabilities
19 **	[ent]	display specific entry. tc= will be expanded.
20 **
21 ** David L. Wasley, U.C.Berkeley
22 ** Kevin Layer: modified for 4.1c and misc changes.
23 ** Kevin Layer: added the printing of terminal capabilities
24 **	in `human' readable form (like that in "man 5 termcap").
25 */
26 
27 #include <stdio.h>
28 #include <sys/file.h>
29 #include <ctype.h>
30 #include <sys/param.h>
31 #include <sys/stat.h>
32 
33 #define NO		0
34 #define YES		1
35 #define CNULL		'\0'
36 #define NOENTRIES	1024
37 #define USAGE		"usage: %s [-Sxdngb] [-f termcapfile] [entry] ...\n"
38 
39 #ifndef	MAXPATHLEN
40 #define MAXPATHLEN	1024
41 #endif
42 
43 struct TcName {
44 	char	name_buf[124];
45 	long	file_pos;
46 } tcNames[NOENTRIES];
47 
48 struct Caps {
49 	char	*cap;
50 	char	*desc;
51 } capList[] =
52 {
53 	"AL",	"Add N new blank lines",
54 	"CC",	"Command char in prototype if settable",
55 	"DC",	"Delete N characters",
56 	"DL",	"Delete N lines",
57 	"DO",	"Move cursor down N lines",
58 	"IC",	"Insert N blank characters",
59 	"LE",	"Move cursor left N positions",
60 	"RI",	"Move cursor right N positions",
61 	"UP",	"Move cursor up N lines",
62 	"ae",	"End alternate character set",
63 	"al",	"Add new blank line",
64 	"am",	"Has automatic margins",
65 	"as",	"Start alternate character set",
66 	"bc",	"Backspace if not ^H",
67 	"bl",	"Audible Bell (default ^G)",
68 	"bs",	"Can backspace with ^H",
69 	"bt",	"Back tab",
70 	"bw",	"Backspace wraps from col 0 to last col",
71 	"cd",	"Clear to end of display",
72 	"ce",	"Clear to end of line",
73 	"ch",	"Like cm, but horizontal motion only",
74 	"cl",	"Clear screen",
75 	"cm",	"Cursor motion",
76 	"co",	"Number of columns in a line",
77 	"cr",	"Carriage return (default ^M)",
78 	"cs",	"Change scrolling region (vt100), like cm",
79 	"ct",	"Clear all tab stops",
80 	"cv",	"Like ch but vertical only.",
81 	"dB",	"Number of millisec of bs delay needed",
82 	"dC",	"Number of millisec of cr delay needed",
83 	"dF",	"Number of millisec of ff delay needed",
84 	"dN",	"Number of millisec of nl delay needed",
85 	"dT",	"Number of millisec of tab delay needed",
86 	"da",	"Display may be retained above",
87 	"db",	"Display may be retained below",
88 	"dc",	"Delete character",
89 	"dl",	"Delete line",
90 	"dm",	"Start Delete mode",
91 	"do",	"Down one line",
92 	"ds",	"Disable status display",
93 	"ed",	"End delete mode",
94 	"ei",	"End insert mode;give \":ei=:\" if ic",
95 	"eo",	"Can erase overstrikes with a blank",
96 	"es",	"Escape seq's ok on status line",
97 	"ff",	"Hardcopy page eject (default ^L)",
98 	"fs",	"From status line sequence",
99 	"hc",	"Hardcopy terminal",
100 	"hd",	"Half-line down (forward 1/2 lf)",
101 	"ho",	"Home cursor (if no cm)",
102 	"hs",	"Has status line",
103 	"hu",	"Half-line up (reverse 1/2 lf)",
104 	"hz",	"Hazeltine; can't print ~'s",
105 	"i2",	"Initialization string (used by sysline(1))",
106 	"ic",	"Insert character",
107 	"if",	"Name of file containing is",
108 	"im",	"Start insert mode;give \":im=:\" if ic",
109 	"in",	"Insert mode distinguishes nulls on display",
110 	"ip",	"Insert pad after character inserted",
111 	"is",	"Initialization string",
112 	"k0",	"Sent by function key 0",
113 	"k1",	"Sent by function key 1",
114 	"k2",	"Sent by function key 2",
115 	"k3",	"Sent by function key 3",
116 	"k4",	"Sent by function key 4",
117 	"k5",	"Sent by function key 5",
118 	"k6",	"Sent by function key 6",
119 	"k7",	"Sent by function key 7",
120 	"k8",	"Sent by function key 8",
121 	"k9",	"Sent by function key 9",
122 	"kb",	"Sent by backspace key",
123 	"kd",	"Sent by down arrow key",
124 	"ke",	"Out of \"keypad transmit\" mode",
125 	"kh",	"Sent by home key",
126 	"kl",	"Sent by left arrow key",
127 	"km",	"Has a \"meta\" key (shift, sets parity bit)",
128 	"kn",	"Number of \"other\" keys",
129 	"ko",	"Tc entries for other non-function keys",
130 	"kr",	"Sent by right arrow key",
131 	"ks",	"Put in \"keypad transmit\" mode",
132 	"ku",	"Sent by up arrow key",
133 	"l0",	"Label on function key 0 (if not \"0\")",
134 	"l1",	"Label on function key 1 (if not \"1\")",
135 	"l2",	"Label on function key 2 (if not \"2\")",
136 	"l3",	"Label on function key 3 (if not \"3\")",
137 	"l4",	"Label on function key 4 (if not \"4\")",
138 	"l5",	"Label on function key 5 (if not \"5\")",
139 	"l6",	"Label on function key 6 (if not \"6\")",
140 	"l7",	"Label on function key 7 (if not \"7\")",
141 	"l8",	"Label on function key 8 (if not \"8\")",
142 	"l9",	"Label on function key 9 (if not \"9\")",
143 	"le",	"Move left",
144 	"li",	"Number of lines on screen or page",
145 	"ll",	"Last line, first column (if no cm)",
146 	"ma",	"Arrow key map, used by vi V2 only",
147 	"mb",	"Enter blinking mode",
148 	"md",	"Enter bold mode",
149 	"me",	"Reset video attributes",
150 	"mh",	"Enter halfbright mode",
151 	"mi",	"Safe to move while in insert mode",
152 	"mk",	"Enter protected mode",
153 	"ml",	"Memory lock on above cursor.",
154 	"mp",	"Turn on protected attribute",
155 	"mr",	"Enter reverse video mode",
156 	"ms",	"Ok to move while in standout/underline mode",
157 	"mu",	"Memory unlock (turn off memory lock).",
158 	"nc",	"No working CR (DM2500,H2000)",
159 	"nd",	"Non-destructive space (cursor right)",
160 	"nl",	"Newline character (default \\n)",
161 	"ns",	"Is a CRT but doesn't scroll.",
162 	"os",	"Terminal overstrikes",
163 	"pb",	"Lowest baud where delays are required",
164 	"pc",	"Pad character (rather than null)",
165 	"pl",	"Program function key N to execute string S (terminfo only)",
166 	"pt",	"Has hardware tabs (may need to use is)",
167 	"rc",	"Restore cursor to position of last sc",
168 	"rf",	"Name of file containing reset codes",
169 	"rs",	"Reset terminal completely to sane modes",
170 	"sc",	"Save cursor position",
171 	"se",	"End stand out mode",
172 	"sf",	"Scroll forwards",
173 	"sg",	"Number of blank chars left by so/se",
174 	"so",	"Begin stand out mode",
175 	"sr",	"Scroll reverse (backwards)",
176 	"st",	"Set a tab in all rows, current column",
177 	"ta",	"Tab (other than ^I or with padding)",
178 	"tc",	"Entry of similar terminal - must be last",
179 	"te",	"String to end programs that use cm",
180 	"ti",	"String to begin programs that use cm",
181 	"ts",	"To status line sequence",
182 	"uc",	"Underscore one char and move past it",
183 	"ue",	"End underscore mode",
184 	"ug",	"Number of blank chars left by us or ue",
185 	"ul",	"Underlines, though no overstrike",
186 	"up",	"Upline (cursor up)",
187 	"us",	"Start underscore mode",
188 	"vb",	"Visible bell (may not move cursor)",
189 	"ve",	"Sequence to end open/visual mode",
190 	"vs",	"Sequence to start open/visual mode",
191 	"vt",	"Virtual terminal number (not supported on all systems)",
192 	"xb",	"Beehive (f1=escape, f2=ctrl C)",
193 	"xn",	"A newline is ignored after a wrap (Concept)",
194 	"xr",	"Return acts like ce \\r \\n (Delta Data)",
195 	"xs",	"Standout not erased by writing over it (HP 264?)",
196 	"xt",	"Destructive tabs, magic so char (Teleray 1061)"
197 };
198 
199 #define NOCAPS	(sizeof capList / sizeof *capList)
200 
201 #ifdef DEBUG
202 int		Dflag = NO;
203 #endif
204 int		xflag = NO;
205 int		Sflag = YES;
206 int		sflag = NO;
207 int		dflag = NO;
208 int		nflag = NO;
209 int		gflag = NO;
210 int		bflag = NO;
211 int		Uflag = NO;
212 int		tc_loopc;		/* loop counter */
213 char		*tcfile;		/* termcap database pathname */
214 char		cwd[MAXPATHLEN];	/* current working directory */
215 char		tcbuf[2048];		/* buffer for termcap description */
216 char		*lastchar();
217 int		name_cmp();
218 int		ent_cmp();
219 struct TcName	*find_name();
220 char		*getenv();
221 char		*ctime();
222 char		*strncpy();
223 long		ftell();
224 
main(argc,argv,envp)225 main(argc, argv, envp)
226 	int		argc;
227 	char		**argv;
228 	char		**envp;
229 {
230 	char		*av;
231 	struct TcName	*tn;
232 	register char	*bp;
233 	long		pos;
234 	int		n;
235 	struct stat	st;
236 	char		envbuf[256];
237 	FILE		*tcfp;
238 
239 	if ((bp = getenv("TERMCAP")) && *bp == '/')
240 		tcfile = bp;
241 	else
242 		tcfile = "/etc/termcap";
243 
244 	while (--argc > 0)
245 	{
246 		if (*(av = *++argv) == '-')
247 		{
248 			while (*++av)
249 			{
250 				switch (*av)
251 				{
252 				/* use alternate termcap file */
253 				case 'f':
254 					if (argc-- <= 0)
255 					{
256 						fprintf(stderr,
257 						    "-f needs a filename\n");
258 						exit(1);
259 					}
260 					tcfile = *++argv;
261 					break;
262 
263 				/* only check for dup names */
264 				case 'd':
265 					nflag = YES;
266 					/* fall thru */
267 
268 				/* look for duplicated names */
269 				case 'D':
270 					dflag = YES;
271 					continue;
272 
273 				case 'U':
274 					Uflag = YES;
275 					continue;
276 
277 				/* strip the two name off */
278 				case 's':
279 					sflag = YES;
280 					continue;
281 
282 				/* sort the name array */
283 				case 'S':
284 					Sflag = NO;
285 					continue;
286 
287 #ifdef DEBUG
288 				case 'T':
289 					Dflag = YES;
290 					continue;
291 #endif
292 
293 				/* sort on generic names */
294 				case 'g':
295 					gflag = YES;
296 					continue;
297 
298 				/* expand entries in 'full mode' */
299 				case 'x':
300 					xflag = YES;
301 					continue;
302 
303 				/* show bare entry */
304 				case 'b':
305 					bflag = YES;
306 					continue;
307 
308 				default:
309 					fprintf(stderr, "showtc: unknown flag: -%c\n", *av);
310 					fprintf(stderr, USAGE, argv[0]);
311 					exit(1);
312 				}
313 			}
314 		}
315 		else
316 			break;
317 	}
318 
319 	/*
320 	 * insert the specified TERMCAP file into the environment
321 	 */
322 	if (*tcfile != '/') {
323 		char	*getwd();
324 
325 		if (getwd(cwd) == NULL) {
326 			fprintf(stderr, "showtc: %s\n", cwd);
327 			exit(1);
328 		} else if (strlen(cwd) + strlen(tcfile) + 2 > sizeof cwd) {
329 			fprintf(stderr, "showtc: %s\n",
330 				"Current working directory name too long");
331 			exit(1);
332 		} else {
333 			if (cwd[strlen(cwd) - 1] != '/')
334 				strcat(cwd, "/");
335 			strcat(cwd, tcfile);
336 			tcfile = cwd;
337 		}
338 	}
339 	(void) sprintf(envbuf, "TERMCAP=%s", tcfile);
340 	while (*envp)
341 	{
342 		if (strncmp(*envp, "TERMCAP=", 8) == 0)
343 		{
344 			*envp = envbuf;
345 			break;
346 		}
347 		envp++;
348 	}
349 	if (! *envp)
350 		*envp = envbuf;	/* this may be dangerous */
351 
352 	/*
353 	** if user specified type(s), do only those
354 	*/
355 	if (argc > 0)
356 	{
357 		/*
358 		** look for the users specified term types
359 		*/
360 		while (argc > 0)
361 		{
362 			switch (n = tgetent(tcbuf, *argv))
363 			{
364 				case 1:
365 					if (bflag)
366 						(void) prnt_raw(tcbuf);
367 					else
368 						(void) prnt_ent(tcbuf);
369 					break;
370 
371 				case 0:
372 					fprintf(stderr,
373 					   "showtc: bad entry: %s\n", *argv);
374 					break;
375 
376 				case -1:
377 					fputs("showtc: ", stderr);
378 					perror(tcfile);
379 					exit(1);
380 
381 				default:
382 					fprintf(stderr, "bad return from tgetent: %d\n", n);
383 					exit(1);
384 			}
385 			argc--;
386 			argv++;
387 		}
388 		exit(0);
389 	}
390 
391 	if (bflag)
392 	{
393  		fprintf(stderr, "showtc: -b flag with no entries makes no sense.\n");
394 		exit(1);
395 	}
396 
397 
398 	/*
399 	** if no type was specified, do the whole file
400 	*/
401 	if ((tcfp = fopen(tcfile, "r")) == NULL)
402 	{
403 		perror(tcfile);
404 		exit(1);
405 	}
406 
407 	/*
408 	** identify database, for the record
409 	*/
410 	if (stat(tcfile, &st))
411 	{
412 		perror(tcfile);
413 		exit(1);
414 	}
415 	printf("File: %s, last modified: %s\n", tcfile, ctime(&st.st_mtime));
416 
417 
418 	/*
419 	** build termcap entry table
420 	*/
421 	tn = tcNames;
422 	pos = ftell(tcfp);
423 	bp = tcbuf;
424 	while (fgets(bp, sizeof (tcbuf), tcfp) != NULL)
425 	{
426 		if (tcbuf[0] == '#')
427 		{
428 			pos = ftell(tcfp);
429 			bp = tcbuf;
430 			continue;
431 		}
432 
433 		tn->file_pos = pos;
434 
435 		/*
436 		** get full entry
437 		*/
438 		while (*(bp = lastchar(bp)) == '\\' && fgets(bp, (sizeof tcbuf) - (bp - tcbuf), tcfp))
439 			;
440 		/*
441 		** save the names
442 		*/
443 		for (bp = tcbuf; *bp && *bp != ':'; bp++)
444 			;
445 		*bp = '\0';
446 		(void) strncpy(tn->name_buf, tcbuf,
447 				sizeof tcNames[0].name_buf);
448 
449 		pos = ftell(tcfp);
450 		bp = tcbuf;
451 		tn++;
452 	}
453 	tn->file_pos = -1;
454 
455 	/*
456 	** Look for duplicate names
457 	*/
458 	if (dflag)
459 		check_dup();
460 	if (nflag)
461 		exit(0);
462 
463 #ifdef DEBUG
464 	if (Dflag)
465 	{
466 		for (tn = tcNames; tn->file_pos >= 0; tn++)
467 		{
468 			printf("Entry #%d:\n\t%s\n\tfile_pos = %ld\n",
469 			tn - tcNames, tn->name_buf, tn->file_pos);
470 		}
471 		exit(0);
472 	}
473 #endif
474 
475 	/*
476 	** Order the list
477 	*/
478 	if (Sflag)
479 		qsort((char *)tcNames, tn - tcNames,
480 			sizeof (struct TcName), name_cmp);
481 
482 	/*
483 	** List termcap entry for each name in table
484 	*/
485 	for (tn = tcNames; tn->file_pos >= 0; tn++)
486 	{
487 		tc_loopc = 0;
488 		/*** working toward this ...
489 		(void) prnt_ent(tn);
490 		***/
491 		(void) fseek(tcfp, tn->file_pos, 0);
492 		bp = tcbuf;
493 		while (fgets(bp, (sizeof tcbuf) - (bp - tcbuf), tcfp)
494 			&& *(bp = lastchar(bp)) == '\\')
495 			;
496 		(void) prnt_ent(tcbuf);
497 	}
498 }
499 
500 char *
lastchar(b)501 lastchar(b)
502 	char	*b;
503 {
504 	register char	*p;
505 
506 	p = b + strlen(b) - 1;
507 	while (*p == '\n' || *p == ' ')
508 		p--;
509 	return(p);
510 }
511 
name_cmp(a,b)512 name_cmp(a, b)
513 	char	*a, *b;
514 {
515 	if (gflag)	/* sort on generic names */
516 	{
517 		a += 3;
518 		b += 3;
519 		while (*a && *b && *a != '|' && *a == *b)
520 		{
521 			a++;
522 			b++;
523 		}
524 		if (*a == '|' || *a == CNULL)
525 			return((*b == '|' || *b == CNULL)? 0:-1);
526 		if (*b == '|' || *b == CNULL)
527 			return(1);
528 		return(*a - *b);
529 	}
530 	return(strncmp(a, b, 2));
531 }
532 
prnt_ent(buf)533 prnt_ent(buf)
534 	register char	*buf;
535 {
536 	register char	*name;
537 	char		*getdesc();
538 	char		*caps[256];
539 	register char	**cp;
540 	register char	**tp;
541 	char		tname[3];
542 
543 	cp = caps;
544 	name = buf;
545 	tname[3] = '\0';
546 
547 	while (*buf)
548 	{
549 		switch (*buf)
550 		{
551 		case ':':
552 			*buf++ = '\0';
553 			while (*buf && !isalnum(*buf))
554 				buf++;
555 			if (*buf)
556 			{
557 				/*
558 				 * ignore duplicate cap entries
559 				 */
560 				for (tp = caps; tp < cp; tp++)
561 					if (strncmp(buf, *tp, 2) == 0)
562 						goto skip;
563 				*cp++ = buf;
564 			skip:
565 				/*
566 				 * does user want tc= expanded?
567 				 */
568 				if (xflag && strncmp(buf, "tc=", 3) == 0)
569 				{
570 					/*
571 					 * find end of tc=
572 					 */
573 					while (*buf != ':')
574 						buf++;
575 					*buf = '\0';
576 					/*
577 					 * save term name
578 					 */
579 					tname[0] = name[0];
580 					tname[1] = name[1];
581 					printf("%s: expanding %s\n",
582 						tname, cp[-1]);
583 					/*
584 					 * let tgetent do the work
585 					 */
586 					tgetent(tcbuf, tname);
587 					prnt_ent(tcbuf);
588 					return;
589 				}
590 			}
591 			continue;
592 
593 		case '|':
594 			*buf++ = ',';
595 			continue;
596 
597 		default:
598 			buf++;
599 		}
600 	}
601 	*cp = CNULL;		/* was (char *)0 */
602 
603 	if (Sflag)
604 		qsort((char *) caps, cp - caps, sizeof (char *), ent_cmp);
605 
606 	printf("%s\n", name);
607 	for (cp = caps; *cp; cp++)
608 		if (Uflag) {
609 			if (unknowncap(*cp)) {
610 				printf("%3.3s\n", *cp);
611 			}
612 		} else if (sflag) {
613 			printf("%-45s %s\n", getdesc(*cp), *cp);
614 		} else {
615 			printf("%2.2s  %-45s %s\n", name, getdesc(*cp), *cp);
616 		}
617 	(void) putchar('\n');
618 }
619 
prnt_raw(buf)620 prnt_raw(buf)
621 	char		*buf;
622 {
623 	register char	*b;
624 	register int	len;
625 	register int	n;
626 	char		*index();
627 
628 	len = 0;
629 	b = buf;
630 	while (*b)
631 	{
632 		if ((n = index(b, ':') - b + 1) <= 0)
633 			n = strlen(b);
634 		if (len == 0)			/* first part */
635 		{
636 			printf("%.*s\\\n\t:", n, b);
637 			len = 9 - n;
638 		}
639 		else
640 		{
641 			if ((len + n) >= 75)
642 			{
643 				printf("\\\n\t:");
644 				len = 9;
645 			}
646 			printf("%.*s", n, b);
647 		}
648 		len += n;
649 		b += n;
650 		while (*b && index(" \t:\n", *b))
651 			b++;
652 	}
653 	if (b[-1] != ':')
654 		(void) putchar(':');
655 	(void) putchar('\n');
656 }
657 
ent_cmp(a,b)658 ent_cmp(a, b)
659 	char **a, **b;
660 {
661 	return(strncmp(*a, *b, 2));
662 }
663 
check_dup()664 check_dup()
665 {
666 	/*
667 	** Look for duplicated names
668 	*/
669 	register char		*p;
670 	register char		*q;
671 	register struct TcName	*tn;
672 	register struct TcName	*tm;
673 
674 	tn = tcNames;
675 	while (tn->file_pos >= 0)
676 	{
677 		p = q = tn->name_buf;
678 		while (*q)
679 		{
680 			while (*p && *p != '|')
681 				p++;
682 			if (p != q && (tm = find_name(q, tn + 1, p - q)))
683 			{
684 				fputs("Duplicate name: ", stdout);
685 				while (q != p)
686 					(void) putchar(*q++);
687 				(void) putchar('\n');
688 				puts(tn->name_buf);
689 				puts(tm->name_buf);
690 				puts("---\n");
691 			}
692 			if (*p == '|')
693 				p++;
694 			q = p;
695 		}
696 		tn++;
697 	}
698 }
699 
700 struct TcName *
find_name(name,tn,len)701 find_name(name, tn, len)
702 	register char		*name;
703 	register struct TcName	*tn;
704 	register int		len;
705 {
706 	/*
707 	** find name of length len in tcname structure buffers.
708 	*/
709 	register char	*p;
710 	register char	*buf;
711 	register int	 n;
712 
713 	while (tn->file_pos >= 0)
714 	{
715 		buf = tn->name_buf;
716 		while (*buf)
717 		{
718 			p = name;
719 			n = len;
720 			if (*buf == '|')
721 				buf++;
722 
723 			while (*buf && *buf != '|')
724 			{
725 				if (*p != *buf)
726 				{
727 					while (*buf && *buf != '|')
728 						buf++;
729 					break;
730 				}
731 
732 				if (--n <= 0)
733 				{
734 					buf++;
735 					if (*buf == '|' || *buf == '\0')
736 						return(tn);
737 					while (*buf && *buf != '|')
738 						buf++;
739 					break;
740 				}
741 				buf++;
742 				p++;
743 			}
744 		}
745 		tn++;
746 	}
747 	return((struct TcName *)0);
748 }
749 
750 char *
getdesc(key)751 getdesc(key)
752 	char		*key;
753 {
754 	register int	i;
755 
756 	for (i = 0; i < NOCAPS; i++)
757 		if (strncmp(key, capList[i].cap, 2) == 0)
758 			return (capList[i].desc);
759 	return("");
760 }
761 
unknowncap(key)762 unknowncap(key)
763 	char		*key;
764 {
765 	register int	i;
766 
767 	for (i = 0; i < NOCAPS; i++)
768 		if (strncmp(key, capList[i].cap, 2) == 0)
769 			return (0);
770 	return(1);
771 }
772