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