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