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