1 /* @(#)tgetent.c	1.50 21/07/22 Copyright 1986-2021 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)tgetent.c	1.50 21/07/22 Copyright 1986-2021 J. Schilling";
6 #endif
7 /*
8  *	Access routines for TERMCAP database.
9  *
10  *	Copyright (c) 1986-2021 J. Schilling
11  */
12 /*
13  * The contents of this file are subject to the terms of the
14  * Common Development and Distribution License, Version 1.0 only
15  * (the "License").  You may not use this file except in compliance
16  * with the License.
17  *
18  * See the file CDDL.Schily.txt in this distribution for details.
19  * A copy of the CDDL is also available via the Internet at
20  * http://www.opensource.org/licenses/cddl1.txt
21  *
22  * When distributing Covered Code, include this CDDL HEADER in each
23  * file and include the License file CDDL.Schily.txt from this distribution.
24  */
25 
26 /*
27  * XXX Non POSIX imports from libschily: geterrno()
28  */
29 #ifdef	BSH
30 #include <schily/stdio.h>
31 #include "bsh.h"
32 #endif
33 
34 #include <schily/standard.h>
35 #include <schily/stdlib.h>
36 #include <schily/unistd.h>
37 #include <schily/fcntl.h>
38 #include <schily/string.h>
39 #include <schily/signal.h>
40 #include <schily/errno.h>
41 #include <schily/schily.h>
42 #include <schily/ioctl.h>	/* Need to be before termios.h (BSD botch) */
43 #include <schily/termios.h>
44 #include <schily/ctype.h>
45 #include <schily/utypes.h>
46 #include <schily/termcap.h>
47 
48 #ifdef	NO_LIBSCHILY
49 #define	geterrno()	(errno)
50 #endif
51 #ifdef	BSH
52 #define	getenv		getcurenv
53 #endif
54 
55 #ifdef	pdp11
56 #define	TRDBUF		512	/* Size for read(2) buffer		*/
57 #else
58 #define	TRDBUF		8192	/* Size for read(2) buffer		*/
59 #endif
60 #define	TMAX		1024	/* Historical termcap buffer size	*/
61 #define	MAXLOOP		64	/* Max loop count for tc= directive	*/
62 				/* A tc= nesting of 63 has been seen.	*/
63 #define	TSIZE_SPACE	14	/* Space needed for: "li#xxx:co#yyy:"	*/
64 
65 LOCAL	char	_Eterm[]	= "TERM";
66 LOCAL	char	_Etermcap[]	= "TERMCAP";
67 LOCAL	char	_Etermpath[]	= "TERMPATH";
68 #if	defined(INS_BASE) && defined(PROTOTYPES)
69 LOCAL	char	_termpath[]	= ".termcap /etc/termcap" " " INS_BASE "/etc/termcap";
70 #else
71 LOCAL	char	_termpath[]	= ".termcap /etc/termcap";
72 #endif
73 LOCAL	char	_tc[]		= "tc";
74 				/*
75 				 * Additional terminfo quotes
76 				 * "e^[" "::" ",," "s " "l\n"
77 				 */
78 #ifdef	PROTOTYPES
79 LOCAL	char	_quotetab[]	= "E^^\\\\e::,,s l\nn\nr\rt\ta\ab\bf\fv\v";
80 #else
81 LOCAL	char	_quotetab[]	= "E^^\\\\e::,,s l\nn\nr\rt\ta\07b\bf\fv\v";
82 #endif
83 
84 LOCAL	char	_etoolong[]	= "Termcap entry too long\n";
85 LOCAL	char	_ebad[]		= "Bad termcap entry\n";
86 LOCAL	char	_eloop[]	= "Infinite tc= loop\n";
87 LOCAL	char	_enomem[]	= "No memory for parsing termcap\n";
88 
89 LOCAL	char	*tbuf = 0;
90 LOCAL	int	tbufsize = 0;
91 LOCAL	BOOL	tbufmalloc = FALSE;
92 LOCAL	int	tflags = 0;
93 LOCAL	int	loopcount = 0;
94 
95 EXPORT	int	tgetent		__PR((char *bp, char *name));
96 EXPORT	int	tcsetflags	__PR((int flags));
97 EXPORT	char	*tcgetbuf	__PR((void));
98 LOCAL	int	tchktc		__PR((char *name));
99 LOCAL	BOOL	tmatch		__PR((char *name));
100 LOCAL	char	*tskip		__PR((char *ep));
101 LOCAL	char	*tfind		__PR((char *ep, char *ent));
102 EXPORT	int	tgetnum		__PR((char *ent));
103 EXPORT	BOOL	tgetflag	__PR((char *ent));
104 EXPORT	char	*tgetstr	__PR((char *ent, char **array));
105 EXPORT	char	*tdecode	__PR((char *ep, char **array));
106 #if	defined(TIOCGSIZE) || defined(TIOCGWINSZ)
107 LOCAL	void	tgetsize	__PR((void));
108 LOCAL	void	tdeldup		__PR((char *ent));
109 LOCAL	char	*tinsint	__PR((char *ep, int i));
110 #endif
111 LOCAL	void	tstrip		__PR((void));
112 LOCAL	char	*tmalloc	__PR((int size));
113 LOCAL	char	*trealloc	__PR((char *p, int size));
114 #ifdef	NO_LIBSCHILY
115 #define	ovstrcpy	_ovstrcpy
116 LOCAL	char	*ovstrcpy	__PR((char *p2, const char *p1));
117 #endif
118 LOCAL	void	e_tcname	__PR((char *name));
119 
120 /*
121  * Returns:
122  *	1	Entry found
123  *	0	Could not malloc() buffer with tgetent(NULL, name)
124  *	0	Database file opened, entry not found
125  *	-1	Could not open database file
126  */
127 EXPORT int
tgetent(bp,name)128 tgetent(bp, name)
129 	char	*bp;
130 	char	*name;
131 {
132 			char	rdbuf[TRDBUF];
133 			char	*term;
134 			char	termpath[TMAX];
135 			char	*tp;
136 	register	char	*ep;
137 	register	char	*rbuf = rdbuf;
138 	register	char	c;
139 	register	int	count	= 0;
140 	register	int	tfd;
141 			int	err = 0;
142 
143 	tbufsize = TMAX;
144 	if (tbufmalloc) {
145 		if (tbuf)
146 			free(tbuf);
147 		tbufmalloc = FALSE;
148 	}
149 	tbuf = NULL;
150 	if (name == NULL || *name == '\0') {	/* No TERM name specfied */
151 		if (bp)
152 			bp[0] = '\0';
153 		return (0);
154 	}
155 	if ((tbuf = bp) == NULL) {
156 		tbufmalloc = TRUE;
157 		tbuf = bp = tmalloc(tbufsize);
158 	}
159 	if ((tbuf = bp) == NULL)		/* Could not malloc buffer */
160 		return (0);
161 	bp[0] = '\0';		/* Always start with clean termcap buffer */
162 
163 	/*
164 	 * First look, if TERMCAP exists
165 	 */
166 	if (!(ep = getenv(_Etermcap)) || *ep == '\0') {
167 		/*
168 		 * If no TERMCAP environment or empty TERMCAP environment
169 		 * use default termpath.
170 		 * Search rules:
171 		 * 	If TERMPATH exists, use it
172 		 *	else concat $HOME/ with default termpath
173 		 *	default termpath is .termcap /etc/termcap
174 		 */
175 setpath:
176 		if ((ep = getenv(_Etermpath)) != NULL) {
177 			strncpy(termpath, ep, sizeof (termpath));
178 		} else {
179 			termpath[0] = '\0';
180 			if ((ep = getenv("HOME")) != NULL) {
181 				strncpy(termpath, ep,
182 					sizeof (termpath)-2-sizeof (_termpath));
183 				strcat(termpath, "/");
184 			}
185 			strcat(termpath, _termpath);
186 		}
187 	} else {
188 		if (*ep != '/') {
189 			/*
190 			 * This doesn't seem to be a filename...
191 			 * It must be a preparsed termcap entry.
192 			 */
193 			if (!(term = getenv(_Eterm)) ||
194 						strcmp(name, term) == 0) {
195 				/*
196 				 * If no TERM environment or TERM holds the
197 				 * same strings as "name" use preparsed entry.
198 				 */
199 				tbuf = ep;
200 				count = tmatch(name);
201 				tbuf = bp;
202 				if (count > 0) {
203 					count = strlen(ep) + 1 + TSIZE_SPACE;
204 					if (count > tbufsize) {
205 						if (tbufmalloc) {
206 							tbufsize = count;
207 							tbuf = trealloc(bp,
208 									count);
209 						} else {
210 							tbuf = NULL;
211 							e_tcname(name);
212 							write(STDERR_FILENO,
213 							_etoolong,
214 							sizeof (_etoolong) - 1);
215 						}
216 					}
217 					if (tbuf)
218 						ovstrcpy(tbuf, ep);
219 					goto out; /* We use the preparsed entry */
220 				}
221 			}
222 			/*
223 			 * If the preparsed termcap entry does not match
224 			 * our term, we need to read the file.
225 			 * Set up the internal or external TERMPATH for
226 			 * this purpose.
227 			 */
228 			goto setpath;
229 		}
230 		/*
231 		 * If TERMCAP starts with a '/' use it as TERMPATH.
232 		 */
233 		strncpy(termpath, ep, sizeof (termpath));
234 	}
235 	termpath[sizeof (termpath)-1] = '\0';
236 	tp = termpath;
237 
238 nextfile:
239 	/*
240 	 * Loop over TERMPATH string.
241 	 */
242 	ep = tp;
243 	while (*tp++) {
244 		if (*tp == ' ' || *tp == ':') {
245 			*tp++ = '\0';
246 			break;
247 		}
248 	}
249 	if (*ep == '\0') {			/* End of TERMPATH */
250 		if (err != 0)
251 			return (-1);		/* Signal open error */
252 		return (0);			/* Signal not found */
253 	}
254 
255 	if ((tfd = open(ep, O_RDONLY)) < 0) {
256 		err = geterrno();
257 
258 		strncpy(tbuf, ep, TMAX);	/* Remember failed path */
259 		tbuf[TMAX-1] = 0;
260 #ifdef	SHOULD_WE
261 		if (err == ENOENT || err == EACCES)
262 			goto nextfile;
263 		return (-1);
264 #else
265 		goto nextfile;
266 #endif
267 	}
268 
269 	/*
270 	 * Search TERM entry in one file.
271 	 */
272 	ep = bp;
273 	for (;;) {
274 		if (--count <= 0) {
275 			if ((count = read(tfd, rdbuf, sizeof (rdbuf))) <= 0) {
276 				close(tfd);
277 				goto nextfile;	/* Not found, check next */
278 			}
279 			rbuf = rdbuf;
280 		}
281 		c = *rbuf++;
282 		if (c == '\n') {
283 			if (ep > bp && ep[-1] == '\\') {
284 				ep--;
285 				continue;
286 			}
287 		} else if (ep >= bp + (tbufsize-1)) {
288 			if (tbufmalloc) {
289 				tbufsize += TMAX;
290 				if ((bp = trealloc(bp, tbufsize)) != NULL) {
291 					ep = bp + (ep - tbuf);
292 					tbuf = bp;
293 					*ep++ = c;
294 					continue;
295 				} else {	/* No memory for buffer */
296 					tbuf = NULL;
297 					goto out;
298 				}
299 			}
300 			e_tcname(name);
301 			write(STDERR_FILENO, _etoolong, sizeof (_etoolong) - 1);
302 		} else {
303 			*ep++ = c;
304 			continue;
305 		}
306 		*ep = '\0';
307 		if (tmatch(name)) {		/* Entry matches name */
308 			close(tfd);
309 			goto out;
310 		}
311 		ep = bp;
312 	}
313 out:
314 	count = tchktc(name);
315 	bp = tbuf;
316 	if (tbufmalloc) {
317 		if (count <= 0) {
318 #ifdef	__free_buffer__				/* Keep buf for failed path */
319 			if (bp) {
320 				free(bp);
321 				tbuf = NULL;
322 				tbufmalloc = FALSE;
323 			}
324 #endif
325 			return (count);
326 		}
327 		/*
328 		 * Did change size in tchktc() ?
329 		 */
330 		count = strlen(bp) + 1;
331 		if (count != tbufsize) {
332 			tbufsize = count;
333 			if ((tbuf = bp = trealloc(bp, tbufsize)) == NULL)
334 				return (0);
335 		}
336 		return (1);
337 	}
338 	if (count <= 0)		/* If no match (TERM not found) */
339 		bp[0] = '\0';	/* clear termcap buffer		*/
340 	return (count);
341 }
342 
343 /*
344  * Set the termcap flags.
345  * It allows e.g. to prevent tgetent() from following tc= entries
346  * and from modifying the co# and li# entries.
347  * This is a libxtermcap extension.
348  */
349 EXPORT int
tcsetflags(flags)350 tcsetflags(flags)
351 	int	flags;
352 {
353 	int	oflags = tflags;
354 
355 	tflags = flags;
356 	return (oflags);
357 }
358 
359 /*
360  * Return the current buffer that holds the parsed termcap entry.
361  * This function is needed if the buffer is allocated and a user
362  * likes to do own string parsing on the buffer.
363  * This is a libxtermcap extension.
364  */
365 EXPORT char *
tcgetbuf()366 tcgetbuf()
367 {
368 	return (tbuf);
369 }
370 
371 LOCAL int
tchktc(name)372 tchktc(name)
373 	char	*name;
374 {
375 	register	char	*ep;
376 	register	char	*np;
377 	register	char	*tcname;
378 			char	tcbuf[TMAX];
379 			char	*otbuf = tbuf;
380 			int	otbufsize = tbufsize;
381 			BOOL	otbufmalloc = tbufmalloc;
382 			BOOL	needfree;
383 			char	*xtbuf;
384 			int	ret;
385 
386 	if (tbuf == NULL)
387 		return (0);
388 
389 	ep = tbuf + strlen(tbuf) - 2;
390 	while (ep > tbuf && *--ep != ':') {
391 		if (ep <= tbuf) {
392 			/*
393 			 * There was no colon in tbuf...tbuf + strlen(tbuf) - 3,
394 			 * so there cannot be any valid capability in tbuf.
395 			 * First check for a valid but empty termcap entry,
396 			 * such as: "tname:"
397 			 */
398 			ep = tbuf + strlen(tbuf) - 1;
399 			if (ep > tbuf && *ep == ':') {
400 				return (1);	/* Success */
401 			}
402 			e_tcname(name);
403 			write(STDERR_FILENO, _ebad, sizeof (_ebad) - 1);
404 			return (0);
405 		}
406 	}
407 	ep++;
408 	if (ep[0] != 't' || ep[1] != 'c' || (tflags & TCF_NO_TC) != 0)
409 		goto out;
410 
411 	ep = tfind(tbuf, _tc);
412 	if (ep == NULL || *ep != '=') {
413 		e_tcname(name);
414 		write(STDERR_FILENO, _ebad, sizeof (_ebad) - 1);
415 		return (0);
416 	}
417 	ep -= 2;				/* Correct for propper append */
418 	strncpy(tcbuf, &ep[2], sizeof (tcbuf));
419 	tcname = tcbuf;
420 	tcname[sizeof (tcbuf)-1] = '\0';
421 
422 	do {
423 		tcname++;
424 		for (np = tcname; *np; np++)
425 			if (*np == ':')
426 				break;
427 		*np = '\0';
428 		if (++loopcount > MAXLOOP) {
429 			e_tcname(name);
430 			write(STDERR_FILENO, _eloop, sizeof (_eloop) - 1);
431 			return (0);
432 		}
433 		tbufmalloc = FALSE;		/* Do not free buffer now! */
434 		ret = tgetent(NULL, tcname);
435 		*np = ':';
436 		xtbuf = tbuf;
437 		needfree = tbufmalloc;
438 		tbuf = otbuf;
439 		tbufsize = otbufsize;
440 		tbufmalloc = otbufmalloc;
441 		loopcount = 0;
442 		if (ret != 1) {
443 			if (needfree && xtbuf != NULL)
444 				free(xtbuf);
445 			return (ret);
446 		}
447 		np = tskip(xtbuf);	/* skip over the name part */
448 		/*
449 		 * Add nullbyte and 14 bytes for the space needed by tgetsize()
450 		 */
451 		ret = ep - otbuf + strlen(np) + 1 + TSIZE_SPACE;
452 		if (ret >= (unsigned)(tbufsize-1)) {
453 			if (tbufmalloc) {
454 				tbufsize = ret;
455 				if ((otbuf =
456 				    trealloc(otbuf, tbufsize)) != NULL) {
457 					ep = otbuf + (ep - tbuf);
458 					tbuf = otbuf;
459 				} else {
460 					if (needfree && xtbuf != NULL)
461 						free(xtbuf);
462 					return (0);
463 				}
464 			} else {
465 				e_tcname(name);
466 				write(STDERR_FILENO, _etoolong,
467 							sizeof (_etoolong) - 1);
468 				ret = tbufsize - 1 - (ep - otbuf);
469 				if (ret < 0)
470 					ret = 0;
471 				np[ret] = '\0';
472 			}
473 		}
474 		strcpy(ep, np);
475 		ep += strlen(ep);
476 		if (needfree && xtbuf != NULL)
477 			free(xtbuf);
478 
479 	} while ((tcname = tfind(tcname, _tc)) != NULL && *tcname == '=');
480 out:
481 #if	defined(TIOCGSIZE) || defined(TIOCGWINSZ)
482 	if ((tflags & TCF_NO_SIZE) == 0)
483 		tgetsize();
484 #endif
485 	if ((tflags & TCF_NO_STRIP) == 0)
486 		tstrip();
487 	return (1);
488 }
489 
490 /*
491  * Check if the current 'tbuf' contains a termcap entry for a terminal
492  * that matches 'name'.
493  */
494 LOCAL BOOL
tmatch(name)495 tmatch(name)
496 	char	*name;
497 {
498 	register	char	*np;
499 	register	char	*ep;
500 
501 	if (tbuf == NULL)
502 		return (FALSE);
503 
504 	ep = tbuf;
505 	if (*ep == '#')					/* Kommentar */
506 		return (FALSE);
507 	for (; ; ep++) {
508 		for (np = name; *np; ep++, np++)	/* Solange name	*/
509 			if (*ep != *np)			/* gleich ist	*/
510 				break;
511 		if (*np == '\0') {			/* Name am Ende */
512 			if (*ep == '|' || *ep == ':' || *ep == '\0')
513 				return (TRUE);
514 		}
515 		while (*ep && *ep != '|' && *ep != ':')	/* Rest dieses	*/
516 			ep++;				/* Namens	*/
517 		if (*ep == ':' || *ep == '\0')
518 			return (FALSE);
519 	}
520 }
521 
522 /*
523  * Skip past next ':'.
524  * If the are two consecutive ':', the returned pointer may point to ':'.
525  */
526 LOCAL char *
tskip(ep)527 tskip(ep)
528 	register	char	*ep;
529 {
530 	while (*ep) {
531 		if (*ep++ == ':')
532 			return (ep);	/* return first ':'	*/
533 	}
534 	return (ep);			/* not found		*/
535 }
536 
537 /*
538  * Find a two charater entry in string that is found in 'ep'.
539  * Return the character that follows the two character entry (if found)
540  * or NULL if the entry could not be found.
541  */
542 LOCAL char *
tfind(ep,ent)543 tfind(ep, ent)
544 	register	char	*ep;
545 			char	*ent;
546 {
547 	register	char	e0 = ent[0];
548 	register	char	e1 = ent[1];
549 
550 	for (;;) {
551 		ep = tskip(ep);
552 		if (*ep == '\0')
553 			break;
554 		if (*ep == ':')
555 			continue;
556 		if (e0 != *ep++)
557 			continue;
558 		if (*ep == '\0')
559 			break;
560 		if (e1 != *ep++)
561 			continue;
562 		return (ep);
563 	}
564 	return ((char *)NULL);
565 }
566 
567 /*
568  * Search for a numeric entry in form 'en#123' to represent a decimal number
569  * or 'en#0123' to represent a octal number.
570  * Return numeric value or -1 if found 'en@'.
571  */
572 EXPORT int
tgetnum(ent)573 tgetnum(ent)
574 	char	*ent;
575 {
576 	register	Uchar	*ep = (Uchar *)tbuf;
577 	register	int	val;
578 	register	int	base;
579 
580 	if (tbuf == NULL)
581 		return (-1);
582 
583 	for (;;) {
584 		ep = (Uchar *)tfind((char *)ep, ent);
585 		if (!ep || *ep == '@')
586 			return (-1);
587 		if (*ep == '#')
588 			break;
589 	}
590 	base = 10;
591 	if (*++ep == '0')
592 		base = 8;
593 	for (val = 0; isdigit(*ep); ) {
594 		val *= base;
595 		val += (*ep++ - '0');
596 	}
597 	return (val);
598 }
599 
600 /*
601  * Search for a boolean entry in form 'en' to represent a TRUE value
602  * or 'en@' to represent a FALSE value.
603  * An entry in the form 'en@' is mainly used to overwrite similar entries
604  * found later from a tc= entry.
605  */
606 EXPORT BOOL
tgetflag(ent)607 tgetflag(ent)
608 	char	*ent;
609 {
610 	register	char	*ep = tbuf;
611 
612 	if (tbuf == NULL)
613 		return (FALSE);
614 
615 	for (;;) {
616 		ep = tfind(ep, ent);
617 		if (!ep || *ep == '@')
618 			return (FALSE);
619 		if (*ep == '\0' || *ep == ':')
620 			return (TRUE);
621 	}
622 }
623 
624 /*
625  * Search for a string entry in form 'en=val'.
626  * Return string parameter or NULL if found 'en@'.
627  */
628 EXPORT char *
tgetstr(ent,array)629 tgetstr(ent, array)
630 	char	*ent;
631 	char	*array[];
632 {
633 	register	char	*ep = tbuf;
634 			char	*np = NULL;
635 			char	*ap = NULL;
636 			char	buf[TMAX];
637 
638 	if (tbuf == NULL)
639 		return ((char *)0);
640 
641 	if (array == NULL) {
642 		np = buf;
643 		array = &np;
644 	}
645 	for (;;) {
646 		ep = tfind(ep, ent);
647 		if (!ep || *ep == '@')
648 			return ((char *)NULL);
649 		if (*ep == '=') {
650 			if (np && strlen(ep) >= sizeof (buf)) {
651 				ap = np = tmalloc(strlen(ep));
652 				if (np == NULL)
653 					return (np);
654 				array = &np;
655 			}
656 			ep = tdecode(++ep, array);
657 			if (ep && np) {
658 				np = ep;
659 				ep = tmalloc(strlen(ep)+1);
660 				if (ep != NULL)
661 					strcpy(ep, np);
662 				if (ap)
663 					free(ap);
664 			}
665 			return (ep);
666 		}
667 	}
668 }
669 
670 #define	isoctal(c)	((c) >= '0' && (c) <= '7')
671 /*
672  * Decode a string and replace the escape sequences by what
673  * they mean (e.g. \E by ESC).
674  * The space used to hold the decoded string is taken from
675  * the second parameter.
676  * Note that old 'vi' implementations limit the total space for
677  * all decoded strings to 256 bytes.
678  */
679 EXPORT char *
tdecode(pp,array)680 tdecode(pp, array)
681 			char	*pp;
682 			char	*array[];
683 {
684 			int	i;
685 	register	Uchar	c;
686 	register	Uchar	*ep = (Uchar *)pp;
687 	register	Uchar	*bp;
688 	register	Uchar	*tp;
689 
690 	bp = (Uchar *)array[0];
691 
692 	for (; (c = *ep++) && c != ':'; *bp++ = c) {
693 		if (c == '^') {
694 			c = *ep++;
695 			if (c == '\0')
696 				break;
697 			else if (c == '?')
698 				c |= 0x40;
699 			else
700 				c &= 0x1f;
701 			continue;
702 		} else if (c != '\\') {
703 			continue;
704 		}
705 		/*
706 		 * Handle the \xxx and \C escape sequences here:
707 		 */
708 		c = *ep++;
709 		if (c == '\0')
710 			break;
711 		if (isoctal(c)) {
712 			for (c -= '0', i = 3; --i > 0 &&
713 			    *ep && isoctal(*ep); ) {
714 				c <<= 3;
715 				c |= *ep++ - '0';
716 			}
717 			if (*ep == '\0')
718 				break;
719 			/*
720 			 * Terminfo maps NULL chars to 0200
721 			 */
722 			if (c == '\0')
723 				c = '\200';
724 		} else for (tp = (Uchar *)_quotetab; *tp; tp++) {
725 			if (*tp++ == c) {
726 				c = *tp;
727 				break;
728 			}
729 		}
730 	}
731 	*bp++ = '\0';
732 	ep = (Uchar *)array[0];
733 	array[0] = (char *)bp;
734 	return ((char *)ep);
735 }
736 
737 #if	defined(TIOCGSIZE) || defined(TIOCGWINSZ)
738 
739 /*
740  * Get the current size of the terminal (window) and insert the
741  * apropriate values for 'li#' and 'co#' before the other terminal
742  * capabilities.
743  */
744 LOCAL void
tgetsize()745 tgetsize()
746 {
747 #ifdef	TIOCGWINSZ
748 	struct		winsize ws;
749 #else
750 	struct		ttysize	ts;
751 #endif
752 	register	int	lines = 0;
753 	register	int	cols = 0;
754 	register	char	*ep;
755 	register	char	*lp;
756 	register	char	*cp;
757 			int	len;
758 
759 	if (tbuf == NULL)
760 		return;
761 
762 #ifdef	TIOCGWINSZ
763 	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) >= 0) {
764 		lines = ws.ws_row;
765 		cols = ws.ws_col;
766 	}
767 #else
768 	if (ioctl(STDOUT_FILENO, TIOCGSIZE, (char *)&ts) >= 0) {
769 		lines = ts.ts_lines;
770 		cols = ts.ts_cols;
771 	}
772 #endif
773 	if (lines == 0 || cols == 0 || lines > 999 || cols > 999)
774 		return;
775 
776 	len = strlen(tbuf) + 1 + TSIZE_SPACE;
777 	if (len > tbufsize) {
778 		if (tbufmalloc) {
779 			tbufsize = len;
780 			if ((tbuf = trealloc(tbuf, tbufsize)) == NULL)
781 				return;
782 		} else {
783 			return;
784 		}
785 	}
786 	ep = tskip(tbuf);		/* skip over the name part */
787 	/*
788 	 * Backwards copy to create a gap for the string we like to add.
789 	 */
790 	lp = &tbuf[len-1-TSIZE_SPACE];	/* The curent end of the buffer */
791 	for (cp = &lp[TSIZE_SPACE]; lp >= ep; cp--, lp--)
792 		*cp = *lp;
793 
794 	*ep++ = 'l';
795 	*ep++ = 'i';
796 	*ep++ = '#';
797 	ep = tinsint(ep, lines);
798 	*ep++ = ':';
799 	*ep++ = 'c';
800 	*ep++ = 'o';
801 	*ep++ = '#';
802 	ep = tinsint(ep, cols);
803 	*ep++ = ':';
804 	while (ep <= cp)
805 		*ep++ = ' ';
806 	*--ep = ':';
807 }
808 
809 /*
810  * Delete duplicate named numeric entries.
811  */
812 LOCAL void
tdeldup(ent)813 tdeldup(ent)
814 			char	*ent;
815 {
816 	register	char	*ep;
817 	register	char	*p;
818 
819 	if (tbuf == NULL)
820 		return;
821 
822 	if ((ep = tfind(tbuf, ent)) != NULL) {
823 		while ((ep = tfind(ep, ent)) && *ep == '#') {
824 			p = ep;
825 			while (*p)
826 				if (*p++ == ':')
827 					break;
828 			ep -= 3;
829 			ovstrcpy(ep, --p);
830 		}
831 	}
832 }
833 
834 /*
835  * Insert a number into a terminal capability buffer.
836  */
837 LOCAL char *
tinsint(ep,i)838 tinsint(ep, i)
839 	register	char	*ep;
840 	register	int	i;
841 {
842 	register	char	c;
843 
844 	if ((c = i / 100) != 0) {
845 		*ep++ = c + '0';
846 		i %= 100;
847 		if (i / 10 == 0)
848 			*ep++ = '0';
849 	}
850 	if ((c = i / 10) != 0)
851 		*ep++ = c + '0';
852 	*ep++ = i % 10 + '0';
853 	return (ep);
854 }
855 
856 #endif	/* defined(TIOCGSIZE) || defined(TIOCGWINSZ) */
857 
858 /*
859  * Strip down the termcap entry to make it as short as possible.
860  * This is done by first deleting duplicate 'li#' and 'co#' entries
861  * and then removing succesive ':' chars and spaces between ':'.
862  */
863 LOCAL void
tstrip()864 tstrip()
865 {
866 	register	char	*bp = tbuf;
867 	register	char	*p;
868 
869 	if (bp == NULL)
870 		return;
871 
872 #if	defined(TIOCGSIZE) || defined(TIOCGWINSZ)
873 	tdeldup("li");
874 	tdeldup("co");
875 #endif
876 
877 #ifdef	needed
878 	while (*bp) {
879 		if (*bp++ == ':') {
880 			if (*bp == ':') {
881 				p = bp;
882 				while (*p == ':')
883 					p++;
884 				ovstrcpy(bp, p);
885 			}
886 		}
887 	}
888 	bp = tbuf;
889 #endif
890 	while (*bp) {
891 		if (*bp == '\\') {
892 			++bp;
893 			if (*bp++ == '\0')
894 				break;
895 			continue;
896 		}
897 		if (*bp++ == ':') {
898 			if (*bp == ':' || *bp == ' ' || *bp == '\t') {
899 				p = bp;
900 				while (*p)
901 					if (*p++ == ':')
902 						break;
903 				ovstrcpy(bp--, p);
904 			}
905 		}
906 	}
907 }
908 
909 LOCAL char *
tmalloc(size)910 tmalloc(size)
911 	int	size;
912 {
913 	char	*ret;
914 
915 	if ((ret = malloc(size)) != NULL)
916 		return (ret);
917 	write(STDERR_FILENO, _enomem, sizeof (_enomem) - 1);
918 	return ((char *)NULL);
919 }
920 
921 LOCAL char *
trealloc(p,size)922 trealloc(p, size)
923 	char	*p;
924 	int	size;
925 {
926 	char	*ret;
927 
928 	if ((ret = realloc(p, size)) != NULL)
929 		return (ret);
930 	write(STDERR_FILENO, _enomem, sizeof (_enomem) - 1);
931 	return ((char *)NULL);
932 }
933 
934 #ifdef	NO_LIBSCHILY
935 /*
936  * A strcpy() that works with overlapping buffers
937  */
938 LOCAL char *
ovstrcpy(p2,p1)939 ovstrcpy(p2, p1)
940 	register char		*p2;
941 	register const char	*p1;
942 {
943 	char	*ret = p2;
944 
945 	while ((*p2++ = *p1++) != '\0')
946 		;
947 
948 	return (ret);
949 }
950 #endif
951 
952 LOCAL void
e_tcname(name)953 e_tcname(name)
954 	char	*name;
955 {
956 	write(STDERR_FILENO, name, strlen(name));
957 	write(STDERR_FILENO, ": ", 2);
958 }
959