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