xref: /titanic_51/usr/src/cmd/captoinfo/otermcap.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
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 /*	Copyright (c) 1988 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 /* Copyright (c) 1979 Regents of the University of California	*/
34 /* Modified to:							*/
35 /* 1) remember the name of the first tc= parameter		*/
36 /*	encountered during parsing.				*/
37 /* 2) handle multiple invocations of tgetent().			*/
38 /* 3) tskip() is now available outside of the library.		*/
39 /* 4) remember $TERM name for error messages.			*/
40 /* 5) have a larger buffer.					*/
41 /* 6) really fix the bug that 5) got around. This fix by	*/
42 /*		Marion Hakanson, orstcs!hakanson		*/
43 
44 
45 #include "otermcap.h"
46 #define	MAXHOP	32	/* max number of tc= indirections */
47 
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #include <fcntl.h>
51 #include <sys/uio.h>
52 #include <unistd.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <ctype.h>
57 
58 #include <signal.h>   /* use this file to determine if this is SVR4.0 system */
59 #ifdef SIGSTOP /* SVR4.0 and beyond */
60 #define	E_TERMCAP "/usr/share/lib/termcap"
61 #else
62 #define	E_TERMCAP "/etc/termcap"
63 #endif
64 
65 /*
66  * termcap - routines for dealing with the terminal capability data base
67  *
68  * BUG:		Should use a "last" pointer in tbuf, so that searching
69  *		for capabilities alphabetically would not be a n**2/2
70  *		process when large numbers of capabilities are given.
71  * Note:	If we add a last pointer now we will screw up the
72  *		tc capability. We really should compile termcap.
73  *
74  * Essentially all the work here is scanning and decoding escapes
75  * in string capabilities.  We don't use stdio because the editor
76  * doesn't, and because living w/o it is not hard.
77  */
78 
79 static	char *tbuf;
80 static	int hopcount;	/* detect infinite loops in termcap, init 0 */
81 char	*tskip();
82 char	*otgetstr();
83 
84 /* Tony Hansen */
85 int	TLHtcfound = 0;
86 char	TLHtcname[16];
87 static	char *termname;
88 
89 static int _tgetent(char *, char *);
90 static int otnchktc();
91 static char *tdecode(char *, char **);
92 static int otnamatch(char *);
93 /*
94  * Get an entry for terminal name in buffer bp,
95  * from the termcap file.  Parse is very rudimentary;
96  * we just notice escaped newlines.
97  */
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()
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 /* static - TLH removed */ 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 otgetnum(char *id)
306 {
307 	int i, base;
308 	char *bp = tbuf;
309 
310 	for (;;) {
311 		bp = tskip(bp);
312 		if (*bp == 0)
313 			return (-1);
314 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
315 			continue;
316 		if (*bp == '@')
317 			return (-1);
318 		if (*bp != '#')
319 			continue;
320 		bp++;
321 		base = 10;
322 		if (*bp == '0')
323 			base = 8;
324 		i = 0;
325 		while (isdigit(*bp))
326 			i *= base, i += *bp++ - '0';
327 		return (i);
328 	}
329 }
330 
331 /*
332  * Handle a flag option.
333  * Flag options are given "naked", i.e. followed by a : or the end
334  * of the buffer.  Return 1 if we find the option, or 0 if it is
335  * not given.
336  */
337 otgetflag(char *id)
338 {
339 	char *bp = tbuf;
340 
341 	for (;;) {
342 		bp = tskip(bp);
343 		if (!*bp)
344 			return (0);
345 		if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
346 			if (!*bp || *bp == ':')
347 				return (1);
348 			else if (*bp == '@')
349 				return (0);
350 		}
351 	}
352 }
353 
354 /*
355  * Get a string valued option.
356  * These are given as
357  *	cl=^Z
358  * Much decoding is done on the strings, and the strings are
359  * placed in area, which is a ref parameter which is updated.
360  * No checking on area overflow.
361  */
362 char *
363 otgetstr(char *id, char **area)
364 {
365 	char *bp = tbuf;
366 
367 	for (; ; ) {
368 		bp = tskip(bp);
369 		if (!*bp)
370 			return (0);
371 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
372 			continue;
373 		if (*bp == '@')
374 			return (0);
375 		if (*bp != '=')
376 			continue;
377 		bp++;
378 		return (tdecode(bp, area));
379 	}
380 }
381 
382 /*
383  * Tdecode does the grung work to decode the
384  * string capability escapes.
385  */
386 static char *
387 tdecode(char *str, char **area)
388 {
389 	char *cp;
390 	int c;
391 	char *dp;
392 	int i;
393 
394 	cp = *area;
395 	while ((c = *str++) != '\0' && c != ':') {
396 		switch (c) {
397 
398 		case '^':
399 			c = *str++ & 037;
400 			break;
401 
402 		case '\\':
403 			dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
404 			c = *str++;
405 nextc:
406 			if (*dp++ == c) {
407 				c = *dp++;
408 				break;
409 			}
410 			dp++;
411 			if (*dp)
412 				goto nextc;
413 			if (isdigit(c)) {
414 				c -= '0', i = 2;
415 				do
416 					c <<= 3, c |= *str++ - '0';
417 				while (--i && isdigit(*str));
418 			}
419 			break;
420 		}
421 		*cp++ = c;
422 	}
423 	*cp++ = 0;
424 	str = *area;
425 	*area = cp;
426 	return (str);
427 }
428