1 /* 2 * Copyright 1997 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 7 /* All Rights Reserved */ 8 9 /* 10 * Copyright (c) 1980 Regents of the University of California. 11 * All rights reserved. The Berkeley software License Agreement 12 * specifies the terms and conditions for redistribution. 13 */ 14 15 /*LINTLIBRARY*/ 16 17 #if 0 18 static char 19 sccsid[] = "@(#)termcap.c 1.11 88/02/08 SMI"; /* from UCB 5.1 6/5/85 */ 20 #endif 21 22 #define BUFSIZ 1024 23 #define MAXHOP 32 /* max number of tc= indirections */ 24 #define E_TERMCAP "/etc/termcap" 25 26 #include <sys/types.h> 27 #include <unistd.h> 28 #include <stdlib.h> 29 #include <stddef.h> 30 #include <string.h> 31 #include <strings.h> 32 #include <ctype.h> 33 34 /* 35 * termcap - routines for dealing with the terminal capability data base 36 * 37 * BUG: Should use a "last" pointer in tbuf, so that searching 38 * for capabilities alphabetically would not be a n**2/2 39 * process when large numbers of capabilities are given. 40 * Note: If we add a last pointer now we will screw up the 41 * tc capability. We really should compile termcap. 42 * 43 * Essentially all the work here is scanning and decoding escapes 44 * in string capabilities. We don't use stdio because the editor 45 * doesn't, and because living w/o it is not hard. 46 */ 47 48 static char *tbuf; 49 static int hopcount; /* detect infinite loops in termcap, init 0 */ 50 51 /* forward declarations */ 52 static char *tdecode(char *, char **); 53 static void tngetsize(char *); 54 static char *tskip(char *bp); 55 static char *appendsmalldec(char *, int); 56 int tnamatch(char *); 57 int tnchktc(void); 58 59 /* 60 * Get an entry for terminal name in buffer bp, 61 * from the termcap file. Parse is very rudimentary; 62 * we just notice escaped newlines. 63 */ 64 65 int 66 tgetent(char *bp, char *name) 67 { 68 char *cp; 69 int c; 70 int i = 0; 71 ssize_t cnt = 0; 72 char ibuf[BUFSIZ]; 73 int tf; 74 75 tbuf = bp; 76 tf = -1; 77 #ifndef V6 78 cp = getenv("TERMCAP"); 79 /* 80 * TERMCAP can have one of two things in it. It can be the 81 * name of a file to use instead of /etc/termcap. In this 82 * case it better start with a "/". Or it can be an entry to 83 * use so we don't have to read the file. In this case it 84 * has to already have the newlines crunched out. 85 */ 86 if (cp && *cp) { 87 if (*cp == '/') { 88 tf = open(cp, 0); 89 } else { 90 tbuf = cp; 91 c = tnamatch(name); 92 tbuf = bp; 93 if (c) { 94 (void) strcpy(bp, cp); 95 return (tnchktc()); 96 } 97 } 98 } 99 if (tf < 0) 100 tf = open(E_TERMCAP, 0); 101 #else 102 tf = open(E_TERMCAP, 0); 103 #endif 104 if (tf < 0) 105 return (-1); 106 for (;;) { 107 cp = bp; 108 for (;;) { 109 if (i == cnt) { 110 cnt = read(tf, ibuf, BUFSIZ); 111 if (cnt <= 0) { 112 (void) close(tf); 113 return (0); 114 } 115 i = 0; 116 } 117 c = ibuf[i++]; 118 if (c == '\n') { 119 if (cp > bp && cp[-1] == '\\') { 120 cp--; 121 continue; 122 } 123 break; 124 } 125 if (cp >= bp+BUFSIZ) { 126 (void) write(2, "Termcap entry too long\n", 23); 127 break; 128 } else 129 *cp++ = (char) c; 130 } 131 *cp = 0; 132 133 /* 134 * The real work for the match. 135 */ 136 if (tnamatch(name)) { 137 (void) close(tf); 138 return (tnchktc()); 139 } 140 } 141 } 142 143 /* 144 * tnchktc: check the last entry, see if it's tc=xxx. If so, 145 * recursively find xxx and append that entry (minus the names) 146 * to take the place of the tc=xxx entry. This allows termcap 147 * entries to say "like an HP2621 but doesn't turn on the labels". 148 * Note that this works because of the left to right scan. 149 */ 150 151 int 152 tnchktc(void) 153 { 154 char *p, *q; 155 char tcname[16]; /* name of similar terminal */ 156 char tcbuf[BUFSIZ]; 157 char *holdtbuf = tbuf; 158 ptrdiff_t l; 159 160 p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 161 while (*--p != ':') 162 if (p < tbuf) { 163 (void) write(2, "Bad termcap entry\n", 18); 164 return (0); 165 } 166 p++; 167 /* p now points to beginning of last field */ 168 if (p[0] != 't' || p[1] != 'c') { 169 tngetsize(tbuf); 170 return (1); 171 } 172 (void) strcpy(tcname, p+3); 173 q = tcname; 174 while (*q && *q != ':') 175 q++; 176 *q = 0; 177 if (++hopcount > MAXHOP) { 178 (void) write(2, "Infinite tc= loop\n", 18); 179 return (0); 180 } 181 if (tgetent(tcbuf, tcname) != 1) { 182 hopcount = 0; /* unwind recursion */ 183 return (0); 184 } 185 for (q = tcbuf; *q != ':'; q++) 186 ; 187 l = p - holdtbuf + strlen(q); 188 if (l > BUFSIZ) { 189 (void) write(2, "Termcap entry too long\n", 23); 190 q[BUFSIZ - (p-tbuf)] = 0; 191 } 192 (void) strcpy(p, q+1); 193 tbuf = holdtbuf; 194 hopcount = 0; /* unwind recursion */ 195 tngetsize(tbuf); 196 return (1); 197 } 198 199 /* 200 * Tnamatch deals with name matching. The first field of the termcap 201 * entry is a sequence of names separated by |'s, so we compare 202 * against each such name. The normal : terminator after the last 203 * name (before the first field) stops us. 204 */ 205 206 int 207 tnamatch(char *np) 208 { 209 char *Np, *Bp; 210 211 Bp = tbuf; 212 if (*Bp == '#') 213 return (0); 214 for (;;) { 215 for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 216 continue; 217 if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 218 return (1); 219 while (*Bp && *Bp != ':' && *Bp != '|') 220 Bp++; 221 if (*Bp == 0 || *Bp == ':') 222 return (0); 223 Bp++; 224 } 225 } 226 227 /* 228 * Skip to the next field. Notice that this is very dumb, not 229 * knowing about \: escapes or any such. If necessary, :'s can be put 230 * into the termcap file in octal. 231 */ 232 233 static char * 234 tskip(char *bp) 235 { 236 237 while (*bp && *bp != ':') 238 bp++; 239 if (*bp == ':') { 240 do { 241 bp++; 242 while (isspace(*bp)) 243 bp++; 244 } while (*bp == ':'); 245 } 246 return (bp); 247 } 248 249 /* 250 * Return the (numeric) option id. 251 * Numeric options look like 252 * li#80 253 * i.e. the option string is separated from the numeric value by 254 * a # character. If the option is not found we return -1. 255 * Note that we handle octal numbers beginning with 0. 256 */ 257 258 int 259 tgetnum(char *id) 260 { 261 int i, base; 262 char *bp = tbuf; 263 264 for (;;) { 265 bp = tskip(bp); 266 if (*bp == 0) 267 return (-1); 268 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 269 continue; 270 if (*bp == '@') 271 return (-1); 272 if (*bp != '#') 273 continue; 274 bp++; 275 base = 10; 276 if (*bp == '0') 277 base = 8; 278 i = 0; 279 while (isdigit(*bp)) 280 i *= base, i += *bp++ - '0'; 281 return (i); 282 } 283 } 284 285 /* 286 * Handle a flag option. 287 * Flag options are given "naked", i.e. followed by a : or the end 288 * of the buffer. Return 1 if we find the option, or 0 if it is 289 * not given. 290 */ 291 292 int 293 tgetflag(char *id) 294 { 295 char *bp = tbuf; 296 297 for (;;) { 298 bp = tskip(bp); 299 if (!*bp) 300 return (0); 301 if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { 302 if (!*bp || *bp == ':') 303 return (1); 304 else if (*bp == '@') 305 return (0); 306 } 307 } 308 } 309 310 /* 311 * Get a string valued option. 312 * These are given as 313 * cl=^Z 314 * Much decoding is done on the strings, and the strings are 315 * placed in area, which is a ref parameter which is updated. 316 * No checking on area overflow. 317 */ 318 319 char * 320 tgetstr(char *id, char **area) 321 { 322 char *bp = tbuf; 323 324 for (;;) { 325 bp = tskip(bp); 326 if (!*bp) 327 return (0); 328 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 329 continue; 330 if (*bp == '@') 331 return (0); 332 if (*bp != '=') 333 continue; 334 bp++; 335 return (tdecode(bp, area)); 336 } 337 } 338 339 /* 340 * Tdecode does the grung work to decode the 341 * string capability escapes. 342 */ 343 344 static char * 345 tdecode(char *str, char **area) 346 { 347 char *cp; 348 int c; 349 char *dp; 350 int i; 351 352 cp = *area; 353 while (((c = *str++) != 0) && c != ':') { 354 switch (c) { 355 356 case '^': 357 c = *str++ & 037; 358 break; 359 360 case '\\': 361 dp = "E\033^^\\\\::n\nr\rt\tb\bf\f"; 362 c = *str++; 363 nextc: 364 if (*dp++ == c) { 365 c = *dp++; 366 break; 367 } 368 dp++; 369 if (*dp) 370 goto nextc; 371 if (isdigit(c)) { 372 c -= '0', i = 2; 373 do 374 c <<= 3, c |= *str++ - '0'; 375 while (--i && isdigit(*str)); 376 } 377 break; 378 } 379 *cp++ = (char) c; 380 } 381 *cp++ = 0; 382 str = *area; 383 *area = cp; 384 return (str); 385 } 386 387 #include <sys/ioctl.h> 388 389 static void 390 tngetsize(char *bp) 391 { 392 struct winsize ws; 393 char *np, *cp; 394 395 if (ioctl(1, TIOCGWINSZ, (char *)&ws) < 0) 396 return; 397 if (ws.ws_row == 0 || ws.ws_col == 0 || 398 ws.ws_row > 999 || ws.ws_col > 999) 399 return; 400 cp = index(bp, ':'); /* find start of description */ 401 bp = rindex(bp, 0); /* find end of description */ 402 np = bp + 15; /* allow enough room for stuff below */ 403 while (bp >= cp) /* move description right 15 chars */ 404 *np-- = *bp--; 405 bp++; /* bp now points to where ':' used to be */ 406 *bp++ = ':'; 407 *bp++ = 'l'; 408 *bp++ = 'i'; 409 *bp++ = '#'; 410 bp = appendsmalldec(bp, ws.ws_row); 411 *bp++ = ':'; 412 *bp++ = 'c'; 413 *bp++ = 'o'; 414 *bp++ = '#'; 415 bp = appendsmalldec(bp, ws.ws_col); 416 *bp++ = ':'; 417 while (bp <= np) /* space fill to start of orig description */ 418 *bp++ = ' '; 419 } 420 421 static char * 422 appendsmalldec(char *bp, int val) 423 { 424 int i; 425 426 if ((i = val / 100) != 0) { 427 *bp++ = '0' + i; 428 val %= 100; 429 if (0 == val / 10) 430 *bp++ = '0'; /* place holder because next test fails */ 431 } 432 if ((i = val / 10) != 0) 433 *bp++ = '0' + i; 434 *bp++ = '0' + val % 10; 435 return (bp); 436 } 437