1 /* vi:set ts=8 sts=4 sw=4 noet: */ 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) 17 # include <sgtty.h> 18 #endif 19 20 static int getent(char *, char *, FILE *, int); 21 static int nextent(char *, FILE *, int); 22 static int _match(char *, char *); 23 static char *_addfmt(char *, char *, int); 24 static char *_find(char *, char *); 25 26 /* 27 * Global variables for termlib 28 */ 29 30 char *tent; /* Pointer to terminal entry, set by tgetent */ 31 char PC = 0; /* Pad character, default NULL */ 32 char *UP = 0, *BC = 0; /* Pointers to UP and BC strings from database */ 33 short 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 69 tgetent( 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 143 getent(char *tbuf, char *term, FILE *termcap, int buflen) 144 { 145 char *tptr; 146 int tlen = strlen(term); 147 148 while (nextent(tbuf, termcap, buflen)) /* For each possible entry */ 149 { 150 tptr = tbuf; 151 while (*tptr && *tptr != ':') /* : terminates name field */ 152 { 153 char *nexttptr; 154 155 while (*tptr == '|') /* | separates names */ 156 tptr++; 157 nexttptr = _find(tptr, ":|"); /* Rhialto */ 158 if (tptr + tlen == nexttptr && 159 _match(tptr, term) == tlen) /* FOUND! */ 160 { 161 tent = tbuf; 162 return 1; 163 } 164 else /* Look for next name */ 165 tptr = nexttptr; 166 } 167 } 168 return 0; 169 } 170 171 /* 172 * Read 1 entry from TERMCAP file. 173 */ 174 static int 175 nextent(char *tbuf, FILE *termcap, int buflen) 176 { 177 char *lbuf = tbuf; /* lbuf=line buffer */ 178 /* read lines straight into buffer */ 179 180 while (lbuf < tbuf+buflen && /* There's room and */ 181 fgets(lbuf, (int)(tbuf+buflen-lbuf), termcap)) /* another line */ 182 { 183 int llen = strlen(lbuf); 184 185 if (*lbuf == '#') /* eat comments */ 186 continue; 187 if (lbuf[-1] == ':' && /* and whitespace */ 188 lbuf[0] == '\t' && 189 lbuf[1] == ':') 190 { 191 STRMOVE(lbuf, lbuf + 2); 192 llen -= 2; 193 } 194 if (lbuf[llen-2] == '\\') /* and continuations */ 195 lbuf += llen-2; 196 else 197 { 198 lbuf[llen-1]=0; /* no continuation, return */ 199 return 1; 200 } 201 } 202 203 return 0; /* ran into end of file */ 204 } 205 206 /* 207 * Module: tgetflag 208 * 209 * Purpose: returns flag true or false as to the existence of a given entry. 210 * used with 'bs', 'am', etc... 211 * 212 * Calling conventions: id is the 2 character capability id. 213 * 214 * Returned values: 1 for success, 0 for failure. 215 */ 216 217 int 218 tgetflag(char *id) 219 { 220 char buf[256], *ptr = buf; 221 222 return tgetstr(id, &ptr) ? 1 : 0; 223 } 224 225 /* 226 * Module: tgetnum 227 * 228 * Purpose: get numeric value such as 'li' or 'co' from termcap. 229 * 230 * Calling conventions: id = 2 character id. 231 * 232 * Returned values: -1 for failure, else numerical value. 233 */ 234 235 int 236 tgetnum(char *id) 237 { 238 char *ptr, buf[256]; 239 ptr = buf; 240 241 if (tgetstr(id, &ptr)) 242 return atoi(buf); 243 else 244 return 0; 245 } 246 247 /* 248 * Module: tgetstr 249 * 250 * Purpose: get terminal capability string from database. 251 * 252 * Calling conventions: id is the two character capability id. 253 * (*buf) points into a hold buffer for the 254 * id. the capability is copied into the buffer 255 * and (*buf) is advanced to point to the next 256 * free byte in the buffer. 257 * 258 * Returned values: 0 = no such entry, otherwise returns original 259 * (*buf) (now a pointer to the string). 260 * 261 * Notes 262 * It also decodes certain escape sequences in the buffer. 263 * they should be obvious from the code: 264 * \E = escape. 265 * \n, \r, \t, \f, \b match the 'c' escapes. 266 * ^x matches control-x (^@...^_). 267 * \nnn matches nnn octal. 268 * \x, where x is anything else, matches x. I differ 269 * from the standard library here, in that I allow ^: to match 270 * :. 271 * 272 */ 273 274 char * 275 tgetstr(char *id, char **buf) 276 { 277 int len = strlen(id); 278 char *tmp=tent; 279 char *hold; 280 int i; 281 282 do { 283 tmp = _find(tmp, ":"); /* For each field */ 284 while (*tmp == ':') /* skip empty fields */ 285 tmp++; 286 if (!*tmp) 287 break; 288 289 if (_match(id, tmp) == len) { 290 tmp += len; /* find '=' '@' or '#' */ 291 if (*tmp == '@') /* :xx@: entry for tc */ 292 return 0; /* deleted entry */ 293 hold= *buf; 294 while (*++tmp && *tmp != ':') { /* not at end of field */ 295 switch(*tmp) { 296 case '\\': /* Expand escapes here */ 297 switch(*++tmp) { 298 case 0: /* ignore backslashes */ 299 tmp--; /* at end of entry */ 300 break; /* shouldn't happen */ 301 case 'e': 302 case 'E': /* ESC */ 303 *(*buf)++ = ESC; 304 break; 305 case 'n': /* \n */ 306 *(*buf)++ = '\n'; 307 break; 308 case 'r': /* \r */ 309 *(*buf)++ = '\r'; 310 break; 311 case 't': /* \t */ 312 *(*buf)++ = '\t'; 313 break; 314 case 'b': /* \b */ 315 *(*buf)++ = '\b'; 316 break; 317 case 'f': /* \f */ 318 *(*buf)++ = '\f'; 319 break; 320 case '0': /* \nnn */ 321 case '1': 322 case '2': 323 case '3': 324 case '4': 325 case '5': 326 case '6': 327 case '7': 328 case '8': 329 case '9': 330 **buf = 0; 331 /* get up to three digits */ 332 for (i = 0; i < 3 && VIM_ISDIGIT(*tmp); ++i) 333 **buf = **buf * 8 + *tmp++ - '0'; 334 (*buf)++; 335 tmp--; 336 break; 337 default: /* \x, for all other x */ 338 *(*buf)++= *tmp; 339 } 340 break; 341 case '^': /* control characters */ 342 ++tmp; 343 *(*buf)++ = Ctrl_chr(*tmp); 344 break; 345 default: 346 *(*buf)++ = *tmp; 347 } 348 } 349 *(*buf)++ = 0; 350 return hold; 351 } 352 } while (*tmp); 353 354 return 0; 355 } 356 357 /* 358 * Module: tgoto 359 * 360 * Purpose: decode cm cursor motion string. 361 * 362 * Calling conventions: cm is cursor motion string. line, col, are the 363 * desired destination. 364 * 365 * Returned values: a string pointing to the decoded string, or "OOPS" if it 366 * cannot be decoded. 367 * 368 * Notes 369 * The accepted escapes are: 370 * %d as in printf, 0 origin. 371 * %2, %3 like %02d, %03d in printf. 372 * %. like %c 373 * %+x adds <x> to value, then %. 374 * %>xy if value>x, adds y. No output. 375 * %i increments line& col, no output. 376 * %r reverses order of line&col. No output. 377 * %% prints as a single %. 378 * %n exclusive or row & col with 0140. 379 * %B BCD, no output. 380 * %D reverse coding (x-2*(x%16)), no output. 381 */ 382 383 char * 384 tgoto( 385 char *cm, /* cm string, from termcap */ 386 int col, /* column, x position */ 387 int line) /* line, y position */ 388 { 389 char gx, gy, /* x, y */ 390 *ptr, /* pointer in 'cm' */ 391 reverse = 0, /* reverse flag */ 392 *bufp, /* pointer in returned string */ 393 addup = 0, /* add upline */ 394 addbak = 0, /* add backup */ 395 c; 396 static char buffer[32]; 397 398 if (!cm) 399 return "OOPS"; /* Kludge, but standard */ 400 401 bufp = buffer; 402 ptr = cm; 403 404 while (*ptr) { 405 if ((c = *ptr++) != '%') { /* normal char */ 406 *bufp++ = c; 407 } else { /* % escape */ 408 switch(c = *ptr++) { 409 case 'd': /* decimal */ 410 bufp = _addfmt(bufp, "%d", line); 411 line = col; 412 break; 413 case '2': /* 2 digit decimal */ 414 bufp = _addfmt(bufp, "%02d", line); 415 line = col; 416 break; 417 case '3': /* 3 digit decimal */ 418 bufp = _addfmt(bufp, "%03d", line); 419 line = col; 420 break; 421 case '>': /* %>xy: if >x, add y */ 422 gx = *ptr++; 423 gy = *ptr++; 424 if (col>gx) col += gy; 425 if (line>gx) line += gy; 426 break; 427 case '+': /* %+c: add c */ 428 line += *ptr++; 429 case '.': /* print x/y */ 430 if (line == '\t' || /* these are */ 431 line == '\n' || /* chars that */ 432 line == '\004' || /* UNIX hates */ 433 line == '\0') { 434 line++; /* so go to next pos */ 435 if (reverse == (line == col)) 436 addup=1; /* and mark UP */ 437 else 438 addbak=1; /* or BC */ 439 } 440 *bufp++=line; 441 line = col; 442 break; 443 case 'r': /* r: reverse */ 444 gx = line; 445 line = col; 446 col = gx; 447 reverse = 1; 448 break; 449 case 'i': /* increment (1-origin screen) */ 450 col++; 451 line++; 452 break; 453 case '%': /* %%=% literally */ 454 *bufp++='%'; 455 break; 456 case 'n': /* magic DM2500 code */ 457 line ^= 0140; 458 col ^= 0140; 459 break; 460 case 'B': /* bcd encoding */ 461 line = line/10<<4+line%10; 462 col = col/10<<4+col%10; 463 break; 464 case 'D': /* magic Delta Data code */ 465 line = line-2*(line&15); 466 col = col-2*(col&15); 467 break; 468 default: /* Unknown escape */ 469 return "OOPS"; 470 } 471 } 472 } 473 474 if (addup) /* add upline */ 475 if (UP) { 476 ptr=UP; 477 while (VIM_ISDIGIT(*ptr) || *ptr == '.') 478 ptr++; 479 if (*ptr == '*') 480 ptr++; 481 while (*ptr) 482 *bufp++ = *ptr++; 483 } 484 485 if (addbak) /* add backspace */ 486 if (BC) { 487 ptr=BC; 488 while (VIM_ISDIGIT(*ptr) || *ptr == '.') 489 ptr++; 490 if (*ptr == '*') 491 ptr++; 492 while (*ptr) 493 *bufp++ = *ptr++; 494 } 495 else 496 *bufp++='\b'; 497 498 *bufp = 0; 499 500 return(buffer); 501 } 502 503 /* 504 * Module: tputs 505 * 506 * Purpose: decode padding information 507 * 508 * Calling conventions: cp = string to be padded, affcnt = # of items affected 509 * (lines, characters, whatever), outc = routine to output 1 character. 510 * 511 * Returned values: none 512 * 513 * Notes 514 * cp has padding information ahead of it, in the form 515 * nnnTEXT or nnn*TEXT. nnn is the number of milliseconds to delay, 516 * and may be a decimal (nnn.mmm). If the asterisk is given, then 517 * the delay is multiplied by afcnt. The delay is produced by outputting 518 * a number of nulls (or other padding char) after printing the 519 * TEXT. 520 * 521 */ 522 523 long _bauds[16]={ 524 0, 50, 75, 110, 525 134, 150, 200, 300, 526 600, 1200, 1800, 2400, 527 4800, 9600, 19200, 19200 }; 528 529 int 530 tputs( 531 char *cp, /* string to print */ 532 int affcnt, /* Number of lines affected */ 533 void (*outc)(unsigned int)) /* routine to output 1 character */ 534 { 535 long frac, /* 10^(#digits after decimal point) */ 536 counter, /* digits */ 537 atol(const char *); 538 539 if (VIM_ISDIGIT(*cp)) { 540 counter = 0; 541 frac = 1000; 542 while (VIM_ISDIGIT(*cp)) 543 counter = counter * 10L + (long)(*cp++ - '0'); 544 if (*cp == '.') 545 while (VIM_ISDIGIT(*++cp)) { 546 counter = counter * 10L + (long)(*cp++ - '0'); 547 frac = frac * 10; 548 } 549 if (*cp!='*') { /* multiply by affected lines */ 550 if (affcnt>1) affcnt = 1; 551 } 552 else 553 cp++; 554 555 /* Calculate number of characters for padding counter/frac ms delay */ 556 if (ospeed) 557 counter = (counter * _bauds[ospeed] * (long)affcnt) / frac; 558 559 while (*cp) /* output string */ 560 (*outc)(*cp++); 561 if (ospeed) 562 while (counter--) /* followed by pad characters */ 563 (*outc)(PC); 564 } 565 else 566 while (*cp) 567 (*outc)(*cp++); 568 return 0; 569 } 570 571 /* 572 * Module: tutil.c 573 * 574 * Purpose: Utility routines for TERMLIB functions. 575 * Returns length of text common to s1 and s2. 576 */ 577 static int 578 _match(char *s1, char *s2) 579 { 580 int i = 0; 581 582 while (s1[i] && s1[i] == s2[i]) 583 i++; 584 585 return i; 586 } 587 588 /* 589 * finds next c in s that's a member of set, returns pointer 590 */ 591 static char * 592 _find(char *s, char *set) 593 { 594 for(; *s; s++) 595 { 596 char *ptr = set; 597 598 while (*ptr && *s != *ptr) 599 ptr++; 600 601 if (*ptr) 602 return s; 603 } 604 605 return s; 606 } 607 608 /* 609 * add val to buf according to format fmt 610 */ 611 static char * 612 _addfmt(char *buf, char *fmt, int val) 613 { 614 sprintf(buf, fmt, val); 615 while (*buf) 616 buf++; 617 return buf; 618 } 619