xref: /freebsd/contrib/tcsh/ed.screen.c (revision 1669d8afc64812c8d2d1d147ae1fd42ff441e1b1)
1 /* $Header: /p/tcsh/cvsroot/tcsh/ed.screen.c,v 3.75 2006/08/24 20:56:31 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.75 2006/08/24 20:56:31 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(CGETS(7, 1, "\n\tTcsh thinks your terminal has the\n"));
387     xprintf(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(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