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