1/* vi:set ts=8 sts=4 sw=4: */ 2/* 3 * The following software is (C) 1984 Peter da Silva, the Mad Australian, in 4 * the public domain. It may be re-distributed for any purpose with the 5 * inclusion of this notice. 6 */ 7 8/* Modified by Bram Moolenaar for use with VIM - Vi Improved. */ 9/* A few bugs removed by Olaf 'Rhialto' Seibert. */ 10 11/* TERMLIB: Terminal independent database. */ 12 13#include "vim.h" 14#include "termlib.pro" 15 16#if !defined(AMIGA) && !defined(VMS) && !defined(MACOS) && !defined(RISCOS) 17# include <sgtty.h> 18#endif 19 20static int getent __ARGS((char *, char *, FILE *, int)); 21static int nextent __ARGS((char *, FILE *, int)); 22static int _match __ARGS((char *, char *)); 23static char *_addfmt __ARGS((char *, char *, int)); 24static char *_find __ARGS((char *, char *)); 25 26/* 27 * Global variables for termlib 28 */ 29 30char *tent; /* Pointer to terminal entry, set by tgetent */ 31char PC = 0; /* Pad character, default NULL */ 32char *UP = 0, *BC = 0; /* Pointers to UP and BC strings from database */ 33short ospeed; /* Baud rate (1-16, 1=300, 16=19200), as in stty */ 34 35/* 36 * Module: tgetent 37 * 38 * Purpose: Get termcap entry for <term> into buffer at <tbuf>. 39 * 40 * Calling conventions: char tbuf[TBUFSZ+], term=canonical name for terminal. 41 * 42 * Returned values: 1 = success, -1 = can't open file, 43 * 0 = can't find terminal. 44 * 45 * Notes: 46 * - Should probably supply static buffer. 47 * - Uses environment variables "TERM" and "TERMCAP". If TERM = term (that is, 48 * if the argument matches the environment) then it looks at TERMCAP. 49 * - If TERMCAP begins with a slash, then it assumes this is the file to 50 * search rather than /etc/termcap. 51 * - If TERMCAP does not begin with a slash, and it matches TERM, then this is 52 * used as the entry. 53 * - This could be simplified considerably for non-UNIX systems. 54 */ 55 56#ifndef TERMCAPFILE 57# ifdef AMIGA 58# define TERMCAPFILE "s:termcap" 59# else 60# ifdef VMS 61# define TERMCAPFILE "VIMRUNTIME:termcap" 62# else 63# define TERMCAPFILE "/etc/termcap" 64# endif 65# endif 66#endif 67 68 int 69tgetent(tbuf, term) 70 char *tbuf; /* Buffer to hold termcap entry, TBUFSZ bytes max */ 71 char *term; /* Name of terminal */ 72{ 73 char tcbuf[32]; /* Temp buffer to handle */ 74 char *tcptr = tcbuf; /* extended entries */ 75 char *tcap = TERMCAPFILE; /* Default termcap file */ 76 char *tmp; 77 FILE *termcap; 78 int retval = 0; 79 int len; 80 81 if ((tmp = (char *)mch_getenv((char_u *)"TERMCAP")) != NULL) 82 { 83 if (*tmp == '/') /* TERMCAP = name of termcap file */ 84 { 85 tcap = tmp ; 86#if defined(AMIGA) 87 /* Convert /usr/share/lib/termcap to usr:share/lib/termcap */ 88 tcap++; 89 tmp = strchr(tcap, '/'); 90 if (tmp) 91 *tmp = ':'; 92#endif 93 } 94 else /* TERMCAP = termcap entry itself */ 95 { 96 int tlen = strlen(term); 97 98 while (*tmp && *tmp != ':') /* Check if TERM matches */ 99 { 100 char *nexttmp; 101 102 while (*tmp == '|') 103 tmp++; 104 nexttmp = _find(tmp, ":|"); /* Rhialto */ 105 if (tmp+tlen == nexttmp && _match(tmp, term) == tlen) 106 { 107 strcpy(tbuf, tmp); 108 tent = tbuf; 109 return 1; 110 } 111 else 112 tmp = nexttmp; 113 } 114 } 115 } 116 if (!(termcap = mch_fopen(tcap, "r"))) 117 { 118 strcpy(tbuf, tcap); 119 return -1; 120 } 121 122 len = 0; 123 while (getent(tbuf + len, term, termcap, TBUFSZ - len)) 124 { 125 tcptr = tcbuf; /* Rhialto */ 126 if ((term = tgetstr("tc", &tcptr))) /* extended entry */ 127 { 128 rewind(termcap); 129 len = strlen(tbuf); 130 } 131 else 132 { 133 retval = 1; 134 tent = tbuf; /* reset it back to the beginning */ 135 break; 136 } 137 } 138 fclose(termcap); 139 return retval; 140} 141 142 static int 143getent(tbuf, term, termcap, buflen) 144 char *tbuf, *term; 145 FILE *termcap; 146 int buflen; 147{ 148 char *tptr; 149 int tlen = strlen(term); 150 151 while (nextent(tbuf, termcap, buflen)) /* For each possible entry */ 152 { 153 tptr = tbuf; 154 while (*tptr && *tptr != ':') /* : terminates name field */ 155 { 156 char *nexttptr; 157 158 while (*tptr == '|') /* | separates names */ 159 tptr++; 160 nexttptr = _find(tptr, ":|"); /* Rhialto */ 161 if (tptr + tlen == nexttptr && 162 _match(tptr, term) == tlen) /* FOUND! */ 163 { 164 tent = tbuf; 165 return 1; 166 } 167 else /* Look for next name */ 168 tptr = nexttptr; 169 } 170 } 171 return 0; 172} 173 174 static int 175nextent(tbuf, termcap, buflen) /* Read 1 entry from TERMCAP file */ 176 char *tbuf; 177 FILE *termcap; 178 int buflen; 179{ 180 char *lbuf = tbuf; /* lbuf=line buffer */ 181 /* read lines straight into buffer */ 182 183 while (lbuf < tbuf+buflen && /* There's room and */ 184 fgets(lbuf, (int)(tbuf+buflen-lbuf), termcap)) /* another line */ 185 { 186 int llen = strlen(lbuf); 187 188 if (*lbuf == '#') /* eat comments */ 189 continue; 190 if (lbuf[-1] == ':' && /* and whitespace */ 191 lbuf[0] == '\t' && 192 lbuf[1] == ':') 193 { 194 STRMOVE(lbuf, lbuf + 2); 195 llen -= 2; 196 } 197 if (lbuf[llen-2] == '\\') /* and continuations */ 198 lbuf += llen-2; 199 else 200 { 201 lbuf[llen-1]=0; /* no continuation, return */ 202 return 1; 203 } 204 } 205 206 return 0; /* ran into end of file */ 207} 208 209/* 210 * Module: tgetflag 211 * 212 * Purpose: returns flag true or false as to the existence of a given entry. 213 * used with 'bs', 'am', etc... 214 * 215 * Calling conventions: id is the 2 character capability id. 216 * 217 * Returned values: 1 for success, 0 for failure. 218 */ 219 220 int 221tgetflag(id) 222 char *id; 223{ 224 char buf[256], *ptr = buf; 225 226 return tgetstr(id, &ptr) ? 1 : 0; 227} 228 229/* 230 * Module: tgetnum 231 * 232 * Purpose: get numeric value such as 'li' or 'co' from termcap. 233 * 234 * Calling conventions: id = 2 character id. 235 * 236 * Returned values: -1 for failure, else numerical value. 237 */ 238 239 int 240tgetnum(id) 241 char *id; 242{ 243 char *ptr, buf[256]; 244 ptr = buf; 245 246 if (tgetstr(id, &ptr)) 247 return atoi(buf); 248 else 249 return 0; 250} 251 252/* 253 * Module: tgetstr 254 * 255 * Purpose: get terminal capability string from database. 256 * 257 * Calling conventions: id is the two character capability id. 258 * (*buf) points into a hold buffer for the 259 * id. the capability is copied into the buffer 260 * and (*buf) is advanced to point to the next 261 * free byte in the buffer. 262 * 263 * Returned values: 0 = no such entry, otherwise returns original 264 * (*buf) (now a pointer to the string). 265 * 266 * Notes 267 * It also decodes certain escape sequences in the buffer. 268 * they should be obvious from the code: 269 * \E = escape. 270 * \n, \r, \t, \f, \b match the 'c' escapes. 271 * ^x matches control-x (^@...^_). 272 * \nnn matches nnn octal. 273 * \x, where x is anything else, matches x. I differ 274 * from the standard library here, in that I allow ^: to match 275 * :. 276 * 277 */ 278 279 char * 280tgetstr(id, buf) 281 char *id, **buf; 282{ 283 int len = strlen(id); 284 char *tmp=tent; 285 char *hold; 286 int i; 287 288 do { 289 tmp = _find(tmp, ":"); /* For each field */ 290 while (*tmp == ':') /* skip empty fields */ 291 tmp++; 292 if (!*tmp) 293 break; 294 295 if (_match(id, tmp) == len) { 296 tmp += len; /* find '=' '@' or '#' */ 297 if (*tmp == '@') /* :xx@: entry for tc */ 298 return 0; /* deleted entry */ 299 hold= *buf; 300 while (*++tmp && *tmp != ':') { /* not at end of field */ 301 switch(*tmp) { 302 case '\\': /* Expand escapes here */ 303 switch(*++tmp) { 304 case 0: /* ignore backslashes */ 305 tmp--; /* at end of entry */ 306 break; /* shouldn't happen */ 307 case 'e': 308 case 'E': /* ESC */ 309 *(*buf)++ = ESC; 310 break; 311 case 'n': /* \n */ 312 *(*buf)++ = '\n'; 313 break; 314 case 'r': /* \r */ 315 *(*buf)++ = '\r'; 316 break; 317 case 't': /* \t */ 318 *(*buf)++ = '\t'; 319 break; 320 case 'b': /* \b */ 321 *(*buf)++ = '\b'; 322 break; 323 case 'f': /* \f */ 324 *(*buf)++ = '\f'; 325 break; 326 case '0': /* \nnn */ 327 case '1': 328 case '2': 329 case '3': 330 case '4': 331 case '5': 332 case '6': 333 case '7': 334 case '8': 335 case '9': 336 **buf = 0; 337 /* get up to three digits */ 338 for (i = 0; i < 3 && VIM_ISDIGIT(*tmp); ++i) 339 **buf = **buf * 8 + *tmp++ - '0'; 340 (*buf)++; 341 tmp--; 342 break; 343 default: /* \x, for all other x */ 344 *(*buf)++= *tmp; 345 } 346 break; 347 case '^': /* control characters */ 348 ++tmp; 349 *(*buf)++ = Ctrl_chr(*tmp); 350 break; 351 default: 352 *(*buf)++ = *tmp; 353 } 354 } 355 *(*buf)++ = 0; 356 return hold; 357 } 358 } while (*tmp); 359 360 return 0; 361} 362 363/* 364 * Module: tgoto 365 * 366 * Purpose: decode cm cursor motion string. 367 * 368 * Calling conventions: cm is cursor motion string. line, col, are the 369 * desired destination. 370 * 371 * Returned values: a string pointing to the decoded string, or "OOPS" if it 372 * cannot be decoded. 373 * 374 * Notes 375 * The accepted escapes are: 376 * %d as in printf, 0 origin. 377 * %2, %3 like %02d, %03d in printf. 378 * %. like %c 379 * %+x adds <x> to value, then %. 380 * %>xy if value>x, adds y. No output. 381 * %i increments line& col, no output. 382 * %r reverses order of line&col. No output. 383 * %% prints as a single %. 384 * %n exclusive or row & col with 0140. 385 * %B BCD, no output. 386 * %D reverse coding (x-2*(x%16)), no output. 387 */ 388 389 char * 390tgoto(cm, col, line) 391 char *cm; /* cm string, from termcap */ 392 int col, /* column, x position */ 393 line; /* line, y position */ 394{ 395 char gx, gy, /* x, y */ 396 *ptr, /* pointer in 'cm' */ 397 reverse = 0, /* reverse flag */ 398 *bufp, /* pointer in returned string */ 399 addup = 0, /* add upline */ 400 addbak = 0, /* add backup */ 401 c; 402 static char buffer[32]; 403 404 if (!cm) 405 return "OOPS"; /* Kludge, but standard */ 406 407 bufp = buffer; 408 ptr = cm; 409 410 while (*ptr) { 411 if ((c = *ptr++) != '%') { /* normal char */ 412 *bufp++ = c; 413 } else { /* % escape */ 414 switch(c = *ptr++) { 415 case 'd': /* decimal */ 416 bufp = _addfmt(bufp, "%d", line); 417 line = col; 418 break; 419 case '2': /* 2 digit decimal */ 420 bufp = _addfmt(bufp, "%02d", line); 421 line = col; 422 break; 423 case '3': /* 3 digit decimal */ 424 bufp = _addfmt(bufp, "%03d", line); 425 line = col; 426 break; 427 case '>': /* %>xy: if >x, add y */ 428 gx = *ptr++; 429 gy = *ptr++; 430 if (col>gx) col += gy; 431 if (line>gx) line += gy; 432 break; 433 case '+': /* %+c: add c */ 434 line += *ptr++; 435 case '.': /* print x/y */ 436 if (line == '\t' || /* these are */ 437 line == '\n' || /* chars that */ 438 line == '\004' || /* UNIX hates */ 439 line == '\0') { 440 line++; /* so go to next pos */ 441 if (reverse == (line == col)) 442 addup=1; /* and mark UP */ 443 else 444 addbak=1; /* or BC */ 445 } 446 *bufp++=line; 447 line = col; 448 break; 449 case 'r': /* r: reverse */ 450 gx = line; 451 line = col; 452 col = gx; 453 reverse = 1; 454 break; 455 case 'i': /* increment (1-origin screen) */ 456 col++; 457 line++; 458 break; 459 case '%': /* %%=% literally */ 460 *bufp++='%'; 461 break; 462 case 'n': /* magic DM2500 code */ 463 line ^= 0140; 464 col ^= 0140; 465 break; 466 case 'B': /* bcd encoding */ 467 line = line/10<<4+line%10; 468 col = col/10<<4+col%10; 469 break; 470 case 'D': /* magic Delta Data code */ 471 line = line-2*(line&15); 472 col = col-2*(col&15); 473 break; 474 default: /* Unknown escape */ 475 return "OOPS"; 476 } 477 } 478 } 479 480 if (addup) /* add upline */ 481 if (UP) { 482 ptr=UP; 483 while (VIM_ISDIGIT(*ptr) || *ptr == '.') 484 ptr++; 485 if (*ptr == '*') 486 ptr++; 487 while (*ptr) 488 *bufp++ = *ptr++; 489 } 490 491 if (addbak) /* add backspace */ 492 if (BC) { 493 ptr=BC; 494 while (VIM_ISDIGIT(*ptr) || *ptr == '.') 495 ptr++; 496 if (*ptr == '*') 497 ptr++; 498 while (*ptr) 499 *bufp++ = *ptr++; 500 } 501 else 502 *bufp++='\b'; 503 504 *bufp = 0; 505 506 return(buffer); 507} 508 509/* 510 * Module: tputs 511 * 512 * Purpose: decode padding information 513 * 514 * Calling conventions: cp = string to be padded, affcnt = # of items affected 515 * (lines, characters, whatever), outc = routine to output 1 character. 516 * 517 * Returned values: none 518 * 519 * Notes 520 * cp has padding information ahead of it, in the form 521 * nnnTEXT or nnn*TEXT. nnn is the number of milliseconds to delay, 522 * and may be a decimal (nnn.mmm). If the asterisk is given, then 523 * the delay is multiplied by afcnt. The delay is produced by outputting 524 * a number of nulls (or other padding char) after printing the 525 * TEXT. 526 * 527 */ 528 529long _bauds[16]={ 530 0, 50, 75, 110, 531 134, 150, 200, 300, 532 600, 1200, 1800, 2400, 533 4800, 9600, 19200, 19200 }; 534 535 int 536tputs(cp, affcnt, outc) 537 char *cp; /* string to print */ 538 int affcnt; /* Number of lines affected */ 539 void (*outc) __ARGS((unsigned int));/* routine to output 1 character */ 540{ 541 long frac, /* 10^(#digits after decimal point) */ 542 counter, /* digits */ 543 atol __ARGS((const char *)); 544 545 if (VIM_ISDIGIT(*cp)) { 546 counter = 0; 547 frac = 1000; 548 while (VIM_ISDIGIT(*cp)) 549 counter = counter * 10L + (long)(*cp++ - '0'); 550 if (*cp == '.') 551 while (VIM_ISDIGIT(*++cp)) { 552 counter = counter * 10L + (long)(*cp++ - '0'); 553 frac = frac * 10; 554 } 555 if (*cp!='*') { /* multiply by affected lines */ 556 if (affcnt>1) affcnt = 1; 557 } 558 else 559 cp++; 560 561 /* Calculate number of characters for padding counter/frac ms delay */ 562 if (ospeed) 563 counter = (counter * _bauds[ospeed] * (long)affcnt) / frac; 564 565 while (*cp) /* output string */ 566 (*outc)(*cp++); 567 if (ospeed) 568 while (counter--) /* followed by pad characters */ 569 (*outc)(PC); 570 } 571 else 572 while (*cp) 573 (*outc)(*cp++); 574 return 0; 575} 576 577/* 578 * Module: tutil.c 579 * 580 * Purpose: Utility routines for TERMLIB functions. 581 * 582 */ 583 static int 584_match(s1, s2) /* returns length of text common to s1 and s2 */ 585 char *s1, *s2; 586{ 587 int i = 0; 588 589 while (s1[i] && s1[i] == s2[i]) 590 i++; 591 592 return i; 593} 594 595/* 596 * finds next c in s that's a member of set, returns pointer 597 */ 598 static char * 599_find(s, set) 600 char *s, *set; 601{ 602 for(; *s; s++) 603 { 604 char *ptr = set; 605 606 while (*ptr && *s != *ptr) 607 ptr++; 608 609 if (*ptr) 610 return s; 611 } 612 613 return s; 614} 615 616/* 617 * add val to buf according to format fmt 618 */ 619 static char * 620_addfmt(buf, fmt, val) 621 char *buf, *fmt; 622 int val; 623{ 624 sprintf(buf, fmt, val); 625 while (*buf) 626 buf++; 627 return buf; 628} 629