1 /* 2 * ed.screen.c: Editor/termcap-curses interface 3 */ 4 /*- 5 * Copyright (c) 1980, 1991 The Regents of the University of California. 6 * All rights reserved. 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. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 #include "sh.h" 33 #include "ed.h" 34 #include "tc.h" 35 #include "ed.defns.h" 36 37 /* #define DEBUG_LITERAL */ 38 39 /* 40 * IMPORTANT NOTE: these routines are allowed to look at the current screen 41 * and the current possition assuming that it is correct. If this is not 42 * true, then the update will be WRONG! This is (should be) a valid 43 * assumption... 44 */ 45 46 #define TC_BUFSIZE 2048 47 48 #define GoodStr(a) (tstr[a].str != NULL && tstr[a].str[0] != '\0') 49 #define Str(a) tstr[a].str 50 #define Val(a) tval[a].val 51 52 static const struct { 53 const char *b_name; 54 speed_t b_rate; 55 } baud_rate[] = { 56 57 #ifdef B0 58 { "0", B0 }, 59 #endif 60 #ifdef B50 61 { "50", B50 }, 62 #endif 63 #ifdef B75 64 { "75", B75 }, 65 #endif 66 #ifdef B110 67 { "110", B110 }, 68 #endif 69 #ifdef B134 70 { "134", B134 }, 71 #endif 72 #ifdef B150 73 { "150", B150 }, 74 #endif 75 #ifdef B200 76 { "200", B200 }, 77 #endif 78 #ifdef B300 79 { "300", B300 }, 80 #endif 81 #ifdef B600 82 { "600", B600 }, 83 #endif 84 #ifdef B900 85 { "900", B900 }, 86 #endif 87 #ifdef B1200 88 { "1200", B1200 }, 89 #endif 90 #ifdef B1800 91 { "1800", B1800 }, 92 #endif 93 #ifdef B2400 94 { "2400", B2400 }, 95 #endif 96 #ifdef B3600 97 { "3600", B3600 }, 98 #endif 99 #ifdef B4800 100 { "4800", B4800 }, 101 #endif 102 #ifdef B7200 103 { "7200", B7200 }, 104 #endif 105 #ifdef B9600 106 { "9600", B9600 }, 107 #endif 108 #ifdef EXTA 109 { "19200", EXTA }, 110 #endif 111 #ifdef B19200 112 { "19200", B19200 }, 113 #endif 114 #ifdef EXTB 115 { "38400", EXTB }, 116 #endif 117 #ifdef B38400 118 { "38400", B38400 }, 119 #endif 120 { NULL, 0 } 121 }; 122 123 #define T_at7 0 124 #define T_al 1 125 #define T_bl 2 126 #define T_cd 3 127 #define T_ce 4 128 #define T_ch 5 129 #define T_cl 6 130 #define T_dc 7 131 #define T_dl 8 132 #define T_dm 9 133 #define T_ed 10 134 #define T_ei 11 135 #define T_fs 12 136 #define T_ho 13 137 #define T_ic 14 138 #define T_im 15 139 #define T_ip 16 140 #define T_kd 17 141 #define T_kh 18 142 #define T_kl 19 143 #define T_kr 20 144 #define T_ku 21 145 #define T_md 22 146 #define T_me 23 147 #define T_mr 24 148 #define T_nd 25 149 #define T_se 26 150 #define T_so 27 151 #define T_ts 28 152 #define T_up 29 153 #define T_us 30 154 #define T_ue 31 155 #define T_vb 32 156 #define T_DC 33 157 #define T_DO 34 158 #define T_IC 35 159 #define T_LE 36 160 #define T_RI 37 161 #define T_UP 38 162 #define T_str 39 163 164 static struct termcapstr { 165 const char *name; 166 const char *long_name; 167 char *str; 168 } tstr[T_str + 1]; 169 170 171 #define T_am 0 172 #define T_pt 1 173 #define T_li 2 174 #define T_co 3 175 #define T_km 4 176 #define T_xn 5 177 #define T_val 6 178 static struct termcapval { 179 const char *name; 180 const char *long_name; 181 int val; 182 } tval[T_val + 1]; 183 184 void 185 terminit(void) 186 { 187 #ifdef NLS_CATALOGS 188 int i; 189 190 for (i = 0; i < T_str + 1; i++) 191 xfree((ptr_t)(intptr_t)tstr[i].long_name); 192 193 for (i = 0; i < T_val + 1; i++) 194 xfree((ptr_t)(intptr_t)tval[i].long_name); 195 #endif 196 197 tstr[T_al].name = "al"; 198 tstr[T_al].long_name = CSAVS(4, 1, "add new blank line"); 199 200 tstr[T_bl].name = "bl"; 201 tstr[T_bl].long_name = CSAVS(4, 2, "audible bell"); 202 203 tstr[T_cd].name = "cd"; 204 tstr[T_cd].long_name = CSAVS(4, 3, "clear to bottom"); 205 206 tstr[T_ce].name = "ce"; 207 tstr[T_ce].long_name = CSAVS(4, 4, "clear to end of line"); 208 209 tstr[T_ch].name = "ch"; 210 tstr[T_ch].long_name = CSAVS(4, 5, "cursor to horiz pos"); 211 212 tstr[T_cl].name = "cl"; 213 tstr[T_cl].long_name = CSAVS(4, 6, "clear screen"); 214 215 tstr[T_dc].name = "dc"; 216 tstr[T_dc].long_name = CSAVS(4, 7, "delete a character"); 217 218 tstr[T_dl].name = "dl"; 219 tstr[T_dl].long_name = CSAVS(4, 8, "delete a line"); 220 221 tstr[T_dm].name = "dm"; 222 tstr[T_dm].long_name = CSAVS(4, 9, "start delete mode"); 223 224 tstr[T_ed].name = "ed"; 225 tstr[T_ed].long_name = CSAVS(4, 10, "end delete mode"); 226 227 tstr[T_ei].name = "ei"; 228 tstr[T_ei].long_name = CSAVS(4, 11, "end insert mode"); 229 230 tstr[T_fs].name = "fs"; 231 tstr[T_fs].long_name = CSAVS(4, 12, "cursor from status line"); 232 233 tstr[T_ho].name = "ho"; 234 tstr[T_ho].long_name = CSAVS(4, 13, "home cursor"); 235 236 tstr[T_ic].name = "ic"; 237 tstr[T_ic].long_name = CSAVS(4, 14, "insert character"); 238 239 tstr[T_im].name = "im"; 240 tstr[T_im].long_name = CSAVS(4, 15, "start insert mode"); 241 242 tstr[T_ip].name = "ip"; 243 tstr[T_ip].long_name = CSAVS(4, 16, "insert padding"); 244 245 tstr[T_kd].name = "kd"; 246 tstr[T_kd].long_name = CSAVS(4, 17, "sends cursor down"); 247 248 tstr[T_kl].name = "kl"; 249 tstr[T_kl].long_name = CSAVS(4, 18, "sends cursor left"); 250 251 tstr[T_kr].name = "kr"; 252 tstr[T_kr].long_name = CSAVS(4, 19, "sends cursor right"); 253 254 tstr[T_ku].name = "ku"; 255 tstr[T_ku].long_name = CSAVS(4, 20, "sends cursor up"); 256 257 tstr[T_md].name = "md"; 258 tstr[T_md].long_name = CSAVS(4, 21, "begin bold"); 259 260 tstr[T_me].name = "me"; 261 tstr[T_me].long_name = CSAVS(4, 22, "end attributes"); 262 263 tstr[T_nd].name = "nd"; 264 tstr[T_nd].long_name = CSAVS(4, 23, "non destructive space"); 265 266 tstr[T_se].name = "se"; 267 tstr[T_se].long_name = CSAVS(4, 24, "end standout"); 268 269 tstr[T_so].name = "so"; 270 tstr[T_so].long_name = CSAVS(4, 25, "begin standout"); 271 272 tstr[T_ts].name = "ts"; 273 tstr[T_ts].long_name = CSAVS(4, 26, "cursor to status line"); 274 275 tstr[T_up].name = "up"; 276 tstr[T_up].long_name = CSAVS(4, 27, "cursor up one"); 277 278 tstr[T_us].name = "us"; 279 tstr[T_us].long_name = CSAVS(4, 28, "begin underline"); 280 281 tstr[T_ue].name = "ue"; 282 tstr[T_ue].long_name = CSAVS(4, 29, "end underline"); 283 284 tstr[T_vb].name = "vb"; 285 tstr[T_vb].long_name = CSAVS(4, 30, "visible bell"); 286 287 tstr[T_DC].name = "DC"; 288 tstr[T_DC].long_name = CSAVS(4, 31, "delete multiple chars"); 289 290 tstr[T_DO].name = "DO"; 291 tstr[T_DO].long_name = CSAVS(4, 32, "cursor down multiple"); 292 293 tstr[T_IC].name = "IC"; 294 tstr[T_IC].long_name = CSAVS(4, 33, "insert multiple chars"); 295 296 tstr[T_LE].name = "LE"; 297 tstr[T_LE].long_name = CSAVS(4, 34, "cursor left multiple"); 298 299 tstr[T_RI].name = "RI"; 300 tstr[T_RI].long_name = CSAVS(4, 35, "cursor right multiple"); 301 302 tstr[T_UP].name = "UP"; 303 tstr[T_UP].long_name = CSAVS(4, 36, "cursor up multiple"); 304 305 tstr[T_kh].name = "kh"; 306 tstr[T_kh].long_name = CSAVS(4, 43, "send cursor home"); 307 308 tstr[T_at7].name = "@7"; 309 tstr[T_at7].long_name = CSAVS(4, 44, "send cursor end"); 310 311 tstr[T_mr].name = "mr"; 312 tstr[T_mr].long_name = CSAVS(4, 45, "begin reverse video"); 313 314 tstr[T_str].name = NULL; 315 tstr[T_str].long_name = NULL; 316 317 318 tval[T_am].name = "am"; 319 tval[T_am].long_name = CSAVS(4, 37, "Has automatic margins"); 320 321 tval[T_pt].name = "pt"; 322 tval[T_pt].long_name = CSAVS(4, 38, "Can use physical tabs"); 323 324 tval[T_li].name = "li"; 325 tval[T_li].long_name = CSAVS(4, 39, "Number of lines"); 326 327 tval[T_co].name = "co"; 328 tval[T_co].long_name = CSAVS(4, 40, "Number of columns"); 329 330 tval[T_km].name = "km"; 331 tval[T_km].long_name = CSAVS(4, 41, "Has meta key"); 332 333 tval[T_xn].name = "xn"; 334 tval[T_xn].long_name = CSAVS(4, 42, "Newline ignored at right margin"); 335 336 tval[T_val].name = NULL; 337 tval[T_val].long_name = NULL; 338 } 339 340 /* 341 * A very useful table from justin@crim.ca (Justin Bur) :-) 342 * (Modified by per@erix.ericsson.se (Per Hedeland) 343 * - first (and second:-) case fixed) 344 * 345 * Description Termcap variables tcsh behavior 346 * am xn UseRightmost SendCRLF 347 * -------------- ------- ------- ------------ ------------ 348 * Automargins yes no yes no 349 * Magic Margins yes yes yes no 350 * No Wrap no -- yes yes 351 */ 352 353 static int me_all = 0; /* does two or more of the attributes use me */ 354 355 static void ReBufferDisplay (void); 356 static void TCset (struct termcapstr *, const char *); 357 358 359 static void 360 TCset(struct termcapstr *t, const char *cap) 361 { 362 if (cap == NULL || *cap == '\0') { 363 xfree(t->str); 364 t->str = NULL; 365 } else { 366 size_t size; 367 368 size = strlen(cap) + 1; 369 t->str = xrealloc(t->str, size); 370 memcpy(t->str, cap, size); 371 } 372 } 373 374 375 /*ARGSUSED*/ 376 void 377 TellTC(void) 378 { 379 struct termcapstr *t; 380 char *first, *s; 381 382 xprintf("%s", CGETS(7, 1, "\n\tTcsh thinks your terminal has the\n")); 383 xprintf("%s", CGETS(7, 2, "\tfollowing characteristics:\n\n")); 384 xprintf(CGETS(7, 3, "\tIt has %d columns and %d lines\n"), 385 Val(T_co), Val(T_li)); 386 s = strsave(T_HasMeta ? CGETS(7, 5, "a") : CGETS(7, 6, "no")); 387 cleanup_push(s, xfree); 388 first = s; 389 xprintf(CGETS(7, 4, "\tIt has %s meta key\n"), s); 390 s = strsave(T_Tabs ? "" : CGETS(7, 8, " not")); 391 cleanup_push(s, xfree); 392 xprintf(CGETS(7, 7, "\tIt can%s use tabs\n"), s); 393 s = strsave((T_Margin&MARGIN_AUTO) ? 394 CGETS(7, 10, "has") : CGETS(7, 11, "does not have")); 395 cleanup_push(s, xfree); 396 xprintf(CGETS(7, 9, "\tIt %s automatic margins\n"), s); 397 if (T_Margin & MARGIN_AUTO) { 398 s = strsave((T_Margin & MARGIN_MAGIC) ? 399 CGETS(7, 10, "has") : CGETS(7, 11, "does not have")); 400 cleanup_push(s, xfree); 401 xprintf(CGETS(7, 12, "\tIt %s magic margins\n"), s); 402 } 403 for (t = tstr; t->name != NULL; t++) { 404 s = strsave(t->str && *t->str ? t->str : CGETS(7, 13, "(empty)")); 405 cleanup_push(s, xfree); 406 xprintf("\t%36s (%s) == %s\n", t->long_name, t->name, s); 407 cleanup_until(s); 408 } 409 xputchar('\n'); 410 cleanup_until(first); 411 } 412 413 414 static void 415 ReBufferDisplay(void) 416 { 417 int i; 418 Char **b; 419 420 b = Display; 421 Display = NULL; 422 blkfree(b); 423 b = Vdisplay; 424 Vdisplay = NULL; 425 blkfree(b); 426 TermH = Val(T_co); 427 TermV = (INBUFSIZE * 4) / TermH + 1;/*FIXBUF*/ 428 b = xmalloc(sizeof(*b) * (TermV + 1)); 429 for (i = 0; i < TermV; i++) 430 b[i] = xmalloc(sizeof(*b[i]) * (TermH + 1)); 431 b[TermV] = NULL; 432 Display = b; 433 b = xmalloc(sizeof(*b) * (TermV + 1)); 434 for (i = 0; i < TermV; i++) 435 b[i] = xmalloc(sizeof(*b[i]) * (TermH + 1)); 436 b[TermV] = NULL; 437 Vdisplay = b; 438 } 439 440 void 441 SetTC(char *what, char *how) 442 { 443 struct termcapstr *ts; 444 struct termcapval *tv; 445 446 /* 447 * Do the strings first 448 */ 449 setname("settc"); 450 for (ts = tstr; ts->name != NULL; ts++) 451 if (strcmp(ts->name, what) == 0) 452 break; 453 if (ts->name != NULL) { 454 TCset(ts, how); 455 /* 456 * Reset variables 457 */ 458 if (GoodStr(T_me) && GoodStr(T_ue)) 459 me_all = (strcmp(Str(T_me), Str(T_ue)) == 0); 460 else 461 me_all = 0; 462 if (GoodStr(T_me) && GoodStr(T_se)) 463 me_all |= (strcmp(Str(T_me), Str(T_se)) == 0); 464 465 T_CanCEOL = GoodStr(T_ce); 466 T_CanDel = GoodStr(T_dc) || GoodStr(T_DC); 467 T_CanIns = GoodStr(T_im) || GoodStr(T_ic) || GoodStr(T_IC); 468 T_CanUP = GoodStr(T_up) || GoodStr(T_UP); 469 return; 470 } 471 472 /* 473 * Do the numeric ones second 474 */ 475 for (tv = tval; tv->name != NULL; tv++) 476 if (strcmp(tv->name, what) == 0) 477 break; 478 479 if (tv->name != NULL) { 480 if (tv == &tval[T_pt] || tv == &tval[T_km] || 481 tv == &tval[T_am] || tv == &tval[T_xn]) { 482 if (strcmp(how, "yes") == 0) 483 tv->val = 1; 484 else if (strcmp(how, "no") == 0) 485 tv->val = 0; 486 else { 487 stderror(ERR_SETTCUS, tv->name); 488 return; 489 } 490 T_Tabs = Val(T_pt); 491 T_HasMeta = Val(T_km); 492 T_Margin = Val(T_am) ? MARGIN_AUTO : 0; 493 T_Margin |= Val(T_xn) ? MARGIN_MAGIC : 0; 494 if (tv == &tval[T_am] || tv == &tval[T_xn]) 495 ChangeSize(Val(T_li), Val(T_co)); 496 return; 497 } 498 else { 499 tv->val = atoi(how); 500 T_Cols = (Char) Val(T_co); 501 T_Lines = (Char) Val(T_li); 502 if (tv == &tval[T_co] || tv == &tval[T_li]) 503 ChangeSize(Val(T_li), Val(T_co)); 504 return; 505 } 506 } 507 stderror(ERR_NAME | ERR_TCCAP, what); 508 return; 509 } 510 511 512 /* 513 * Print the termcap string out with variable substitution 514 */ 515 void 516 EchoTC(Char **v) 517 { 518 char *cap, *scap, *cv; 519 int arg_need, arg_cols, arg_rows; 520 int verbose = 0, silent = 0; 521 char *area; 522 static const char fmts[] = "%s\n", fmtd[] = "%d\n"; 523 struct termcapstr *t; 524 char buf[TC_BUFSIZE]; 525 Char **globbed; 526 527 area = buf; 528 529 setname("echotc"); 530 531 v = glob_all_or_error(v); 532 globbed = v; 533 cleanup_push(globbed, blk_cleanup); 534 535 if (!*v || *v[0] == '\0') 536 goto end; 537 if (v[0][0] == '-') { 538 switch (v[0][1]) { 539 case 'v': 540 verbose = 1; 541 break; 542 case 's': 543 silent = 1; 544 break; 545 default: 546 stderror(ERR_NAME | ERR_TCUSAGE); 547 break; 548 } 549 v++; 550 } 551 if (!*v || *v[0] == '\0') 552 goto end; 553 cv = strsave(short2str(*v)); 554 cleanup_push(cv, xfree); 555 if (strcmp(cv, "tabs") == 0) { 556 xprintf(fmts, T_Tabs ? CGETS(7, 14, "yes") : 557 CGETS(7, 15, "no")); 558 goto end_flush; 559 } 560 else if (strcmp(cv, "meta") == 0) { 561 xprintf(fmts, Val(T_km) ? CGETS(7, 14, "yes") : 562 CGETS(7, 15, "no")); 563 goto end_flush; 564 } 565 else if (strcmp(cv, "xn") == 0) { 566 xprintf(fmts, T_Margin & MARGIN_MAGIC ? CGETS(7, 14, "yes") : 567 CGETS(7, 15, "no")); 568 goto end_flush; 569 } 570 else if (strcmp(cv, "am") == 0) { 571 xprintf(fmts, T_Margin & MARGIN_AUTO ? CGETS(7, 14, "yes") : 572 CGETS(7, 15, "no")); 573 goto end_flush; 574 } 575 else if (strcmp(cv, "baud") == 0) { 576 int i; 577 578 for (i = 0; baud_rate[i].b_name != NULL; i++) 579 if (T_Speed == baud_rate[i].b_rate) { 580 xprintf(fmts, baud_rate[i].b_name); 581 goto end_flush; 582 } 583 xprintf(fmtd, 0); 584 goto end_flush; 585 } 586 else if (strcmp(cv, "rows") == 0 || strcmp(cv, "lines") == 0 || 587 strcmp(cv, "li") == 0) { 588 xprintf(fmtd, Val(T_li)); 589 goto end_flush; 590 } 591 else if (strcmp(cv, "cols") == 0 || strcmp(cv, "co") == 0) { 592 xprintf(fmtd, Val(T_co)); 593 goto end_flush; 594 } 595 596 /* 597 * Try to use our local definition first 598 */ 599 scap = NULL; 600 for (t = tstr; t->name != NULL; t++) 601 if (strcmp(t->name, cv) == 0) { 602 scap = t->str; 603 break; 604 } 605 if (t->name == NULL) 606 scap = tgetstr(cv, &area); 607 if (!scap || scap[0] == '\0') { 608 if (tgetflag(cv)) { 609 xprintf("%s", CGETS(7, 14, "yes\n")); 610 goto end; 611 } 612 if (silent) 613 goto end; 614 else 615 stderror(ERR_NAME | ERR_TCCAP, cv); 616 } 617 618 /* 619 * Count home many values we need for this capability. 620 */ 621 for (cap = scap, arg_need = 0; *cap; cap++) 622 if (*cap == '%') 623 switch (*++cap) { 624 case 'd': 625 case '2': 626 case '3': 627 case '.': 628 case '+': 629 arg_need++; 630 break; 631 case '%': 632 case '>': 633 case 'i': 634 case 'r': 635 case 'n': 636 case 'B': 637 case 'D': 638 break; 639 default: 640 /* 641 * hpux has lot's of them... 642 */ 643 if (verbose) 644 stderror(ERR_NAME | ERR_TCPARM, *cap); 645 /* This is bad, but I won't complain */ 646 break; 647 } 648 649 switch (arg_need) { 650 case 0: 651 v++; 652 if (*v && *v[0]) { 653 if (silent) 654 goto end; 655 else 656 stderror(ERR_NAME | ERR_TCARGS, cv, arg_need); 657 } 658 (void) tputs(scap, 1, PUTRAW); 659 break; 660 case 1: 661 v++; 662 if (!*v || *v[0] == '\0') 663 stderror(ERR_NAME | ERR_TCNARGS, cv, 1); 664 arg_cols = 0; 665 arg_rows = atoi(short2str(*v)); 666 v++; 667 if (*v && *v[0]) { 668 if (silent) 669 goto end; 670 else 671 stderror(ERR_NAME | ERR_TCARGS, cv, arg_need); 672 } 673 (void) tputs(tgoto(scap, arg_cols, arg_rows), 1, PUTRAW); 674 break; 675 default: 676 /* This is wrong, but I will ignore it... */ 677 if (verbose) 678 stderror(ERR_NAME | ERR_TCARGS, cv, arg_need); 679 /*FALLTHROUGH*/ 680 case 2: 681 v++; 682 if (!*v || *v[0] == '\0') { 683 if (silent) 684 goto end; 685 else 686 stderror(ERR_NAME | ERR_TCNARGS, cv, 2); 687 } 688 arg_cols = atoi(short2str(*v)); 689 v++; 690 if (!*v || *v[0] == '\0') { 691 if (silent) 692 goto end; 693 else 694 stderror(ERR_NAME | ERR_TCNARGS, cv, 2); 695 } 696 arg_rows = atoi(short2str(*v)); 697 v++; 698 if (*v && *v[0]) { 699 if (silent) 700 goto end; 701 else 702 stderror(ERR_NAME | ERR_TCARGS, cv, arg_need); 703 } 704 (void) tputs(tgoto(scap, arg_cols, arg_rows), arg_rows, PUTRAW); 705 break; 706 } 707 end_flush: 708 flush(); 709 end: 710 cleanup_until(globbed); 711 } 712 713 int GotTermCaps = 0; 714 715 static struct { 716 Char *name; 717 int key; 718 XmapVal fun; 719 int type; 720 } arrow[] = { 721 #define A_K_DN 0 722 { STRdown, T_kd, { 0 }, 0 }, 723 #define A_K_UP 1 724 { STRup, T_ku, { 0 }, 0 }, 725 #define A_K_LT 2 726 { STRleft, T_kl, { 0 }, 0 }, 727 #define A_K_RT 3 728 { STRright, T_kr, { 0 }, 0 }, 729 #define A_K_HO 4 730 { STRhome, T_kh, { 0 }, 0 }, 731 #define A_K_EN 5 732 { STRend, T_at7, { 0 }, 0} 733 }; 734 #define A_K_NKEYS 6 735 736 void 737 ResetArrowKeys(void) 738 { 739 arrow[A_K_DN].fun.cmd = F_DOWN_HIST; 740 arrow[A_K_DN].type = XK_CMD; 741 742 arrow[A_K_UP].fun.cmd = F_UP_HIST; 743 arrow[A_K_UP].type = XK_CMD; 744 745 arrow[A_K_LT].fun.cmd = F_CHARBACK; 746 arrow[A_K_LT].type = XK_CMD; 747 748 arrow[A_K_RT].fun.cmd = F_CHARFWD; 749 arrow[A_K_RT].type = XK_CMD; 750 751 arrow[A_K_HO].fun.cmd = F_TOBEG; 752 arrow[A_K_HO].type = XK_CMD; 753 754 arrow[A_K_EN].fun.cmd = F_TOEND; 755 arrow[A_K_EN].type = XK_CMD; 756 } 757 758 void 759 DefaultArrowKeys(void) 760 { 761 static Char strA[] = {033, '[', 'A', '\0'}; 762 static Char strB[] = {033, '[', 'B', '\0'}; 763 static Char strC[] = {033, '[', 'C', '\0'}; 764 static Char strD[] = {033, '[', 'D', '\0'}; 765 static Char strH[] = {033, '[', 'H', '\0'}; 766 static Char strF[] = {033, '[', 'F', '\0'}; 767 static Char stOA[] = {033, 'O', 'A', '\0'}; 768 static Char stOB[] = {033, 'O', 'B', '\0'}; 769 static Char stOC[] = {033, 'O', 'C', '\0'}; 770 static Char stOD[] = {033, 'O', 'D', '\0'}; 771 static Char stOH[] = {033, 'O', 'H', '\0'}; 772 static Char stOF[] = {033, 'O', 'F', '\0'}; 773 774 CStr cs; 775 #ifndef IS_ASCII 776 if (strA[0] == 033) 777 { 778 strA[0] = CTL_ESC('\033'); 779 strB[0] = CTL_ESC('\033'); 780 strC[0] = CTL_ESC('\033'); 781 strD[0] = CTL_ESC('\033'); 782 strH[0] = CTL_ESC('\033'); 783 strF[0] = CTL_ESC('\033'); 784 stOA[0] = CTL_ESC('\033'); 785 stOB[0] = CTL_ESC('\033'); 786 stOC[0] = CTL_ESC('\033'); 787 stOD[0] = CTL_ESC('\033'); 788 stOH[0] = CTL_ESC('\033'); 789 stOF[0] = CTL_ESC('\033'); 790 } 791 #endif 792 793 cs.len = 3; 794 795 cs.buf = strA; AddXkey(&cs, &arrow[A_K_UP].fun, arrow[A_K_UP].type); 796 cs.buf = strB; AddXkey(&cs, &arrow[A_K_DN].fun, arrow[A_K_DN].type); 797 cs.buf = strC; AddXkey(&cs, &arrow[A_K_RT].fun, arrow[A_K_RT].type); 798 cs.buf = strD; AddXkey(&cs, &arrow[A_K_LT].fun, arrow[A_K_LT].type); 799 cs.buf = strH; AddXkey(&cs, &arrow[A_K_HO].fun, arrow[A_K_HO].type); 800 cs.buf = strF; AddXkey(&cs, &arrow[A_K_EN].fun, arrow[A_K_EN].type); 801 cs.buf = stOA; AddXkey(&cs, &arrow[A_K_UP].fun, arrow[A_K_UP].type); 802 cs.buf = stOB; AddXkey(&cs, &arrow[A_K_DN].fun, arrow[A_K_DN].type); 803 cs.buf = stOC; AddXkey(&cs, &arrow[A_K_RT].fun, arrow[A_K_RT].type); 804 cs.buf = stOD; AddXkey(&cs, &arrow[A_K_LT].fun, arrow[A_K_LT].type); 805 cs.buf = stOH; AddXkey(&cs, &arrow[A_K_HO].fun, arrow[A_K_HO].type); 806 cs.buf = stOF; AddXkey(&cs, &arrow[A_K_EN].fun, arrow[A_K_EN].type); 807 808 if (VImode) { 809 cs.len = 2; 810 cs.buf = &strA[1]; AddXkey(&cs, &arrow[A_K_UP].fun, arrow[A_K_UP].type); 811 cs.buf = &strB[1]; AddXkey(&cs, &arrow[A_K_DN].fun, arrow[A_K_DN].type); 812 cs.buf = &strC[1]; AddXkey(&cs, &arrow[A_K_RT].fun, arrow[A_K_RT].type); 813 cs.buf = &strD[1]; AddXkey(&cs, &arrow[A_K_LT].fun, arrow[A_K_LT].type); 814 cs.buf = &strH[1]; AddXkey(&cs, &arrow[A_K_HO].fun, arrow[A_K_HO].type); 815 cs.buf = &strF[1]; AddXkey(&cs, &arrow[A_K_EN].fun, arrow[A_K_EN].type); 816 cs.buf = &stOA[1]; AddXkey(&cs, &arrow[A_K_UP].fun, arrow[A_K_UP].type); 817 cs.buf = &stOB[1]; AddXkey(&cs, &arrow[A_K_DN].fun, arrow[A_K_DN].type); 818 cs.buf = &stOC[1]; AddXkey(&cs, &arrow[A_K_RT].fun, arrow[A_K_RT].type); 819 cs.buf = &stOD[1]; AddXkey(&cs, &arrow[A_K_LT].fun, arrow[A_K_LT].type); 820 cs.buf = &stOH[1]; AddXkey(&cs, &arrow[A_K_HO].fun, arrow[A_K_HO].type); 821 cs.buf = &stOF[1]; AddXkey(&cs, &arrow[A_K_EN].fun, arrow[A_K_EN].type); 822 } 823 } 824 825 826 int 827 SetArrowKeys(const CStr *name, XmapVal *fun, int type) 828 { 829 int i; 830 for (i = 0; i < A_K_NKEYS; i++) 831 if (Strcmp(name->buf, arrow[i].name) == 0) { 832 arrow[i].fun = *fun; 833 arrow[i].type = type; 834 return 0; 835 } 836 return -1; 837 } 838 839 int 840 IsArrowKey(Char *name) 841 { 842 int i; 843 for (i = 0; i < A_K_NKEYS; i++) 844 if (Strcmp(name, arrow[i].name) == 0) 845 return 1; 846 return 0; 847 } 848 849 int 850 ClearArrowKeys(const CStr *name) 851 { 852 int i; 853 for (i = 0; i < A_K_NKEYS; i++) 854 if (Strcmp(name->buf, arrow[i].name) == 0) { 855 arrow[i].type = XK_NOD; 856 return 0; 857 } 858 return -1; 859 } 860 861 void 862 PrintArrowKeys(const CStr *name) 863 { 864 int i; 865 866 for (i = 0; i < A_K_NKEYS; i++) 867 if (name->len == 0 || Strcmp(name->buf, arrow[i].name) == 0) 868 if (arrow[i].type != XK_NOD) 869 printOne(arrow[i].name, &arrow[i].fun, arrow[i].type); 870 } 871 872 873 void 874 BindArrowKeys(void) 875 { 876 KEYCMD *map, *dmap; 877 int i, j; 878 char *p; 879 CStr cs; 880 881 if (!GotTermCaps) 882 return; 883 map = VImode ? CcAltMap : CcKeyMap; 884 dmap = VImode ? CcViCmdMap : CcEmacsMap; 885 886 DefaultArrowKeys(); 887 888 for (i = 0; i < A_K_NKEYS; i++) { 889 p = tstr[arrow[i].key].str; 890 if (p && *p) { 891 j = (unsigned char) *p; 892 cs.buf = str2short(p); 893 cs.len = Strlen(cs.buf); 894 /* 895 * Assign the arrow keys only if: 896 * 897 * 1. They are multi-character arrow keys and the user 898 * has not re-assigned the leading character, or 899 * has re-assigned the leading character to be F_XKEY 900 * 2. They are single arrow keys pointing to an unassigned key. 901 */ 902 if (arrow[i].type == XK_NOD) { 903 ClearXkey(map, &cs); 904 } 905 else { 906 if (p[1] && (dmap[j] == map[j] || map[j] == F_XKEY)) { 907 AddXkey(&cs, &arrow[i].fun, arrow[i].type); 908 map[j] = F_XKEY; 909 } 910 else if (map[j] == F_UNASSIGNED) { 911 ClearXkey(map, &cs); 912 if (arrow[i].type == XK_CMD) 913 map[j] = arrow[i].fun.cmd; 914 else 915 AddXkey(&cs, &arrow[i].fun, arrow[i].type); 916 } 917 } 918 } 919 } 920 } 921 922 static Char cur_atr = 0; /* current attributes */ 923 924 void 925 SetAttributes(Char atr) 926 { 927 atr &= ATTRIBUTES; 928 if (atr != cur_atr) { 929 if (me_all && GoodStr(T_me)) { 930 if (((cur_atr & BOLD) && !(atr & BOLD)) || 931 ((cur_atr & UNDER) && !(atr & UNDER)) || 932 ((cur_atr & STANDOUT) && !(atr & STANDOUT))) { 933 (void) tputs(Str(T_me), 1, PUTPURE); 934 cur_atr = 0; 935 } 936 } 937 if ((atr & BOLD) != (cur_atr & BOLD)) { 938 if (atr & BOLD) { 939 if (GoodStr(T_md) && GoodStr(T_me)) { 940 (void) tputs(Str(T_md), 1, PUTPURE); 941 cur_atr |= BOLD; 942 } 943 } 944 else { 945 if (GoodStr(T_md) && GoodStr(T_me)) { 946 (void) tputs(Str(T_me), 1, PUTPURE); 947 if ((cur_atr & STANDOUT) && GoodStr(T_se)) { 948 (void) tputs(Str(T_se), 1, PUTPURE); 949 cur_atr &= ~STANDOUT; 950 } 951 if ((cur_atr & UNDER) && GoodStr(T_ue)) { 952 (void) tputs(Str(T_ue), 1, PUTPURE); 953 cur_atr &= ~UNDER; 954 } 955 cur_atr &= ~BOLD; 956 } 957 } 958 } 959 if ((atr & STANDOUT) != (cur_atr & STANDOUT)) { 960 if (atr & STANDOUT) { 961 if (GoodStr(T_so) && GoodStr(T_se)) { 962 (void) tputs(Str(T_so), 1, PUTPURE); 963 cur_atr |= STANDOUT; 964 } 965 } 966 else { 967 if (GoodStr(T_se)) { 968 (void) tputs(Str(T_se), 1, PUTPURE); 969 cur_atr &= ~STANDOUT; 970 } 971 } 972 } 973 if ((atr & UNDER) != (cur_atr & UNDER)) { 974 if (atr & UNDER) { 975 if (GoodStr(T_us) && GoodStr(T_ue)) { 976 (void) tputs(Str(T_us), 1, PUTPURE); 977 cur_atr |= UNDER; 978 } 979 } 980 else { 981 if (GoodStr(T_ue)) { 982 (void) tputs(Str(T_ue), 1, PUTPURE); 983 cur_atr &= ~UNDER; 984 } 985 } 986 } 987 } 988 } 989 990 int highlighting = 0; 991 992 void 993 StartHighlight(void) 994 { 995 (void) tputs(Str(T_mr), 1, PUTPURE); 996 highlighting = 1; 997 } 998 999 void 1000 StopHighlight(void) 1001 { 1002 (void) tputs(Str(T_me), 1, PUTPURE); 1003 highlighting = 0; 1004 } 1005 1006 /* PWP 6-27-88 -- if the tty driver thinks that we can tab, we ask termcap */ 1007 int 1008 CanWeTab(void) 1009 { 1010 return (Val(T_pt)); 1011 } 1012 1013 /* move to line <where> (first line == 0) as efficiently as possible; */ 1014 void 1015 MoveToLine(int where) 1016 { 1017 int del; 1018 1019 if (where == CursorV) 1020 return; 1021 1022 if (where > TermV) { 1023 #ifdef DEBUG_SCREEN 1024 xprintf("MoveToLine: where is ridiculous: %d\r\n", where); 1025 flush(); 1026 #endif /* DEBUG_SCREEN */ 1027 return; 1028 } 1029 1030 del = where - CursorV; 1031 1032 if (del > 0) { 1033 while (del > 0) { 1034 if ((T_Margin & MARGIN_AUTO) && Display[CursorV][0] != '\0') { 1035 size_t h; 1036 1037 for (h = TermH - 1; h > 0 && Display[CursorV][h] == CHAR_DBWIDTH; 1038 h--) 1039 ; 1040 /* move without newline */ 1041 MoveToChar(h); 1042 so_write(&Display[CursorV][CursorH], TermH - CursorH); /* updates CursorH/V*/ 1043 del--; 1044 } 1045 else { 1046 if ((del > 1) && GoodStr(T_DO)) { 1047 (void) tputs(tgoto(Str(T_DO), del, del), del, PUTPURE); 1048 del = 0; 1049 } 1050 else { 1051 for ( ; del > 0; del--) 1052 (void) putraw('\n'); 1053 CursorH = 0; /* because the \n will become \r\n */ 1054 } 1055 } 1056 } 1057 } 1058 else { /* del < 0 */ 1059 if (GoodStr(T_UP) && (-del > 1 || !GoodStr(T_up))) 1060 (void) tputs(tgoto(Str(T_UP), -del, -del), -del, PUTPURE); 1061 else { 1062 int i; 1063 if (GoodStr(T_up)) 1064 for (i = 0; i < -del; i++) 1065 (void) tputs(Str(T_up), 1, PUTPURE); 1066 } 1067 } 1068 CursorV = where; /* now where is here */ 1069 } 1070 1071 void 1072 MoveToChar(int where) /* move to character position (where) */ 1073 { /* as efficiently as possible */ 1074 int del; 1075 1076 mc_again: 1077 if (where == CursorH) 1078 return; 1079 1080 if (where >= TermH) { 1081 #ifdef DEBUG_SCREEN 1082 xprintf("MoveToChar: where is riduculous: %d\r\n", where); 1083 flush(); 1084 #endif /* DEBUG_SCREEN */ 1085 return; 1086 } 1087 1088 if (!where) { /* if where is first column */ 1089 (void) putraw('\r'); /* do a CR */ 1090 CursorH = 0; 1091 return; 1092 } 1093 1094 del = where - CursorH; 1095 1096 if ((del < -4 || del > 4) && GoodStr(T_ch)) 1097 /* go there directly */ 1098 (void) tputs(tgoto(Str(T_ch), where, where), where, PUTPURE); 1099 else { 1100 int i; 1101 if (del > 0) { /* moving forward */ 1102 if ((del > 4) && GoodStr(T_RI)) 1103 (void) tputs(tgoto(Str(T_RI), del, del), del, PUTPURE); 1104 else { 1105 /* if I can do tabs, use them */ 1106 if (T_Tabs) { 1107 if ((CursorH & 0370) != (where & ~0x7) 1108 && Display[CursorV][where & ~0x7] != CHAR_DBWIDTH) { 1109 /* if not within tab stop */ 1110 for (i = (CursorH & 0370); i < (where & ~0x7); i += 8) 1111 (void) putraw('\t'); /* then tab over */ 1112 CursorH = where & ~0x7; 1113 /* Note: considering that we often want to go to 1114 TermH - 1 for the wrapping, it would be nice to 1115 optimize this case by tabbing to the last column 1116 - but this doesn't work for all terminals! */ 1117 } 1118 } 1119 /* it's usually cheaper to just write the chars, so we do. */ 1120 1121 /* NOTE THAT so_write() WILL CHANGE CursorH!!! */ 1122 so_write(&Display[CursorV][CursorH], where - CursorH); 1123 1124 } 1125 } 1126 else { /* del < 0 := moving backward */ 1127 if ((-del > 4) && GoodStr(T_LE)) 1128 (void) tputs(tgoto(Str(T_LE), -del, -del), -del, PUTPURE); 1129 else { /* can't go directly there */ 1130 /* if the "cost" is greater than the "cost" from col 0 */ 1131 if (T_Tabs ? (-del > ((where >> 3) + (where & 07))) 1132 : (-del > where)) { 1133 (void) putraw('\r'); /* do a CR */ 1134 CursorH = 0; 1135 goto mc_again; /* and try again */ 1136 } 1137 for (i = 0; i < -del; i++) 1138 (void) putraw('\b'); 1139 } 1140 } 1141 } 1142 CursorH = where; /* now where is here */ 1143 } 1144 1145 void 1146 so_write(Char *cp, int n) 1147 { 1148 int cur_pos, prompt_len = 0, region_start = 0, region_end = 0; 1149 1150 if (n <= 0) 1151 return; /* catch bugs */ 1152 1153 if (n > TermH) { 1154 #ifdef DEBUG_SCREEN 1155 xprintf("so_write: n is riduculous: %d\r\n", n); 1156 flush(); 1157 #endif /* DEBUG_SCREEN */ 1158 return; 1159 } 1160 1161 if (adrof(STRhighlight)) { 1162 /* find length of prompt */ 1163 Char *promptc; 1164 for (promptc = Prompt; *promptc; promptc++); 1165 prompt_len = promptc - Prompt; 1166 1167 /* find region start and end points */ 1168 if (IncMatchLen) { 1169 region_start = (Cursor - InputBuf) + prompt_len; 1170 region_end = region_start + IncMatchLen; 1171 } else if (MarkIsSet) { 1172 region_start = (min(Cursor, Mark) - InputBuf) + prompt_len; 1173 region_end = (max(Cursor, Mark) - InputBuf) + prompt_len; 1174 } 1175 } 1176 1177 do { 1178 if (adrof(STRhighlight)) { 1179 cur_pos = CursorV * TermH + CursorH; 1180 if (!highlighting && 1181 cur_pos >= region_start && cur_pos < region_end) 1182 StartHighlight(); 1183 else if (highlighting && cur_pos >= region_end) 1184 StopHighlight(); 1185 1186 /* don't highlight over the cursor. the highlighting's reverse 1187 * video would cancel it out. :P */ 1188 if (highlighting && cur_pos == (Cursor - InputBuf) + prompt_len) 1189 StopHighlight(); 1190 } 1191 1192 if (*cp != CHAR_DBWIDTH) { 1193 if (*cp & LITERAL) { 1194 Char *d; 1195 #ifdef DEBUG_LITERAL 1196 xprintf("so: litnum %d\r\n", (int)(*cp & ~LITERAL)); 1197 #endif /* DEBUG_LITERAL */ 1198 for (d = litptr + (*cp & ~LITERAL) * LIT_FACTOR; *d; d++) 1199 (void) putwraw(*d); 1200 } 1201 else 1202 (void) putwraw(*cp); 1203 } 1204 cp++; 1205 CursorH++; 1206 } while (--n); 1207 1208 if (adrof(STRhighlight) && highlighting) 1209 StopHighlight(); 1210 1211 if (CursorH >= TermH) { /* wrap? */ 1212 if (T_Margin & MARGIN_AUTO) { /* yes */ 1213 CursorH = 0; 1214 CursorV++; 1215 if (T_Margin & MARGIN_MAGIC) { 1216 /* force the wrap to avoid the "magic" situation */ 1217 Char xc; 1218 if ((xc = Display[CursorV][CursorH]) != '\0') { 1219 so_write(&xc, 1); 1220 while (Display[CursorV][CursorH] == CHAR_DBWIDTH) 1221 CursorH++; 1222 } 1223 else { 1224 (void) putraw(' '); 1225 CursorH = 1; 1226 } 1227 } 1228 } 1229 else /* no wrap, but cursor stays on screen */ 1230 CursorH = TermH - 1; 1231 } 1232 } 1233 1234 1235 void 1236 DeleteChars(int num) /* deletes <num> characters */ 1237 { 1238 if (num <= 0) 1239 return; 1240 1241 if (!T_CanDel) { 1242 #ifdef DEBUG_EDIT 1243 xprintf(CGETS(7, 16, "ERROR: cannot delete\r\n")); 1244 #endif /* DEBUG_EDIT */ 1245 flush(); 1246 return; 1247 } 1248 1249 if (num > TermH) { 1250 #ifdef DEBUG_SCREEN 1251 xprintf(CGETS(7, 17, "DeleteChars: num is riduculous: %d\r\n"), num); 1252 flush(); 1253 #endif /* DEBUG_SCREEN */ 1254 return; 1255 } 1256 1257 if (GoodStr(T_DC)) /* if I have multiple delete */ 1258 if ((num > 1) || !GoodStr(T_dc)) { /* if dc would be more expen. */ 1259 (void) tputs(tgoto(Str(T_DC), num, num), num, PUTPURE); 1260 return; 1261 } 1262 1263 if (GoodStr(T_dm)) /* if I have delete mode */ 1264 (void) tputs(Str(T_dm), 1, PUTPURE); 1265 1266 if (GoodStr(T_dc)) /* else do one at a time */ 1267 while (num--) 1268 (void) tputs(Str(T_dc), 1, PUTPURE); 1269 1270 if (GoodStr(T_ed)) /* if I have delete mode */ 1271 (void) tputs(Str(T_ed), 1, PUTPURE); 1272 } 1273 1274 /* Puts terminal in insert character mode, or inserts num characters in the 1275 line */ 1276 void 1277 Insert_write(Char *cp, int num) 1278 { 1279 if (num <= 0) 1280 return; 1281 if (!T_CanIns) { 1282 #ifdef DEBUG_EDIT 1283 xprintf(CGETS(7, 18, "ERROR: cannot insert\r\n")); 1284 #endif /* DEBUG_EDIT */ 1285 flush(); 1286 return; 1287 } 1288 1289 if (num > TermH) { 1290 #ifdef DEBUG_SCREEN 1291 xprintf(CGETS(7, 19, "StartInsert: num is riduculous: %d\r\n"), num); 1292 flush(); 1293 #endif /* DEBUG_SCREEN */ 1294 return; 1295 } 1296 1297 if (GoodStr(T_IC)) /* if I have multiple insert */ 1298 if ((num > 1) || !GoodStr(T_ic)) { /* if ic would be more expen. */ 1299 (void) tputs(tgoto(Str(T_IC), num, num), num, PUTPURE); 1300 so_write(cp, num); /* this updates CursorH/V */ 1301 return; 1302 } 1303 1304 if (GoodStr(T_im) && GoodStr(T_ei)) { /* if I have insert mode */ 1305 (void) tputs(Str(T_im), 1, PUTPURE); 1306 1307 so_write(cp, num); /* this updates CursorH/V */ 1308 1309 if (GoodStr(T_ip)) /* have to make num chars insert */ 1310 (void) tputs(Str(T_ip), 1, PUTPURE); 1311 1312 (void) tputs(Str(T_ei), 1, PUTPURE); 1313 return; 1314 } 1315 1316 do { 1317 if (GoodStr(T_ic)) /* have to make num chars insert */ 1318 (void) tputs(Str(T_ic), 1, PUTPURE); /* insert a char */ 1319 1320 so_write(cp++, 1); /* this updates CursorH/V */ 1321 1322 if (GoodStr(T_ip)) /* have to make num chars insert */ 1323 (void) tputs(Str(T_ip), 1, PUTPURE);/* pad the inserted char */ 1324 1325 } while (--num); 1326 1327 } 1328 1329 /* clear to end of line. There are num characters to clear */ 1330 void 1331 ClearEOL(int num) 1332 { 1333 int i; 1334 1335 if (num <= 0) 1336 return; 1337 1338 if (T_CanCEOL && GoodStr(T_ce)) 1339 (void) tputs(Str(T_ce), 1, PUTPURE); 1340 else { 1341 for (i = 0; i < num; i++) 1342 (void) putraw(' '); 1343 CursorH += num; /* have written num spaces */ 1344 } 1345 } 1346 1347 void 1348 ClearScreen(void) 1349 { /* clear the whole screen and home */ 1350 if (GoodStr(T_cl)) 1351 /* send the clear screen code */ 1352 (void) tputs(Str(T_cl), Val(T_li), PUTPURE); 1353 else if (GoodStr(T_ho) && GoodStr(T_cd)) { 1354 (void) tputs(Str(T_ho), Val(T_li), PUTPURE); /* home */ 1355 /* clear to bottom of screen */ 1356 (void) tputs(Str(T_cd), Val(T_li), PUTPURE); 1357 } 1358 else { 1359 (void) putraw('\r'); 1360 (void) putraw('\n'); 1361 } 1362 } 1363 1364 void 1365 SoundBeep(void) 1366 { /* produce a sound */ 1367 beep_cmd (); 1368 if (adrof(STRnobeep)) 1369 return; 1370 1371 if (GoodStr(T_vb) && adrof(STRvisiblebell)) 1372 (void) tputs(Str(T_vb), 1, PUTPURE); /* visible bell */ 1373 else if (GoodStr(T_bl)) 1374 /* what termcap says we should use */ 1375 (void) tputs(Str(T_bl), 1, PUTPURE); 1376 else 1377 (void) putraw(CTL_ESC('\007')); /* an ASCII bell; ^G */ 1378 } 1379 1380 void 1381 ClearToBottom(void) 1382 { /* clear to the bottom of the screen */ 1383 if (GoodStr(T_cd)) 1384 (void) tputs(Str(T_cd), Val(T_li), PUTPURE); 1385 else if (GoodStr(T_ce)) 1386 (void) tputs(Str(T_ce), Val(T_li), PUTPURE); 1387 } 1388 1389 void 1390 GetTermCaps(void) 1391 { /* read in the needed terminal capabilites */ 1392 int i; 1393 const char *ptr; 1394 char buf[TC_BUFSIZE]; 1395 static char bp[TC_BUFSIZE]; 1396 char *area; 1397 struct termcapstr *t; 1398 1399 1400 #ifdef SIG_WINDOW 1401 sigset_t oset, set; 1402 int lins, cols; 1403 1404 /* don't want to confuse things here */ 1405 sigemptyset(&set); 1406 sigaddset(&set, SIG_WINDOW); 1407 (void)sigprocmask(SIG_BLOCK, &set, &oset); 1408 cleanup_push(&oset, sigprocmask_cleanup); 1409 #endif /* SIG_WINDOW */ 1410 area = buf; 1411 1412 GotTermCaps = 1; 1413 1414 setname("gettermcaps"); 1415 ptr = getenv("TERM"); 1416 1417 #ifdef apollo 1418 /* 1419 * If we are on a pad, we pretend that we are dumb. Otherwise the termcap 1420 * library will put us in a weird screen mode, thinking that we are going 1421 * to use curses 1422 */ 1423 if (isapad()) 1424 ptr = "dumb"; 1425 #endif /* apollo */ 1426 1427 if (!ptr || !ptr[0] || !strcmp(ptr, "wm") || !strcmp(ptr,"dmx")) 1428 ptr = "dumb"; 1429 1430 setzero(bp, TC_BUFSIZE); 1431 1432 i = tgetent(bp, ptr); 1433 if (i <= 0) { 1434 if (i == -1) { 1435 #if (SYSVREL == 0) || defined(IRIS3D) 1436 xprintf(CGETS(7, 20, 1437 "%s: The terminal database could not be opened.\n"), progname); 1438 } 1439 else if (i == 0) { 1440 #endif /* SYSVREL */ 1441 xprintf(CGETS(7, 21, 1442 "%s: No entry for terminal type \"%s\"\n"), progname, 1443 getenv("TERM")); 1444 } 1445 xprintf(CGETS(7, 22, "%s: using dumb terminal settings.\n"), progname); 1446 Val(T_co) = 80; /* do a dumb terminal */ 1447 Val(T_pt) = Val(T_km) = Val(T_li) = 0; 1448 for (t = tstr; t->name != NULL; t++) 1449 TCset(t, NULL); 1450 } 1451 else { 1452 /* Can we tab */ 1453 Val(T_pt) = tgetflag("pt") && !tgetflag("xt"); 1454 /* do we have a meta? */ 1455 Val(T_km) = (tgetflag("km") || tgetflag("MT")); 1456 Val(T_am) = tgetflag("am"); 1457 Val(T_xn) = tgetflag("xn"); 1458 Val(T_co) = tgetnum("co"); 1459 Val(T_li) = tgetnum("li"); 1460 for (t = tstr; t->name != NULL; t++) 1461 TCset(t, tgetstr(t->name, &area)); 1462 } 1463 if (Val(T_co) < 2) 1464 Val(T_co) = 80; /* just in case */ 1465 if (Val(T_li) < 1) 1466 Val(T_li) = 24; 1467 1468 T_Cols = (Char) Val(T_co); 1469 T_Lines = (Char) Val(T_li); 1470 if (T_Tabs) 1471 T_Tabs = Val(T_pt); 1472 T_HasMeta = Val(T_km); 1473 T_Margin = Val(T_am) ? MARGIN_AUTO : 0; 1474 T_Margin |= Val(T_xn) ? MARGIN_MAGIC : 0; 1475 T_CanCEOL = GoodStr(T_ce); 1476 T_CanDel = GoodStr(T_dc) || GoodStr(T_DC); 1477 T_CanIns = GoodStr(T_im) || GoodStr(T_ic) || GoodStr(T_IC); 1478 T_CanUP = GoodStr(T_up) || GoodStr(T_UP); 1479 if (GoodStr(T_me) && GoodStr(T_ue)) 1480 me_all = (strcmp(Str(T_me), Str(T_ue)) == 0); 1481 else 1482 me_all = 0; 1483 if (GoodStr(T_me) && GoodStr(T_se)) 1484 me_all |= (strcmp(Str(T_me), Str(T_se)) == 0); 1485 1486 1487 #ifdef DEBUG_SCREEN 1488 if (!T_CanUP) { 1489 xprintf(CGETS(7, 23, "%s: WARNING: Your terminal cannot move up.\n", 1490 progname)); 1491 xprintf(CGETS(7, 24, "Editing may be odd for long lines.\n")); 1492 } 1493 if (!T_CanCEOL) 1494 xprintf(CGETS(7, 25, "no clear EOL capability.\n")); 1495 if (!T_CanDel) 1496 xprintf(CGETS(7, 26, "no delete char capability.\n")); 1497 if (!T_CanIns) 1498 xprintf(CGETS(7, 27, "no insert char capability.\n")); 1499 #endif /* DEBUG_SCREEN */ 1500 1501 1502 1503 #ifdef SIG_WINDOW 1504 (void) GetSize(&lins, &cols); /* get the correct window size */ 1505 ChangeSize(lins, cols); 1506 1507 cleanup_until(&oset); /* can change it again */ 1508 #else /* SIG_WINDOW */ 1509 ChangeSize(Val(T_li), Val(T_co)); 1510 #endif /* SIG_WINDOW */ 1511 1512 BindArrowKeys(); 1513 } 1514 1515 #ifdef SIG_WINDOW 1516 /* GetSize(): 1517 * Return the new window size in lines and cols, and 1518 * true if the size was changed. This can fail if SHIN 1519 * is not a tty, but it will work in most cases. 1520 */ 1521 int 1522 GetSize(int *lins, int *cols) 1523 { 1524 *cols = Val(T_co); 1525 *lins = Val(T_li); 1526 1527 #ifdef TIOCGWINSZ 1528 # define KNOWsize 1529 # ifndef lint 1530 { 1531 struct winsize ws; /* from 4.3 */ 1532 1533 if (ioctl(SHIN, TIOCGWINSZ, (ioctl_t) &ws) != -1) { 1534 if (ws.ws_col) 1535 *cols = ws.ws_col; 1536 if (ws.ws_row) 1537 *lins = ws.ws_row; 1538 } 1539 } 1540 # endif /* !lint */ 1541 #else /* TIOCGWINSZ */ 1542 # ifdef TIOCGSIZE 1543 # define KNOWsize 1544 { 1545 struct ttysize ts; /* from Sun */ 1546 1547 if (ioctl(SHIN, TIOCGSIZE, (ioctl_t) &ts) != -1) { 1548 if (ts.ts_cols) 1549 *cols = ts.ts_cols; 1550 if (ts.ts_lines) 1551 *lins = ts.ts_lines; 1552 } 1553 } 1554 # endif /* TIOCGSIZE */ 1555 #endif /* TIOCGWINSZ */ 1556 1557 return (Val(T_co) != *cols || Val(T_li) != *lins); 1558 } 1559 1560 #endif /* SIG_WINDOW */ 1561 1562 #ifdef KNOWsize 1563 static int 1564 UpdateVal(const Char *tag, int value, Char *termcap, Char *backup) 1565 { 1566 Char *ptr, *p; 1567 if ((ptr = Strstr(termcap, tag)) == NULL) { 1568 (void)Strcpy(backup, termcap); 1569 return 0; 1570 } else { 1571 size_t len = (ptr - termcap) + Strlen(tag); 1572 (void)Strncpy(backup, termcap, len); 1573 backup[len] = '\0'; 1574 p = Itoa(value, 0, 0); 1575 (void) Strcat(backup + len, p); 1576 xfree(p); 1577 ptr = Strchr(ptr, ':'); 1578 if (ptr) 1579 (void) Strcat(backup, ptr); 1580 return 1; 1581 } 1582 } 1583 #endif 1584 1585 void 1586 ChangeSize(int lins, int cols) 1587 { 1588 /* 1589 * Just in case 1590 */ 1591 Val(T_co) = (cols < 2) ? 80 : cols; 1592 Val(T_li) = (lins < 1) ? 24 : lins; 1593 1594 #ifdef KNOWsize 1595 /* 1596 * We want to affect the environment only when we have a valid 1597 * setup, not when we get bad settings. Consider the following scenario: 1598 * We just logged in, and we have not initialized the editor yet. 1599 * We reset termcap with tset, and not $TERMCAP has the right 1600 * terminal size. But since the editor is not initialized yet, and 1601 * the kernel's notion of the terminal size might be wrong we arrive 1602 * here with lines = columns = 0. If we reset the environment we lose 1603 * our only chance to get the window size right. 1604 */ 1605 if (Val(T_co) == cols && Val(T_li) == lins) { 1606 Char *p; 1607 char *tptr; 1608 1609 if (getenv("COLUMNS")) { 1610 p = Itoa(Val(T_co), 0, 0); 1611 cleanup_push(p, xfree); 1612 tsetenv(STRCOLUMNS, p); 1613 cleanup_until(p); 1614 } 1615 1616 if (getenv("LINES")) { 1617 p = Itoa(Val(T_li), 0, 0); 1618 cleanup_push(p, xfree); 1619 tsetenv(STRLINES, p); 1620 cleanup_until(p); 1621 } 1622 1623 if ((tptr = getenv("TERMCAP")) != NULL) { 1624 /* Leave 64 characters slop in case we enlarge the termcap string */ 1625 Char termcap[TC_BUFSIZE+64], backup[TC_BUFSIZE+64], *ptr; 1626 int changed; 1627 1628 ptr = str2short(tptr); 1629 (void) Strncpy(termcap, ptr, TC_BUFSIZE); 1630 termcap[TC_BUFSIZE-1] = '\0'; 1631 1632 changed = UpdateVal(STRco, Val(T_co), termcap, backup); 1633 changed |= UpdateVal(STRli, Val(T_li), termcap, backup); 1634 1635 if (changed) { 1636 /* 1637 * Chop the termcap string at TC_BUFSIZE-1 characters to avoid 1638 * core-dumps in the termcap routines 1639 */ 1640 termcap[TC_BUFSIZE - 1] = '\0'; 1641 tsetenv(STRTERMCAP, termcap); 1642 } 1643 } 1644 } 1645 #endif /* KNOWsize */ 1646 1647 ReBufferDisplay(); /* re-make display buffers */ 1648 ClearDisp(); 1649 } 1650