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