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