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