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