OpenGrok

Cross Reference: termcap.c
xref: /onnv/onnv-gate/usr/src/ucblib/libtermcap/termcap.c
Home | History | Annotate | Line # | Download | only in libtermcap
      1 /*
      2  * Copyright 1997 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
      7 /*	  All Rights Reserved  	*/
      8 
      9 /*
     10  * Copyright (c) 1980 Regents of the University of California.
     11  * All rights reserved.  The Berkeley software License Agreement
     12  * specifies the terms and conditions for redistribution.
     13  */
     14 
     15 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     16 
     17 /*LINTLIBRARY*/
     18 
     19 #if 0
     20 static char
     21 sccsid[] = "@(#)termcap.c 1.11 88/02/08 SMI"; /* from UCB 5.1 6/5/85 */
     22 #endif
     23 
     24 #define	BUFSIZ		1024
     25 #define	MAXHOP		32	/* max number of tc= indirections */
     26 #define	E_TERMCAP	"/etc/termcap"
     27 
     28 #include <sys/types.h>
     29 #include <unistd.h>
     30 #include <stdlib.h>
     31 #include <stddef.h>
     32 #include <string.h>
     33 #include <strings.h>
     34 #include <ctype.h>
     35 
     36 /*
     37  * termcap - routines for dealing with the terminal capability data base
     38  *
     39  * BUG:		Should use a "last" pointer in tbuf, so that searching
     40  *		for capabilities alphabetically would not be a n**2/2
     41  *		process when large numbers of capabilities are given.
     42  * Note:	If we add a last pointer now we will screw up the
     43  *		tc capability. We really should compile termcap.
     44  *
     45  * Essentially all the work here is scanning and decoding escapes
     46  * in string capabilities.  We don't use stdio because the editor
     47  * doesn't, and because living w/o it is not hard.
     48  */
     49 
     50 static	char *tbuf;
     51 static	int hopcount;	/* detect infinite loops in termcap, init 0 */
     52 
     53 /* forward declarations */
     54 static char *tdecode(char *, char **);
     55 static void tngetsize(char *);
     56 static char *tskip(char *bp);
     57 static char *appendsmalldec(char *, int);
     58 int tnamatch(char *);
     59 int tnchktc(void);
     60 
     61 /*
     62  * Get an entry for terminal name in buffer bp,
     63  * from the termcap file.  Parse is very rudimentary;
     64  * we just notice escaped newlines.
     65  */
     66 
     67 int
     68 tgetent(char *bp, char *name)
     69 {
     70 	char *cp;
     71 	int c;
     72 	int i = 0;
     73 	ssize_t cnt = 0;
     74 	char ibuf[BUFSIZ];
     75 	int tf;
     76 
     77 	tbuf = bp;
     78 	tf = -1;
     79 #ifndef V6
     80 	cp = getenv("TERMCAP");
     81 	/*
     82 	 * TERMCAP can have one of two things in it. It can be the
     83 	 * name of a file to use instead of /etc/termcap. In this
     84 	 * case it better start with a "/". Or it can be an entry to
     85 	 * use so we don't have to read the file. In this case it
     86 	 * has to already have the newlines crunched out.
     87 	 */
     88 	if (cp && *cp) {
     89 		if (*cp == '/') {
     90 			tf = open(cp, 0);
     91 		} else {
     92 			tbuf = cp;
     93 			c = tnamatch(name);
     94 			tbuf = bp;
     95 			if (c) {
     96 				(void) strcpy(bp, cp);
     97 				return (tnchktc());
     98 			}
     99 		}
    100 	}
    101 	if (tf < 0)
    102 		tf = open(E_TERMCAP, 0);
    103 #else
    104 	tf = open(E_TERMCAP, 0);
    105 #endif
    106 	if (tf < 0)
    107 		return (-1);
    108 	for (;;) {
    109 		cp = bp;
    110 		for (;;) {
    111 			if (i == cnt) {
    112 				cnt = read(tf, ibuf, BUFSIZ);
    113 				if (cnt <= 0) {
    114 					(void) close(tf);
    115 					return (0);
    116 				}
    117 				i = 0;
    118 			}
    119 			c = ibuf[i++];
    120 			if (c == '\n') {
    121 				if (cp > bp && cp[-1] == '\\') {
    122 					cp--;
    123 					continue;
    124 				}
    125 				break;
    126 			}
    127 			if (cp >= bp+BUFSIZ) {
    128 				(void) write(2, "Termcap entry too long\n", 23);
    129 				break;
    130 			} else
    131 				*cp++ = (char) c;
    132 		}
    133 		*cp = 0;
    134 
    135 		/*
    136 		 * The real work for the match.
    137 		 */
    138 		if (tnamatch(name)) {
    139 			(void) close(tf);
    140 			return (tnchktc());
    141 		}
    142 	}
    143 }
    144 
    145 /*
    146  * tnchktc: check the last entry, see if it's tc=xxx. If so,
    147  * recursively find xxx and append that entry (minus the names)
    148  * to take the place of the tc=xxx entry. This allows termcap
    149  * entries to say "like an HP2621 but doesn't turn on the labels".
    150  * Note that this works because of the left to right scan.
    151  */
    152 
    153 int
    154 tnchktc(void)
    155 {
    156 	char *p, *q;
    157 	char tcname[16];	/* name of similar terminal */
    158 	char tcbuf[BUFSIZ];
    159 	char *holdtbuf = tbuf;
    160 	ptrdiff_t l;
    161 
    162 	p = tbuf + strlen(tbuf) - 2;	/* before the last colon */
    163 	while (*--p != ':')
    164 		if (p < tbuf) {
    165 			(void) write(2, "Bad termcap entry\n", 18);
    166 			return (0);
    167 		}
    168 	p++;
    169 	/* p now points to beginning of last field */
    170 	if (p[0] != 't' || p[1] != 'c') {
    171 		tngetsize(tbuf);
    172 		return (1);
    173 	}
    174 	(void) strcpy(tcname, p+3);
    175 	q = tcname;
    176 	while (*q && *q != ':')
    177 		q++;
    178 	*q = 0;
    179 	if (++hopcount > MAXHOP) {
    180 		(void) write(2, "Infinite tc= loop\n", 18);
    181 		return (0);
    182 	}
    183 	if (tgetent(tcbuf, tcname) != 1) {
    184 		hopcount = 0;		/* unwind recursion */
    185 		return (0);
    186 	}
    187 	for (q = tcbuf; *q != ':'; q++)
    188 		;
    189 	l = p - holdtbuf + strlen(q);
    190 	if (l > BUFSIZ) {
    191 		(void) write(2, "Termcap entry too long\n", 23);
    192 		q[BUFSIZ - (p-tbuf)] = 0;
    193 	}
    194 	(void) strcpy(p, q+1);
    195 	tbuf = holdtbuf;
    196 	hopcount = 0;			/* unwind recursion */
    197 	tngetsize(tbuf);
    198 	return (1);
    199 }
    200 
    201 /*
    202  * Tnamatch deals with name matching.  The first field of the termcap
    203  * entry is a sequence of names separated by |'s, so we compare
    204  * against each such name.  The normal : terminator after the last
    205  * name (before the first field) stops us.
    206  */
    207 
    208 int
    209 tnamatch(char *np)
    210 {
    211 	char *Np, *Bp;
    212 
    213 	Bp = tbuf;
    214 	if (*Bp == '#')
    215 		return (0);
    216 	for (;;) {
    217 		for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
    218 			continue;
    219 		if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
    220 			return (1);
    221 		while (*Bp && *Bp != ':' && *Bp != '|')
    222 			Bp++;
    223 		if (*Bp == 0 || *Bp == ':')
    224 			return (0);
    225 		Bp++;
    226 	}
    227 }
    228 
    229 /*
    230  * Skip to the next field.  Notice that this is very dumb, not
    231  * knowing about \: escapes or any such.  If necessary, :'s can be put
    232  * into the termcap file in octal.
    233  */
    234 
    235 static char *
    236 tskip(char *bp)
    237 {
    238 
    239 	while (*bp && *bp != ':')
    240 		bp++;
    241 	if (*bp == ':') {
    242 		do {
    243 			bp++;
    244 			while (isspace(*bp))
    245 				bp++;
    246 		} while (*bp == ':');
    247 	}
    248 	return (bp);
    249 }
    250 
    251 /*
    252  * Return the (numeric) option id.
    253  * Numeric options look like
    254  *	li#80
    255  * i.e. the option string is separated from the numeric value by
    256  * a # character.  If the option is not found we return -1.
    257  * Note that we handle octal numbers beginning with 0.
    258  */
    259 
    260 int
    261 tgetnum(char *id)
    262 {
    263 	int i, base;
    264 	char *bp = tbuf;
    265 
    266 	for (;;) {
    267 		bp = tskip(bp);
    268 		if (*bp == 0)
    269 			return (-1);
    270 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
    271 			continue;
    272 		if (*bp == '@')
    273 			return (-1);
    274 		if (*bp != '#')
    275 			continue;
    276 		bp++;
    277 		base = 10;
    278 		if (*bp == '0')
    279 			base = 8;
    280 		i = 0;
    281 		while (isdigit(*bp))
    282 			i *= base, i += *bp++ - '0';
    283 		return (i);
    284 	}
    285 }
    286 
    287 /*
    288  * Handle a flag option.
    289  * Flag options are given "naked", i.e. followed by a : or the end
    290  * of the buffer.  Return 1 if we find the option, or 0 if it is
    291  * not given.
    292  */
    293 
    294 int
    295 tgetflag(char *id)
    296 {
    297 	char *bp = tbuf;
    298 
    299 	for (;;) {
    300 		bp = tskip(bp);
    301 		if (!*bp)
    302 			return (0);
    303 		if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
    304 			if (!*bp || *bp == ':')
    305 				return (1);
    306 			else if (*bp == '@')
    307 				return (0);
    308 		}
    309 	}
    310 }
    311 
    312 /*
    313  * Get a string valued option.
    314  * These are given as
    315  *	cl=^Z
    316  * Much decoding is done on the strings, and the strings are
    317  * placed in area, which is a ref parameter which is updated.
    318  * No checking on area overflow.
    319  */
    320 
    321 char *
    322 tgetstr(char *id, char **area)
    323 {
    324 	char *bp = tbuf;
    325 
    326 	for (;;) {
    327 		bp = tskip(bp);
    328 		if (!*bp)
    329 			return (0);
    330 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
    331 			continue;
    332 		if (*bp == '@')
    333 			return (0);
    334 		if (*bp != '=')
    335 			continue;
    336 		bp++;
    337 		return (tdecode(bp, area));
    338 	}
    339 }
    340 
    341 /*
    342  * Tdecode does the grung work to decode the
    343  * string capability escapes.
    344  */
    345 
    346 static char *
    347 tdecode(char *str, char **area)
    348 {
    349 	char *cp;
    350 	int c;
    351 	char *dp;
    352 	int i;
    353 
    354 	cp = *area;
    355 	while (((c = *str++) != 0) && c != ':') {
    356 		switch (c) {
    357 
    358 		case '^':
    359 			c = *str++ & 037;
    360 			break;
    361 
    362 		case '\\':
    363 			dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
    364 			c = *str++;
    365 nextc:
    366 			if (*dp++ == c) {
    367 				c = *dp++;
    368 				break;
    369 			}
    370 			dp++;
    371 			if (*dp)
    372 				goto nextc;
    373 			if (isdigit(c)) {
    374 				c -= '0', i = 2;
    375 				do
    376 					c <<= 3, c |= *str++ - '0';
    377 				while (--i && isdigit(*str));
    378 			}
    379 			break;
    380 		}
    381 		*cp++ = (char) c;
    382 	}
    383 	*cp++ = 0;
    384 	str = *area;
    385 	*area = cp;
    386 	return (str);
    387 }
    388 
    389 #include <sys/ioctl.h>
    390 
    391 static void
    392 tngetsize(char *bp)
    393 {
    394 	struct winsize ws;
    395 	char *np, *cp;
    396 
    397 	if (ioctl(1, TIOCGWINSZ, (char *)&ws) < 0)
    398 		return;
    399 	if (ws.ws_row == 0 || ws.ws_col == 0 ||
    400 	    ws.ws_row > 999 || ws.ws_col > 999)
    401 		return;
    402 	cp = index(bp, ':');	/* find start of description */
    403 	bp = rindex(bp, 0);	/* find end of description */
    404 	np = bp + 15;		/* allow enough room for stuff below */
    405 	while (bp >= cp)	/* move description right 15 chars */
    406 		*np-- = *bp--;
    407 	bp++;			/* bp now points to where ':' used to be */
    408 	*bp++ = ':';
    409 	*bp++ = 'l';
    410 	*bp++ = 'i';
    411 	*bp++ = '#';
    412 	bp = appendsmalldec(bp, ws.ws_row);
    413 	*bp++ = ':';
    414 	*bp++ = 'c';
    415 	*bp++ = 'o';
    416 	*bp++ = '#';
    417 	bp = appendsmalldec(bp, ws.ws_col);
    418 	*bp++ = ':';
    419 	while (bp <= np)	/* space fill to start of orig description */
    420 		*bp++ = ' ';
    421 }
    422 
    423 static char *
    424 appendsmalldec(char *bp, int val)
    425 {
    426 	int	i;
    427 
    428 	if ((i = val / 100) != 0) {
    429 		*bp++ = '0' + i;
    430 		val %= 100;
    431 		if (0 == val / 10)
    432 			*bp++ = '0'; /* place holder because next test fails */
    433 	}
    434 	if ((i = val / 10) != 0)
    435 		*bp++ = '0' + i;
    436 	*bp++ = '0' + val % 10;
    437 	return (bp);
    438 }
    439