terminal.c revision 12565
1/*- 2 * Copyright (c) 1992, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Christos Zoulas of Cornell University. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37#if !defined(lint) && !defined(SCCSID) 38static char sccsid[] = "@(#)term.c 8.2 (Berkeley) 4/30/95"; 39#endif /* not lint && not SCCSID */ 40 41/* 42 * term.c: Editor/termcap-curses interface 43 * We have to declare a static variable here, since the 44 * termcap putchar routine does not take an argument! 45 */ 46#include "sys.h" 47#include <stdio.h> 48#include <signal.h> 49#include <string.h> 50#include <stdlib.h> 51#include <unistd.h> 52#include <termcap.h> 53#include <sys/types.h> 54 55#include "el.h" 56 57/* 58 * IMPORTANT NOTE: these routines are allowed to look at the current screen 59 * and the current possition assuming that it is correct. If this is not 60 * true, then the update will be WRONG! This is (should be) a valid 61 * assumption... 62 */ 63 64#define TC_BUFSIZE 2048 65 66#define GoodStr(a) (el->el_term.t_str[a] != NULL && \ 67 el->el_term.t_str[a][0] != '\0') 68#define Str(a) el->el_term.t_str[a] 69#define Val(a) el->el_term.t_val[a] 70 71private struct termcapstr { 72 char *name; 73 char *long_name; 74} tstr[] = { 75 76#define T_al 0 77 { "al", "add new blank line" }, 78#define T_bl 1 79 { "bl", "audible bell" }, 80#define T_cd 2 81 { "cd", "clear to bottom" }, 82#define T_ce 3 83 { "ce", "clear to end of line" }, 84#define T_ch 4 85 { "ch", "cursor to horiz pos" }, 86#define T_cl 5 87 { "cl", "clear screen" }, 88#define T_dc 6 89 { "dc", "delete a character" }, 90#define T_dl 7 91 { "dl", "delete a line" }, 92#define T_dm 8 93 { "dm", "start delete mode" }, 94#define T_ed 9 95 { "ed", "end delete mode" }, 96#define T_ei 10 97 { "ei", "end insert mode" }, 98#define T_fs 11 99 { "fs", "cursor from status line" }, 100#define T_ho 12 101 { "ho", "home cursor" }, 102#define T_ic 13 103 { "ic", "insert character" }, 104#define T_im 14 105 { "im", "start insert mode" }, 106#define T_ip 15 107 { "ip", "insert padding" }, 108#define T_kd 16 109 { "kd", "sends cursor down" }, 110#define T_kl 17 111 { "kl", "sends cursor left" }, 112#define T_kr 18 113 { "kr", "sends cursor right" }, 114#define T_ku 19 115 { "ku", "sends cursor up" }, 116#define T_md 20 117 { "md", "begin bold" }, 118#define T_me 21 119 { "me", "end attributes" }, 120#define T_nd 22 121 { "nd", "non destructive space" }, 122#define T_se 23 123 { "se", "end standout" }, 124#define T_so 24 125 { "so", "begin standout" }, 126#define T_ts 25 127 { "ts", "cursor to status line" }, 128#define T_up 26 129 { "up", "cursor up one" }, 130#define T_us 27 131 { "us", "begin underline" }, 132#define T_ue 28 133 { "ue", "end underline" }, 134#define T_vb 29 135 { "vb", "visible bell" }, 136#define T_DC 30 137 { "DC", "delete multiple chars" }, 138#define T_DO 31 139 { "DO", "cursor down multiple" }, 140#define T_IC 32 141 { "IC", "insert multiple chars" }, 142#define T_LE 33 143 { "LE", "cursor left multiple" }, 144#define T_RI 34 145 { "RI", "cursor right multiple" }, 146#define T_UP 35 147 { "UP", "cursor up multiple" }, 148#define T_str 36 149 { NULL, NULL } 150}; 151 152private struct termcapval { 153 char *name; 154 char *long_name; 155} tval[] = { 156#define T_pt 0 157 { "pt", "has physical tabs" }, 158#define T_li 1 159 { "li", "Number of lines" }, 160#define T_co 2 161 { "co", "Number of columns" }, 162#define T_km 3 163 { "km", "Has meta key" }, 164#define T_xt 4 165 { "xt", "Tab chars destructive" }, 166#define T_MT 5 167 { "MT", "Has meta key" }, /* XXX? */ 168#define T_val 6 169 { NULL, NULL, } 170}; 171 172/* do two or more of the attributes use me */ 173 174private void term_rebuffer_display __P((EditLine *)); 175private void term_free_display __P((EditLine *)); 176private void term_alloc_display __P((EditLine *)); 177private void term_alloc __P((EditLine *, 178 struct termcapstr *, char *)); 179private void term_init_arrow __P((EditLine *)); 180private void term_reset_arrow __P((EditLine *)); 181 182 183private FILE *term_outfile = NULL; /* XXX: How do we fix that? */ 184 185 186/* term_setflags(): 187 * Set the terminal capability flags 188 */ 189private void 190term_setflags(el) 191 EditLine *el; 192{ 193 EL_FLAGS = 0; 194 if (el->el_tty.t_tabs) 195 EL_FLAGS |= (Val(T_pt) && !Val(T_xt)) ? TERM_CAN_TAB : 0; 196 197 EL_FLAGS |= (Val(T_km) || Val(T_MT)) ? TERM_HAS_META : 0; 198 EL_FLAGS |= GoodStr(T_ce) ? TERM_CAN_CEOL : 0; 199 EL_FLAGS |= (GoodStr(T_dc) || GoodStr(T_DC)) ? TERM_CAN_DELETE : 0; 200 EL_FLAGS |= (GoodStr(T_im) || GoodStr(T_ic) || GoodStr(T_IC)) ? 201 TERM_CAN_INSERT : 0; 202 EL_FLAGS |= (GoodStr(T_up) || GoodStr(T_UP)) ? TERM_CAN_UP : 0; 203 204 if (GoodStr(T_me) && GoodStr(T_ue)) 205 EL_FLAGS |= (strcmp(Str(T_me), Str(T_ue)) == 0) ? TERM_CAN_ME : 0; 206 else 207 EL_FLAGS &= ~TERM_CAN_ME; 208 if (GoodStr(T_me) && GoodStr(T_se)) 209 EL_FLAGS |= (strcmp(Str(T_me), Str(T_se)) == 0) ? TERM_CAN_ME : 0; 210 211 212#ifdef DEBUG_SCREEN 213 if (!EL_CAN_UP) { 214 (void) fprintf(el->el_errfile, "WARNING: Your terminal cannot move up.\n"); 215 (void) fprintf(el->el_errfile, "Editing may be odd for long lines.\n"); 216 } 217 if (!EL_CAN_CEOL) 218 (void) fprintf(el->el_errfile, "no clear EOL capability.\n"); 219 if (!EL_CAN_DELETE) 220 (void) fprintf(el->el_errfile, "no delete char capability.\n"); 221 if (!EL_CAN_INSERT) 222 (void) fprintf(el->el_errfile, "no insert char capability.\n"); 223#endif /* DEBUG_SCREEN */ 224} 225 226 227/* term_init(): 228 * Initialize the terminal stuff 229 */ 230protected int 231term_init(el) 232 EditLine *el; 233{ 234 el->el_term.t_buf = (char *) el_malloc(TC_BUFSIZE); 235 el->el_term.t_cap = (char *) el_malloc(TC_BUFSIZE); 236 el->el_term.t_fkey = (fkey_t *) el_malloc(4 * sizeof(fkey_t)); 237 (void) memset(el->el_term.t_fkey, 0, 4 * sizeof(fkey_t)); 238 el->el_term.t_loc = 0; 239 el->el_term.t_str = (char **) el_malloc(T_str * sizeof(char*)); 240 (void) memset(el->el_term.t_str, 0, T_str * sizeof(char*)); 241 el->el_term.t_val = (int *) el_malloc(T_val * sizeof(int)); 242 (void) memset(el->el_term.t_val, 0, T_val * sizeof(char*)); 243 term_outfile = el->el_outfile; 244 (void) term_set(el, NULL); 245 term_init_arrow(el); 246 return 0; 247} 248 249/* term_end(): 250 * Clean up the terminal stuff 251 */ 252protected void 253term_end(el) 254 EditLine *el; 255{ 256 el_free((ptr_t) el->el_term.t_buf); 257 el->el_term.t_buf = NULL; 258 el_free((ptr_t) el->el_term.t_cap); 259 el->el_term.t_cap = NULL; 260 el->el_term.t_loc = 0; 261 el_free((ptr_t) el->el_term.t_str); 262 el->el_term.t_str = NULL; 263 el_free((ptr_t) el->el_term.t_val); 264 el->el_term.t_val = NULL; 265 term_free_display(el); 266} 267 268 269/* term_alloc(): 270 * Maintain a string pool for termcap strings 271 */ 272private void 273term_alloc(el, t, cap) 274 EditLine *el; 275 struct termcapstr *t; 276 char *cap; 277{ 278 char termbuf[TC_BUFSIZE]; 279 int tlen, clen; 280 char **tlist = el->el_term.t_str; 281 char **tmp, **str = &tlist[t - tstr]; 282 283 if (cap == NULL || *cap == '\0') { 284 *str = NULL; 285 return; 286 } 287 else 288 clen = strlen(cap); 289 290 tlen = *str == NULL ? 0 : strlen(*str); 291 292 /* 293 * New string is shorter; no need to allocate space 294 */ 295 if (clen <= tlen) { 296 (void) strcpy(*str, cap); 297 return; 298 } 299 300 /* 301 * New string is longer; see if we have enough space to append 302 */ 303 if (el->el_term.t_loc + 3 < TC_BUFSIZE) { 304 (void) strcpy(*str = &el->el_term.t_buf[el->el_term.t_loc], cap); 305 el->el_term.t_loc += clen + 1; /* one for \0 */ 306 return; 307 } 308 309 /* 310 * Compact our buffer; no need to check compaction, cause we know it 311 * fits... 312 */ 313 tlen = 0; 314 for (tmp = tlist; tmp < &tlist[T_str]; tmp++) 315 if (*tmp != NULL && *tmp != '\0' && *tmp != *str) { 316 char *ptr; 317 318 for (ptr = *tmp; *ptr != '\0'; termbuf[tlen++] = *ptr++) 319 continue; 320 termbuf[tlen++] = '\0'; 321 } 322 memcpy(el->el_term.t_buf, termbuf, TC_BUFSIZE); 323 el->el_term.t_loc = tlen; 324 if (el->el_term.t_loc + 3 >= TC_BUFSIZE) { 325 (void) fprintf(el->el_errfile, "Out of termcap string space.\n"); 326 return; 327 } 328 (void) strcpy(*str = &el->el_term.t_buf[el->el_term.t_loc], cap); 329 el->el_term.t_loc += clen + 1; /* one for \0 */ 330 return; 331} /* end term_alloc */ 332 333 334/* term_rebuffer_display(): 335 * Rebuffer the display after the screen changed size 336 */ 337private void 338term_rebuffer_display(el) 339 EditLine *el; 340{ 341 coord_t *c = &el->el_term.t_size; 342 343 term_free_display(el); 344 345 /* make this public, -1 to avoid wraps */ 346 c->h = Val(T_co) - 1; 347 c->v = (EL_BUFSIZ * 4) / c->h + 1; 348 349 term_alloc_display(el); 350} /* end term_rebuffer_display */ 351 352 353/* term_alloc_display(): 354 * Allocate a new display. 355 */ 356private void 357term_alloc_display(el) 358 EditLine *el; 359{ 360 int i; 361 char **b; 362 coord_t *c = &el->el_term.t_size; 363 364 b = (char **) el_malloc((size_t) (sizeof(char *) * (c->v + 1))); 365 for (i = 0; i < c->v; i++) 366 b[i] = (char *) el_malloc((size_t) (sizeof(char) * (c->h + 1))); 367 b[c->v] = NULL; 368 el->el_display = b; 369 370 b = (char **) el_malloc((size_t) (sizeof(char *) * (c->v + 1))); 371 for (i = 0; i < c->v; i++) 372 b[i] = (char *) el_malloc((size_t) (sizeof(char) * (c->h + 1))); 373 b[c->v] = NULL; 374 el->el_vdisplay = b; 375 376} /* end term_alloc_display */ 377 378 379/* term_free_display(): 380 * Free the display buffers 381 */ 382private void 383term_free_display(el) 384 EditLine *el; 385{ 386 char **b; 387 char **bufp; 388 389 b = el->el_display; 390 el->el_display = NULL; 391 if (b != NULL) { 392 for (bufp = b; *bufp != NULL; bufp++) 393 el_free((ptr_t) *bufp); 394 el_free((ptr_t) b); 395 } 396 b = el->el_vdisplay; 397 el->el_vdisplay = NULL; 398 if (b != NULL) { 399 for (bufp = b; *bufp != NULL; bufp++) 400 el_free((ptr_t) * bufp); 401 el_free((ptr_t) b); 402 } 403} /* end term_free_display */ 404 405 406/* term_move_to_line(): 407 * move to line <where> (first line == 0) 408 * as efficiently as possible 409 */ 410protected void 411term_move_to_line(el, where) 412 EditLine *el; 413 int where; 414{ 415 int del, i; 416 417 if (where == el->el_cursor.v) 418 return; 419 420 if (where > el->el_term.t_size.v) { 421#ifdef DEBUG_SCREEN 422 (void) fprintf(el->el_errfile, 423 "term_move_to_line: where is ridiculous: %d\r\n", where); 424#endif /* DEBUG_SCREEN */ 425 return; 426 } 427 428 if ((del = where - el->el_cursor.v) > 0) { 429 if ((del > 1) && GoodStr(T_DO)) 430 (void) tputs(tgoto(Str(T_DO), del, del), del, term__putc); 431 else { 432 for (i = 0; i < del; i++) 433 term__putc('\n'); 434 el->el_cursor.h = 0; /* because the \n will become \r\n */ 435 } 436 } 437 else { /* del < 0 */ 438 if (GoodStr(T_UP) && (-del > 1 || !GoodStr(T_up))) 439 (void) tputs(tgoto(Str(T_UP), -del, -del), -del, term__putc); 440 else { 441 if (GoodStr(T_up)) 442 for (i = 0; i < -del; i++) 443 (void) tputs(Str(T_up), 1, term__putc); 444 } 445 } 446 el->el_cursor.v = where; /* now where is here */ 447} /* end term_move_to_line */ 448 449 450/* term_move_to_char(): 451 * Move to the character position specified 452 */ 453protected void 454term_move_to_char(el, where) 455 EditLine *el; 456 int where; 457{ 458 int del, i; 459 460mc_again: 461 if (where == el->el_cursor.h) 462 return; 463 464 if (where > (el->el_term.t_size.h + 1)) { 465#ifdef DEBUG_SCREEN 466 (void) fprintf(el->el_errfile, 467 "term_move_to_char: where is riduculous: %d\r\n", where); 468#endif /* DEBUG_SCREEN */ 469 return; 470 } 471 472 if (!where) { /* if where is first column */ 473 term__putc('\r'); /* do a CR */ 474 el->el_cursor.h = 0; 475 return; 476 } 477 478 del = where - el->el_cursor.h; 479 480 if ((del < -4 || del > 4) && GoodStr(T_ch)) 481 /* go there directly */ 482 (void) tputs(tgoto(Str(T_ch), where, where), where, term__putc); 483 else { 484 if (del > 0) { /* moving forward */ 485 if ((del > 4) && GoodStr(T_RI)) 486 (void) tputs(tgoto(Str(T_RI), del, del), del, term__putc); 487 else { 488 if (EL_CAN_TAB) { /* if I can do tabs, use them */ 489 if ((el->el_cursor.h & 0370) != (where & 0370)) { 490 /* if not within tab stop */ 491 for (i = (el->el_cursor.h & 0370); 492 i < (where & 0370); i += 8) 493 term__putc('\t'); /* then tab over */ 494 el->el_cursor.h = where & 0370; 495 } 496 } 497 /* it's usually cheaper to just write the chars, so we do. */ 498 499 /* NOTE THAT term_overwrite() WILL CHANGE el->el_cursor.h!!! */ 500 term_overwrite(el, 501 &el->el_display[el->el_cursor.v][el->el_cursor.h], 502 where - el->el_cursor.h); 503 504 } 505 } 506 else { /* del < 0 := moving backward */ 507 if ((-del > 4) && GoodStr(T_LE)) 508 (void) tputs(tgoto(Str(T_LE), -del, -del), -del, term__putc); 509 else { /* can't go directly there */ 510 /* if the "cost" is greater than the "cost" from col 0 */ 511 if (EL_CAN_TAB ? (-del > ((where >> 3) + (where & 07))) 512 : (-del > where)) { 513 term__putc('\r'); /* do a CR */ 514 el->el_cursor.h = 0; 515 goto mc_again; /* and try again */ 516 } 517 for (i = 0; i < -del; i++) 518 term__putc('\b'); 519 } 520 } 521 } 522 el->el_cursor.h = where; /* now where is here */ 523} /* end term_move_to_char */ 524 525 526/* term_overwrite(): 527 * Overstrike num characters 528 */ 529protected void 530term_overwrite(el, cp, n) 531 EditLine *el; 532 char *cp; 533 int n; 534{ 535 if (n <= 0) 536 return; /* catch bugs */ 537 538 if (n > (el->el_term.t_size.h + 1)) { 539#ifdef DEBUG_SCREEN 540 (void) fprintf(el->el_errfile, "term_overwrite: n is riduculous: %d\r\n", n); 541#endif /* DEBUG_SCREEN */ 542 return; 543 } 544 545 do { 546 term__putc(*cp++); 547 el->el_cursor.h++; 548 } while (--n); 549} /* end term_overwrite */ 550 551 552/* term_deletechars(): 553 * Delete num characters 554 */ 555protected void 556term_deletechars(el, num) 557 EditLine *el; 558 int num; 559{ 560 if (num <= 0) 561 return; 562 563 if (!EL_CAN_DELETE) { 564#ifdef DEBUG_EDIT 565 (void) fprintf(el->el_errfile, " ERROR: cannot delete \n"); 566#endif /* DEBUG_EDIT */ 567 return; 568 } 569 570 if (num > el->el_term.t_size.h) { 571#ifdef DEBUG_SCREEN 572 (void) fprintf(el->el_errfile, 573 "term_deletechars: num is riduculous: %d\r\n", num); 574#endif /* DEBUG_SCREEN */ 575 return; 576 } 577 578 if (GoodStr(T_DC)) /* if I have multiple delete */ 579 if ((num > 1) || !GoodStr(T_dc)) { /* if dc would be more expen. */ 580 (void) tputs(tgoto(Str(T_DC), num, num), num, term__putc); 581 return; 582 } 583 584 if (GoodStr(T_dm)) /* if I have delete mode */ 585 (void) tputs(Str(T_dm), 1, term__putc); 586 587 if (GoodStr(T_dc)) /* else do one at a time */ 588 while (num--) 589 (void) tputs(Str(T_dc), 1, term__putc); 590 591 if (GoodStr(T_ed)) /* if I have delete mode */ 592 (void) tputs(Str(T_ed), 1, term__putc); 593} /* end term_deletechars */ 594 595 596/* term_insertwrite(): 597 * Puts terminal in insert character mode or inserts num 598 * characters in the line 599 */ 600protected void 601term_insertwrite(el, cp, num) 602 EditLine *el; 603 char *cp; 604 int num; 605{ 606 if (num <= 0) 607 return; 608 if (!EL_CAN_INSERT) { 609#ifdef DEBUG_EDIT 610 (void) fprintf(el->el_errfile, " ERROR: cannot insert \n"); 611#endif /* DEBUG_EDIT */ 612 return; 613 } 614 615 if (num > el->el_term.t_size.h) { 616#ifdef DEBUG_SCREEN 617 (void) fprintf(el->el_errfile, "StartInsert: num is riduculous: %d\r\n", num); 618#endif /* DEBUG_SCREEN */ 619 return; 620 } 621 622 if (GoodStr(T_IC)) /* if I have multiple insert */ 623 if ((num > 1) || !GoodStr(T_ic)) { /* if ic would be more expen. */ 624 (void) tputs(tgoto(Str(T_IC), num, num), num, term__putc); 625 term_overwrite(el, cp, num); /* this updates el_cursor.h */ 626 return; 627 } 628 629 if (GoodStr(T_im) && GoodStr(T_ei)) { /* if I have insert mode */ 630 (void) tputs(Str(T_im), 1, term__putc); 631 632 el->el_cursor.h += num; 633 do 634 term__putc(*cp++); 635 while (--num); 636 637 if (GoodStr(T_ip)) /* have to make num chars insert */ 638 (void) tputs(Str(T_ip), 1, term__putc); 639 640 (void) tputs(Str(T_ei), 1, term__putc); 641 return; 642 } 643 644 do { 645 if (GoodStr(T_ic)) /* have to make num chars insert */ 646 (void) tputs(Str(T_ic), 1, term__putc); /* insert a char */ 647 648 term__putc(*cp++); 649 650 el->el_cursor.h++; 651 652 if (GoodStr(T_ip)) /* have to make num chars insert */ 653 (void) tputs(Str(T_ip), 1, term__putc);/* pad the inserted char */ 654 655 } while (--num); 656} /* end term_insertwrite */ 657 658 659/* term_clear_EOL(): 660 * clear to end of line. There are num characters to clear 661 */ 662protected void 663term_clear_EOL(el, num) 664 EditLine *el; 665 int num; 666{ 667 int i; 668 669 if (EL_CAN_CEOL && GoodStr(T_ce)) 670 (void) tputs(Str(T_ce), 1, term__putc); 671 else { 672 for (i = 0; i < num; i++) 673 term__putc(' '); 674 el->el_cursor.h += num; /* have written num spaces */ 675 } 676} /* end term_clear_EOL */ 677 678 679/* term_clear_screen(): 680 * Clear the screen 681 */ 682protected void 683term_clear_screen(el) 684 EditLine *el; 685{ /* clear the whole screen and home */ 686 if (GoodStr(T_cl)) 687 /* send the clear screen code */ 688 (void) tputs(Str(T_cl), Val(T_li), term__putc); 689 else if (GoodStr(T_ho) && GoodStr(T_cd)) { 690 (void) tputs(Str(T_ho), Val(T_li), term__putc); /* home */ 691 /* clear to bottom of screen */ 692 (void) tputs(Str(T_cd), Val(T_li), term__putc); 693 } 694 else { 695 term__putc('\r'); 696 term__putc('\n'); 697 } 698} /* end term_clear_screen */ 699 700 701/* term_beep(): 702 * Beep the way the terminal wants us 703 */ 704protected void 705term_beep(el) 706 EditLine *el; 707{ 708 if (GoodStr(T_vb)) 709 (void) tputs(Str(T_vb), 1, term__putc); /* visible bell */ 710 else if (GoodStr(T_bl)) 711 /* what termcap says we should use */ 712 (void) tputs(Str(T_bl), 1, term__putc); 713 else 714 term__putc('\007'); /* an ASCII bell; ^G */ 715} /* end term_beep */ 716 717 718#ifdef notdef 719/* term_clear_to_bottom(): 720 * Clear to the bottom of the screen 721 */ 722protected void 723term_clear_to_bottom(el) 724 EditLine *el; 725{ 726 if (GoodStr(T_cd)) 727 (void) tputs(Str(T_cd), Val(T_li), term__putc); 728 else if (GoodStr(T_ce)) 729 (void) tputs(Str(T_ce), Val(T_li), term__putc); 730} /* end term_clear_to_bottom */ 731#endif 732 733 734/* term_set(): 735 * Read in the terminal capabilities from the requested terminal 736 */ 737protected int 738term_set(el, term) 739 EditLine *el; 740 char *term; 741{ 742 int i; 743 char buf[TC_BUFSIZE]; 744 char *area; 745 struct termcapstr *t; 746 sigset_t oset, nset; 747 int lins, cols; 748 749 (void) sigemptyset(&nset); 750 (void) sigaddset(&nset, SIGWINCH); 751 (void) sigprocmask(SIG_BLOCK, &nset, &oset); 752 753 area = buf; 754 755 756 if (term == NULL) 757 term = getenv("TERM"); 758 759 if (!term || !term[0]) 760 term = "dumb"; 761 762 memset(el->el_term.t_cap, 0, TC_BUFSIZE); 763 764 i = tgetent(el->el_term.t_cap, term); 765 766 if (i <= 0) { 767 if (i == -1) 768#ifdef __FreeBSD__ 769 (void) fprintf(el->el_errfile, "Cannot open /usr/share/misc/termcap.\n"); 770#else 771 (void) fprintf(el->el_errfile, "Cannot open /etc/termcap.\n"); 772#endif 773 else if (i == 0) 774 (void) fprintf(el->el_errfile, 775 "No entry for terminal type \"%s\"\n", term); 776 (void) fprintf(el->el_errfile, "using dumb terminal settings.\n"); 777 Val(T_co) = 80; /* do a dumb terminal */ 778 Val(T_pt) = Val(T_km) = Val(T_li) = 0; 779 Val(T_xt) = Val(T_MT); 780 for (t = tstr; t->name != NULL; t++) 781 term_alloc(el, t, NULL); 782 } 783 else { 784 /* Can we tab */ 785 Val(T_pt) = tgetflag("pt"); 786 Val(T_xt) = tgetflag("xt"); 787 /* do we have a meta? */ 788 Val(T_km) = tgetflag("km"); 789 Val(T_MT) = tgetflag("MT"); 790 /* Get the size */ 791 Val(T_co) = tgetnum("co"); 792 Val(T_li) = tgetnum("li"); 793 for (t = tstr; t->name != NULL; t++) 794 term_alloc(el, t, tgetstr(t->name, &area)); 795 } 796 797 if (Val(T_co) < 2) 798 Val(T_co) = 80; /* just in case */ 799 if (Val(T_li) < 1) 800 Val(T_li) = 24; 801 802 el->el_term.t_size.v = Val(T_co); 803 el->el_term.t_size.h = Val(T_li); 804 805 term_setflags(el); 806 807 (void) term_get_size(el, &lins, &cols);/* get the correct window size */ 808 term_change_size(el, lins, cols); 809 (void) sigprocmask(SIG_SETMASK, &oset, NULL); 810 term_bind_arrow(el); 811 return 0; 812} /* end term_set */ 813 814 815/* term_get_size(): 816 * Return the new window size in lines and cols, and 817 * true if the size was changed. 818 */ 819protected int 820term_get_size(el, lins, cols) 821 EditLine *el; 822 int *lins, *cols; 823{ 824 825 *cols = Val(T_co); 826 *lins = Val(T_li); 827 828#ifdef TIOCGWINSZ 829 { 830 struct winsize ws; 831 if (ioctl(el->el_infd, TIOCGWINSZ, (ioctl_t) &ws) != -1) { 832 if (ws.ws_col) 833 *cols = ws.ws_col; 834 if (ws.ws_row) 835 *lins = ws.ws_row; 836 } 837 } 838#endif 839#ifdef TIOCGSIZE 840 { 841 struct ttysize ts; 842 if (ioctl(el->el_infd, TIOCGSIZE, (ioctl_t) &ts) != -1) { 843 if (ts.ts_cols) 844 *cols = ts.ts_cols; 845 if (ts.ts_lines) 846 *lins = ts.ts_lines; 847 } 848 } 849#endif 850 return (Val(T_co) != *cols || Val(T_li) != *lins); 851} /* end term_get_size */ 852 853 854/* term_change_size(): 855 * Change the size of the terminal 856 */ 857protected void 858term_change_size(el, lins, cols) 859 EditLine *el; 860 int lins, cols; 861{ 862 /* 863 * Just in case 864 */ 865 Val(T_co) = (cols < 2) ? 80 : cols; 866 Val(T_li) = (lins < 1) ? 24 : lins; 867 868 term_rebuffer_display(el); /* re-make display buffers */ 869 re_clear_display(el); 870} /* end term_change_size */ 871 872 873/* term_init_arrow(): 874 * Initialize the arrow key bindings from termcap 875 */ 876private void 877term_init_arrow(el) 878 EditLine *el; 879{ 880 fkey_t *arrow = el->el_term.t_fkey; 881 882 arrow[A_K_DN].name = "down"; 883 arrow[A_K_DN].key = T_kd; 884 arrow[A_K_DN].fun.cmd = ED_NEXT_HISTORY; 885 arrow[A_K_DN].type = XK_CMD; 886 887 arrow[A_K_UP].name = "up"; 888 arrow[A_K_UP].key = T_ku; 889 arrow[A_K_UP].fun.cmd = ED_PREV_HISTORY; 890 arrow[A_K_UP].type = XK_CMD; 891 892 arrow[A_K_LT].name = "left"; 893 arrow[A_K_LT].key = T_kl; 894 arrow[A_K_LT].fun.cmd = ED_PREV_CHAR; 895 arrow[A_K_LT].type = XK_CMD; 896 897 arrow[A_K_RT].name = "right"; 898 arrow[A_K_RT].key = T_kr; 899 arrow[A_K_RT].fun.cmd = ED_NEXT_CHAR; 900 arrow[A_K_RT].type = XK_CMD; 901 902} 903 904 905/* term_reset_arrow(): 906 * Reset arrow key bindings 907 */ 908private void 909term_reset_arrow(el) 910 EditLine *el; 911{ 912 fkey_t *arrow = el->el_term.t_fkey; 913 static char strA[] = {033, '[', 'A', '\0'}; 914 static char strB[] = {033, '[', 'B', '\0'}; 915 static char strC[] = {033, '[', 'C', '\0'}; 916 static char strD[] = {033, '[', 'D', '\0'}; 917 static char stOA[] = {033, 'O', 'A', '\0'}; 918 static char stOB[] = {033, 'O', 'B', '\0'}; 919 static char stOC[] = {033, 'O', 'C', '\0'}; 920 static char stOD[] = {033, 'O', 'D', '\0'}; 921 922 key_add(el, strA, &arrow[A_K_UP].fun, arrow[A_K_UP].type); 923 key_add(el, strB, &arrow[A_K_DN].fun, arrow[A_K_DN].type); 924 key_add(el, strC, &arrow[A_K_RT].fun, arrow[A_K_RT].type); 925 key_add(el, strD, &arrow[A_K_LT].fun, arrow[A_K_LT].type); 926 key_add(el, stOA, &arrow[A_K_UP].fun, arrow[A_K_UP].type); 927 key_add(el, stOB, &arrow[A_K_DN].fun, arrow[A_K_DN].type); 928 key_add(el, stOC, &arrow[A_K_RT].fun, arrow[A_K_RT].type); 929 key_add(el, stOD, &arrow[A_K_LT].fun, arrow[A_K_LT].type); 930 931 if (el->el_map.type == MAP_VI) { 932 key_add(el, &strA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type); 933 key_add(el, &strB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type); 934 key_add(el, &strC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type); 935 key_add(el, &strD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type); 936 key_add(el, &stOA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type); 937 key_add(el, &stOB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type); 938 key_add(el, &stOC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type); 939 key_add(el, &stOD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type); 940 } 941} 942 943 944/* term_set_arrow(): 945 * Set an arrow key binding 946 */ 947protected int 948term_set_arrow(el, name, fun, type) 949 EditLine *el; 950 char *name; 951 key_value_t *fun; 952 int type; 953{ 954 fkey_t *arrow = el->el_term.t_fkey; 955 int i; 956 957 for (i = 0; i < A_K_NKEYS; i++) 958 if (strcmp(name, arrow[i].name) == 0) { 959 arrow[i].fun = *fun; 960 arrow[i].type = type; 961 return 0; 962 } 963 return -1; 964} 965 966 967/* term_clear_arrow(): 968 * Clear an arrow key binding 969 */ 970protected int 971term_clear_arrow(el, name) 972 EditLine *el; 973 char *name; 974{ 975 fkey_t *arrow = el->el_term.t_fkey; 976 int i; 977 978 for (i = 0; i < A_K_NKEYS; i++) 979 if (strcmp(name, arrow[i].name) == 0) { 980 arrow[i].type = XK_NOD; 981 return 0; 982 } 983 return -1; 984} 985 986 987/* term_print_arrow(): 988 * Print the arrow key bindings 989 */ 990protected void 991term_print_arrow(el, name) 992 EditLine *el; 993 char *name; 994{ 995 int i; 996 fkey_t *arrow = el->el_term.t_fkey; 997 998 for (i = 0; i < A_K_NKEYS; i++) 999 if (*name == '\0' || strcmp(name, arrow[i].name) == 0) 1000 if (arrow[i].type != XK_NOD) 1001 key_kprint(el, arrow[i].name, &arrow[i].fun, arrow[i].type); 1002} 1003 1004 1005/* term_bind_arrow(): 1006 * Bind the arrow keys 1007 */ 1008protected void 1009term_bind_arrow(el) 1010 EditLine *el; 1011{ 1012 el_action_t *map, *dmap; 1013 int i, j; 1014 char *p; 1015 fkey_t *arrow = el->el_term.t_fkey; 1016 1017 /* Check if the components needed are initialized */ 1018 if (el->el_term.t_buf == NULL || el->el_map.key == NULL) 1019 return; 1020 1021 map = el->el_map.type == MAP_VI ? el->el_map.alt : el->el_map.key; 1022 dmap = el->el_map.type == MAP_VI ? el->el_map.vic : el->el_map.emacs; 1023 1024 term_reset_arrow(el); 1025 1026 for (i = 0; i < 4; i++) { 1027 p = el->el_term.t_str[arrow[i].key]; 1028 if (p && *p) { 1029 j = (unsigned char) *p; 1030 /* 1031 * Assign the arrow keys only if: 1032 * 1033 * 1. They are multi-character arrow keys and the user 1034 * has not re-assigned the leading character, or 1035 * has re-assigned the leading character to be 1036 * ED_SEQUENCE_LEAD_IN 1037 * 2. They are single arrow keys pointing to an unassigned key. 1038 */ 1039 if (arrow[i].type == XK_NOD) 1040 key_clear(el, map, p); 1041 else { 1042 if (p[1] && (dmap[j] == map[j] || 1043 map[j] == ED_SEQUENCE_LEAD_IN)) { 1044 key_add(el, p, &arrow[i].fun, arrow[i].type); 1045 map[j] = ED_SEQUENCE_LEAD_IN; 1046 } 1047 else if (map[j] == ED_UNASSIGNED) { 1048 key_clear(el, map, p); 1049 if (arrow[i].type == XK_CMD) 1050 map[j] = arrow[i].fun.cmd; 1051 else 1052 key_add(el, p, &arrow[i].fun, arrow[i].type); 1053 } 1054 } 1055 } 1056 } 1057} 1058 1059 1060/* term__putc(): 1061 * Add a character 1062 */ 1063protected int 1064term__putc(c) 1065 int c; 1066{ 1067 return fputc(c, term_outfile); 1068} /* end term__putc */ 1069 1070 1071/* term__flush(): 1072 * Flush output 1073 */ 1074protected void 1075term__flush() 1076{ 1077 (void) fflush(term_outfile); 1078} /* end term__flush */ 1079 1080 1081/* term_telltc(): 1082 * Print the current termcap characteristics 1083 */ 1084protected int 1085/*ARGSUSED*/ 1086term_telltc(el, argc, argv) 1087 EditLine *el; 1088 int argc; 1089 char **argv; 1090{ 1091 struct termcapstr *t; 1092 char **ts; 1093 char upbuf[EL_BUFSIZ]; 1094 1095 (void) fprintf(el->el_outfile, "\n\tYour terminal has the\n"); 1096 (void) fprintf(el->el_outfile, "\tfollowing characteristics:\n\n"); 1097 (void) fprintf(el->el_outfile, "\tIt has %d columns and %d lines\n", 1098 Val(T_co), Val(T_li)); 1099 (void) fprintf(el->el_outfile, 1100 "\tIt has %s meta key\n", EL_HAS_META ? "a" : "no"); 1101 (void) fprintf(el->el_outfile, 1102 "\tIt can%suse tabs\n", EL_CAN_TAB ? " " : "not "); 1103#ifdef notyet 1104 (void) fprintf(el->el_outfile, "\tIt %s automatic margins\n", 1105 (T_Margin&MARGIN_AUTO)? "has": "does not have"); 1106 if (T_Margin & MARGIN_AUTO) 1107 (void) fprintf(el->el_outfile, "\tIt %s magic margins\n", 1108 (T_Margin&MARGIN_MAGIC)?"has":"does not have"); 1109#endif 1110 1111 for (t = tstr, ts = el->el_term.t_str; t->name != NULL; t++, ts++) 1112 (void) fprintf(el->el_outfile, "\t%25s (%s) == %s\n", t->long_name, 1113 t->name, *ts && **ts ? 1114 key__decode_str(*ts, upbuf, "") : "(empty)"); 1115 (void) fputc('\n', el->el_outfile); 1116 return 0; 1117} 1118 1119 1120/* term_settc(): 1121 * Change the current terminal characteristics 1122 */ 1123protected int 1124/*ARGSUSED*/ 1125term_settc(el, argc, argv) 1126 EditLine *el; 1127 int argc; 1128 char **argv; 1129{ 1130 struct termcapstr *ts; 1131 struct termcapval *tv; 1132 char *what, *how; 1133 1134 if (argv == NULL || argv[1] == NULL || argv[2] == NULL) 1135 return -1; 1136 1137 what = argv[1]; 1138 how = argv[2]; 1139 1140 /* 1141 * Do the strings first 1142 */ 1143 for (ts = tstr; ts->name != NULL; ts++) 1144 if (strcmp(ts->name, what) == 0) 1145 break; 1146 1147 if (ts->name != NULL) { 1148 term_alloc(el, ts, how); 1149 term_setflags(el); 1150 return 0; 1151 } 1152 1153 /* 1154 * Do the numeric ones second 1155 */ 1156 for (tv = tval; tv->name != NULL; tv++) 1157 if (strcmp(tv->name, what) == 0) 1158 break; 1159 1160 if (tv->name != NULL) { 1161 if (tv == &tval[T_pt] || tv == &tval[T_km] 1162#ifdef notyet 1163 || tv == &tval[T_am] || tv == &tval[T_xn] 1164#endif 1165 ) { 1166 if (strcmp(how, "yes") == 0) 1167 el->el_term.t_val[tv - tval] = 1; 1168 else if (strcmp(how, "no") == 0) 1169 el->el_term.t_val[tv - tval] = 0; 1170 else { 1171 (void) fprintf(el->el_errfile, "settc: Bad value `%s'.\n", how); 1172 return -1; 1173 } 1174 term_setflags(el); 1175 term_change_size(el, Val(T_li), Val(T_co)); 1176 return 0; 1177 } 1178 else { 1179 el->el_term.t_val[tv - tval] = atoi(how); 1180 el->el_term.t_size.v = Val(T_co); 1181 el->el_term.t_size.h = Val(T_li); 1182 if (tv == &tval[T_co] || tv == &tval[T_li]) 1183 term_change_size(el, Val(T_li), Val(T_co)); 1184 return 0; 1185 } 1186 } 1187 return -1; 1188} 1189 1190 1191/* term_echotc(): 1192 * Print the termcap string out with variable substitution 1193 */ 1194protected int 1195/*ARGSUSED*/ 1196term_echotc(el, argc, argv) 1197 EditLine *el; 1198 int argc; 1199 char **argv; 1200{ 1201 char *cap, *scap; 1202 int arg_need, arg_cols, arg_rows; 1203 int verbose = 0, silent = 0; 1204 char *area; 1205 static char *fmts = "%s\n", *fmtd = "%d\n"; 1206 struct termcapstr *t; 1207 char buf[TC_BUFSIZE]; 1208 1209 area = buf; 1210 1211 if (argv == NULL || argv[1] == NULL) 1212 return -1; 1213 argv++; 1214 1215 if (argv[0][0] == '-') { 1216 switch (argv[0][1]) { 1217 case 'v': 1218 verbose = 1; 1219 break; 1220 case 's': 1221 silent = 1; 1222 break; 1223 default: 1224 /* stderror(ERR_NAME | ERR_TCUSAGE); */ 1225 break; 1226 } 1227 argv++; 1228 } 1229 if (!*argv || *argv[0] == '\0') 1230 return 0; 1231 if (strcmp(*argv, "tabs") == 0) { 1232 (void) fprintf(el->el_outfile, fmts, EL_CAN_TAB ? "yes" : "no"); 1233 return 0; 1234 } 1235 else if (strcmp(*argv, "meta") == 0) { 1236 (void) fprintf(el->el_outfile, fmts, Val(T_km) ? "yes" : "no"); 1237 return 0; 1238 } 1239#ifdef notyet 1240 else if (strcmp(*argv, "xn") == 0) { 1241 (void) fprintf(el->el_outfile, fmts, T_Margin & MARGIN_MAGIC ? 1242 "yes" : "no"); 1243 return 0; 1244 } 1245 else if (strcmp(*argv, "am") == 0) { 1246 (void) fprintf(el->el_outfile, fmts, T_Margin & MARGIN_AUTO ? 1247 "yes" : "no"); 1248 return 0; 1249 } 1250#endif 1251 else if (strcmp(*argv, "baud") == 0) { 1252 (void) fprintf(el->el_outfile, "%ld\n", el->el_tty.t_speed); 1253 return 0; 1254 } 1255 else if (strcmp(*argv, "rows") == 0 || strcmp(*argv, "lines") == 0) { 1256 (void) fprintf(el->el_outfile, fmtd, Val(T_li)); 1257 return 0; 1258 } 1259 else if (strcmp(*argv, "cols") == 0) { 1260 (void) fprintf(el->el_outfile, fmtd, Val(T_co)); 1261 return 0; 1262 } 1263 1264 /* 1265 * Try to use our local definition first 1266 */ 1267 scap = NULL; 1268 for (t = tstr; t->name != NULL; t++) 1269 if (strcmp(t->name, *argv) == 0) { 1270 scap = el->el_term.t_str[t - tstr]; 1271 break; 1272 } 1273 if (t->name == NULL) 1274 scap = tgetstr(*argv, &area); 1275 if (!scap || scap[0] == '\0') { 1276 if (!silent) 1277 (void) fprintf(el->el_errfile, 1278 "echotc: Termcap parameter `%s' not found.\n", *argv); 1279 return -1; 1280 } 1281 1282 /* 1283 * Count home many values we need for this capability. 1284 */ 1285 for (cap = scap, arg_need = 0; *cap; cap++) 1286 if (*cap == '%') 1287 switch (*++cap) { 1288 case 'd': 1289 case '2': 1290 case '3': 1291 case '.': 1292 case '+': 1293 arg_need++; 1294 break; 1295 case '%': 1296 case '>': 1297 case 'i': 1298 case 'r': 1299 case 'n': 1300 case 'B': 1301 case 'D': 1302 break; 1303 default: 1304 /* 1305 * hpux has lot's of them... 1306 */ 1307 if (verbose) 1308 (void) fprintf(el->el_errfile, 1309 "echotc: Warning: unknown termcap %% `%c'.\n", *cap); 1310 /* This is bad, but I won't complain */ 1311 break; 1312 } 1313 1314 switch (arg_need) { 1315 case 0: 1316 argv++; 1317 if (*argv && *argv[0]) { 1318 if (!silent) 1319 (void) fprintf(el->el_errfile, 1320 "echotc: Warning: Extra argument `%s'.\n", *argv); 1321 return -1; 1322 } 1323 (void) tputs(scap, 1, term__putc); 1324 break; 1325 case 1: 1326 argv++; 1327 if (!*argv || *argv[0] == '\0') { 1328 if (!silent) 1329 (void) fprintf(el->el_errfile, 1330 "echotc: Warning: Missing argument.\n"); 1331 return -1; 1332 } 1333 arg_cols = 0; 1334 arg_rows = atoi(*argv); 1335 argv++; 1336 if (*argv && *argv[0]) { 1337 if (!silent) 1338 (void) fprintf(el->el_errfile, 1339 "echotc: Warning: Extra argument `%s'.\n", *argv); 1340 return -1; 1341 } 1342 (void) tputs(tgoto(scap, arg_cols, arg_rows), 1, term__putc); 1343 break; 1344 default: 1345 /* This is wrong, but I will ignore it... */ 1346 if (verbose) 1347 (void) fprintf(el->el_errfile, 1348 "echotc: Warning: Too many required arguments (%d).\n", 1349 arg_need); 1350 /*FALLTHROUGH*/ 1351 case 2: 1352 argv++; 1353 if (!*argv || *argv[0] == '\0') { 1354 if (!silent) 1355 (void) fprintf(el->el_errfile, 1356 "echotc: Warning: Missing argument.\n"); 1357 return -1; 1358 } 1359 arg_cols = atoi(*argv); 1360 argv++; 1361 if (!*argv || *argv[0] == '\0') { 1362 if (!silent) 1363 (void) fprintf(el->el_errfile, 1364 "echotc: Warning: Missing argument.\n"); 1365 return -1; 1366 } 1367 arg_rows = atoi(*argv); 1368 argv++; 1369 if (*argv && *argv[0]) { 1370 if (!silent) 1371 (void) fprintf(el->el_errfile, 1372 "echotc: Warning: Extra argument `%s'.\n", *argv); 1373 return -1; 1374 } 1375 (void) tputs(tgoto(scap, arg_cols, arg_rows), arg_rows, term__putc); 1376 break; 1377 } 1378 return 0; 1379} 1380