1: /* Work-alike for termcap, plus extra features. 2: Copyright (C) 1985 Richard M. Stallman. 3: 4: This file is part of GNU Emacs. 5: 6: GNU Emacs is distributed in the hope that it will be useful, 7: but WITHOUT ANY WARRANTY. No author or distributor 8: accepts responsibility to anyone for the consequences of using it 9: or for whether it serves any particular purpose or works at all, 10: unless he says so in writing. Refer to the GNU Emacs General Public 11: License for full details. 12: 13: Everyone is granted permission to copy, modify and redistribute 14: GNU Emacs, but only under the conditions described in the 15: GNU Emacs General Public License. A copy of this license is 16: supposed to have been given to you along with GNU Emacs so you 17: can know your rights and responsibilities. It should be in a 18: file named COPYING. Among other things, the copyright notice 19: and this notice must be preserved on all copies. */ 20: 21: 22: 23: /* BUFSIZE is the initial size allocated for the buffer 24: for reading the termcap file. 25: It is not a limit. 26: Make it large normally for speed. 27: Make it variable when debugging, so can exercise 28: increasing the space dynamically. */ 29: 30: #ifndef BUFSIZE 31: #ifdef DEBUG 32: #define BUFSIZE bufsize 33: 34: int bufsize = 128; 35: #else 36: #define BUFSIZE 2048 37: #endif 38: #endif 39: 40: static 41: memory_out () 42: { 43: write (2, "Virtual memory exhausted\n", 25); 44: exit (1); 45: } 46: 47: static int 48: xmalloc (size) 49: int size; 50: { 51: register tem = malloc (size); 52: if (!tem) 53: memory_out (); 54: return tem; 55: } 56: 57: static int 58: xrealloc (ptr, size) 59: int ptr; 60: int size; 61: { 62: register tem = realloc (ptr, size); 63: if (!tem) 64: memory_out (); 65: return tem; 66: } 67: 68: /* Looking up capabilities in the entry already found */ 69: 70: /* The pointer to the data made by tgetent is left here 71: for tgetnum, tgetflag and tgetstr to find. */ 72: 73: static char *term_entry; 74: 75: static char *tgetst1 (); 76: 77: /* This is the main subroutine that is used to search 78: an entry for a particular capability */ 79: 80: static char * 81: find_capability (bp, cap) 82: register char *bp, *cap; 83: { 84: for (; *bp; bp++) 85: if (bp[0] == ':' 86: && bp[1] == cap[0] 87: && bp[2] == cap[1]) 88: return &bp[4]; 89: return 0; 90: } 91: 92: int 93: tgetnum (cap) 94: char *cap; 95: { 96: register char *ptr = find_capability (term_entry, cap); 97: if (!ptr || ptr[-1] != '#') 98: return -1; 99: return atoi (ptr); 100: } 101: 102: int 103: tgetflag (cap) 104: char *cap; 105: { 106: register char *ptr = find_capability (term_entry, cap); 107: return 0 != ptr && ptr[-1] == ':'; 108: } 109: 110: /* Look up a string-valued capability `cap'. 111: If `area' is nonzero, it points to a pointer to a block in which 112: to store the string. That pointer is advanced over the space used. 113: If `area' is zero, space is allocated with `malloc'. */ 114: 115: char * 116: tgetstr (cap, area) 117: char *cap; 118: char **area; 119: { 120: register char *ptr = find_capability (term_entry, cap); 121: if (!ptr || (ptr[-1] != '=' && ptr[-1] != '~')) 122: return 0; 123: return tgetst1 (ptr, area); 124: } 125: 126: /* Table, indexed by a character in range 0100 to 0140 with 0100 subtracted, 127: gives meaning of character following \, or a space if no special meaning. 128: Eight characters per line within the string. */ 129: 130: static char esctab[] 131: = " \007\010 \033\014 \ 132: \012 \ 133: \015 \011 \013 \ 134: "; 135: 136: /* Given a pointer to a string value inside a termcap entry (`ptr'), 137: copy the value and process \ and ^ abbreviations. 138: Copy into block that *area points to, 139: or to newly allocated storage if area is 0. */ 140: 141: static char * 142: tgetst1 (ptr, area) 143: char *ptr; 144: char **area; 145: { 146: register char *p, *r; 147: register int c; 148: register int size; 149: char *ret; 150: register int c1; 151: 152: if (!ptr) 153: return 0; 154: 155: /* `ret' gets address of where to store the string */ 156: if (!area) 157: { 158: /* Compute size of block needed (may overestimate) */ 159: p = ptr; 160: while ((c = *p++) && c != ':'); 161: ret = (char *) xmalloc (p - ptr + 1); 162: } 163: else 164: ret = *area; 165: 166: /* Copy the string value, stopping at null or colon. */ 167: /* Also process ^ and \ abbreviations. */ 168: p = ptr; 169: r = ret; 170: while ((c = *p++) && c != ':') 171: { 172: if (c == '^') 173: c = *p++ & 037; 174: else if (c == '\\') 175: { 176: c = *p++; 177: if (c >= '0' && c <= '7') 178: { 179: c -= '0'; 180: size = 0; 181: 182: while (++size < 3 && (c1 = *p) >= '0' && c1 <= '7') 183: { 184: c *= 8; 185: c += c1 - '0'; 186: p++; 187: } 188: } 189: else if (c >= 0100 && c < 0200) 190: { 191: c1 = esctab[(c & ~040) - 0100]; 192: if (c1 != ' ') 193: c = c1; 194: } 195: } 196: *r++ = c; 197: } 198: *r = 0; 199: /* Update *area */ 200: if (area) 201: *area = r + 1; 202: return ret; 203: } 204: 205: /* Outputting a string with padding */ 206: 207: short ospeed; 208: char PC; 209: 210: /* Actual baud rate if positive; 211: - baud rate / 100 if negative. */ 212: 213: static short speeds[] = 214: { 215: 0, 50, 75, 110, 135, 150, 200, -3, -6, -12, 216: -18, -24, -48, -96, -192, -384 217: }; 218: 219: tputs (string, nlines, outfun) 220: register char *string; 221: int nlines; 222: register int (*outfun) (); 223: { 224: register int padcount = 0; 225: 226: if (string == (char *) 0) 227: return; 228: while (*string >= '0' && *string <= '9') 229: { 230: padcount += *string++ - '0'; 231: padcount *= 10; 232: } 233: if (*string == '.') 234: { 235: string++; 236: padcount += *string++ - '0'; 237: } 238: if (*string == '*') 239: { 240: string++; 241: padcount *= nlines; 242: } 243: while (*string) 244: (*outfun) (*string++); 245: 246: /* padcount is now in units of tenths of msec. */ 247: padcount *= speeds[ospeed]; 248: padcount /= 1000; 249: if (speeds[ospeed] < 0) 250: padcount = -padcount; 251: else 252: padcount /= 100; 253: 254: while (padcount-- > 0) 255: (*outfun) (PC); 256: } 257: 258: /* Finding the termcap entry in the termcap data base */ 259: 260: struct buffer 261: { 262: char *beg; 263: int size; 264: char *ptr; 265: int ateof; 266: int full; 267: }; 268: 269: /* Forward declarations of static functions */ 270: 271: static int scan_file (); 272: static char *gobble_line (); 273: static int compare_contin (); 274: static int name_match (); 275: 276: /* Find the termcap entry data for terminal type `name' 277: and store it in the block that `bp' points to. 278: Record its address for future use. 279: 280: If `bp' is zero, space is dynamically allocated. */ 281: 282: int 283: tgetent (bp, name) 284: char *bp, *name; 285: { 286: register char *tem; 287: register int fd; 288: struct buffer buf; 289: register char *bp1; 290: char *bp2; 291: char *term; 292: int malloc_size = 0; 293: register int c; 294: 295: tem = (char *) getenv ("TERMCAP"); 296: 297: /* If tem is non-null and starts with /, 298: it is a file name to use instead of /etc/termcap. 299: If it is non-null and does not start with /, 300: it is the entry itself, but only if it contains 301: a name matching NAME. */ 302: 303: if (tem && *tem != '/' && name_match (tem, name)) 304: { 305: if (!bp) 306: bp = tem; 307: else 308: strcpy (bp, tem); 309: goto ret; 310: } 311: 312: if (!tem) 313: tem = "/etc/termcap"; 314: 315: /* Here we know we must search a file and tem has its name. */ 316: 317: fd = open (tem, 0, 0); 318: if (fd < 0) 319: return -1; 320: 321: buf.size = BUFSIZE; 322: buf.beg = (char *) xmalloc (buf.size); 323: term = name; 324: 325: if (!bp) 326: { 327: malloc_size = buf.size; 328: bp = (char *) xmalloc (malloc_size); 329: } 330: bp1 = bp; 331: 332: while (term) 333: { 334: /* Scan file, reading it via buf, till find start of main entry */ 335: if (scan_file (term, fd, &buf) == 0) 336: return 0; 337: 338: /* Free old `term' if appropriate. */ 339: if (term != name) 340: free (term); 341: 342: /* If `bp' is malloc'd by us, make sure it is big enough. */ 343: if (malloc_size) 344: { 345: malloc_size = bp1 - bp + buf.size; 346: tem = (char *) xrealloc (bp, malloc_size); 347: bp1 += tem - bp; 348: bp = tem; 349: } 350: 351: bp2 = bp1; 352: 353: /* Copy the line of the entry from buf into bp. */ 354: tem = buf.ptr; 355: while ((*bp1++ = c = *tem++) && c != '\n') 356: /* Drop out any \ newline sequence, and following whitespace */ 357: if (c == '\\' && *tem == '\n') 358: { 359: bp1--; 360: tem++; 361: while ((c = *tem++) == ' ' || c == '\t'); 362: tem--; 363: } 364: *bp1 = 0; 365: 366: /* Does this entry refer to another terminal type's entry? */ 367: /* If something is found, copy it into heap and null-terminate it */ 368: term = tgetst1 (find_capability (bp2, "tc", '='), 0); 369: } 370: 371: close (fd); 372: free (buf.beg); 373: 374: if (malloc_size) 375: { 376: bp = (char *) xrealloc (bp, bp1 - bp + 1); 377: } 378: 379: ret: 380: term_entry = bp; 381: if (malloc_size) 382: return (int) bp; 383: return 1; 384: } 385: 386: /* Given file open on `fd' and buffer `bufp', 387: scan the file from the beginning until a line is found 388: that starts the entry for terminal type `string'. 389: Returns 1 if successful, with that line in `bufp', 390: or returns 0 if no entry found in the file. */ 391: 392: static int 393: scan_file (string, fd, bufp) 394: char *string; 395: int fd; 396: register struct buffer *bufp; 397: { 398: register char *tem; 399: register char *end; 400: 401: bufp->ptr = bufp->beg; 402: bufp->full = 0; 403: bufp->ateof = 0; 404: *bufp->ptr = 0; 405: 406: lseek (fd, 0L, 0); 407: 408: while (!bufp->ateof) 409: { 410: /* Read a line into the buffer */ 411: end = 0; 412: do 413: { 414: /* if it is continued, append another line to it, 415: until a non-continued line ends */ 416: end = gobble_line (fd, bufp, end); 417: } 418: while (!bufp->ateof && end[-2] == '\\'); 419: 420: if (*bufp->ptr != '#' 421: && name_match (bufp->ptr, string)) 422: return 1; 423: 424: /* Discard the line just processed */ 425: bufp->ptr = end; 426: } 427: return 0; 428: } 429: 430: /* Return nonzero if NAME is one of the names specified 431: by termcap entry LINE. */ 432: 433: static int 434: name_match (line, name) 435: char *line, *name; 436: { 437: register char *tem; 438: 439: if (!compare_contin (line, name)) 440: return 1; 441: /* This line starts an entry. Is it the right one? */ 442: for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++) 443: if (*tem == '|' && !compare_contin (tem + 1, name)) 444: return 1; 445: 446: return 0; 447: } 448: 449: static int 450: compare_contin (str1, str2) 451: register char *str1, *str2; 452: { 453: register int c1, c2; 454: while (1) 455: { 456: c1 = *str1++; 457: c2 = *str2++; 458: while (c1 == '\\' && *str1 == '\n') 459: { 460: str1++; 461: while ((c1 = *str1++) == ' ' || c1 == '\t'); 462: } 463: if (c2 == '\0') /* end of type being looked up */ 464: { 465: if (c1 == '|' || c1 == ':') /* If end of name in data base, */ 466: return 0; /* we win. */ 467: else 468: return 1; 469: } 470: else if (c1 != c2) 471: return 1; 472: } 473: } 474: 475: /* Make sure that the buffer <- `bufp' contains a full line 476: of the file open on `fd', starting at the place `bufp->ptr' 477: points to. Can read more of the file, discard stuff before 478: `bufp->ptr', or make the buffer bigger. 479: 480: Returns the pointer to after the newline ending the line, 481: or to the end of the file, if there is no newline to end it. 482: 483: Can also merge on continuation lines. If `append_end' is 484: nonzero, it points past the newline of a line that is 485: continued; we add another line onto it and regard the whole 486: thing as one line. The caller decides when a line is continued. */ 487: 488: static char * 489: gobble_line (fd, bufp, append_end) 490: int fd; 491: register struct buffer *bufp; 492: char *append_end; 493: { 494: register char *end; 495: register int nread; 496: register char *buf = bufp->beg; 497: register char *tem; 498: 499: if (append_end == 0) 500: append_end = bufp->ptr; 501: 502: while (1) 503: { 504: end = append_end; 505: while (*end && *end != '\n') end++; 506: if (*end) 507: break; 508: if (bufp->ateof) 509: return buf + bufp->full; 510: if (bufp->ptr == buf) 511: { 512: if (bufp->full == bufp->size) 513: { 514: bufp->size *= 2; 515: tem = (char *) xrealloc (buf, bufp->size); 516: bufp->ptr += tem - buf; 517: append_end += tem - buf; 518: bufp->beg = buf = tem; 519: } 520: } 521: else 522: { 523: append_end -= bufp->ptr - buf; 524: bcopy (bufp->ptr, buf, bufp->full -= bufp->ptr - buf); 525: bufp->ptr = buf; 526: } 527: if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full))) 528: bufp->ateof = 1; 529: bufp->full += nread; 530: if (bufp->full != bufp->size) 531: buf[bufp->full] = 0; 532: } 533: return end + 1; 534: } 535: 536: #ifdef DEBUG 537: 538: #include <stdio.h> 539: 540: main (argc, argv) 541: int argc; 542: char **argv; 543: { 544: char *term; 545: char *buf; 546: 547: if (!strcmp (argv[1], "-f")) 548: { 549: argv++; 550: bufsize = 2048; 551: } 552: term = argv[1]; 553: printf ("TERM: %s\n", term); 554: 555: buf = (char *) tgetent (0, term); 556: if ((int) buf <= 0) 557: { 558: printf ("No entry.\n"); 559: return 0; 560: } 561: 562: printf ("Entry: %s\n", buf); 563: 564: tprint ("cm"); 565: tprint ("AL"); 566: 567: printf ("co: %d\n", tgetnum ("co")); 568: printf ("am: %d\n", tgetflag ("am")); 569: } 570: 571: tprint (cap) 572: char *cap; 573: { 574: char *x = tgetstr (cap, 0); 575: register char *y; 576: 577: printf ("%s: ", cap); 578: if (x) 579: { 580: for (y = x; *y; y++) 581: if (*y <= ' ' || *y == 0177) 582: printf ("\\%0o", *y); 583: else 584: putchar (*y); 585: free (x); 586: } 587: else 588: printf ("none"); 589: putchar ('\n'); 590: } 591: 592: #endif /* DEBUG */