Home | History | Annotate | Download | only in captoinfo
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*	Copyright (c) 1988 AT&T	*/
     28 /*	  All Rights Reserved  	*/
     29 
     30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     31 
     32 /* Copyright (c) 1979 Regents of the University of California	*/
     33 /* Modified to:							*/
     34 /* 1) remember the name of the first tc= parameter		*/
     35 /*	encountered during parsing.				*/
     36 /* 2) handle multiple invocations of tgetent().			*/
     37 /* 3) tskip() is now available outside of the library.		*/
     38 /* 4) remember $TERM name for error messages.			*/
     39 /* 5) have a larger buffer.					*/
     40 /* 6) really fix the bug that 5) got around. This fix by	*/
     41 /*		Marion Hakanson, orstcs!hakanson		*/
     42 
     43 
     44 #include "otermcap.h"
     45 #define	MAXHOP	32	/* max number of tc= indirections */
     46 
     47 #include <sys/types.h>
     48 #include <sys/stat.h>
     49 #include <fcntl.h>
     50 #include <sys/uio.h>
     51 #include <unistd.h>
     52 #include <stdio.h>
     53 #include <stdlib.h>
     54 #include <string.h>
     55 #include <ctype.h>
     56 
     57 #include <signal.h>   /* use this file to determine if this is SVR4.0 system */
     58 #ifdef SIGSTOP /* SVR4.0 and beyond */
     59 #define	E_TERMCAP "/usr/share/lib/termcap"
     60 #else
     61 #define	E_TERMCAP "/etc/termcap"
     62 #endif
     63 
     64 /*
     65  * termcap - routines for dealing with the terminal capability data base
     66  *
     67  * BUG:		Should use a "last" pointer in tbuf, so that searching
     68  *		for capabilities alphabetically would not be a n**2/2
     69  *		process when large numbers of capabilities are given.
     70  * Note:	If we add a last pointer now we will screw up the
     71  *		tc capability. We really should compile termcap.
     72  *
     73  * Essentially all the work here is scanning and decoding escapes
     74  * in string capabilities.  We don't use stdio because the editor
     75  * doesn't, and because living w/o it is not hard.
     76  */
     77 
     78 static	char *tbuf;
     79 static	int hopcount;	/* detect infinite loops in termcap, init 0 */
     80 char	*tskip(char *);
     81 char	*otgetstr(char *, char **);
     82 
     83 /* Tony Hansen */
     84 int	TLHtcfound = 0;
     85 char	TLHtcname[16];
     86 static	char *termname;
     87 
     88 static int _tgetent(char *, char *);
     89 static int otnchktc(void);
     90 static char *tdecode(char *, char **);
     91 static int otnamatch(char *);
     92 /*
     93  * Get an entry for terminal name in buffer bp,
     94  * from the termcap file.  Parse is very rudimentary;
     95  * we just notice escaped newlines.
     96  */
     97 int
     98 otgetent(char *bp, char *name)
     99 {
    100 	/* Tony Hansen */
    101 	int ret;
    102 	TLHtcfound = 0;
    103 	hopcount = 0;
    104 	termname = name;
    105 	ret = _tgetent(bp, name);
    106 	/*
    107 	 * There is some sort of bug in the check way down below to prevent
    108 	 * buffer overflow. I really don't want to track it down, so I
    109 	 * upped the standard buffer size and check here to see if the created
    110 	 * buffer is larger than the old buffer size.
    111 	 */
    112 	if (strlen(bp) >= 1024)
    113 		(void) fprintf(stderr,
    114 		    "tgetent(): TERM=%s: Termcap entry is too long.\n",
    115 		    termname);
    116 	return (ret);
    117 }
    118 
    119 static int
    120 _tgetent(char *bp, char *name)
    121 {
    122 	char *cp;
    123 	int c;
    124 	int i = 0, cnt = 0;
    125 	char ibuf[TBUFSIZE];
    126 	char *cp2;
    127 	int tf;
    128 
    129 	tbuf = bp;
    130 	tf = 0;
    131 #ifndef V6
    132 	cp = getenv("TERMCAP");
    133 	/*
    134 	 * TERMCAP can have one of two things in it. It can be the
    135 	 * name of a file to use instead of /etc/termcap. In this
    136 	 * case it better start with a "/". Or it can be an entry to
    137 	 * use so we don't have to read the file. In this case it
    138 	 * has to already have the newlines crunched out.
    139 	 */
    140 	if (cp && *cp) {
    141 		if (*cp != '/') {
    142 			cp2 = getenv("TERM");
    143 			if (cp2 == (char *)0 || strcmp(name, cp2) == 0) {
    144 				(void) strcpy(bp, cp);
    145 				return (otnchktc());
    146 			} else {
    147 				tf = open(E_TERMCAP, 0);
    148 			}
    149 		} else
    150 			tf = open(cp, 0);
    151 	}
    152 	if (tf == 0)
    153 		tf = open(E_TERMCAP, 0);
    154 #else
    155 	tf = open(E_TERMCAP, 0);
    156 #endif
    157 	if (tf < 0)
    158 		return (-1);
    159 	for (; ; ) {
    160 		cp = bp;
    161 		for (; ; ) {
    162 			if (i == cnt) {
    163 				cnt = read(tf, ibuf, TBUFSIZE);
    164 				if (cnt <= 0) {
    165 					(void) close(tf);
    166 					return (0);
    167 				}
    168 				i = 0;
    169 			}
    170 			c = ibuf[i++];
    171 			if (c == '\n') {
    172 				if (cp > bp && cp[-1] == '\\') {
    173 					cp--;
    174 					continue;
    175 				}
    176 				break;
    177 			}
    178 			if (cp >= bp + TBUFSIZE) {
    179 				(void) fprintf(stderr, "tgetent(): TERM=%s: "
    180 				    "Termcap entry too long\n", termname);
    181 				break;
    182 			} else
    183 				*cp++ = c;
    184 		}
    185 		*cp = 0;
    186 
    187 		/*
    188 		 * The real work for the match.
    189 		 */
    190 		if (otnamatch(name)) {
    191 			(void) close(tf);
    192 			return (otnchktc());
    193 		}
    194 	}
    195 }
    196 
    197 /*
    198  * otnchktc: check the last entry, see if it's tc=xxx. If so,
    199  * recursively find xxx and append that entry (minus the names)
    200  * to take the place of the tc=xxx entry. This allows termcap
    201  * entries to say "like an HP2621 but doesn't turn on the labels".
    202  * Note that this works because of the left to right scan.
    203  */
    204 static int
    205 otnchktc(void)
    206 {
    207 	char *p, *q;
    208 #define	TERMNAMESIZE 16
    209 	char tcname[TERMNAMESIZE];	/* name of similar terminal */
    210 	char tcbuf[TBUFSIZE];
    211 	char *holdtbuf = tbuf;
    212 	int l;
    213 
    214 	p = tbuf + strlen(tbuf) - 2;	/* before the last colon */
    215 	while (*--p != ':')
    216 		if (p < tbuf) {
    217 			(void) fprintf(stderr, "tnchktc(): TERM=%s: Bad "
    218 			    "termcap entry\n", termname);
    219 			return (0);
    220 		}
    221 	p++;
    222 	/* p now points to beginning of last field */
    223 	if (p[0] != 't' || p[1] != 'c')
    224 		return (1);
    225 	(void) strncpy(tcname, p + 3, TERMNAMESIZE);	/* TLH */
    226 	q = tcname;
    227 	while (*q && *q != ':')
    228 		q++;
    229 	*q = 0;
    230 	if (++hopcount > MAXHOP) {
    231 		(void) fprintf(stderr, "tnchktc(): TERM=%s: Infinite tc= "
    232 		    "loop\n", termname);
    233 		return (0);
    234 	}
    235 	if (_tgetent(tcbuf, tcname) != 1)
    236 		return (0);
    237 	/* Tony Hansen */
    238 	TLHtcfound++;
    239 	(void) strcpy(TLHtcname, tcname);
    240 
    241 	for (q = tcbuf; *q != ':'; q++)
    242 		;
    243 	l = p - holdtbuf + strlen(q);
    244 	if (l > TBUFSIZE) {
    245 		(void) fprintf(stderr, "tnchktc(): TERM=%s: Termcap entry "
    246 		    "too long\n", termname);
    247 		q[TBUFSIZE - (p - holdtbuf)] = 0;
    248 	}
    249 	(void) strcpy(p, q + 1);
    250 	tbuf = holdtbuf;
    251 	return (1);
    252 }
    253 
    254 /*
    255  * Tnamatch deals with name matching.  The first field of the termcap
    256  * entry is a sequence of names separated by |'s, so we compare
    257  * against each such name.  The normal : terminator after the last
    258  * name (before the first field) stops us.
    259  */
    260 static int
    261 otnamatch(char *np)
    262 {
    263 	char *Np, *Bp;
    264 
    265 	Bp = tbuf;
    266 	if (*Bp == '#')
    267 		return (0);
    268 	for (;;) {
    269 		for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
    270 			continue;
    271 		if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
    272 			return (1);
    273 		while (*Bp && *Bp != ':' && *Bp != '|')
    274 			Bp++;
    275 		if (*Bp == 0 || *Bp == ':')
    276 			return (0);
    277 		Bp++;
    278 	}
    279 }
    280 
    281 /*
    282  * Skip to the next field.  Notice that this is very dumb, not
    283  * knowing about \: escapes or any such.  If necessary, :'s can be put
    284  * into the termcap file in octal.
    285  */
    286 char *
    287 tskip(char *bp)
    288 {
    289 
    290 	while (*bp && *bp != ':')
    291 		bp++;
    292 	if (*bp == ':')
    293 		bp++;
    294 	return (bp);
    295 }
    296 
    297 /*
    298  * Return the (numeric) option id.
    299  * Numeric options look like
    300  *	li#80
    301  * i.e. the option string is separated from the numeric value by
    302  * a # character.  If the option is not found we return -1.
    303  * Note that we handle octal numbers beginning with 0.
    304  */
    305 int
    306 otgetnum(char *id)
    307 {
    308 	int i, base;
    309 	char *bp = tbuf;
    310 
    311 	for (;;) {
    312 		bp = tskip(bp);
    313 		if (*bp == 0)
    314 			return (-1);
    315 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
    316 			continue;
    317 		if (*bp == '@')
    318 			return (-1);
    319 		if (*bp != '#')
    320 			continue;
    321 		bp++;
    322 		base = 10;
    323 		if (*bp == '0')
    324 			base = 8;
    325 		i = 0;
    326 		while (isdigit(*bp))
    327 			i *= base, i += *bp++ - '0';
    328 		return (i);
    329 	}
    330 }
    331 
    332 /*
    333  * Handle a flag option.
    334  * Flag options are given "naked", i.e. followed by a : or the end
    335  * of the buffer.  Return 1 if we find the option, or 0 if it is
    336  * not given.
    337  */
    338 int
    339 otgetflag(char *id)
    340 {
    341 	char *bp = tbuf;
    342 
    343 	for (;;) {
    344 		bp = tskip(bp);
    345 		if (!*bp)
    346 			return (0);
    347 		if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
    348 			if (!*bp || *bp == ':')
    349 				return (1);
    350 			else if (*bp == '@')
    351 				return (0);
    352 		}
    353 	}
    354 }
    355 
    356 /*
    357  * Get a string valued option.
    358  * These are given as
    359  *	cl=^Z
    360  * Much decoding is done on the strings, and the strings are
    361  * placed in area, which is a ref parameter which is updated.
    362  * No checking on area overflow.
    363  */
    364 char *
    365 otgetstr(char *id, char **area)
    366 {
    367 	char *bp = tbuf;
    368 
    369 	for (; ; ) {
    370 		bp = tskip(bp);
    371 		if (!*bp)
    372 			return (0);
    373 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
    374 			continue;
    375 		if (*bp == '@')
    376 			return (0);
    377 		if (*bp != '=')
    378 			continue;
    379 		bp++;
    380 		return (tdecode(bp, area));
    381 	}
    382 }
    383 
    384 /*
    385  * Tdecode does the grung work to decode the
    386  * string capability escapes.
    387  */
    388 static char *
    389 tdecode(char *str, char **area)
    390 {
    391 	char *cp;
    392 	int c;
    393 	char *dp;
    394 	int i;
    395 
    396 	cp = *area;
    397 	while ((c = *str++) != '\0' && c != ':') {
    398 		switch (c) {
    399 
    400 		case '^':
    401 			c = *str++ & 037;
    402 			break;
    403 
    404 		case '\\':
    405 			dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
    406 			c = *str++;
    407 nextc:
    408 			if (*dp++ == c) {
    409 				c = *dp++;
    410 				break;
    411 			}
    412 			dp++;
    413 			if (*dp)
    414 				goto nextc;
    415 			if (isdigit(c)) {
    416 				c -= '0', i = 2;
    417 				do
    418 					c <<= 3, c |= *str++ - '0';
    419 				while (--i && isdigit(*str))
    420 					;
    421 			}
    422 			break;
    423 		}
    424 		*cp++ = c;
    425 	}
    426 	*cp++ = 0;
    427 	str = *area;
    428 	*area = cp;
    429 	return (str);
    430 }
    431