1 /* @(#)cap.c	1.60 21/08/20 Copyright 2000-2021 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)cap.c	1.60 21/08/20 Copyright 2000-2021 J. Schilling";
6 #endif
7 /*
8  *	termcap		a TERMCAP compiler
9  *
10  *	The termcap database is an ASCII representation of the data
11  *	so people may believe that there is no need for a compiler.
12  *	Syntax checks and unification however are a property of compilers.
13  *	We check for correct data types, output all entries in a unique
14  *	order and recode all strings with the same escape notation.
15  *	This is needed in to compare two entries and it makes life easier.
16  *
17  *	Copyright (c) 2000-2021 J. Schilling
18  */
19 /*
20  * The contents of this file are subject to the terms of the
21  * Common Development and Distribution License, Version 1.0 only
22  * (the "License").  You may not use this file except in compliance
23  * with the License.
24  *
25  * See the file CDDL.Schily.txt in this distribution for details.
26  * A copy of the CDDL is also available via the Internet at
27  * http://www.opensource.org/licenses/cddl1.txt
28  *
29  * When distributing Covered Code, include this CDDL HEADER in each
30  * file and include the License file CDDL.Schily.txt from this distribution.
31  */
32 
33 #include <schily/stdio.h>
34 #include <schily/stdlib.h>
35 #include <schily/unistd.h>
36 #include <schily/standard.h>
37 #include <schily/fcntl.h>
38 #include <schily/string.h>
39 #include <schily/termcap.h>
40 #include <schily/getargs.h>
41 #define	SCHILY_PRINT
42 #define	GT_COMERR		/* #define comerr gtcomerr */
43 #define	GT_ERROR		/* #define error gterror   */
44 #include <schily/schily.h>
45 #include <schily/nlsdefs.h>
46 
47 #define	TBUF	2048
48 
49 typedef struct {
50 	char	*tc_name;	/* Termcap name */
51 	char	*tc_iname;	/* Terminfo name */
52 	char	*tc_var;	/* Curses Variable name */
53 	char	*tc_comment;	/* Explanation */
54 	int	tc_flags;
55 } clist;
56 
57 /*
58  * Definitions for tc_flags
59  */
60 #define	C_BOOL		0x01	/* This is a boolean entry */
61 #define	C_INT		0x02	/* This is a numeric entry */
62 #define	C_STRING	0x04	/* This is a string entry */
63 #define	C_TC		0x08	/* This is a tc= string entry */
64 #define	C_PAD		0x10	/* This rentry requires padding */
65 #define	C_PADN		0x20	/* Padding based on affect count */
66 #define	C_PARM		0x40	/* Padding based on affect count */
67 #define	C_OLD		0x100	/* This is an old termcap only entry */
68 #define	C_CURIOUS	0x200	/* This is a curious termcap entry */
69 
70 /*
71  * The list of capabilities for the termcap command.
72  * This contains the Termcap Name, the Terminfo Name and a Comment.
73  */
74 LOCAL clist caplist[] = {
75 #include "caplist.c"
76 };
77 
78 LOCAL	int	ncaps = sizeof (caplist) / sizeof (caplist[0]);
79 
80 LOCAL	BOOL	nodisabled = FALSE;
81 LOCAL	BOOL	nounknown = FALSE;
82 LOCAL	BOOL	nowarn = FALSE;
83 LOCAL	BOOL	dooctal = FALSE;
84 LOCAL	BOOL	docaret = FALSE;
85 LOCAL	BOOL	gnugoto = FALSE;
86 LOCAL	BOOL	oneline = FALSE;
87 
88 #ifdef	HAVE_SETVBUF
89 LOCAL	char	obuf[4096];
90 #else
91 #ifdef	HAVE_SETVBUF
92 LOCAL	char	obuf[BUFSIZ];
93 #endif
94 #endif
95 
96 LOCAL	void	init_clist	__PR((void));
97 LOCAL	char *	tskip		__PR((char *ep));
98 LOCAL	char *	tfind		__PR((char *ep, char *ent));
99 LOCAL	void	dumplist	__PR((void));
100 LOCAL	void	usage		__PR((int ex));
101 EXPORT	int	main		__PR((int ac, char **av));
102 LOCAL	void	checkentries	__PR((char *tname, int *slenp));
103 LOCAL	char	*type2str	__PR((int type));
104 LOCAL	void	checkbad	__PR((char *tname, char *unknown, char *disabled));
105 LOCAL	void	outcap		__PR((char *tname, char *unknown, char *disabled, BOOL obsolete_last));
106 LOCAL	char *	checkgoto	__PR((char *tname, char *ent, char *cm, int col, int line));
107 LOCAL	char *	checkquote	__PR((char *tname, char *s));
108 LOCAL	char *	quote		__PR((char *s));
109 LOCAL	char *	requote		__PR((char *s));
110 LOCAL	void	compile_ent	__PR((char *tname, BOOL	do_tc, BOOL obsolete_last));
111 LOCAL	void	read_names	__PR((char *fname, BOOL	do_tc, BOOL obsolete_last));
112 
113 /*
114  * Initialize the tc_flags struct member in "caplist".
115  */
116 LOCAL void
init_clist()117 init_clist()
118 {
119 	int	i;
120 	int	flags = 0;
121 	int	flags2;
122 
123 	for (i = 0; i < ncaps; i++) {
124 		flags2 = 0;
125 
126 		/*
127 		 * Process meta entries.
128 		 */
129 		if (caplist[i].tc_name[0] == '-' &&
130 		    caplist[i].tc_name[1] == '-') {
131 		}
132 
133 		if (streql(caplist[i].tc_var, "BOOL")) {
134 
135 			flags &= ~(C_BOOL|C_INT|C_STRING);
136 			flags |= C_BOOL;
137 		}
138 		if (streql(caplist[i].tc_var, "INT")) {
139 
140 			flags &= ~(C_BOOL|C_INT|C_STRING);
141 			flags |= C_INT;
142 		}
143 		if (streql(caplist[i].tc_var, "STRING")) {
144 
145 			flags &= ~(C_BOOL|C_INT|C_STRING);
146 			flags |= C_STRING;
147 		}
148 		if (streql(caplist[i].tc_var, "TC")) {
149 
150 			flags |= C_TC;
151 		}
152 		if (streql(caplist[i].tc_var, "COMMENT")) {
153 
154 			flags &= ~(C_BOOL|C_INT|C_STRING);
155 			flags &= ~C_OLD;
156 		}
157 		if (streql(caplist[i].tc_var, "OBSOLETE")) {
158 			/*
159 			 * OBSOLETE is used together with BOOL, INT or STRING
160 			 */
161 			flags |= C_OLD;
162 		}
163 		if (streql(caplist[i].tc_var, "CURIOUS")) {
164 			/*
165 			 * CURIOUS is a special tag for the tc= entry.
166 			 */
167 			flags &= ~C_OLD;
168 			flags |= C_CURIOUS;
169 		}
170 		if (caplist[i].tc_comment[0] != '\0') {
171 			char	*p = caplist[i].tc_comment;
172 
173 			p += strlen(p) - 1;
174 
175 			if (*p == ')' && strchr("NP*", p[-1]) != NULL) {
176 				while (strchr("NP*", *--p) != NULL) {
177 					if (*p == 'N')
178 						flags2 |= C_PARM;
179 					if (*p == 'P')
180 						flags2 |= C_PAD;
181 					if (*p == '*')
182 						flags2 |= C_PADN;
183 				}
184 			}
185 		}
186 		caplist[i].tc_flags = flags | flags2;
187 	}
188 }
189 
190 
191 /*
192  * Skip past next ':'.
193  * If there are two consecutive ':', the returned pointer may point to ':'.
194  *
195  * A copy from the local function libxtermcap:tgetent.c:tskip()
196  */
197 LOCAL char *
tskip(ep)198 tskip(ep)
199 	register	char	*ep;
200 {
201 	while (*ep) {
202 		if (*ep++ == ':')
203 			return (ep);
204 	}
205 	return (ep);
206 }
207 
208 /*
209  * A copy from the local function libxtermcap:tgetent.c:tfind()
210  */
211 LOCAL char *
tfind(ep,ent)212 tfind(ep, ent)
213 	register	char	*ep;
214 			char	*ent;
215 {
216 	register	char	e0 = ent[0];
217 	register	char	e1 = ent[1];
218 
219 	for (;;) {
220 		ep = tskip(ep);
221 		if (*ep == '\0')
222 			break;
223 		if (*ep == ':')
224 			continue;
225 		if (e0 != *ep++)
226 			continue;
227 		if (*ep == '\0')
228 			break;
229 		if (e1 != *ep++)
230 			continue;
231 		return (ep);
232 	}
233 	return ((char *) NULL);
234 }
235 
236 /*
237  * Dump the all entries from "caplist".
238  * Skip all special entries.
239  */
240 LOCAL void
dumplist()241 dumplist()
242 {
243 	int	i;
244 	int	j;
245 	char	parms[8];
246 
247 	for (i = 0; i < ncaps; i++) {
248 		int l;
249 		/*
250 		 * Skip meta entries.
251 		 */
252 		if (caplist[i].tc_name[0] == '-' &&
253 		    caplist[i].tc_name[1] == '-')
254 			continue;
255 		if (caplist[i].tc_name[0] == '.' &&
256 		    caplist[i].tc_name[1] == '.')
257 			continue;
258 
259 		parms[0] = '\0';
260 		j = 0;
261 		if (caplist[i].tc_flags & C_PARM)
262 			parms[j++] = 'N';
263 		if (caplist[i].tc_flags & C_PAD)
264 			parms[j++] = 'P';
265 		if (caplist[i].tc_flags & C_PADN)
266 			parms[j++] = '*';
267 		parms[j] = '\0';
268 
269 
270 		l = strlen(caplist[i].tc_var);
271 		printf("{\"%s\",	\"%s\",%s\"%s\"},%s	/*%c%c%-3s %s */\n",
272 			caplist[i].tc_name,
273 			caplist[i].tc_iname,
274 			strlen(caplist[i].tc_iname) >= 5 ? "\t":"\t\t",
275 			caplist[i].tc_var,
276 			l >= 20 ? "":
277 			l >= 12 ? "\t":
278 			l >= 4 ? "\t\t": "\t\t\t",
279 			(caplist[i].tc_flags & C_BOOL) ? 'B':
280 			(caplist[i].tc_flags & C_INT) ? 'I':
281 			(caplist[i].tc_flags & C_STRING) ? 'S': '?',
282 			(caplist[i].tc_flags & C_OLD) ? 'O': ' ',
283 			parms,
284 			caplist[i].tc_comment);
285 	}
286 }
287 
288 LOCAL void
usage(ex)289 usage(ex)
290 	int	ex;
291 {
292 	error("Usage: %s\n", get_progname());
293 	error("Options:\n");
294 	error("-e		use termcap entry from TERMCAP environment if present\n");
295 	error("-help		print this help\n");
296 	error("-version	print version number\n");
297 	error("-dumplist	dump internal capability list\n");
298 	error("-inorder	print caps in order, else print outdated caps last\n");
299 	error("-noinorder	switch off -inorder, print unknown and outdated caps last\n");
300 	error("-dooctal	prefer '\\003' before '^C' when creating escaped strings\n");
301 	error("-docaret	prefer '^M' before '\\r' when creating escaped strings\n");
302 	error("if=name		input file for termcap compiling\n");
303 	error("-gnugoto	allow GNU tgoto() format extensions '%%C' and '%%m'.\n");
304 	error("-nodisabled	do not output disabled termcap entries\n");
305 	error("-nounknown	do not output unkonwn termcap entries\n");
306 	error("-nowarn		do not warn about problems that could be fixed\n");
307 	error("-oneline	output termcap entries in a single line\n");
308 	error("-s		Output commands to set and export TERM and TERMCAP.\n");
309 	error("-tc		follow tc= entries and generate cumulative output\n");
310 	error("-v		increase verbosity level\n");
311 	error("With if= name, -inorder is default\n");
312 	exit(ex);
313 }
314 
315 EXPORT int
main(ac,av)316 main(ac, av)
317 	int	ac;
318 	char	*av[];
319 {
320 	int	cac;
321 	char	*const *cav;
322 	char	*tbuf;			/* Termcap buffer */
323 	char	unknown[5*TBUF];	/* Buffer fuer "unknown" Entries */
324 	char	disabled[5*TBUF];	/* Buffer fuer :..xx: Entries */
325 	char	*tname = getenv("TERM");
326 	char	*tcap = getenv("TERMCAP");
327 	int	slen = 0;
328 	int	fullen;
329 	int	strippedlen;
330 	BOOL	help = FALSE;
331 	BOOL	prvers = FALSE;
332 	BOOL	dodump = FALSE;
333 	BOOL	inorder = FALSE;
334 	BOOL	noinorder = FALSE;
335 	BOOL	sflag = FALSE;
336 	int	verbose = 0;
337 	BOOL	do_tc = FALSE;
338 	BOOL	useenv = FALSE;
339 	char	*infile = NULL;
340 
341 	save_args(ac, av);
342 
343 	(void) setlocale(LC_ALL, "");
344 
345 #ifdef  USE_NLS
346 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
347 #define	TEXT_DOMAIN "termcap"	/* Use this only if it weren't */
348 #endif
349 	{ char	*dir;
350 	dir = searchfileinpath("share/locale", F_OK,
351 					SIP_ANY_FILE|SIP_NO_PATH, NULL);
352 	if (dir)
353 		(void) bindtextdomain(TEXT_DOMAIN, dir);
354 	else
355 #if defined(PROTOTYPES) && defined(INS_BASE)
356 	(void) bindtextdomain(TEXT_DOMAIN, INS_BASE "/share/locale");
357 #else
358 	(void) bindtextdomain(TEXT_DOMAIN, "/usr/share/locale");
359 #endif
360 	(void) textdomain(TEXT_DOMAIN);
361 	}
362 #endif 	/* USE_NLS */
363 
364 #ifdef	HAVE_SETVBUF
365 	setvbuf(stdout, obuf, _IOFBF, sizeof (obuf));
366 #else
367 #ifdef	HAVE_SETVBUF
368 	setbuf(stdout, obuf);
369 #endif
370 #endif
371 	init_clist();
372 
373 	cac = ac;
374 	cav = av;
375 	cac--, cav++;
376 	if (getallargs(&cac, &cav, "help,version,e,dumplist,inorder,noinorder,oneline,v+,s,tc,if*,nodisabled,nounknown,nowarn,dooctal,docaret,gnugoto",
377 				&help, &prvers,
378 				&useenv,
379 				&dodump, &inorder, &noinorder,
380 				&oneline,
381 				&verbose,
382 				&sflag,
383 				&do_tc,
384 				&infile,
385 				&nodisabled, &nounknown,
386 				&nowarn, &dooctal, &docaret,
387 				&gnugoto) < 0) {
388 		errmsgno(EX_BAD, "Bad option '%s'\n", cav[0]);
389 		usage(EX_BAD);
390 	}
391 	if (help)
392 		usage(0);
393 	if (prvers) {
394 		gtprintf("termcap %s %s (%s-%s-%s)\n\n", "1.60", "2021/08/20",
395 				HOST_CPU, HOST_VENDOR, HOST_OS);
396 		gtprintf("Copyright (C) 2000-2021 %s\n", _("J�rg Schilling"));
397 		gtprintf("This is free software; see the source for copying conditions.  There is NO\n");
398 		gtprintf("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
399 		exit(0);
400 	}
401 
402 	if (dodump) {
403 		dumplist();
404 		exit(0);
405 	}
406 
407 	if (infile)
408 		inorder = TRUE;
409 	if (noinorder)
410 		inorder = FALSE;
411 
412 
413 	if (!useenv && tcap && *tcap != '/')
414 		*tcap = '\0';
415 
416 	if (infile) {
417 		char	env[2048];
418 
419 		if (tcap)
420 			*tcap = '\0';
421 		if (tname)
422 			*tname = '\0';
423 		snprintf(env, sizeof (env), "TERMPATH=%s", infile);
424 		putenv(env);
425 		read_names(infile, do_tc, !inorder);
426 		exit(0);
427 	}
428 
429 	/*
430 	 * Check existence & unstripped Termcap len
431 	 *
432 	 * TCF_NO_TC	Don't follow tc= entries
433 	 * TCF_NO_SIZE	Don't get actual ttysize (li#/co#)
434 	 * TCF_NO_STRIP	Don't strip down termcap buffer
435 	 *
436 	 * If called with the -s option, this is the only place where we
437 	 * retrieve the termcap entry, so use the default settings in this case.
438 	 */
439 	if (!sflag)
440 		tcsetflags((do_tc?0:TCF_NO_TC)|TCF_NO_SIZE|TCF_NO_STRIP);
441 	if (tgetent(NULL, tname) != 1)
442 		comerr("no term '%s' found\n", tname);
443 	tbuf = tcgetbuf();
444 	fullen = strlen(tbuf);
445 
446 	if (sflag) {
447 		char	*p = getenv("SHELL");
448 		int	is_csh = FALSE;
449 
450 		if (p) {
451 			int	len = strlen(p);
452 
453 			if (len >= 3 && streql(&p[len-3], "csh"))
454 				is_csh = TRUE;
455 		}
456 		if (is_csh) {
457 			/*
458 			 * csh
459 			 */
460 			printf("set noglob;\n");
461 			printf("setenv TERM %s;\n", tname);
462 			printf("setenv TERMCAP '%s';\n", tbuf);
463 			printf("unset noglob;\n");
464 		} else {
465 			/*
466 			 * Bourne Shell and compatible
467 			 */
468 			printf("export TERMCAP TERM;\n");
469 			printf("TERM=%s;\n", tname);
470 			printf("TERMCAP='%s';\n", tbuf);
471 		}
472 		exit(0);
473 	}
474 
475 	/*
476 	 * Get Stripped len
477 	 */
478 	tcsetflags((do_tc?0:TCF_NO_TC)|TCF_NO_SIZE);
479 	tgetent(NULL, tname);
480 	tbuf = tcgetbuf();
481 	strippedlen = strlen(tbuf);
482 
483 	if (verbose > 0)
484 		checkentries(tname, &slen);
485 
486 	if (verbose > 1) {
487 		printf("tbuf: '%s'\n", tbuf);
488 		printf("full tbuf len: %d stripped tbuf len: %d\n", fullen, strippedlen);
489 		printf("string length: %d\n", slen);
490 	}
491 
492 	checkbad(tname, unknown, disabled);
493 	outcap(tname, unknown, disabled, !inorder);
494 
495 	return (0);
496 }
497 
498 /*
499  * Check entries for correct type and print them
500  */
501 LOCAL void
checkentries(tname,slenp)502 checkentries(tname, slenp)
503 	char	*tname;
504 	int	*slenp;
505 {
506 	char	stbuf[10*TBUF];	/* String buffer zum zaehlen */
507 	char	*sbp;		/* String buffer Ende */
508 	int	i;
509 	int	b;		/* Fuer bool/int Werte */
510 	char	*p;
511 	char	*p2;
512 	char	*pe;
513 	char	*tbuf = tcgetbuf();
514 
515 	/*
516 	 * Print first part of Termcap entry
517 	 */
518 	p = strchr(tbuf, ':');
519 	if (p)
520 		i = p - tbuf;
521 	else
522 		i = strlen(tbuf);
523 	printf("tbuf: '%.*s'\n", i, tbuf);
524 
525 	b = strlen(tbuf);
526 	if (b > 2*TBUF) {
527 		error("%s: termcap entry too long (%d bytes).\n", tname, b);
528 		return;
529 	}
530 
531 	sbp = stbuf;
532 	p2 = tbuf;
533 	for (i = 0; i < ncaps; i++) {
534 		/*
535 		 * Skip meta entries.
536 		 */
537 		if (caplist[i].tc_name[0] == '-' &&
538 		    caplist[i].tc_name[1] == '-')
539 			continue;
540 
541 		if (!(pe = tfind(tbuf, caplist[i].tc_name)))
542 			continue;
543 
544 		if ((caplist[i].tc_flags & (C_BOOL|C_INT|C_STRING)) == 0)
545 			continue;
546 
547 		if ((caplist[i].tc_flags & C_OLD) != 0) {
548 			printf("OBSOLETE ");
549 		}
550 
551 		if (*pe == '@') {
552 			printf("'%s' -> %s", caplist[i].tc_name,
553 				((caplist[i].tc_flags & C_STRING) != 0)? "null @" :
554 				(((caplist[i].tc_flags & C_INT) != 0)? "-1 @" :
555 				(((caplist[i].tc_flags & C_BOOL) != 0)? "FALSE @" :
556 				"unknown-@")));
557 			printf("		%s", caplist[i].tc_comment);
558 			printf("\n");
559 			continue;
560 		}
561 		if (i == ncaps -1) {
562 			p2 = tfind(p2, "tc");
563 			if (p2 == NULL)
564 				break;
565 			if (*p2 == '=') {
566 				char	*n = tskip(p2);
567 
568 				p = &p2[1];
569 				n--;			/* Backstep from ':' */
570 				if ((n - p) >= TBUF) {
571 					error("NOTICE(%s). string '%.*s' too long.\n",
572 					tname, (int)(n - p2)+2, p2-2);
573 					break;
574 				}
575 				strlcpy(sbp, p, n-p+1);
576 				p = sbp;	/* The tc= parameter */
577 			} else
578 				break;
579 		} else {
580 			char	*e = tfind(tbuf, caplist[i].tc_name);
581 			char	*n = tskip(e);
582 
583 			/*
584 			 * e and n are always != NULL.
585 			 */
586 			p = NULL;
587 			if (*e == '=' && (n - e) > 1024) {
588 				/*
589 				 * Warn if the string is > 1024, but let up to
590 				 * 4096 bytes appear to be able to count the
591 				 * real length.
592 				 */
593 				error("NOTICE(%s). string '%.*s' longer than %d.\n",
594 					tname, (int)(n - e)+1, e-2, 1024);
595 			}
596 			if (*e != '=') {
597 				/*
598 				 * Not a string type entry.
599 				 */
600 				/* EMPTY */
601 				;
602 			} else if ((n - e) > 4*TBUF) {
603 				error("NOTICE(%s). string '%.*s' too long.\n",
604 					tname, (int)(n - e)+1, e-2);
605 			} else if ((sbp - stbuf) > 4*TBUF) {
606 				error(
607 				"NOTICE(%s). '%.*s' string table overflow.\n",
608 					tname, (int)(n - e)+1, e-2);
609 			} else {
610 				p = tgetstr(caplist[i].tc_name, &sbp);
611 			}
612 		}
613 		if (caplist[i].tc_name[0] == 'm' &&
614 		    caplist[i].tc_name[1] == 'a' &&
615 		    (caplist[i].tc_flags & C_STRING) == 0) {
616 			/*
617 			 * The "ma" entry exists as numeric and string
618 			 * capability. We are currently not looking for
619 			 * the string capability, so ignore this entry.
620 			 */
621 			/* EMPTY */
622 			;
623 		} else if (p) {
624 			/*
625 			 * XXX Option zum Deaktivieren des Quoten
626 			 */
627 			p = quote(p);
628 			printf("'%s' -> '%s'", caplist[i].tc_name, p);
629 			printf("		%s", caplist[i].tc_comment);
630 
631 			if ((caplist[i].tc_flags & C_STRING) == 0)
632 				printf(" -> WARNING: TYPE mismatch");
633 			printf("\n");
634 			if (i == ncaps -1)
635 				i--;
636 			continue;
637 		}
638 		b = tgetnum(caplist[i].tc_name);
639 
640 		if (caplist[i].tc_name[0] == 'm' &&
641 		    caplist[i].tc_name[1] == 'a' &&
642 		    (caplist[i].tc_flags & C_INT) == 0) {
643 			/*
644 			 * The "ma" entry exists as numeric and string
645 			 * capability. We are currently not looking for
646 			 * the numeric capability, so ignore this entry.
647 			 */
648 			/* EMPTY */
649 			;
650 		} else if (b >= 0) {
651 			printf("'%s' -> %d", caplist[i].tc_name, b);
652 			printf("		%s", caplist[i].tc_comment);
653 			if ((caplist[i].tc_flags & C_INT) == 0)
654 				printf(" -> WARNING: TYPE mismatch");
655 			printf("\n");
656 			continue;
657 		}
658 
659 		b = tgetflag(caplist[i].tc_name);
660 		printf("'%s' -> %s", caplist[i].tc_name, b?"TRUE":"FALSE");
661 		printf("		%s", caplist[i].tc_comment);
662 		if ((caplist[i].tc_flags & C_BOOL) == 0)
663 			printf(" -> WARNING: TYPE mismatch");
664 		printf("\n");
665 		continue;
666 
667 	}
668 	if (slenp)
669 		*slenp = sbp - stbuf;
670 }
671 
672 LOCAL char *
type2str(type)673 type2str(type)
674 	int	type;
675 {
676 	switch (type & 0x07) {
677 
678 	case C_INT:	return ("INT");
679 	case C_STRING:	return ("STRING");
680 	case C_BOOL:	return ("BOOL");
681 	default:	return ("<unknown>");
682 	}
683 }
684 
685 /*
686  * Check for bad termcap entries
687  */
688 LOCAL void
checkbad(tname,unknown,disabled)689 checkbad(tname, unknown, disabled)
690 	char	*tname;
691 	char	*unknown;
692 	char	*disabled;
693 {
694 	char	ent[3];		/* Space to hold termcap entry */
695 	char	*up;		/* Unknown buffer Ende */
696 	char	*dp;		/* Disabled buffer Ende */
697 	int	rb;		/* Fuer bool/int Werte */
698 	char	*rp;		/* Fuer string Werte */
699 	BOOL	found;		/* Correct type already found */
700 	int	i;
701 	char	*p;
702 	char	*p2;
703 	char	*xp;
704 	char	*tbuf = tcgetbuf();
705 	BOOL	out_tty = isatty(STDOUT_FILENO);
706 
707 	p = tskip(tbuf);
708 
709 	i = strlen(tbuf);
710 	if (i > 2*TBUF) {
711 		printf("# BAD(%s). Skipping long entry (%d bytes): '%s'\n",
712 					tname,
713 					i,
714 					tbuf);
715 		if (!out_tty)
716 		error("BAD(%s). Skipping long entry (%d bytes): '%.*s'\n",
717 					tname,
718 					i,
719 					(int)(p - tbuf - 1), tbuf);
720 		return;
721 	}
722 
723 	up = unknown;
724 	dp = disabled;
725 	while (*p) {
726 		while (*p == ':')
727 			p = tskip(p);
728 		if (*p == '\0')
729 			break;
730 		/*
731 		 * Avoid to warn about bad termcap entries as long as we
732 		 * implement support for the terminfo escape '\:'.
733 		 */
734 		if (p > (tbuf+1) && p[-1] == ':' && p[-2] == '\\') {
735 			p = tskip(p);
736 			continue;
737 		}
738 		if (p[1] == '\0' ||
739 		    p[1] == ':') {
740 			if (p[0] == '\t' ||	/* This is an indented line */
741 			    p[0] == ' ') {	/* also ignore single space */
742 				p = tskip(p);
743 				continue;
744 			}
745 			printf("# NOTICE(%s). Short entry (':%c%s') removed\n",
746 				tname, p[0], p[1]?":":"");
747 			if (!out_tty)
748 			error("NOTICE(%s). Short entry (':%c%s') removed\n",
749 				tname, p[0], p[1]?":":"");
750 			p = tskip(p);
751 			continue;
752 		}
753 		checkquote(tname, p);
754 		if (p[2] != ':' && p[2] != '@' && p[2] != '#' && p[2] != '=') {
755 			p2 = tskip(p);
756 			if (p[0] == ' ' || p[0] == '\t') {
757 				printf("# BAD(%s). Skipping %sblank entry: '%.*s'\n",
758 							tname,
759 							(p[1] == ' ' || p[1] == '\t')?
760 							"":"partially ",
761 							(int)(p2 - p - 1), p);
762 				if (!out_tty)
763 				error("BAD(%s). Skipping %sblank entry: '%.*s'\n",
764 							tname,
765 							(p[1] == ' ' || p[1] == '\t')?
766 							"":"partially ",
767 							(int)(p2 - p - 1), p);
768 				p = tskip(p);
769 				continue;
770 			}
771 			if (p[0] == '.') {
772 				if (strncmp(p, "..DISABLED:", 11) == 0 ||
773 				    strncmp(p, "..OBSOLETE:", 11) == 0 ||
774 				    strncmp(p, "..UNKNOWN:", 10) == 0) {
775 					p = tskip(p);
776 					continue;
777 				}
778 				printf("# NOTICE(%s). Disabled entry: '%.*s'\n",
779 							tname, (int)(p2 - p - 1), p);
780 				if (!out_tty)
781 				error("NOTICE(%s). Disabled entry: '%.*s'\n",
782 							tname, (int)(p2 - p - 1), p);
783 				if (1) {
784 					strncpy(dp, p, p2 - p);
785 					dp += p2 - p;
786 				} else {
787 					strncpy(dp, p, p2 - p);
788 					dp[p2 - p] = '\0';
789 					strcpy(dp, requote(dp));
790 					dp += strlen(dp);
791 					*dp++ = ':';
792 				}
793 				p = tskip(p);
794 				continue;
795 			}
796 			xp = p;
797 			if (xp > &tbuf[2])
798 				xp = &p[-2];
799 			while (xp > tbuf && *xp != ':')
800 				--xp;
801 			printf("# BAD(%s). Illegal entry (3rd char '%c' for ':%c%c%c'): '%.*s'\n",
802 					tname, p[2], p[0], p[1], p[2], (int)(p2 - xp - 1), xp);
803 			if (!out_tty)
804 			error("BAD(%s). Illegal entry (3rd char '%c' for '%c%c%c'): '%.*s'\n",
805 					tname, p[2], p[0], p[1], p[2], (int)(p2 - p - 1), p);
806 			p = tskip(p);
807 			continue;
808 		}
809 		if ((p[0] == ' ' || p[0] == '\t') &&
810 		    (p[1] == ' ' || p[1] == '\t')) {
811 			p2 = tskip(p);
812 			printf("# BAD(%s). Skipping blank entry: '%.*s'\n",
813 						tname, (int)(p2 - p - 1), p);
814 			if (!out_tty)
815 			error("BAD(%s). Skipping blank entry: '%.*s'\n",
816 							tname, (int)(p2 - p - 1), p);
817 			p = tskip(p);
818 			continue;
819 		}
820 		strncpy(ent, p, 2);
821 		ent[2] = '\0';
822 
823 		for (i = 0; i < ncaps; i++) {
824 			/*
825 			 * Skip meta entries.
826 			 */
827 			if (caplist[i].tc_name[0] == '-' &&
828 			    caplist[i].tc_name[1] == '-')
829 				continue;
830 
831 			if (caplist[i].tc_name[0] == 'm' &&
832 			    caplist[i].tc_name[1] == 'a' &&
833 			    (caplist[i].tc_flags & C_STRING) == 0) {
834 				/*
835 				 * We found the "ma" entry in the numeric
836 				 * variant. If this was a string cap, continue
837 				 * searching for the matching caplist entry.
838 				 * The "ma=" entry is past "ma#" in our list.
839 				 */
840 				if (p[2] == '=')
841 					continue;
842 			}
843 
844 			if (streql(ent, caplist[i].tc_name))
845 				break;
846 		}
847 		if (i == ncaps) {
848 			p2 = tskip(p);
849 			printf("# NOTICE(%s). Unknown entry ('%s'): '%.*s'\n",
850 						tname, ent, (int)(p2 - p - 1), p);
851 			if (!out_tty)
852 			error("NOTICE(%s). Unknown entry ('%s'): '%.*s'\n",
853 						tname, ent, (int)(p2 - p - 1), p);
854 			if (1) {
855 				strncpy(up, p, p2 - p);
856 				up += p2 - p;
857 			} else {
858 				strncpy(up, p, p2 - p);
859 				up[p2 - p] = '\0';
860 				strcpy(up, requote(up));
861 				up += strlen(up);
862 				*up++ = ':';
863 			}
864 		} else if ((caplist[i].tc_flags & (C_STRING|C_PARM)) == (C_STRING|C_PARM)) {
865 
866 			if (p[2] == '=') {
867 				char	buf[5*TBUF];
868 				char	*bp = buf;
869 				char	*val = tdecode(&p[3], &bp);
870 
871 				checkgoto(tname, ent, val, 0, 0);
872 			}
873 		}
874 
875 		if (i == ncaps) {
876 			/*
877 			 * This is an unknown entry that has already been
878 			 * handled above. We cannot check the type for unknown
879 			 * entries, so continue to check the other entries.
880 			 */
881 			p = tskip(p);
882 			continue;
883 		}
884 		found = FALSE;
885 		rp = tgetstr(caplist[i].tc_name, NULL);
886 		if (rp)
887 			found = TRUE;
888 		if (caplist[i].tc_name[0] == 'm' &&
889 		    caplist[i].tc_name[1] == 'a' &&
890 		    (caplist[i].tc_flags & C_STRING) == 0) {
891 			/* EMPTY */
892 			;
893 		} else if (rp) {
894 			if ((caplist[i].tc_flags & C_STRING) == 0) {
895 				p2 = tskip(p);
896 				printf("# BAD(%s). Type mismatch '%s' in '%.*s' is STRING should be %s\n",
897 					tname, ent, (int)(p2 - p - 1), p, type2str(caplist[i].tc_flags));
898 			}
899 		}
900 		if (rp) {		/* tgetstr() return was malloc()ed */
901 			free(rp);
902 			rp = NULL;
903 		}
904 		rb = tgetnum(caplist[i].tc_name);
905 		if (rb >= 0)
906 			found = TRUE;
907 		if (caplist[i].tc_name[0] == 'm' &&
908 		    caplist[i].tc_name[1] == 'a' &&
909 		    (caplist[i].tc_flags & C_INT) == 0) {
910 			/* EMPTY */
911 			;
912 		} else if (rb >= 0) {
913 			if ((caplist[i].tc_flags & C_INT) == 0) {
914 				p2 = tskip(p);
915 				printf("# BAD(%s). Type mismatch '%s' in '%.*s' is INT should be %s\n",
916 					tname, ent, (int)(p2 - p - 1), p, type2str(caplist[i].tc_flags));
917 			}
918 		}
919 
920 		if (!found && p[2] != '@') {
921 			rb = tgetflag(caplist[i].tc_name);
922 			if ((caplist[i].tc_flags & C_BOOL) == 0) {
923 				p2 = tskip(p);
924 				if (rb == 0 && p[2] != '@') {
925 					printf("# NOTICE(%s). Canceled entry '%s@' followed by '%.*s' expected type %s\n",
926 						tname, ent, (int)(p2 - p - 1), p, type2str(caplist[i].tc_flags));
927 				} else {
928 					printf("# BAD(%s). Type mismatch '%s' in '%.*s' is BOOL should be %s\n",
929 						tname, ent, (int)(p2 - p - 1), p, type2str(caplist[i].tc_flags));
930 				}
931 			}
932 		}
933 
934 		p = tskip(p);
935 	}
936 	*up = '\0';
937 	*dp = '\0';
938 	if (unknown[0] != '\0') {
939 		error("NOTICE(%s). Unknown: '%s'\n", tname, unknown);
940 	}
941 	if (disabled[0] != '\0') {
942 		error("NOTICE(%s). Disabled: '%s'\n", tname, disabled);
943 	}
944 }
945 
946 LOCAL int	itotype[6] = { 0, C_BOOL, C_INT, C_STRING, 0, C_TC|C_STRING };
947 #ifdef	DEBUG
948 LOCAL char	*itoname[6] = { "0", "C_BOOL", "C_INT", "C_STRING", "C_UNKNOWN", "C_TC" };
949 #endif
950 
951 LOCAL void
outcap(tname,unknown,disabled,obsolete_last)952 outcap(tname, unknown, disabled, obsolete_last)
953 	char	*tname;
954 	char	*unknown;
955 	char	*disabled;
956 	BOOL	obsolete_last;	/* obsolete_last == !inorder */
957 {
958 	char	stbuf[10*TBUF];	/* String buffer zum zaehlen */
959 	char	line[10*TBUF];	/* Fuer Einzelausgabe */
960 	char	*sbp;		/* String buffer Ende */
961 	int	llen;
962 	int	curlen;
963 	int	i;
964 	int	j = 0;
965 	int	b;		/* Fuer bool/int Werte */
966 	int	flags;
967 	char	*p;
968 	char	*p2;
969 	char	*pe;
970 	char	*tbuf = tcgetbuf();
971 BOOL	didobsolete = FALSE;
972 
973 
974 	b = strlen(tbuf);
975 	if (b > 2*TBUF) {
976 		return;
977 	}
978 
979 	p = strchr(tbuf, ':');
980 	if (p)
981 		i = p - tbuf;
982 	else
983 		i = strlen(tbuf);
984 	printf("%.*s:", i, tbuf);	/* Print Terminal name/label */
985 	llen = i + 1;
986 
987 	sbp = stbuf;
988 	flags = 0;
989 	p2 = tbuf;
990 
991 	/*
992 	 * If obsolete_last is FALSE, then this loop goes from j=1..5,
993 	 * else, there is only one loop with j=-1 as the loop stops with j==0.
994 	 */
995 	for (j = obsolete_last ? -1:1; j != 0 && j <= 5; j++)
996 	for (i = 0; i < ncaps; i++) {
997 #ifdef	DEBUG
998 		flush();
999 		error("caplist[%d]->'%c%c' (%s)->'%.10s' j=%d\n",
1000 			i, caplist[i].tc_name[0], caplist[i].tc_name[1],
1001 			itoname[j], tfind(tbuf, caplist[i].tc_name), j);
1002 #endif
1003 		/*
1004 		 * Skip meta entries.
1005 		 */
1006 		if (caplist[i].tc_name[0] == '-' &&
1007 		    caplist[i].tc_name[1] == '-')
1008 			continue;
1009 
1010 		/*
1011 		 * Print unknown entries in order at the end of the respective
1012 		 * block. Do not mark inline them and requote the strings.
1013 		 */
1014 		if (j >= 1 && j <= 3 && i == ncaps -1) {
1015 			char	*px = unknown;
1016 			int	t = itotype[j];
1017 			char	*val = NULL;	/* Keep GCC happy */
1018 
1019 			while (*px) {
1020 				pe = px;
1021 				px = tskip(pe);
1022 
1023 				if (t == C_BOOL) {
1024 					if (pe[2] != ':' && pe[2] != '@')
1025 						continue;
1026 				} else if (t == C_INT) {
1027 					if (pe[2] != '#')
1028 						continue;
1029 				} else {
1030 					if (pe[2] != '=')
1031 						continue;
1032 					val = requote(&pe[3]);
1033 				}
1034 				p = pe;
1035 				curlen = px - pe;
1036 				if (p[2] == '=')
1037 					curlen = strlen(val) + 4;
1038 				if (curlen <= 0)
1039 					break;
1040 				if (flags != t && !oneline) {
1041 					printf("\\\n\t:");
1042 					llen = 9;
1043 					flags = t;
1044 				}
1045 				if ((llen > 9) && ((llen + curlen) >= 79 &&
1046 				   !oneline)) {
1047 					printf("\\\n\t:");
1048 					llen = 9 + curlen;
1049 				} else {
1050 					llen += curlen;
1051 				}
1052 				if (p[2] == '=')
1053 					printf("%.2s=%.*s:", p, curlen, val);
1054 				else
1055 					printf("%.*s", curlen, p);
1056 			}
1057 		}
1058 		/*
1059 		 * If j < 0, sort order is order from caplist array
1060 		 * and thus obsolete entries appear past non obsolete entries.
1061 		 *
1062 		 * If j > 0, sort order is BOOL -> INT -> STRING
1063 		 */
1064 		if (j > 0 && (caplist[i].tc_flags & (C_BOOL|C_INT|C_STRING|C_TC)) != itotype[j])
1065 			continue;
1066 
1067 		if ((caplist[i].tc_flags & (C_BOOL|C_INT|C_STRING)) == 0) {
1068 			if (streql(caplist[i].tc_var, "unknown")) {
1069 				if (j > 0)	/* Printed in order before */
1070 					continue;
1071 				if (unknown[0] == '\0')
1072 					continue;
1073 				if (nounknown)
1074 					continue;
1075 
1076 				if ((llen > 9 || flags == 0) && !oneline)
1077 					printf("\\\n\t:");
1078 				if (!oneline) {
1079 					printf("%s%s",
1080 						"..UNKNOWN:\\\n\t:",
1081 						unknown);
1082 				}
1083 				llen = 99;
1084 			} else {
1085 				if (disabled[0] == '\0')
1086 					continue;
1087 				if (nodisabled)
1088 					continue;
1089 
1090 				if ((llen > 9 || flags == 0) && !oneline)
1091 					printf("\\\n\t:");
1092 				if (!oneline) {
1093 					printf("%s%s",
1094 						"..DISABLED:\\\n\t:",
1095 						disabled);
1096 				}
1097 				llen = 99;
1098 			}
1099 			continue;
1100 		}
1101 
1102 		if (!(pe = tfind(tbuf, caplist[i].tc_name)))
1103 			continue;
1104 
1105 		if (*pe == '@') {
1106 			curlen = sprintf(line, "%s@:", caplist[i].tc_name);
1107 			goto printit;
1108 		}
1109 		if (i == ncaps -1) {
1110 			/*
1111 			 * tfind() first calls tskip() but the last printed
1112 			 * entry may be just before the "tc=" enty and did
1113 			 * already call p2 = tskip(pe). In this case, we must
1114 			 * not call tfind().
1115 			 */
1116 			if (strncmp(p2, "tc=", 3) == 0)
1117 				p2 = &p2[2];
1118 			else
1119 				p2 = tfind(p2, "tc");
1120 			if (p2 == NULL)
1121 				break;				/* End of loop */
1122 			if (*p2 == '=') {
1123 				p = &p2[1];
1124 				strcpy(sbp, p);
1125 				if ((p = strchr(sbp, ':')) != NULL)
1126 					*p = '\0';
1127 				p = sbp;
1128 			} else {
1129 				break;				/* End of loop */
1130 			}
1131 			curlen = sprintf(line, "%s=%s:", caplist[i].tc_name, p);
1132 			i--;
1133 			goto printit;
1134 		} else {
1135 			p = tgetstr(caplist[i].tc_name, &sbp);
1136 		}
1137 
1138 		if (p) {
1139 			if (caplist[i].tc_flags & C_INT) {	/* check type for "ma" */
1140 				if (caplist[i].tc_name[0] == 'm' &&
1141 				    caplist[i].tc_name[1] == 'a') {
1142 					/*
1143 					 * The "ma" entry exists as numeric and
1144 					 * string capability. We are currently
1145 					 * not looking for the string
1146 					 * capability, so try to check for int.
1147 					 */
1148 					goto trynum;
1149 				}
1150 			}
1151 
1152 			if (caplist[i].tc_flags & C_STRING) {	/* check type for "ma" */
1153 				curlen = sprintf(line, "%s=%s:", caplist[i].tc_name, quote(p));
1154 				goto printit;
1155 			} else {
1156 				p2 = tskip(pe);
1157 				error("%s: Illegal entry '%s' '%.*s' (should not be a string)\n",
1158 						tname, caplist[i].tc_name,
1159 						(int)(p2 - pe - 1), pe);
1160 				continue;
1161 			}
1162 		}
1163 trynum:
1164 		b = tgetnum(caplist[i].tc_name);
1165 		if (b >= 0) {
1166 			if (caplist[i].tc_flags & C_INT) {	/* check type for "ma" */
1167 				curlen = sprintf(line, "%s#%d:", caplist[i].tc_name, b);
1168 				goto printit;
1169 			} else {
1170 				p2 = tskip(pe);
1171 				if (caplist[i].tc_name[0] == 'm' &&
1172 				    caplist[i].tc_name[1] == 'a') {
1173 					/*
1174 					 * The "ma" entry exists as numeric and
1175 					 * string capability. We are currently
1176 					 * not looking for the string
1177 					 * capability. We will check the string
1178 					 * entry later in the caplist[i] loop.
1179 					 */
1180 					continue;
1181 				}
1182 				error("%s: Illegal entry '%s' '%.*s' (should not be a number)\n",
1183 						tname, caplist[i].tc_name,
1184 						(int)(p2 - pe - 1), pe);
1185 				continue;
1186 			}
1187 		}
1188 
1189 		b = tgetflag(caplist[i].tc_name);
1190 		if (b != 0) {
1191 			if (caplist[i].tc_flags & C_BOOL) {
1192 				curlen = sprintf(line, "%s:", caplist[i].tc_name);
1193 				goto printit;
1194 			} else {
1195 				p2 = tskip(pe);
1196 				error("%s: Illegal entry '%s' '%.*s' (should not be a bool)\n",
1197 						tname, caplist[i].tc_name,
1198 						(int)(p2 - pe - 1), pe);
1199 				continue;
1200 			}
1201 		}
1202 		p2 = tskip(pe);
1203 		if (caplist[i].tc_flags & C_INT) {	/* check type for "ma" */
1204 			if (caplist[i].tc_name[0] == 'm' &&
1205 			    caplist[i].tc_name[1] == 'a' &&
1206 			    *pe == '=') {
1207 				/*
1208 				 * This entry will be checked later.
1209 				 * The "ma=" entry is past "ma#" in our list.
1210 				 */
1211 				continue;
1212 			}
1213 		}
1214 		error("%s: Illegal entry '%s' '%.*s'\n",
1215 						tname, caplist[i].tc_name,
1216 						(int)(p2 - pe - 1), pe);
1217 		continue;
1218 
1219 printit:
1220 		if (!oneline &&
1221 		    flags != (caplist[i].tc_flags & (C_BOOL|C_INT|C_STRING|C_TC))) {
1222 			printf("\\\n\t:");
1223 			llen = 9;
1224 			flags = caplist[i].tc_flags & (C_BOOL|C_INT|C_STRING|C_TC);
1225 		}
1226 
1227 		/*
1228 		 * If j > 0, sort order is BOOL -> INT -> STRING
1229 		 * Do not print the OBSOLETE header in this case.
1230 		 */
1231 		if (!oneline &&
1232 		    j < 0 && (caplist[i].tc_flags & C_OLD) != 0) {
1233 			if (!didobsolete) {
1234 				if (llen > 9)
1235 					printf("\\\n\t:");
1236 				if (!oneline) {
1237 					printf("..OBSOLETE:\\\n\t:");
1238 				}
1239 				llen = 9;
1240 				didobsolete = TRUE;
1241 			}
1242 		}
1243 
1244 /*error("line: '%s', llen: %d curlen: %d sum: %d\n", line, llen, curlen, llen + curlen);*/
1245 		p = line;
1246 		curlen = strlen(p);
1247 		if ((llen > 9) && ((llen + curlen) >= 79) && !oneline) {
1248 			printf("\\\n\t:");
1249 			llen = 9 + curlen;
1250 		} else {
1251 			llen += curlen;
1252 		}
1253 		printf("%s", p);
1254 	}
1255 	printf("\n");
1256 	flush();
1257 }
1258 
1259 
1260 #define	OBUF_SIZE	80
1261 
1262 /*
1263  * Perform string preparation/conversion for cursor addressing.
1264  * The string cm contains a format string.
1265  *
1266  * A copy from the local function libxtermcap:tgoto.c:tgoto()
1267  */
1268 LOCAL char *
checkgoto(tname,ent,cm,col,line)1269 checkgoto(tname, ent, cm, col, line)
1270 	char	*tname;
1271 	char	*ent;
1272 	char	*cm;
1273 	int	col;
1274 	int	line;
1275 {
1276 	static	char	outbuf[OBUF_SIZE];	/* Where the output goes to */
1277 		char	xbuf[10];		/* for %. corrections	    */
1278 	register char	*op = outbuf;
1279 	register char	*p = cm;
1280 	register int	c;
1281 	register int	val = line;
1282 		int	usecol = 0;
1283 		BOOL	out_tty = isatty(STDOUT_FILENO);
1284 		BOOL	hadbad = FALSE;
1285 
1286 	if (p == 0) {
1287 badfmt:
1288 		/*
1289 		 * Be compatible to 'vi' in case of bad format.
1290 		 */
1291 		return ("OOPS");
1292 	}
1293 	xbuf[0] = 0;
1294 	while ((c = *p++) != '\0') {
1295 		if ((op + 5) >= &outbuf[OBUF_SIZE])
1296 			goto overflow;
1297 
1298 		if (c != '%') {
1299 			*op++ = c;
1300 			continue;
1301 		}
1302 		switch (c = *p++) {
1303 
1304 		case '%':		/* %% -> %			*/
1305 					/* This is from BSD		*/
1306 			*op++ = c;
1307 			continue;
1308 
1309 		case 'd':		/* output as printf("%d"...	*/
1310 					/* This is from BSD (use val)	*/
1311 			if (val < 10)
1312 				goto onedigit;
1313 			if (val < 100)
1314 				goto twodigits;
1315 			/*FALLTHROUGH*/
1316 
1317 		case '3':		/* output as printf("%03d"...	*/
1318 					/* This is from BSD (use val)	*/
1319 			if (val >= 1000) {
1320 				*op++ = '0' + (val / 1000);
1321 				val %= 1000;
1322 			}
1323 			*op++ = '0' + (val / 100);
1324 			val %= 100;
1325 			/*FALLTHROUGH*/
1326 
1327 		case '2':		/* output as printf("%02d"...	*/
1328 					/* This is from BSD (use val)	*/
1329 		twodigits:
1330 			*op++ = '0' + val / 10;
1331 		onedigit:
1332 			*op++ = '0' + val % 10;
1333 		nextparam:
1334 			usecol ^= 1;
1335 		setval:
1336 			val = usecol ? col : line;
1337 			continue;
1338 
1339 		case 'C': 		/* For c-100: print quotient of	*/
1340 					/* value by 96, if nonzero,	*/
1341 					/* then do like %+.		*/
1342 					/* This is from GNU (use val)	*/
1343 			if (!gnugoto)
1344 				goto badchar;
1345 			if (val >= 96) {
1346 				*op++ = val / 96;
1347 				val %= 96;
1348 			}
1349 			/*FALLTHROUGH*/
1350 
1351 		case '+':		/* %+x like %c but add x before	*/
1352 					/* This is from BSD (use val)	*/
1353 			val += *p++;
1354 			/*FALLTHROUGH*/
1355 
1356 		case '.':		/* output as printf("%c" but...	*/
1357 					/* This is from BSD (use val)	*/
1358 			if (usecol || UP)  {
1359 				/*
1360 				 * We assume that backspace works and we don't
1361 				 * need to test for BC too.
1362 				 *
1363 				 * If you did not call stty tabs while termcap
1364 				 * is used you will get other problems, so we
1365 				 * exclude tab from the execptions.
1366 				 */
1367 				while (val == 0 || val == '\004' ||
1368 					/* val == '\t' || */ val == '\n') {
1369 
1370 					strcat(xbuf,
1371 						usecol ? (BC?BC:"\b") : UP);
1372 					val++;
1373 				}
1374 			}
1375 			*op++ = val;
1376 			goto nextparam;
1377 
1378 		case '>':		/* %>xy if val > x add y	*/
1379 					/* This is from BSD (chng state)*/
1380 
1381 			if (val > *p++)
1382 				val += *p++;
1383 			else
1384 				p++;
1385 			continue;
1386 
1387 		case 'B':		/* convert to BCD char coding	*/
1388 					/* This is from BSD (chng state)*/
1389 
1390 			val += 6 * (val / 10);
1391 			continue;
1392 
1393 		case 'D':		/* weird Delta Data conversion	*/
1394 					/* This is from BSD (chng state)*/
1395 
1396 			val -= 2 * (val % 16);
1397 			continue;
1398 
1399 		case 'i':		/* increment row/col by one	*/
1400 					/* This is from BSD (chng state)*/
1401 			col++;
1402 			line++;
1403 			val++;
1404 			continue;
1405 
1406 		case 'm':		/* xor both parameters by 0177	*/
1407 					/* This is from GNU (chng state)*/
1408 			if (!gnugoto)
1409 				goto badchar;
1410 			col ^= 0177;
1411 			line ^= 0177;
1412 			goto setval;
1413 
1414 		case 'n':		/* xor both parameters by 0140	*/
1415 					/* This is from BSD (chng state)*/
1416 			col ^= 0140;
1417 			line ^= 0140;
1418 			goto setval;
1419 
1420 		case 'r':		/* reverse row/col		*/
1421 					/* This is from BSD (chng state)*/
1422 			usecol = 1;
1423 			goto setval;
1424 
1425 		default:
1426 		badchar:
1427 			printf("# BAD(%s). Bad format '%%%c' in '%s=%s'\n", tname, c, ent, quote(cm));
1428 			if (!out_tty)
1429 			error("BAD(%s). Bad format '%%%c' in '%s=%s'\n", tname, c, ent, quote(cm));
1430 			hadbad = TRUE;
1431 /*			goto badfmt;*/
1432 		}
1433 	}
1434 	/*
1435 	 * append to output if there is space...
1436 	 */
1437 	if ((op + strlen(xbuf)) >= &outbuf[OBUF_SIZE]) {
1438 overflow:
1439 		printf("# BAD(%s). Buffer overflow in '%s=%s'\n", tname, ent, quote(cm));
1440 		if (!out_tty)
1441 		error("BAD(%s). Buffer overflow in '%s=%s'\n", tname, ent, quote(cm));
1442 		return ("OVERFLOW");
1443 	}
1444 	if (hadbad)
1445 		goto badfmt;
1446 
1447 	for (p = xbuf; *p; )
1448 		*op++ = *p++;
1449 	*op = '\0';
1450 	return (outbuf);
1451 }
1452 
1453 LOCAL	char	_quotetab[]	= "E^^\\\\n\nr\rt\tb\bf\f";
1454 
1455 #define	isoctal(c)	((c) >= '0' && (c) <= '7')
1456 
1457 LOCAL char *
checkquote(tname,s)1458 checkquote(tname, s)
1459 	char	*tname;
1460 	char	*s;
1461 {
1462 static			char	out[5*TBUF];
1463 			char	nm[16];
1464 			int	i;
1465 	register	Uchar	c;
1466 	register	Uchar	*ep = (Uchar *)s;
1467 	register	Uchar	*bp;
1468 	register	Uchar	*tp;
1469 			char	*p;
1470 			BOOL	out_tty = isatty(STDOUT_FILENO);
1471 
1472 	out[0] = '\0';
1473 	if (s[0] == ' ' || s[0] == '\t')
1474 		return (out);
1475 
1476 	ep = (Uchar *)strchr(s, '=');
1477 	if (ep == NULL)
1478 		return (out);
1479 	i = ep - (Uchar *)s;
1480 	ep++;
1481 	p = tskip(s);
1482 	if (ep > (Uchar *)p)
1483 		return (out);
1484 
1485 	strlcpy(nm, s, sizeof (nm));
1486 	if (i < sizeof (nm))
1487 		nm[i] = '\0';
1488 
1489 	bp = (Uchar *)out;
1490 
1491 	for (; (c = *ep++) && c != ':'; *bp++ = c) {
1492 		if (c == '^') {
1493 			c = *ep++;
1494 			if (c == '\0') {
1495 				printf(
1496 				"# NOTICE(%s). ^ quoting followed by no character in '%s'\n",
1497 							tname, s);
1498 				if (!out_tty)
1499 				error("NOTICE(%s). ^ quoting followed by no character in '%s'\n",
1500 							tname, s);
1501 				break;
1502 			}
1503 			c &= 0x1F;
1504 		} else if (c == '\\') {
1505 			c = *ep++;
1506 			if (c == '\0') {
1507 				printf(
1508 				"# NOTICE(%s). \\ quoting followed by no character in '%s'\n",
1509 							tname, s);
1510 				if (!out_tty)
1511 				error("NOTICE(%s). \\ quoting followed by no character in '%s'\n",
1512 							tname, s);
1513 				break;
1514 			}
1515 			if (isoctal(c)) {
1516 				for (c -= '0', i = 3; --i > 0 && isoctal(*ep); ) {
1517 					c <<= 3;
1518 					c |= *ep++ - '0';
1519 				}
1520 				if (c == 0) {
1521 					char	*p2 = tskip(s);
1522 					int	len;
1523 					int	pos = (char *)ep - s;
1524 
1525 					len = p2 - s - (*p2?1:0);
1526 					pos -= 4-i;
1527 					if (!nowarn && nm[0] != '.') {
1528 					printf("# NOTICE(%s). NULL char (fixed) in entry ('%s') at abs position %d in '%.*s'\n",
1529 							tname, nm, pos, len, s);
1530 					if (!out_tty)
1531 					error("NOTICE(%s). NULL char (fixed) in entry ('%s') at abs position %d in '%.*s'\n",
1532 							tname, nm, pos, len, s);
1533 					}
1534 				}
1535 #ifdef	__checkoctal__
1536 				if (i > 0) {
1537 					char	*p2 = tskip(s);
1538 					int	len;
1539 					int	pos = (char *)ep - s;
1540 
1541 					len = p2 - s - (*p2?1:0);
1542 					printf(
1543 					"# NOTICE(%s). Nonoctal char '%c' in entry ('%s') at position %d (abs %d) in '%.*s'\n",
1544 							tname, *ep, nm, 4-i, pos, len, s);
1545 					if (!out_tty)
1546 					error("NOTICE(%s). Nonoctal char '%c' in entry ('%s') at position %d (abs %d) in '%.*s'\n",
1547 							tname, *ep, nm, 4-i, pos, len, s);
1548 				}
1549 #endif
1550 			} else {
1551 				for (tp = (Uchar *)_quotetab; *tp; tp++) {
1552 					if (*tp++ == c) {
1553 						c = *tp;
1554 						break;
1555 					}
1556 				}
1557 				/*
1558 				 * Terminfo quotes not in termcap:
1559 				 * \a	->	'^G'
1560 				 * \e	->	'^['
1561 				 * \:	->	':'
1562 				 * \,	->	','
1563 				 * \s	->	' '
1564 				 * \l	->	'\n'
1565 				 */
1566 				if (*tp == '\0') {
1567 					char	*p2 = tskip(s);
1568 					int	len;
1569 					int	pos = (char *)&ep[-1] - s;
1570 
1571 					len = p2 - s - (*p2?1:0);
1572 					if (!nowarn) {
1573 					printf("# NOTICE(%s). Badly quoted char '\\%c' %sin ('%s') at abs position %d in '%.*s'\n",
1574 							tname, c, strchr("ae:,sl", c)?"(fixed) ":"", nm, pos, len, s);
1575 					if (!out_tty)
1576 					error("NOTICE(%s). Badly quoted char '\\%c' %sin ('%s') at abs position %d in '%.*s'\n",
1577 							tname, c, strchr("ae:,sl", c)?"(fixed) ":"", nm, pos, len, s);
1578 					}
1579 				}
1580 			}
1581 		}
1582 	}
1583 	*bp++ = '\0';
1584 	return (out);
1585 }
1586 
1587 LOCAL char *
quote(s)1588 quote(s)
1589 	char	*s;
1590 {
1591 static	char	out[10*TBUF];
1592 	char	*p1;
1593 	char	*p2;
1594 	unsigned char	c;
1595 
1596 	for (p1 = s, p2 = out; *p1; ) {
1597 		c = *p1++;
1598 		if (c == 033) {		/* ESC -> \E */
1599 			*p2++ = '\\';
1600 			*p2++ = 'E';
1601 		} else if (c == '\\') {	/* \ -> \\ */
1602 			*p2++ = '\\';
1603 			*p2++ = '\\';
1604 		} else if (c == '^') {	/* ^ -> \^ */
1605 			*p2++ = '\\';
1606 			*p2++ = '^';
1607 		} else if (!docaret && c == '\r') {	/* CR -> \r */
1608 			*p2++ = '\\';
1609 			*p2++ = 'r';
1610 		} else if (!docaret && c == '\n') {	/* NL -> \n */
1611 			*p2++ = '\\';
1612 			*p2++ = 'n';
1613 		} else if (!docaret && c == '\t') {	/* TAB -> \t */
1614 			*p2++ = '\\';
1615 			*p2++ = 't';
1616 		} else if (!docaret && c == '\b') {	/* BS -> \b */
1617 			*p2++ = '\\';
1618 			*p2++ = 'b';
1619 		} else if (!docaret && c == '\f') {	/* FF -> \f */
1620 			*p2++ = '\\';
1621 			*p2++ = 'f';
1622 		} else if (!dooctal &&
1623 			    c <= 0x1F) {	/* Control C -> ^C */
1624 			*p2++ = '^';
1625 			*p2++ = '@' + c;
1626 		} else if (c == ':' || c <= 0x1F || c >= 0x7F) {
1627 			*p2++ = '\\';
1628 			*p2++ = '0' + (c / 64) % 8;
1629 			*p2++ = '0' + (c / 8) % 8;
1630 			*p2++ = '0' + c % 8;
1631 		} else {
1632 			*p2++ = c;
1633 		}
1634 	}
1635 	*p2 = '\0';
1636 	return (out);
1637 }
1638 
1639 /*
1640  *   A number of escape sequences are  provided  in  the  string-
1641  *   valued capabilities for easy encoding of characters there:
1642  *
1643  *        \E   maps to ESC
1644  *        ^X   maps to CTRL-X for any appropriate character X
1645  *        \n   maps to LINEFEED
1646  *        \r   maps to RETURN
1647  *        \t   maps to TAB
1648  *        \b   maps to BACKSPACE
1649  *        \f   maps to FORMFEED
1650  *
1651  *   Finally, characters may be given as three octal digits after
1652  *   a  backslash  (for  example,  \123),  and  the  characters ^
1653  *   (caret) and \ (backslash) may be given as \^ and \\  respec-
1654  *   tively.
1655  *
1656  *   If it is necessary to place a : in a capability it  must  be
1657  *   escaped in octal as \072.
1658  *
1659  *   If it is necessary to place a  NUL  character  in  a  string
1660  *   capability  it  must be encoded as \200.  (The routines that
1661  *   deal with termcap use C strings and strip the high  bits  of
1662  *   the  output  very  late,  so that a \200 comes out as a \000
1663  *   would.)
1664  */
1665 
1666 LOCAL char *
requote(s)1667 requote(s)
1668 	char	*s;
1669 {
1670 	char	buf[5*TBUF];
1671 	char	*bp = buf;
1672 
1673 	tdecode(s, &bp);
1674 	return (quote(buf));
1675 }
1676 
1677 LOCAL void
compile_ent(tname,do_tc,obsolete_last)1678 compile_ent(tname, do_tc, obsolete_last)
1679 	char	*tname;
1680 	BOOL	do_tc;
1681 	BOOL	obsolete_last;
1682 {
1683 	char	unknown[5*TBUF];	/* Buffer fuer "unknown" Entries */
1684 	char	disabled[5*TBUF];	/* Buffer fuer :..xx: Entries */
1685 
1686 	tcsetflags((do_tc?0:TCF_NO_TC)|TCF_NO_SIZE|TCF_NO_STRIP);
1687 	if (tgetent(NULL, tname) != 1)
1688 		return;
1689 /*	tbuf = tcgetbuf();*/
1690 /*	strippedlen = strlen(tbuf);*/
1691 
1692 /*	checkentries(tname, &slen);*/
1693 
1694 /*	printf("tbuf: '%s'\n", tbuf);*/
1695 /*printf("full tbuf len: %d stripped tbuf len: %d\n", fullen, strippedlen);*/
1696 /*printf("string length: %d\n", slen);*/
1697 
1698 	checkbad(tname, unknown, disabled);
1699 	outcap(tname, unknown, disabled, obsolete_last);
1700 }
1701 
1702 
1703 #define	TRDBUF	8192
1704 #define	TMAX	1024
1705 #define	TINC	1
1706 
1707 LOCAL void
read_names(fname,do_tc,obsolete_last)1708 read_names(fname, do_tc, obsolete_last)
1709 	char	*fname;
1710 	BOOL	do_tc;
1711 	BOOL	obsolete_last;
1712 {
1713 			char	nbuf[TMAX];
1714 			char	rdbuf[TRDBUF];
1715 			char	*tbuf;
1716 			char	*name = nbuf;
1717 	register	char	*bp;
1718 	register	char	*ep;
1719 	register	char	*rbuf = rdbuf;
1720 	register	char	c;
1721 	register	int	count	= 0;
1722 	register	int	tfd;
1723 			int	nents = 0;
1724 			int	tbufsize = TINC;
1725 
1726 	tcsetflags(TCF_NO_TC|TCF_NO_SIZE);
1727 
1728 	tbuf = bp = malloc(tbufsize);
1729 	if (bp == NULL)
1730 		comerr("Cannot malloc termcap parsing buffer.\n");
1731 
1732 
1733 	if (fname == NULL)
1734 		fname = "/etc/termcap";
1735 	tfd = open(fname, O_RDONLY);
1736 	if (tfd < 0)
1737 		comerr("Cannot open '%s'\n", fname);
1738 
1739 	/*
1740 	 * Search TERM entry in one file.
1741 	 */
1742 	ep = bp;
1743 	for (;;) {
1744 		if (--count <= 0) {
1745 			if ((count = read(tfd, rdbuf, sizeof (rdbuf))) <= 0) {
1746 				close(tfd);
1747 				error("Found %d terminal entries.\n", nents);
1748 				error("TBuf size %d.\n", tbufsize);
1749 				free(tbuf);
1750 				return;
1751 			}
1752 			rbuf = rdbuf;
1753 		}
1754 		c = *rbuf++;
1755 		if (c == '\n') {
1756 			if (ep > bp && ep[-1] == '\\') {
1757 				ep--;
1758 				continue;
1759 			}
1760 		} else if (ep >= bp + (tbufsize-1)) {
1761 			tbufsize += TINC;
1762 			if ((bp = realloc(bp, tbufsize)) != NULL) {
1763 				ep = bp + (ep - tbuf);
1764 				tbuf = bp;
1765 				*ep++ = c;
1766 				continue;
1767 			} else {
1768 				comerr("Cannot grow termcap parsing buffer.\n");
1769 			}
1770 		} else {
1771 			*ep++ = c;
1772 			continue;
1773 		}
1774 		*ep = '\0';
1775 
1776 		if (tbuf[0] != '\0' && tbuf[0] != '#') {
1777 			char	*p;
1778 
1779 /*			printf("NAME: '%.20s'\n", tbuf);*/
1780 			p = strchr(tbuf, '|');
1781 			if (p == &tbuf[2]) {
1782 				++p;
1783 				p = strchr(p, '|');
1784 			}
1785 			if (p == 0)
1786 				p = strchr(tbuf, ':');
1787 			if (p) {
1788 				int amt = p - tbuf;
1789 
1790 				nents++;
1791 				if (amt > (sizeof (nbuf)-1))
1792 					amt = sizeof (nbuf)-1;
1793 				strncpy(name, tbuf, amt);
1794 				nbuf[amt] = '\0';
1795 /*				printf("name: %s'\n", name);*/
1796 				compile_ent(name, do_tc, obsolete_last);
1797 				tcsetflags(TCF_NO_TC|TCF_NO_SIZE);
1798 			} else {
1799 				/*
1800 				 * This line is not a termcap entry
1801 				 */
1802 				printf("%s\n", tbuf);
1803 			}
1804 		} else {
1805 			/*
1806 			 * This line is comment
1807 			 */
1808 			printf("%s\n", tbuf);
1809 		}
1810 		ep = bp;
1811 	}
1812 }
1813