1 /* $Header: /p/tcsh/cvsroot/tcsh/ed.screen.c,v 3.81 2014/03/15 21:25:11 christos Exp $ */
3 * ed.screen.c: Editor/termcap-curses interface
6 * Copyright (c) 1980, 1991 The Regents of the University of California.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
35 RCSID("$tcsh: ed.screen.c,v 3.81 2014/03/15 21:25:11 christos Exp $")
41 /* #define DEBUG_LITERAL */
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
50 #define TC_BUFSIZE 2048
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
168 static struct termcapstr
{
170 const char *long_name
;
182 static struct termcapval
{
184 const char *long_name
;
194 for (i
= 0; i
< T_str
+ 1; i
++)
195 xfree((ptr_t
)(intptr_t)tstr
[i
].long_name
);
197 for (i
= 0; i
< T_val
+ 1; i
++)
198 xfree((ptr_t
)(intptr_t)tval
[i
].long_name
);
201 tstr
[T_al
].name
= "al";
202 tstr
[T_al
].long_name
= CSAVS(4, 1, "add new blank line");
204 tstr
[T_bl
].name
= "bl";
205 tstr
[T_bl
].long_name
= CSAVS(4, 2, "audible bell");
207 tstr
[T_cd
].name
= "cd";
208 tstr
[T_cd
].long_name
= CSAVS(4, 3, "clear to bottom");
210 tstr
[T_ce
].name
= "ce";
211 tstr
[T_ce
].long_name
= CSAVS(4, 4, "clear to end of line");
213 tstr
[T_ch
].name
= "ch";
214 tstr
[T_ch
].long_name
= CSAVS(4, 5, "cursor to horiz pos");
216 tstr
[T_cl
].name
= "cl";
217 tstr
[T_cl
].long_name
= CSAVS(4, 6, "clear screen");
219 tstr
[T_dc
].name
= "dc";
220 tstr
[T_dc
].long_name
= CSAVS(4, 7, "delete a character");
222 tstr
[T_dl
].name
= "dl";
223 tstr
[T_dl
].long_name
= CSAVS(4, 8, "delete a line");
225 tstr
[T_dm
].name
= "dm";
226 tstr
[T_dm
].long_name
= CSAVS(4, 9, "start delete mode");
228 tstr
[T_ed
].name
= "ed";
229 tstr
[T_ed
].long_name
= CSAVS(4, 10, "end delete mode");
231 tstr
[T_ei
].name
= "ei";
232 tstr
[T_ei
].long_name
= CSAVS(4, 11, "end insert mode");
234 tstr
[T_fs
].name
= "fs";
235 tstr
[T_fs
].long_name
= CSAVS(4, 12, "cursor from status line");
237 tstr
[T_ho
].name
= "ho";
238 tstr
[T_ho
].long_name
= CSAVS(4, 13, "home cursor");
240 tstr
[T_ic
].name
= "ic";
241 tstr
[T_ic
].long_name
= CSAVS(4, 14, "insert character");
243 tstr
[T_im
].name
= "im";
244 tstr
[T_im
].long_name
= CSAVS(4, 15, "start insert mode");
246 tstr
[T_ip
].name
= "ip";
247 tstr
[T_ip
].long_name
= CSAVS(4, 16, "insert padding");
249 tstr
[T_kd
].name
= "kd";
250 tstr
[T_kd
].long_name
= CSAVS(4, 17, "sends cursor down");
252 tstr
[T_kl
].name
= "kl";
253 tstr
[T_kl
].long_name
= CSAVS(4, 18, "sends cursor left");
255 tstr
[T_kr
].name
= "kr";
256 tstr
[T_kr
].long_name
= CSAVS(4, 19, "sends cursor right");
258 tstr
[T_ku
].name
= "ku";
259 tstr
[T_ku
].long_name
= CSAVS(4, 20, "sends cursor up");
261 tstr
[T_md
].name
= "md";
262 tstr
[T_md
].long_name
= CSAVS(4, 21, "begin bold");
264 tstr
[T_me
].name
= "me";
265 tstr
[T_me
].long_name
= CSAVS(4, 22, "end attributes");
267 tstr
[T_nd
].name
= "nd";
268 tstr
[T_nd
].long_name
= CSAVS(4, 23, "non destructive space");
270 tstr
[T_se
].name
= "se";
271 tstr
[T_se
].long_name
= CSAVS(4, 24, "end standout");
273 tstr
[T_so
].name
= "so";
274 tstr
[T_so
].long_name
= CSAVS(4, 25, "begin standout");
276 tstr
[T_ts
].name
= "ts";
277 tstr
[T_ts
].long_name
= CSAVS(4, 26, "cursor to status line");
279 tstr
[T_up
].name
= "up";
280 tstr
[T_up
].long_name
= CSAVS(4, 27, "cursor up one");
282 tstr
[T_us
].name
= "us";
283 tstr
[T_us
].long_name
= CSAVS(4, 28, "begin underline");
285 tstr
[T_ue
].name
= "ue";
286 tstr
[T_ue
].long_name
= CSAVS(4, 29, "end underline");
288 tstr
[T_vb
].name
= "vb";
289 tstr
[T_vb
].long_name
= CSAVS(4, 30, "visible bell");
291 tstr
[T_DC
].name
= "DC";
292 tstr
[T_DC
].long_name
= CSAVS(4, 31, "delete multiple chars");
294 tstr
[T_DO
].name
= "DO";
295 tstr
[T_DO
].long_name
= CSAVS(4, 32, "cursor down multiple");
297 tstr
[T_IC
].name
= "IC";
298 tstr
[T_IC
].long_name
= CSAVS(4, 33, "insert multiple chars");
300 tstr
[T_LE
].name
= "LE";
301 tstr
[T_LE
].long_name
= CSAVS(4, 34, "cursor left multiple");
303 tstr
[T_RI
].name
= "RI";
304 tstr
[T_RI
].long_name
= CSAVS(4, 35, "cursor right multiple");
306 tstr
[T_UP
].name
= "UP";
307 tstr
[T_UP
].long_name
= CSAVS(4, 36, "cursor up multiple");
309 tstr
[T_kh
].name
= "kh";
310 tstr
[T_kh
].long_name
= CSAVS(4, 43, "send cursor home");
312 tstr
[T_at7
].name
= "@7";
313 tstr
[T_at7
].long_name
= CSAVS(4, 44, "send cursor end");
315 tstr
[T_mr
].name
= "mr";
316 tstr
[T_mr
].long_name
= CSAVS(4, 45, "begin reverse video");
318 tstr
[T_str
].name
= NULL
;
319 tstr
[T_str
].long_name
= NULL
;
322 tval
[T_am
].name
= "am";
323 tval
[T_am
].long_name
= CSAVS(4, 37, "Has automatic margins");
325 tval
[T_pt
].name
= "pt";
326 tval
[T_pt
].long_name
= CSAVS(4, 38, "Can use physical tabs");
328 tval
[T_li
].name
= "li";
329 tval
[T_li
].long_name
= CSAVS(4, 39, "Number of lines");
331 tval
[T_co
].name
= "co";
332 tval
[T_co
].long_name
= CSAVS(4, 40, "Number of columns");
334 tval
[T_km
].name
= "km";
335 tval
[T_km
].long_name
= CSAVS(4, 41, "Has meta key");
337 tval
[T_xn
].name
= "xn";
338 tval
[T_xn
].long_name
= CSAVS(4, 42, "Newline ignored at right margin");
340 tval
[T_val
].name
= NULL
;
341 tval
[T_val
].long_name
= NULL
;
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)
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
357 static int me_all
= 0; /* does two or more of the attributes use me */
359 static void ReBufferDisplay (void);
360 static void TCset (struct termcapstr
*, const char *);
364 TCset(struct termcapstr
*t
, const char *cap
)
366 if (cap
== NULL
|| *cap
== '\0') {
372 size
= strlen(cap
) + 1;
373 t
->str
= xrealloc(t
->str
, size
);
374 memcpy(t
->str
, cap
, size
);
383 struct termcapstr
*t
;
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
);
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
);
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
);
414 cleanup_until(first
);
419 ReBufferDisplay(void)
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));
437 b
= xmalloc(sizeof(*b
) * (TermV
+ 1));
438 for (i
= 0; i
< TermV
; i
++)
439 b
[i
] = xmalloc(sizeof(*b
[i
]) * (TermH
+ 1));
445 SetTC(char *what
, char *how
)
447 struct termcapstr
*ts
;
448 struct termcapval
*tv
;
451 * Do the strings first
454 for (ts
= tstr
; ts
->name
!= NULL
; ts
++)
455 if (strcmp(ts
->name
, what
) == 0)
457 if (ts
->name
!= NULL
) {
462 if (GoodStr(T_me
) && GoodStr(T_ue
))
463 me_all
= (strcmp(Str(T_me
), Str(T_ue
)) == 0);
466 if (GoodStr(T_me
) && GoodStr(T_se
))
467 me_all
|= (strcmp(Str(T_me
), Str(T_se
)) == 0);
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
);
477 * Do the numeric ones second
479 for (tv
= tval
; tv
->name
!= NULL
; tv
++)
480 if (strcmp(tv
->name
, what
) == 0)
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)
488 else if (strcmp(how
, "no") == 0)
491 stderror(ERR_SETTCUS
, tv
->name
);
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
));
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
));
511 stderror(ERR_NAME
| ERR_TCCAP
, what
);
517 * Print the termcap string out with variable substitution
522 char *cap
, *scap
, *cv
;
523 int arg_need
, arg_cols
, arg_rows
;
524 int verbose
= 0, silent
= 0;
526 static const char fmts
[] = "%s\n", fmtd
[] = "%d\n";
527 struct termcapstr
*t
;
528 char buf
[TC_BUFSIZE
];
535 v
= glob_all_or_error(v
);
537 cleanup_push(globbed
, blk_cleanup
);
539 if (!*v
|| *v
[0] == '\0')
541 if (v
[0][0] == '-') {
550 stderror(ERR_NAME
| ERR_TCUSAGE
);
555 if (!*v
|| *v
[0] == '\0')
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") :
564 else if (strcmp(cv
, "meta") == 0) {
565 xprintf(fmts
, Val(T_km
) ? CGETS(7, 14, "yes") :
569 else if (strcmp(cv
, "xn") == 0) {
570 xprintf(fmts
, T_Margin
& MARGIN_MAGIC
? CGETS(7, 14, "yes") :
574 else if (strcmp(cv
, "am") == 0) {
575 xprintf(fmts
, T_Margin
& MARGIN_AUTO
? CGETS(7, 14, "yes") :
579 else if (strcmp(cv
, "baud") == 0) {
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
);
590 else if (strcmp(cv
, "rows") == 0 || strcmp(cv
, "lines") == 0 ||
591 strcmp(cv
, "li") == 0) {
592 xprintf(fmtd
, Val(T_li
));
595 else if (strcmp(cv
, "cols") == 0 || strcmp(cv
, "co") == 0) {
596 xprintf(fmtd
, Val(T_co
));
601 * Try to use our local definition first
604 for (t
= tstr
; t
->name
!= NULL
; t
++)
605 if (strcmp(t
->name
, cv
) == 0) {
610 scap
= tgetstr(cv
, &area
);
611 if (!scap
|| scap
[0] == '\0') {
613 xprintf("%s", CGETS(7, 14, "yes\n"));
619 stderror(ERR_NAME
| ERR_TCCAP
, cv
);
623 * Count home many values we need for this capability.
625 for (cap
= scap
, arg_need
= 0; *cap
; cap
++)
645 * hpux has lot's of them...
648 stderror(ERR_NAME
| ERR_TCPARM
, *cap
);
649 /* This is bad, but I won't complain */
660 stderror(ERR_NAME
| ERR_TCARGS
, cv
, arg_need
);
662 (void) tputs(scap
, 1, PUTRAW
);
666 if (!*v
|| *v
[0] == '\0')
667 stderror(ERR_NAME
| ERR_TCNARGS
, cv
, 1);
669 arg_rows
= atoi(short2str(*v
));
675 stderror(ERR_NAME
| ERR_TCARGS
, cv
, arg_need
);
677 (void) tputs(tgoto(scap
, arg_cols
, arg_rows
), 1, PUTRAW
);
680 /* This is wrong, but I will ignore it... */
682 stderror(ERR_NAME
| ERR_TCARGS
, cv
, arg_need
);
686 if (!*v
|| *v
[0] == '\0') {
690 stderror(ERR_NAME
| ERR_TCNARGS
, cv
, 2);
692 arg_cols
= atoi(short2str(*v
));
694 if (!*v
|| *v
[0] == '\0') {
698 stderror(ERR_NAME
| ERR_TCNARGS
, cv
, 2);
700 arg_rows
= atoi(short2str(*v
));
706 stderror(ERR_NAME
| ERR_TCARGS
, cv
, arg_need
);
708 (void) tputs(tgoto(scap
, arg_cols
, arg_rows
), arg_rows
, PUTRAW
);
714 cleanup_until(globbed
);
726 { STRdown
, T_kd
, { 0 }, 0 },
728 { STRup
, T_ku
, { 0 }, 0 },
730 { STRleft
, T_kl
, { 0 }, 0 },
732 { STRright
, T_kr
, { 0 }, 0 },
734 { STRhome
, T_kh
, { 0 }, 0 },
736 { STRend
, T_at7
, { 0 }, 0}
743 arrow
[A_K_DN
].fun
.cmd
= F_DOWN_HIST
;
744 arrow
[A_K_DN
].type
= XK_CMD
;
746 arrow
[A_K_UP
].fun
.cmd
= F_UP_HIST
;
747 arrow
[A_K_UP
].type
= XK_CMD
;
749 arrow
[A_K_LT
].fun
.cmd
= F_CHARBACK
;
750 arrow
[A_K_LT
].type
= XK_CMD
;
752 arrow
[A_K_RT
].fun
.cmd
= F_CHARFWD
;
753 arrow
[A_K_RT
].type
= XK_CMD
;
755 arrow
[A_K_HO
].fun
.cmd
= F_TOBEG
;
756 arrow
[A_K_HO
].type
= XK_CMD
;
758 arrow
[A_K_EN
].fun
.cmd
= F_TOEND
;
759 arrow
[A_K_EN
].type
= XK_CMD
;
763 DefaultArrowKeys(void)
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'};
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');
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
);
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
);
831 SetArrowKeys(const CStr
*name
, XmapVal
*fun
, int type
)
834 for (i
= 0; i
< A_K_NKEYS
; i
++)
835 if (Strcmp(name
->buf
, arrow
[i
].name
) == 0) {
837 arrow
[i
].type
= type
;
844 IsArrowKey(Char
*name
)
847 for (i
= 0; i
< A_K_NKEYS
; i
++)
848 if (Strcmp(name
, arrow
[i
].name
) == 0)
854 ClearArrowKeys(const CStr
*name
)
857 for (i
= 0; i
< A_K_NKEYS
; i
++)
858 if (Strcmp(name
->buf
, arrow
[i
].name
) == 0) {
859 arrow
[i
].type
= XK_NOD
;
866 PrintArrowKeys(const CStr
*name
)
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
);
887 map
= VImode
? CcAltMap
: CcKeyMap
;
888 dmap
= VImode
? CcViCmdMap
: CcEmacsMap
;
892 for (i
= 0; i
< A_K_NKEYS
; i
++) {
893 p
= tstr
[arrow
[i
].key
].str
;
895 j
= (unsigned char) *p
;
896 cs
.buf
= str2short(p
);
897 cs
.len
= Strlen(cs
.buf
);
899 * Assign the arrow keys only if:
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.
906 if (arrow
[i
].type
== XK_NOD
) {
910 if (p
[1] && (dmap
[j
] == map
[j
] || map
[j
] == F_XKEY
)) {
911 AddXkey(&cs
, &arrow
[i
].fun
, arrow
[i
].type
);
914 else if (map
[j
] == F_UNASSIGNED
) {
916 if (arrow
[i
].type
== XK_CMD
)
917 map
[j
] = arrow
[i
].fun
.cmd
;
919 AddXkey(&cs
, &arrow
[i
].fun
, arrow
[i
].type
);
926 static Char cur_atr
= 0; /* current attributes */
929 SetAttributes(Char atr
)
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
);
941 if ((atr
& BOLD
) != (cur_atr
& BOLD
)) {
943 if (GoodStr(T_md
) && GoodStr(T_me
)) {
944 (void) tputs(Str(T_md
), 1, PUTPURE
);
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
;
955 if ((cur_atr
& UNDER
) && GoodStr(T_ue
)) {
956 (void) tputs(Str(T_ue
), 1, PUTPURE
);
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
);
972 (void) tputs(Str(T_se
), 1, PUTPURE
);
973 cur_atr
&= ~STANDOUT
;
977 if ((atr
& UNDER
) != (cur_atr
& UNDER
)) {
979 if (GoodStr(T_us
) && GoodStr(T_ue
)) {
980 (void) tputs(Str(T_us
), 1, PUTPURE
);
986 (void) tputs(Str(T_ue
), 1, PUTPURE
);
994 int highlighting
= 0;
999 (void) tputs(Str(T_mr
), 1, PUTPURE
);
1006 (void) tputs(Str(T_me
), 1, PUTPURE
);
1010 /* PWP 6-27-88 -- if the tty driver thinks that we can tab, we ask termcap */
1017 /* move to line <where> (first line == 0) as efficiently as possible; */
1019 MoveToLine(int where
)
1023 if (where
== CursorV
)
1026 if (where
> TermV
) {
1028 xprintf("MoveToLine: where is ridiculous: %d\r\n", where
);
1030 #endif /* DEBUG_SCREEN */
1034 del
= where
- CursorV
;
1038 if ((T_Margin
& MARGIN_AUTO
) && Display
[CursorV
][0] != '\0') {
1041 for (h
= TermH
- 1; h
> 0 && Display
[CursorV
][h
] == CHAR_DBWIDTH
;
1044 /* move without newline */
1046 so_write(&Display
[CursorV
][CursorH
], TermH
- CursorH
); /* updates CursorH/V*/
1050 if ((del
> 1) && GoodStr(T_DO
)) {
1051 (void) tputs(tgoto(Str(T_DO
), del
, del
), del
, PUTPURE
);
1055 for ( ; del
> 0; del
--)
1056 (void) putraw('\n');
1057 CursorH
= 0; /* because the \n will become \r\n */
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
);
1068 for (i
= 0; i
< -del
; i
++)
1069 (void) tputs(Str(T_up
), 1, PUTPURE
);
1072 CursorV
= where
; /* now where is here */
1076 MoveToChar(int where
) /* move to character position (where) */
1077 { /* as efficiently as possible */
1081 if (where
== CursorH
)
1084 if (where
>= TermH
) {
1086 xprintf("MoveToChar: where is riduculous: %d\r\n", where
);
1088 #endif /* DEBUG_SCREEN */
1092 if (!where
) { /* if where is first column */
1093 (void) putraw('\r'); /* do a CR */
1098 del
= where
- CursorH
;
1100 if ((del
< -4 || del
> 4) && GoodStr(T_ch
))
1101 /* go there directly */
1102 (void) tputs(tgoto(Str(T_ch
), where
, where
), where
, PUTPURE
);
1105 if (del
> 0) { /* moving forward */
1106 if ((del
> 4) && GoodStr(T_RI
))
1107 (void) tputs(tgoto(Str(T_RI
), del
, del
), del
, PUTPURE
);
1109 /* if I can do tabs, use them */
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! */
1123 /* it's usually cheaper to just write the chars, so we do. */
1125 /* NOTE THAT so_write() WILL CHANGE CursorH!!! */
1126 so_write(&Display
[CursorV
][CursorH
], where
- CursorH
);
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)))
1137 (void) putraw('\r'); /* do a CR */
1139 goto mc_again
; /* and try again */
1141 for (i
= 0; i
< -del
; i
++)
1142 (void) putraw('\b');
1146 CursorH
= where
; /* now where is here */
1150 so_write(Char
*cp
, int n
)
1152 int cur_pos
, prompt_len
= 0, region_start
= 0, region_end
= 0;
1155 return; /* catch bugs */
1159 xprintf("so_write: n is riduculous: %d\r\n", n
);
1161 #endif /* DEBUG_SCREEN */
1165 if (adrof(STRhighlight
)) {
1166 /* find length of prompt */
1168 for (promptc
= Prompt
; *promptc
; promptc
++);
1169 prompt_len
= promptc
- Prompt
;
1171 /* find region start and end points */
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
;
1182 if (adrof(STRhighlight
)) {
1183 cur_pos
= CursorV
* TermH
+ CursorH
;
1184 if (!highlighting
&&
1185 cur_pos
>= region_start
&& cur_pos
< region_end
)
1187 else if (highlighting
&& cur_pos
>= region_end
)
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
)
1196 if (*cp
!= CHAR_DBWIDTH
) {
1197 if (*cp
& LITERAL
) {
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
++)
1206 (void) putwraw(*cp
);
1212 if (adrof(STRhighlight
) && highlighting
)
1215 if (CursorH
>= TermH
) { /* wrap? */
1216 if (T_Margin
& MARGIN_AUTO
) { /* yes */
1219 if (T_Margin
& MARGIN_MAGIC
) {
1220 /* force the wrap to avoid the "magic" situation */
1222 if ((xc
= Display
[CursorV
][CursorH
]) != '\0') {
1224 while(Display
[CursorV
][CursorH
] == CHAR_DBWIDTH
)
1233 else /* no wrap, but cursor stays on screen */
1234 CursorH
= TermH
- 1;
1240 DeleteChars(int num
) /* deletes <num> characters */
1247 xprintf(CGETS(7, 16, "ERROR: cannot delete\r\n"));
1248 #endif /* DEBUG_EDIT */
1255 xprintf(CGETS(7, 17, "DeleteChars: num is riduculous: %d\r\n"), num
);
1257 #endif /* DEBUG_SCREEN */
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
);
1267 if (GoodStr(T_dm
)) /* if I have delete mode */
1268 (void) tputs(Str(T_dm
), 1, PUTPURE
);
1270 if (GoodStr(T_dc
)) /* else do one at a time */
1272 (void) tputs(Str(T_dc
), 1, PUTPURE
);
1274 if (GoodStr(T_ed
)) /* if I have delete mode */
1275 (void) tputs(Str(T_ed
), 1, PUTPURE
);
1278 /* Puts terminal in insert character mode, or inserts num characters in the
1281 Insert_write(Char
*cp
, int num
)
1287 xprintf(CGETS(7, 18, "ERROR: cannot insert\r\n"));
1288 #endif /* DEBUG_EDIT */
1295 xprintf(CGETS(7, 19, "StartInsert: num is riduculous: %d\r\n"), num
);
1297 #endif /* DEBUG_SCREEN */
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 */
1308 if (GoodStr(T_im
) && GoodStr(T_ei
)) { /* if I have insert mode */
1309 (void) tputs(Str(T_im
), 1, PUTPURE
);
1311 so_write(cp
, num
); /* this updates CursorH/V */
1313 if (GoodStr(T_ip
)) /* have to make num chars insert */
1314 (void) tputs(Str(T_ip
), 1, PUTPURE
);
1316 (void) tputs(Str(T_ei
), 1, PUTPURE
);
1321 if (GoodStr(T_ic
)) /* have to make num chars insert */
1322 (void) tputs(Str(T_ic
), 1, PUTPURE
); /* insert a char */
1324 so_write(cp
++, 1); /* this updates CursorH/V */
1326 if (GoodStr(T_ip
)) /* have to make num chars insert */
1327 (void) tputs(Str(T_ip
), 1, PUTPURE
);/* pad the inserted char */
1333 /* clear to end of line. There are num characters to clear */
1342 if (T_CanCEOL
&& GoodStr(T_ce
))
1343 (void) tputs(Str(T_ce
), 1, PUTPURE
);
1345 for (i
= 0; i
< num
; i
++)
1347 CursorH
+= num
; /* have written num spaces */
1353 { /* clear the whole screen and home */
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
);
1363 (void) putraw('\r');
1364 (void) putraw('\n');
1370 { /* produce a sound */
1372 if (adrof(STRnobeep
))
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
);
1381 (void) putraw(CTL_ESC('\007')); /* an ASCII bell; ^G */
1386 { /* clear to the bottom of the screen */
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
);
1395 { /* read in the needed terminal capabilites */
1398 char buf
[TC_BUFSIZE
];
1399 static char bp
[TC_BUFSIZE
];
1401 struct termcapstr
*t
;
1408 /* don't want to confuse things here */
1410 sigaddset(&set
, SIG_WINDOW
);
1411 (void)sigprocmask(SIG_BLOCK
, &set
, &oset
);
1412 cleanup_push(&oset
, sigprocmask_cleanup
);
1413 #endif /* SIG_WINDOW */
1418 setname("gettermcaps");
1419 ptr
= getenv("TERM");
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
1431 if (!ptr
|| !ptr
[0] || !strcmp(ptr
, "wm") || !strcmp(ptr
,"dmx"))
1434 setzero(bp
, TC_BUFSIZE
);
1436 i
= tgetent(bp
, ptr
);
1439 #if (SYSVREL == 0) || defined(IRIS3D)
1440 xprintf(CGETS(7, 20,
1441 "%s: The terminal database could not be opened.\n"), progname
);
1444 #endif /* SYSVREL */
1445 xprintf(CGETS(7, 21,
1446 "%s: No entry for terminal type \"%s\"\n"), progname
,
1449 xprintf(CGETS(7, 22, "%s: using dumb terminal settings.\n"), progname
);
1450 Val(T_co
) = 80; /* do a dumb terminal */
1451 Val(T_pt
) = Val(T_km
) = Val(T_li
) = 0;
1452 for (t
= tstr
; t
->name
!= NULL
; t
++)
1457 Val(T_pt
) = tgetflag("pt") && !tgetflag("xt");
1458 /* do we have a meta? */
1459 Val(T_km
) = (tgetflag("km") || tgetflag("MT"));
1460 Val(T_am
) = tgetflag("am");
1461 Val(T_xn
) = tgetflag("xn");
1462 Val(T_co
) = tgetnum("co");
1463 Val(T_li
) = tgetnum("li");
1464 for (t
= tstr
; t
->name
!= NULL
; t
++)
1465 TCset(t
, tgetstr(t
->name
, &area
));
1468 Val(T_co
) = 80; /* just in case */
1472 T_Cols
= (Char
) Val(T_co
);
1473 T_Lines
= (Char
) Val(T_li
);
1476 T_HasMeta
= Val(T_km
);
1477 T_Margin
= Val(T_am
) ? MARGIN_AUTO
: 0;
1478 T_Margin
|= Val(T_xn
) ? MARGIN_MAGIC
: 0;
1479 T_CanCEOL
= GoodStr(T_ce
);
1480 T_CanDel
= GoodStr(T_dc
) || GoodStr(T_DC
);
1481 T_CanIns
= GoodStr(T_im
) || GoodStr(T_ic
) || GoodStr(T_IC
);
1482 T_CanUP
= GoodStr(T_up
) || GoodStr(T_UP
);
1483 if (GoodStr(T_me
) && GoodStr(T_ue
))
1484 me_all
= (strcmp(Str(T_me
), Str(T_ue
)) == 0);
1487 if (GoodStr(T_me
) && GoodStr(T_se
))
1488 me_all
|= (strcmp(Str(T_me
), Str(T_se
)) == 0);
1493 xprintf(CGETS(7, 23, "%s: WARNING: Your terminal cannot move up.\n",
1495 xprintf(CGETS(7, 24, "Editing may be odd for long lines.\n"));
1498 xprintf(CGETS(7, 25, "no clear EOL capability.\n"));
1500 xprintf(CGETS(7, 26, "no delete char capability.\n"));
1502 xprintf(CGETS(7, 27, "no insert char capability.\n"));
1503 #endif /* DEBUG_SCREEN */
1508 (void) GetSize(&lins
, &cols
); /* get the correct window size */
1509 ChangeSize(lins
, cols
);
1511 cleanup_until(&oset
); /* can change it again */
1512 #else /* SIG_WINDOW */
1513 ChangeSize(Val(T_li
), Val(T_co
));
1514 #endif /* SIG_WINDOW */
1521 * Return the new window size in lines and cols, and
1522 * true if the size was changed. This can fail if SHIN
1523 * is not a tty, but it will work in most cases.
1526 GetSize(int *lins
, int *cols
)
1535 struct winsize ws
; /* from 4.3 */
1537 if (ioctl(SHIN
, TIOCGWINSZ
, (ioctl_t
) &ws
) != -1) {
1545 #else /* TIOCGWINSZ */
1549 struct ttysize ts
; /* from Sun */
1551 if (ioctl(SHIN
, TIOCGSIZE
, (ioctl_t
) &ts
) != -1) {
1555 *lins
= ts
.ts_lines
;
1558 # endif /* TIOCGSIZE */
1559 #endif /* TIOCGWINSZ */
1561 return (Val(T_co
) != *cols
|| Val(T_li
) != *lins
);
1564 #endif /* SIG_WINDOW */
1568 UpdateVal(const Char
*tag
, int value
, Char
*termcap
, Char
*backup
)
1571 if ((ptr
= Strstr(termcap
, tag
)) == NULL
) {
1572 (void)Strcpy(backup
, termcap
);
1575 size_t len
= (ptr
- termcap
) + Strlen(tag
);
1576 (void)Strncpy(backup
, termcap
, len
);
1578 p
= Itoa(value
, 0, 0);
1579 (void) Strcat(backup
+ len
, p
);
1581 ptr
= Strchr(ptr
, ':');
1583 (void) Strcat(backup
, ptr
);
1589 ChangeSize(int lins
, int cols
)
1594 Val(T_co
) = (cols
< 2) ? 80 : cols
;
1595 Val(T_li
) = (lins
< 1) ? 24 : lins
;
1599 * We want to affect the environment only when we have a valid
1600 * setup, not when we get bad settings. Consider the following scenario:
1601 * We just logged in, and we have not initialized the editor yet.
1602 * We reset termcap with tset, and not $TERMCAP has the right
1603 * terminal size. But since the editor is not initialized yet, and
1604 * the kernel's notion of the terminal size might be wrong we arrive
1605 * here with lines = columns = 0. If we reset the environment we lose
1606 * our only chance to get the window size right.
1608 if (Val(T_co
) == cols
&& Val(T_li
) == lins
) {
1612 if (getenv("COLUMNS")) {
1613 p
= Itoa(Val(T_co
), 0, 0);
1614 cleanup_push(p
, xfree
);
1615 tsetenv(STRCOLUMNS
, p
);
1619 if (getenv("LINES")) {
1620 p
= Itoa(Val(T_li
), 0, 0);
1621 cleanup_push(p
, xfree
);
1622 tsetenv(STRLINES
, p
);
1626 if ((tptr
= getenv("TERMCAP")) != NULL
) {
1627 /* Leave 64 characters slop in case we enlarge the termcap string */
1628 Char termcap
[TC_BUFSIZE
+64], backup
[TC_BUFSIZE
+64], *ptr
;
1630 ptr
= str2short(tptr
);
1631 (void) Strncpy(termcap
, ptr
, TC_BUFSIZE
);
1632 termcap
[TC_BUFSIZE
-1] = '\0';
1634 UpdateVal(STRco
, Val(T_co
), termcap
, backup
);
1635 UpdateVal(STRli
, Val(T_li
), termcap
, backup
);
1638 * Chop the termcap string at TC_BUFSIZE-1 characters to avoid
1639 * core-dumps in the termcap routines
1641 termcap
[TC_BUFSIZE
- 1] = '\0';
1642 tsetenv(STRTERMCAP
, termcap
);
1645 #endif /* KNOWsize */
1647 ReBufferDisplay(); /* re-make display buffers */