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