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