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 /* Copyright (c) 1979 Regents of the University of California */ 31 /* Modified to: */ 32 /* 1) remember the name of the first tc= parameter */ 33 /* encountered during parsing. */ 34 /* 2) handle multiple invocations of tgetent(). */ 35 /* 3) tskip() is now available outside of the library. */ 36 /* 4) remember $TERM name for error messages. */ 37 /* 5) have a larger buffer. */ 38 /* 6) really fix the bug that 5) got around. This fix by */ 39 /* Marion Hakanson, orstcs!hakanson */ 40 41 42 #include "otermcap.h" 43 #define MAXHOP 32 /* max number of tc= indirections */ 44 45 #include <sys/types.h> 46 #include <sys/stat.h> 47 #include <fcntl.h> 48 #include <sys/uio.h> 49 #include <unistd.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <ctype.h> 54 55 #include <signal.h> /* use this file to determine if this is SVR4.0 system */ 56 #ifdef SIGSTOP /* SVR4.0 and beyond */ 57 #define E_TERMCAP "/usr/share/lib/termcap" 58 #else 59 #define E_TERMCAP "/etc/termcap" 60 #endif 61 62 /* 63 * termcap - routines for dealing with the terminal capability data base 64 * 65 * BUG: Should use a "last" pointer in tbuf, so that searching 66 * for capabilities alphabetically would not be a n**2/2 67 * process when large numbers of capabilities are given. 68 * Note: If we add a last pointer now we will screw up the 69 * tc capability. We really should compile termcap. 70 * 71 * Essentially all the work here is scanning and decoding escapes 72 * in string capabilities. We don't use stdio because the editor 73 * doesn't, and because living w/o it is not hard. 74 */ 75 76 static char *tbuf; 77 static int hopcount; /* detect infinite loops in termcap, init 0 */ 78 char *tskip(char *); 79 char *otgetstr(char *, char **); 80 81 /* Tony Hansen */ 82 int TLHtcfound = 0; 83 char TLHtcname[16]; 84 static char *termname; 85 86 static int _tgetent(char *, char *); 87 static int otnchktc(void); 88 static char *tdecode(char *, char **); 89 static int otnamatch(char *); 90 /* 91 * Get an entry for terminal name in buffer bp, 92 * from the termcap file. Parse is very rudimentary; 93 * we just notice escaped newlines. 94 */ 95 int 96 otgetent(char *bp, char *name) 97 { 98 /* Tony Hansen */ 99 int ret; 100 TLHtcfound = 0; 101 hopcount = 0; 102 termname = name; 103 ret = _tgetent(bp, name); 104 /* 105 * There is some sort of bug in the check way down below to prevent 106 * buffer overflow. I really don't want to track it down, so I 107 * upped the standard buffer size and check here to see if the created 108 * buffer is larger than the old buffer size. 109 */ 110 if (strlen(bp) >= 1024) 111 (void) fprintf(stderr, 112 "tgetent(): TERM=%s: Termcap entry is too long.\n", 113 termname); 114 return (ret); 115 } 116 117 static int 118 _tgetent(char *bp, char *name) 119 { 120 char *cp; 121 int c; 122 int i = 0, cnt = 0; 123 char ibuf[TBUFSIZE]; 124 char *cp2; 125 int tf; 126 127 tbuf = bp; 128 tf = 0; 129 #ifndef V6 130 cp = getenv("TERMCAP"); 131 /* 132 * TERMCAP can have one of two things in it. It can be the 133 * name of a file to use instead of /etc/termcap. In this 134 * case it better start with a "/". Or it can be an entry to 135 * use so we don't have to read the file. In this case it 136 * has to already have the newlines crunched out. 137 */ 138 if (cp && *cp) { 139 if (*cp != '/') { 140 cp2 = getenv("TERM"); 141 if (cp2 == (char *)0 || strcmp(name, cp2) == 0) { 142 (void) strcpy(bp, cp); 143 return (otnchktc()); 144 } else { 145 tf = open(E_TERMCAP, 0); 146 } 147 } else 148 tf = open(cp, 0); 149 } 150 if (tf == 0) 151 tf = open(E_TERMCAP, 0); 152 #else 153 tf = open(E_TERMCAP, 0); 154 #endif 155 if (tf < 0) 156 return (-1); 157 for (; ; ) { 158 cp = bp; 159 for (; ; ) { 160 if (i == cnt) { 161 cnt = read(tf, ibuf, TBUFSIZE); 162 if (cnt <= 0) { 163 (void) close(tf); 164 return (0); 165 } 166 i = 0; 167 } 168 c = ibuf[i++]; 169 if (c == '\n') { 170 if (cp > bp && cp[-1] == '\\') { 171 cp--; 172 continue; 173 } 174 break; 175 } 176 if (cp >= bp + TBUFSIZE) { 177 (void) fprintf(stderr, "tgetent(): TERM=%s: " 178 "Termcap entry too long\n", termname); 179 break; 180 } else 181 *cp++ = c; 182 } 183 *cp = 0; 184 185 /* 186 * The real work for the match. 187 */ 188 if (otnamatch(name)) { 189 (void) close(tf); 190 return (otnchktc()); 191 } 192 } 193 } 194 195 /* 196 * otnchktc: check the last entry, see if it's tc=xxx. If so, 197 * recursively find xxx and append that entry (minus the names) 198 * to take the place of the tc=xxx entry. This allows termcap 199 * entries to say "like an HP2621 but doesn't turn on the labels". 200 * Note that this works because of the left to right scan. 201 */ 202 static int 203 otnchktc(void) 204 { 205 char *p, *q; 206 #define TERMNAMESIZE 16 207 char tcname[TERMNAMESIZE]; /* name of similar terminal */ 208 char tcbuf[TBUFSIZE]; 209 char *holdtbuf = tbuf; 210 int l; 211 212 p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 213 while (*--p != ':') 214 if (p < tbuf) { 215 (void) fprintf(stderr, "tnchktc(): TERM=%s: Bad " 216 "termcap entry\n", termname); 217 return (0); 218 } 219 p++; 220 /* p now points to beginning of last field */ 221 if (p[0] != 't' || p[1] != 'c') 222 return (1); 223 (void) strncpy(tcname, p + 3, TERMNAMESIZE); /* TLH */ 224 q = tcname; 225 while (*q && *q != ':') 226 q++; 227 *q = 0; 228 if (++hopcount > MAXHOP) { 229 (void) fprintf(stderr, "tnchktc(): TERM=%s: Infinite tc= " 230 "loop\n", termname); 231 return (0); 232 } 233 if (_tgetent(tcbuf, tcname) != 1) 234 return (0); 235 /* Tony Hansen */ 236 TLHtcfound++; 237 (void) strcpy(TLHtcname, tcname); 238 239 for (q = tcbuf; *q != ':'; q++) 240 ; 241 l = p - holdtbuf + strlen(q); 242 if (l > TBUFSIZE) { 243 (void) fprintf(stderr, "tnchktc(): TERM=%s: Termcap entry " 244 "too long\n", termname); 245 q[TBUFSIZE - (p - holdtbuf)] = 0; 246 } 247 (void) strcpy(p, q + 1); 248 tbuf = holdtbuf; 249 return (1); 250 } 251 252 /* 253 * Tnamatch deals with name matching. The first field of the termcap 254 * entry is a sequence of names separated by |'s, so we compare 255 * against each such name. The normal : terminator after the last 256 * name (before the first field) stops us. 257 */ 258 static int 259 otnamatch(char *np) 260 { 261 char *Np, *Bp; 262 263 Bp = tbuf; 264 if (*Bp == '#') 265 return (0); 266 for (;;) { 267 for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 268 continue; 269 if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 270 return (1); 271 while (*Bp && *Bp != ':' && *Bp != '|') 272 Bp++; 273 if (*Bp == 0 || *Bp == ':') 274 return (0); 275 Bp++; 276 } 277 } 278 279 /* 280 * Skip to the next field. Notice that this is very dumb, not 281 * knowing about \: escapes or any such. If necessary, :'s can be put 282 * into the termcap file in octal. 283 */ 284 char * 285 tskip(char *bp) 286 { 287 288 while (*bp && *bp != ':') 289 bp++; 290 if (*bp == ':') 291 bp++; 292 return (bp); 293 } 294 295 /* 296 * Return the (numeric) option id. 297 * Numeric options look like 298 * li#80 299 * i.e. the option string is separated from the numeric value by 300 * a # character. If the option is not found we return -1. 301 * Note that we handle octal numbers beginning with 0. 302 */ 303 int 304 otgetnum(char *id) 305 { 306 int i, base; 307 char *bp = tbuf; 308 309 for (;;) { 310 bp = tskip(bp); 311 if (*bp == 0) 312 return (-1); 313 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 314 continue; 315 if (*bp == '@') 316 return (-1); 317 if (*bp != '#') 318 continue; 319 bp++; 320 base = 10; 321 if (*bp == '0') 322 base = 8; 323 i = 0; 324 while (isdigit(*bp)) 325 i *= base, i += *bp++ - '0'; 326 return (i); 327 } 328 } 329 330 /* 331 * Handle a flag option. 332 * Flag options are given "naked", i.e. followed by a : or the end 333 * of the buffer. Return 1 if we find the option, or 0 if it is 334 * not given. 335 */ 336 int 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 } 420 break; 421 } 422 *cp++ = c; 423 } 424 *cp++ = 0; 425 str = *area; 426 *area = cp; 427 return (str); 428 } 429