1 /* $NetBSD: terminal.c,v 1.14 2012/05/30 18:21:14 christos Exp $ */
4 * Copyright (c) 1992, 1993
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
8 * Christos Zoulas of Cornell University.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #if !defined(lint) && !defined(SCCSID)
38 static char sccsid
[] = "@(#)term.c 8.2 (Berkeley) 4/30/95";
40 __RCSID("$NetBSD: terminal.c,v 1.14 2012/05/30 18:21:14 christos Exp $");
42 #endif /* not lint && not SCCSID */
45 * terminal.c: Editor/termcap-curses interface
46 * We have to declare a static variable here, since the
47 * termcap putchar routine does not take an argument!
64 /* Solaris's term.h does horrid things. */
65 #if defined(HAVE_TERM_H) && !defined(__sun) && !defined(HAVE_TERMCAP_H)
69 #include <sys/types.h>
70 #include <sys/ioctl.h>
79 * IMPORTANT NOTE: these routines are allowed to look at the current screen
80 * and the current position assuming that it is correct. If this is not
81 * true, then the update will be WRONG! This is (should be) a valid
85 #define TC_BUFSIZE ((size_t)2048)
87 #define GoodStr(a) (el->el_terminal.t_str[a] != NULL && \
88 el->el_terminal.t_str[a][0] != '\0')
89 #define Str(a) el->el_terminal.t_str[a]
90 #define Val(a) el->el_terminal.t_val[a]
92 private const struct termcapstr
{
94 const char *long_name
;
97 { "al", "add new blank line" },
99 { "bl", "audible bell" },
101 { "cd", "clear to bottom" },
103 { "ce", "clear to end of line" },
105 { "ch", "cursor to horiz pos" },
107 { "cl", "clear screen" },
109 { "dc", "delete a character" },
111 { "dl", "delete a line" },
113 { "dm", "start delete mode" },
115 { "ed", "end delete mode" },
117 { "ei", "end insert mode" },
119 { "fs", "cursor from status line" },
121 { "ho", "home cursor" },
123 { "ic", "insert character" },
125 { "im", "start insert mode" },
127 { "ip", "insert padding" },
129 { "kd", "sends cursor down" },
131 { "kl", "sends cursor left" },
133 { "kr", "sends cursor right" },
135 { "ku", "sends cursor up" },
137 { "md", "begin bold" },
139 { "me", "end attributes" },
141 { "nd", "non destructive space" },
143 { "se", "end standout" },
145 { "so", "begin standout" },
147 { "ts", "cursor to status line" },
149 { "up", "cursor up one" },
151 { "us", "begin underline" },
153 { "ue", "end underline" },
155 { "vb", "visible bell" },
157 { "DC", "delete multiple chars" },
159 { "DO", "cursor down multiple" },
161 { "IC", "insert multiple chars" },
163 { "LE", "cursor left multiple" },
165 { "RI", "cursor right multiple" },
167 { "UP", "cursor up multiple" },
169 { "kh", "send cursor home" },
171 { "@7", "send cursor end" },
173 { "kD", "send cursor delete" },
178 private const struct termcapval
{
180 const char *long_name
;
183 { "am", "has automatic margins" },
185 { "pt", "has physical tabs" },
187 { "li", "Number of lines" },
189 { "co", "Number of columns" },
191 { "km", "Has meta key" },
193 { "xt", "Tab chars destructive" },
195 { "xn", "newline ignored at right margin" },
197 { "MT", "Has meta key" }, /* XXX? */
201 /* do two or more of the attributes use me */
203 private void terminal_setflags(EditLine
*);
204 private int terminal_rebuffer_display(EditLine
*);
205 private void terminal_free_display(EditLine
*);
206 private int terminal_alloc_display(EditLine
*);
207 private void terminal_alloc(EditLine
*, const struct termcapstr
*,
209 private void terminal_init_arrow(EditLine
*);
210 private void terminal_reset_arrow(EditLine
*);
211 private int terminal_putc(int);
212 private void terminal_tputs(EditLine
*, const char *, int);
215 private pthread_mutex_t terminal_mutex
= PTHREAD_MUTEX_INITIALIZER
;
217 private FILE *terminal_outfile
= NULL
;
220 /* terminal_setflags():
221 * Set the terminal capability flags
224 terminal_setflags(EditLine
*el
)
227 if (el
->el_tty
.t_tabs
)
228 EL_FLAGS
|= (Val(T_pt
) && !Val(T_xt
)) ? TERM_CAN_TAB
: 0;
230 EL_FLAGS
|= (Val(T_km
) || Val(T_MT
)) ? TERM_HAS_META
: 0;
231 EL_FLAGS
|= GoodStr(T_ce
) ? TERM_CAN_CEOL
: 0;
232 EL_FLAGS
|= (GoodStr(T_dc
) || GoodStr(T_DC
)) ? TERM_CAN_DELETE
: 0;
233 EL_FLAGS
|= (GoodStr(T_im
) || GoodStr(T_ic
) || GoodStr(T_IC
)) ?
235 EL_FLAGS
|= (GoodStr(T_up
) || GoodStr(T_UP
)) ? TERM_CAN_UP
: 0;
236 EL_FLAGS
|= Val(T_am
) ? TERM_HAS_AUTO_MARGINS
: 0;
237 EL_FLAGS
|= Val(T_xn
) ? TERM_HAS_MAGIC_MARGINS
: 0;
239 if (GoodStr(T_me
) && GoodStr(T_ue
))
240 EL_FLAGS
|= (strcmp(Str(T_me
), Str(T_ue
)) == 0) ?
243 EL_FLAGS
&= ~TERM_CAN_ME
;
244 if (GoodStr(T_me
) && GoodStr(T_se
))
245 EL_FLAGS
|= (strcmp(Str(T_me
), Str(T_se
)) == 0) ?
251 (void) fprintf(el
->el_errfile
,
252 "WARNING: Your terminal cannot move up.\n");
253 (void) fprintf(el
->el_errfile
,
254 "Editing may be odd for long lines.\n");
257 (void) fprintf(el
->el_errfile
, "no clear EOL capability.\n");
259 (void) fprintf(el
->el_errfile
, "no delete char capability.\n");
261 (void) fprintf(el
->el_errfile
, "no insert char capability.\n");
262 #endif /* DEBUG_SCREEN */
266 * Initialize the terminal stuff
269 terminal_init(EditLine
*el
)
272 el
->el_terminal
.t_buf
= el_malloc(TC_BUFSIZE
*
273 sizeof(*el
->el_terminal
.t_buf
));
274 if (el
->el_terminal
.t_buf
== NULL
)
276 el
->el_terminal
.t_cap
= el_malloc(TC_BUFSIZE
*
277 sizeof(*el
->el_terminal
.t_cap
));
278 if (el
->el_terminal
.t_cap
== NULL
)
280 el
->el_terminal
.t_fkey
= el_malloc(A_K_NKEYS
*
281 sizeof(*el
->el_terminal
.t_fkey
));
282 if (el
->el_terminal
.t_fkey
== NULL
)
284 el
->el_terminal
.t_loc
= 0;
285 el
->el_terminal
.t_str
= el_malloc(T_str
*
286 sizeof(*el
->el_terminal
.t_str
));
287 if (el
->el_terminal
.t_str
== NULL
)
289 (void) memset(el
->el_terminal
.t_str
, 0, T_str
*
290 sizeof(*el
->el_terminal
.t_str
));
291 el
->el_terminal
.t_val
= el_malloc(T_val
*
292 sizeof(*el
->el_terminal
.t_val
));
293 if (el
->el_terminal
.t_val
== NULL
)
295 (void) memset(el
->el_terminal
.t_val
, 0, T_val
*
296 sizeof(*el
->el_terminal
.t_val
));
297 (void) terminal_set(el
, NULL
);
298 terminal_init_arrow(el
);
303 * Clean up the terminal stuff
306 terminal_end(EditLine
*el
)
309 el_free(el
->el_terminal
.t_buf
);
310 el
->el_terminal
.t_buf
= NULL
;
311 el_free(el
->el_terminal
.t_cap
);
312 el
->el_terminal
.t_cap
= NULL
;
313 el
->el_terminal
.t_loc
= 0;
314 el_free(el
->el_terminal
.t_str
);
315 el
->el_terminal
.t_str
= NULL
;
316 el_free(el
->el_terminal
.t_val
);
317 el
->el_terminal
.t_val
= NULL
;
318 el_free(el
->el_terminal
.t_fkey
);
319 el
->el_terminal
.t_fkey
= NULL
;
320 terminal_free_display(el
);
325 * Maintain a string pool for termcap strings
328 terminal_alloc(EditLine
*el
, const struct termcapstr
*t
, const char *cap
)
330 char termbuf
[TC_BUFSIZE
];
332 char **tlist
= el
->el_terminal
.t_str
;
333 char **tmp
, **str
= &tlist
[t
- tstr
];
335 (void) memset(termbuf
, 0, sizeof(termbuf
));
336 if (cap
== NULL
|| *cap
== '\0') {
342 tlen
= *str
== NULL
? 0 : strlen(*str
);
345 * New string is shorter; no need to allocate space
349 (void) strcpy(*str
, cap
); /* XXX strcpy is safe */
353 * New string is longer; see if we have enough space to append
355 if (el
->el_terminal
.t_loc
+ 3 < TC_BUFSIZE
) {
356 /* XXX strcpy is safe */
357 (void) strcpy(*str
= &el
->el_terminal
.t_buf
[
358 el
->el_terminal
.t_loc
], cap
);
359 el
->el_terminal
.t_loc
+= clen
+ 1; /* one for \0 */
363 * Compact our buffer; no need to check compaction, cause we know it
367 for (tmp
= tlist
; tmp
< &tlist
[T_str
]; tmp
++)
368 if (*tmp
!= NULL
&& *tmp
!= '\0' && *tmp
!= *str
) {
371 for (ptr
= *tmp
; *ptr
!= '\0'; termbuf
[tlen
++] = *ptr
++)
373 termbuf
[tlen
++] = '\0';
375 memcpy(el
->el_terminal
.t_buf
, termbuf
, TC_BUFSIZE
);
376 el
->el_terminal
.t_loc
= tlen
;
377 if (el
->el_terminal
.t_loc
+ 3 >= TC_BUFSIZE
) {
378 (void) fprintf(el
->el_errfile
,
379 "Out of termcap string space.\n");
382 /* XXX strcpy is safe */
383 (void) strcpy(*str
= &el
->el_terminal
.t_buf
[el
->el_terminal
.t_loc
],
385 el
->el_terminal
.t_loc
+= (size_t)clen
+ 1; /* one for \0 */
390 /* terminal_rebuffer_display():
391 * Rebuffer the display after the screen changed size
394 terminal_rebuffer_display(EditLine
*el
)
396 coord_t
*c
= &el
->el_terminal
.t_size
;
398 terminal_free_display(el
);
403 if (terminal_alloc_display(el
) == -1)
409 /* terminal_alloc_display():
410 * Allocate a new display.
413 terminal_alloc_display(EditLine
*el
)
417 coord_t
*c
= &el
->el_terminal
.t_size
;
419 b
= el_malloc(sizeof(*b
) * (size_t)(c
->v
+ 1));
422 for (i
= 0; i
< c
->v
; i
++) {
423 b
[i
] = el_malloc(sizeof(**b
) * (size_t)(c
->h
+ 1));
434 b
= el_malloc(sizeof(*b
) * (size_t)(c
->v
+ 1));
437 for (i
= 0; i
< c
->v
; i
++) {
438 b
[i
] = el_malloc(sizeof(**b
) * (size_t)(c
->h
+ 1));
452 /* terminal_free_display():
453 * Free the display buffers
456 terminal_free_display(EditLine
*el
)
462 el
->el_display
= NULL
;
464 for (bufp
= b
; *bufp
!= NULL
; bufp
++)
469 el
->el_vdisplay
= NULL
;
471 for (bufp
= b
; *bufp
!= NULL
; bufp
++)
478 /* terminal_move_to_line():
479 * move to line <where> (first line == 0)
480 * as efficiently as possible
483 terminal_move_to_line(EditLine
*el
, int where
)
487 if (where
== el
->el_cursor
.v
)
490 if (where
> el
->el_terminal
.t_size
.v
) {
492 (void) fprintf(el
->el_errfile
,
493 "terminal_move_to_line: where is ridiculous: %d\r\n",
495 #endif /* DEBUG_SCREEN */
498 if ((del
= where
- el
->el_cursor
.v
) > 0) {
500 if (EL_HAS_AUTO_MARGINS
&&
501 el
->el_display
[el
->el_cursor
.v
][0] != '\0') {
503 (el
->el_terminal
.t_size
.h
- 1);
506 el
->el_display
[el
->el_cursor
.v
][h
] ==
511 /* move without newline */
512 terminal_move_to_char(el
, (int)h
);
513 terminal_overwrite(el
, &el
->el_display
514 [el
->el_cursor
.v
][el
->el_cursor
.h
],
515 (size_t)(el
->el_terminal
.t_size
.h
-
520 if ((del
> 1) && GoodStr(T_DO
)) {
521 terminal_tputs(el
, tgoto(Str(T_DO
), del
,
525 for (; del
> 0; del
--)
526 terminal__putc(el
, '\n');
527 /* because the \n will become \r\n */
532 } else { /* del < 0 */
533 if (GoodStr(T_UP
) && (-del
> 1 || !GoodStr(T_up
)))
534 terminal_tputs(el
, tgoto(Str(T_UP
), -del
, -del
), -del
);
537 for (; del
< 0; del
++)
538 terminal_tputs(el
, Str(T_up
), 1);
541 el
->el_cursor
.v
= where
;/* now where is here */
545 /* terminal_move_to_char():
546 * Move to the character position specified
549 terminal_move_to_char(EditLine
*el
, int where
)
554 if (where
== el
->el_cursor
.h
)
557 if (where
> el
->el_terminal
.t_size
.h
) {
559 (void) fprintf(el
->el_errfile
,
560 "terminal_move_to_char: where is riduculous: %d\r\n",
562 #endif /* DEBUG_SCREEN */
565 if (!where
) { /* if where is first column */
566 terminal__putc(el
, '\r'); /* do a CR */
570 del
= where
- el
->el_cursor
.h
;
572 if ((del
< -4 || del
> 4) && GoodStr(T_ch
))
573 /* go there directly */
574 terminal_tputs(el
, tgoto(Str(T_ch
), where
, where
), where
);
576 if (del
> 0) { /* moving forward */
577 if ((del
> 4) && GoodStr(T_RI
))
578 terminal_tputs(el
, tgoto(Str(T_RI
), del
, del
),
581 /* if I can do tabs, use them */
583 if ((el
->el_cursor
.h
& 0370) !=
587 el
->el_cursor
.v
][where
& 0370] !=
591 /* if not within tab stop */
593 (el
->el_cursor
.h
& 0370);
599 el
->el_cursor
.h
= where
& ~0x7;
603 * it's usually cheaper to just write the
607 * NOTE THAT terminal_overwrite() WILL CHANGE
610 terminal_overwrite(el
, &el
->el_display
[
611 el
->el_cursor
.v
][el
->el_cursor
.h
],
612 (size_t)(where
- el
->el_cursor
.h
));
615 } else { /* del < 0 := moving backward */
616 if ((-del
> 4) && GoodStr(T_LE
))
617 terminal_tputs(el
, tgoto(Str(T_LE
), -del
, -del
),
619 else { /* can't go directly there */
621 * if the "cost" is greater than the "cost"
625 ((unsigned int)-del
>
626 (((unsigned int) where
>> 3) +
629 terminal__putc(el
, '\r');/* do a CR */
631 goto mc_again
; /* and try again */
633 for (i
= 0; i
< -del
; i
++)
634 terminal__putc(el
, '\b');
638 el
->el_cursor
.h
= where
; /* now where is here */
642 /* terminal_overwrite():
643 * Overstrike num characters
644 * Assumes MB_FILL_CHARs are present to keep the column count correct
647 terminal_overwrite(EditLine
*el
, const Char
*cp
, size_t n
)
652 if (n
> (size_t)el
->el_terminal
.t_size
.h
) {
654 (void) fprintf(el
->el_errfile
,
655 "terminal_overwrite: n is riduculous: %d\r\n", n
);
656 #endif /* DEBUG_SCREEN */
661 /* terminal__putc() ignores any MB_FILL_CHARs */
662 terminal__putc(el
, *cp
++);
666 if (el
->el_cursor
.h
>= el
->el_terminal
.t_size
.h
) { /* wrap? */
667 if (EL_HAS_AUTO_MARGINS
) { /* yes */
670 if (EL_HAS_MAGIC_MARGINS
) {
671 /* force the wrap to avoid the "magic"
674 if ((c
= el
->el_display
[el
->el_cursor
.v
]
675 [el
->el_cursor
.h
]) != '\0') {
676 terminal_overwrite(el
, &c
, (size_t)1);
678 while (el
->el_display
[el
->el_cursor
.v
]
679 [el
->el_cursor
.h
] == MB_FILL_CHAR
)
683 terminal__putc(el
, ' ');
687 } else /* no wrap, but cursor stays on screen */
688 el
->el_cursor
.h
= el
->el_terminal
.t_size
.h
- 1;
693 /* terminal_deletechars():
694 * Delete num characters
697 terminal_deletechars(EditLine
*el
, int num
)
702 if (!EL_CAN_DELETE
) {
704 (void) fprintf(el
->el_errfile
, " ERROR: cannot delete \n");
705 #endif /* DEBUG_EDIT */
708 if (num
> el
->el_terminal
.t_size
.h
) {
710 (void) fprintf(el
->el_errfile
,
711 "terminal_deletechars: num is riduculous: %d\r\n", num
);
712 #endif /* DEBUG_SCREEN */
715 if (GoodStr(T_DC
)) /* if I have multiple delete */
716 if ((num
> 1) || !GoodStr(T_dc
)) { /* if dc would be more
718 terminal_tputs(el
, tgoto(Str(T_DC
), num
, num
), num
);
721 if (GoodStr(T_dm
)) /* if I have delete mode */
722 terminal_tputs(el
, Str(T_dm
), 1);
724 if (GoodStr(T_dc
)) /* else do one at a time */
726 terminal_tputs(el
, Str(T_dc
), 1);
728 if (GoodStr(T_ed
)) /* if I have delete mode */
729 terminal_tputs(el
, Str(T_ed
), 1);
733 /* terminal_insertwrite():
734 * Puts terminal in insert character mode or inserts num
735 * characters in the line
736 * Assumes MB_FILL_CHARs are present to keep column count correct
739 terminal_insertwrite(EditLine
*el
, Char
*cp
, int num
)
743 if (!EL_CAN_INSERT
) {
745 (void) fprintf(el
->el_errfile
, " ERROR: cannot insert \n");
746 #endif /* DEBUG_EDIT */
749 if (num
> el
->el_terminal
.t_size
.h
) {
751 (void) fprintf(el
->el_errfile
,
752 "StartInsert: num is riduculous: %d\r\n", num
);
753 #endif /* DEBUG_SCREEN */
756 if (GoodStr(T_IC
)) /* if I have multiple insert */
757 if ((num
> 1) || !GoodStr(T_ic
)) {
758 /* if ic would be more expensive */
759 terminal_tputs(el
, tgoto(Str(T_IC
), num
, num
), num
);
760 terminal_overwrite(el
, cp
, (size_t)num
);
761 /* this updates el_cursor.h */
764 if (GoodStr(T_im
) && GoodStr(T_ei
)) { /* if I have insert mode */
765 terminal_tputs(el
, Str(T_im
), 1);
767 el
->el_cursor
.h
+= num
;
769 terminal__putc(el
, *cp
++);
772 if (GoodStr(T_ip
)) /* have to make num chars insert */
773 terminal_tputs(el
, Str(T_ip
), 1);
775 terminal_tputs(el
, Str(T_ei
), 1);
779 if (GoodStr(T_ic
)) /* have to make num chars insert */
780 terminal_tputs(el
, Str(T_ic
), 1);
782 terminal__putc(el
, *cp
++);
786 if (GoodStr(T_ip
)) /* have to make num chars insert */
787 terminal_tputs(el
, Str(T_ip
), 1);
788 /* pad the inserted char */
794 /* terminal_clear_EOL():
795 * clear to end of line. There are num characters to clear
798 terminal_clear_EOL(EditLine
*el
, int num
)
802 if (EL_CAN_CEOL
&& GoodStr(T_ce
))
803 terminal_tputs(el
, Str(T_ce
), 1);
805 for (i
= 0; i
< num
; i
++)
806 terminal__putc(el
, ' ');
807 el
->el_cursor
.h
+= num
; /* have written num spaces */
812 /* terminal_clear_screen():
816 terminal_clear_screen(EditLine
*el
)
817 { /* clear the whole screen and home */
820 /* send the clear screen code */
821 terminal_tputs(el
, Str(T_cl
), Val(T_li
));
822 else if (GoodStr(T_ho
) && GoodStr(T_cd
)) {
823 terminal_tputs(el
, Str(T_ho
), Val(T_li
)); /* home */
824 /* clear to bottom of screen */
825 terminal_tputs(el
, Str(T_cd
), Val(T_li
));
827 terminal__putc(el
, '\r');
828 terminal__putc(el
, '\n');
834 * Beep the way the terminal wants us
837 terminal_beep(EditLine
*el
)
840 /* what termcap says we should use */
841 terminal_tputs(el
, Str(T_bl
), 1);
843 terminal__putc(el
, '\007'); /* an ASCII bell; ^G */
848 terminal_get(EditLine
*el
, const char **term
)
850 *term
= el
->el_terminal
.t_name
;
855 * Read in the terminal capabilities from the requested terminal
858 terminal_set(EditLine
*el
, const char *term
)
861 char buf
[TC_BUFSIZE
];
863 const struct termcapstr
*t
;
867 (void) sigemptyset(&nset
);
868 (void) sigaddset(&nset
, SIGWINCH
);
869 (void) sigprocmask(SIG_BLOCK
, &nset
, &oset
);
875 term
= getenv("TERM");
877 if (!term
|| !term
[0])
880 if (strcmp(term
, "emacs") == 0)
881 el
->el_flags
|= EDIT_DISABLED
;
883 (void) memset(el
->el_terminal
.t_cap
, 0, TC_BUFSIZE
);
885 i
= tgetent(el
->el_terminal
.t_cap
, term
);
889 (void) fprintf(el
->el_errfile
,
890 "Cannot read termcap database;\n");
892 (void) fprintf(el
->el_errfile
,
893 "No entry for terminal type \"%s\";\n", term
);
894 (void) fprintf(el
->el_errfile
,
895 "using dumb terminal settings.\n");
896 Val(T_co
) = 80; /* do a dumb terminal */
897 Val(T_pt
) = Val(T_km
) = Val(T_li
) = 0;
898 Val(T_xt
) = Val(T_MT
);
899 for (t
= tstr
; t
->name
!= NULL
; t
++)
900 terminal_alloc(el
, t
, NULL
);
902 /* auto/magic margins */
903 Val(T_am
) = tgetflag("am");
904 Val(T_xn
) = tgetflag("xn");
906 Val(T_pt
) = tgetflag("pt");
907 Val(T_xt
) = tgetflag("xt");
908 /* do we have a meta? */
909 Val(T_km
) = tgetflag("km");
910 Val(T_MT
) = tgetflag("MT");
912 Val(T_co
) = tgetnum("co");
913 Val(T_li
) = tgetnum("li");
914 for (t
= tstr
; t
->name
!= NULL
; t
++) {
915 /* XXX: some systems' tgetstr needs non const */
916 terminal_alloc(el
, t
, tgetstr(strchr(t
->name
, *t
->name
),
922 Val(T_co
) = 80; /* just in case */
926 el
->el_terminal
.t_size
.v
= Val(T_co
);
927 el
->el_terminal
.t_size
.h
= Val(T_li
);
929 terminal_setflags(el
);
931 /* get the correct window size */
932 (void) terminal_get_size(el
, &lins
, &cols
);
933 if (terminal_change_size(el
, lins
, cols
) == -1)
935 (void) sigprocmask(SIG_SETMASK
, &oset
, NULL
);
936 terminal_bind_arrow(el
);
937 el
->el_terminal
.t_name
= term
;
938 return i
<= 0 ? -1 : 0;
942 /* terminal_get_size():
943 * Return the new window size in lines and cols, and
944 * true if the size was changed.
947 terminal_get_size(EditLine
*el
, int *lins
, int *cols
)
956 if (ioctl(el
->el_infd
, TIOCGWINSZ
, &ws
) != -1) {
967 if (ioctl(el
->el_infd
, TIOCGSIZE
, &ts
) != -1) {
975 return Val(T_co
) != *cols
|| Val(T_li
) != *lins
;
979 /* terminal_change_size():
980 * Change the size of the terminal
983 terminal_change_size(EditLine
*el
, int lins
, int cols
)
988 Val(T_co
) = (cols
< 2) ? 80 : cols
;
989 Val(T_li
) = (lins
< 1) ? 24 : lins
;
991 /* re-make display buffers */
992 if (terminal_rebuffer_display(el
) == -1)
994 re_clear_display(el
);
999 /* terminal_init_arrow():
1000 * Initialize the arrow key bindings from termcap
1003 terminal_init_arrow(EditLine
*el
)
1005 funckey_t
*arrow
= el
->el_terminal
.t_fkey
;
1007 arrow
[A_K_DN
].name
= STR("down");
1008 arrow
[A_K_DN
].key
= T_kd
;
1009 arrow
[A_K_DN
].fun
.cmd
= ED_NEXT_HISTORY
;
1010 arrow
[A_K_DN
].type
= XK_CMD
;
1012 arrow
[A_K_UP
].name
= STR("up");
1013 arrow
[A_K_UP
].key
= T_ku
;
1014 arrow
[A_K_UP
].fun
.cmd
= ED_PREV_HISTORY
;
1015 arrow
[A_K_UP
].type
= XK_CMD
;
1017 arrow
[A_K_LT
].name
= STR("left");
1018 arrow
[A_K_LT
].key
= T_kl
;
1019 arrow
[A_K_LT
].fun
.cmd
= ED_PREV_CHAR
;
1020 arrow
[A_K_LT
].type
= XK_CMD
;
1022 arrow
[A_K_RT
].name
= STR("right");
1023 arrow
[A_K_RT
].key
= T_kr
;
1024 arrow
[A_K_RT
].fun
.cmd
= ED_NEXT_CHAR
;
1025 arrow
[A_K_RT
].type
= XK_CMD
;
1027 arrow
[A_K_HO
].name
= STR("home");
1028 arrow
[A_K_HO
].key
= T_kh
;
1029 arrow
[A_K_HO
].fun
.cmd
= ED_MOVE_TO_BEG
;
1030 arrow
[A_K_HO
].type
= XK_CMD
;
1032 arrow
[A_K_EN
].name
= STR("end");
1033 arrow
[A_K_EN
].key
= T_at7
;
1034 arrow
[A_K_EN
].fun
.cmd
= ED_MOVE_TO_END
;
1035 arrow
[A_K_EN
].type
= XK_CMD
;
1037 arrow
[A_K_DE
].name
= STR("delete");
1038 arrow
[A_K_DE
].key
= T_kD
;
1039 arrow
[A_K_DE
].fun
.cmd
= ED_DELETE_NEXT_CHAR
;
1040 arrow
[A_K_DE
].type
= XK_CMD
;
1044 /* terminal_reset_arrow():
1045 * Reset arrow key bindings
1048 terminal_reset_arrow(EditLine
*el
)
1050 funckey_t
*arrow
= el
->el_terminal
.t_fkey
;
1051 static const Char strA
[] = {033, '[', 'A', '\0'};
1052 static const Char strB
[] = {033, '[', 'B', '\0'};
1053 static const Char strC
[] = {033, '[', 'C', '\0'};
1054 static const Char strD
[] = {033, '[', 'D', '\0'};
1055 static const Char strH
[] = {033, '[', 'H', '\0'};
1056 static const Char strF
[] = {033, '[', 'F', '\0'};
1057 static const Char stOA
[] = {033, 'O', 'A', '\0'};
1058 static const Char stOB
[] = {033, 'O', 'B', '\0'};
1059 static const Char stOC
[] = {033, 'O', 'C', '\0'};
1060 static const Char stOD
[] = {033, 'O', 'D', '\0'};
1061 static const Char stOH
[] = {033, 'O', 'H', '\0'};
1062 static const Char stOF
[] = {033, 'O', 'F', '\0'};
1064 keymacro_add(el
, strA
, &arrow
[A_K_UP
].fun
, arrow
[A_K_UP
].type
);
1065 keymacro_add(el
, strB
, &arrow
[A_K_DN
].fun
, arrow
[A_K_DN
].type
);
1066 keymacro_add(el
, strC
, &arrow
[A_K_RT
].fun
, arrow
[A_K_RT
].type
);
1067 keymacro_add(el
, strD
, &arrow
[A_K_LT
].fun
, arrow
[A_K_LT
].type
);
1068 keymacro_add(el
, strH
, &arrow
[A_K_HO
].fun
, arrow
[A_K_HO
].type
);
1069 keymacro_add(el
, strF
, &arrow
[A_K_EN
].fun
, arrow
[A_K_EN
].type
);
1070 keymacro_add(el
, stOA
, &arrow
[A_K_UP
].fun
, arrow
[A_K_UP
].type
);
1071 keymacro_add(el
, stOB
, &arrow
[A_K_DN
].fun
, arrow
[A_K_DN
].type
);
1072 keymacro_add(el
, stOC
, &arrow
[A_K_RT
].fun
, arrow
[A_K_RT
].type
);
1073 keymacro_add(el
, stOD
, &arrow
[A_K_LT
].fun
, arrow
[A_K_LT
].type
);
1074 keymacro_add(el
, stOH
, &arrow
[A_K_HO
].fun
, arrow
[A_K_HO
].type
);
1075 keymacro_add(el
, stOF
, &arrow
[A_K_EN
].fun
, arrow
[A_K_EN
].type
);
1077 if (el
->el_map
.type
!= MAP_VI
)
1079 keymacro_add(el
, &strA
[1], &arrow
[A_K_UP
].fun
, arrow
[A_K_UP
].type
);
1080 keymacro_add(el
, &strB
[1], &arrow
[A_K_DN
].fun
, arrow
[A_K_DN
].type
);
1081 keymacro_add(el
, &strC
[1], &arrow
[A_K_RT
].fun
, arrow
[A_K_RT
].type
);
1082 keymacro_add(el
, &strD
[1], &arrow
[A_K_LT
].fun
, arrow
[A_K_LT
].type
);
1083 keymacro_add(el
, &strH
[1], &arrow
[A_K_HO
].fun
, arrow
[A_K_HO
].type
);
1084 keymacro_add(el
, &strF
[1], &arrow
[A_K_EN
].fun
, arrow
[A_K_EN
].type
);
1085 keymacro_add(el
, &stOA
[1], &arrow
[A_K_UP
].fun
, arrow
[A_K_UP
].type
);
1086 keymacro_add(el
, &stOB
[1], &arrow
[A_K_DN
].fun
, arrow
[A_K_DN
].type
);
1087 keymacro_add(el
, &stOC
[1], &arrow
[A_K_RT
].fun
, arrow
[A_K_RT
].type
);
1088 keymacro_add(el
, &stOD
[1], &arrow
[A_K_LT
].fun
, arrow
[A_K_LT
].type
);
1089 keymacro_add(el
, &stOH
[1], &arrow
[A_K_HO
].fun
, arrow
[A_K_HO
].type
);
1090 keymacro_add(el
, &stOF
[1], &arrow
[A_K_EN
].fun
, arrow
[A_K_EN
].type
);
1094 /* terminal_set_arrow():
1095 * Set an arrow key binding
1098 terminal_set_arrow(EditLine
*el
, const Char
*name
, keymacro_value_t
*fun
,
1101 funckey_t
*arrow
= el
->el_terminal
.t_fkey
;
1104 for (i
= 0; i
< A_K_NKEYS
; i
++)
1105 if (Strcmp(name
, arrow
[i
].name
) == 0) {
1106 arrow
[i
].fun
= *fun
;
1107 arrow
[i
].type
= type
;
1114 /* terminal_clear_arrow():
1115 * Clear an arrow key binding
1118 terminal_clear_arrow(EditLine
*el
, const Char
*name
)
1120 funckey_t
*arrow
= el
->el_terminal
.t_fkey
;
1123 for (i
= 0; i
< A_K_NKEYS
; i
++)
1124 if (Strcmp(name
, arrow
[i
].name
) == 0) {
1125 arrow
[i
].type
= XK_NOD
;
1132 /* terminal_print_arrow():
1133 * Print the arrow key bindings
1136 terminal_print_arrow(EditLine
*el
, const Char
*name
)
1139 funckey_t
*arrow
= el
->el_terminal
.t_fkey
;
1141 for (i
= 0; i
< A_K_NKEYS
; i
++)
1142 if (*name
== '\0' || Strcmp(name
, arrow
[i
].name
) == 0)
1143 if (arrow
[i
].type
!= XK_NOD
)
1144 keymacro_kprint(el
, arrow
[i
].name
,
1145 &arrow
[i
].fun
, arrow
[i
].type
);
1149 /* terminal_bind_arrow():
1150 * Bind the arrow keys
1153 terminal_bind_arrow(EditLine
*el
)
1156 const el_action_t
*dmap
;
1159 funckey_t
*arrow
= el
->el_terminal
.t_fkey
;
1161 /* Check if the components needed are initialized */
1162 if (el
->el_terminal
.t_buf
== NULL
|| el
->el_map
.key
== NULL
)
1165 map
= el
->el_map
.type
== MAP_VI
? el
->el_map
.alt
: el
->el_map
.key
;
1166 dmap
= el
->el_map
.type
== MAP_VI
? el
->el_map
.vic
: el
->el_map
.emacs
;
1168 terminal_reset_arrow(el
);
1170 for (i
= 0; i
< A_K_NKEYS
; i
++) {
1171 Char wt_str
[VISUAL_WIDTH_MAX
];
1175 p
= el
->el_terminal
.t_str
[arrow
[i
].key
];
1178 for (n
= 0; n
< VISUAL_WIDTH_MAX
&& p
[n
]; ++n
)
1180 while (n
< VISUAL_WIDTH_MAX
)
1183 j
= (unsigned char) *p
;
1185 * Assign the arrow keys only if:
1187 * 1. They are multi-character arrow keys and the user
1188 * has not re-assigned the leading character, or
1189 * has re-assigned the leading character to be
1190 * ED_SEQUENCE_LEAD_IN
1191 * 2. They are single arrow keys pointing to an
1194 if (arrow
[i
].type
== XK_NOD
)
1195 keymacro_clear(el
, map
, px
);
1197 if (p
[1] && (dmap
[j
] == map
[j
] ||
1198 map
[j
] == ED_SEQUENCE_LEAD_IN
)) {
1199 keymacro_add(el
, px
, &arrow
[i
].fun
,
1201 map
[j
] = ED_SEQUENCE_LEAD_IN
;
1202 } else if (map
[j
] == ED_UNASSIGNED
) {
1203 keymacro_clear(el
, map
, px
);
1204 if (arrow
[i
].type
== XK_CMD
)
1205 map
[j
] = arrow
[i
].fun
.cmd
;
1207 keymacro_add(el
, px
, &arrow
[i
].fun
,
1218 terminal_putc(int c
)
1220 if (terminal_outfile
== NULL
)
1222 return fputc(c
, terminal_outfile
);
1226 terminal_tputs(EditLine
*el
, const char *cap
, int affcnt
)
1229 pthread_mutex_lock(&terminal_mutex
);
1231 terminal_outfile
= el
->el_outfile
;
1232 (void)tputs(cap
, affcnt
, terminal_putc
);
1234 pthread_mutex_unlock(&terminal_mutex
);
1238 /* terminal__putc():
1242 terminal__putc(EditLine
*el
, Int c
)
1244 char buf
[MB_LEN_MAX
+1];
1246 if (c
== (Int
)MB_FILL_CHAR
)
1248 i
= ct_encode_char(buf
, (size_t)MB_LEN_MAX
, c
);
1252 return fputs(buf
, el
->el_outfile
);
1255 /* terminal__flush():
1259 terminal__flush(EditLine
*el
)
1262 (void) fflush(el
->el_outfile
);
1265 /* terminal_writec():
1266 * Write the given character out, in a human readable form
1269 terminal_writec(EditLine
*el
, Int c
)
1271 Char visbuf
[VISUAL_WIDTH_MAX
+1];
1272 ssize_t vcnt
= ct_visual_char(visbuf
, VISUAL_WIDTH_MAX
, c
);
1275 visbuf
[vcnt
] = '\0';
1276 terminal_overwrite(el
, visbuf
, (size_t)vcnt
);
1277 terminal__flush(el
);
1281 /* terminal_telltc():
1282 * Print the current termcap characteristics
1286 terminal_telltc(EditLine
*el
, int argc
__attribute__((__unused__
)),
1287 const Char
**argv
__attribute__((__unused__
)))
1289 const struct termcapstr
*t
;
1292 (void) fprintf(el
->el_outfile
, "\n\tYour terminal has the\n");
1293 (void) fprintf(el
->el_outfile
, "\tfollowing characteristics:\n\n");
1294 (void) fprintf(el
->el_outfile
, "\tIt has %d columns and %d lines\n",
1295 Val(T_co
), Val(T_li
));
1296 (void) fprintf(el
->el_outfile
,
1297 "\tIt has %s meta key\n", EL_HAS_META
? "a" : "no");
1298 (void) fprintf(el
->el_outfile
,
1299 "\tIt can%suse tabs\n", EL_CAN_TAB
? " " : "not ");
1300 (void) fprintf(el
->el_outfile
, "\tIt %s automatic margins\n",
1301 EL_HAS_AUTO_MARGINS
? "has" : "does not have");
1302 if (EL_HAS_AUTO_MARGINS
)
1303 (void) fprintf(el
->el_outfile
, "\tIt %s magic margins\n",
1304 EL_HAS_MAGIC_MARGINS
? "has" : "does not have");
1306 for (t
= tstr
, ts
= el
->el_terminal
.t_str
; t
->name
!= NULL
; t
++, ts
++) {
1309 ub
= ct_encode_string(ct_visual_string(
1310 ct_decode_string(*ts
, &el
->el_scratch
)),
1315 (void) fprintf(el
->el_outfile
, "\t%25s (%s) == %s\n",
1316 t
->long_name
, t
->name
, ub
);
1318 (void) fputc('\n', el
->el_outfile
);
1323 /* terminal_settc():
1324 * Change the current terminal characteristics
1328 terminal_settc(EditLine
*el
, int argc
__attribute__((__unused__
)),
1331 const struct termcapstr
*ts
;
1332 const struct termcapval
*tv
;
1333 char what
[8], how
[8];
1335 if (argv
== NULL
|| argv
[1] == NULL
|| argv
[2] == NULL
)
1338 strncpy(what
, ct_encode_string(argv
[1], &el
->el_scratch
), sizeof(what
));
1339 what
[sizeof(what
) - 1] = '\0';
1340 strncpy(how
, ct_encode_string(argv
[2], &el
->el_scratch
), sizeof(how
));
1341 how
[sizeof(how
) - 1] = '\0';
1344 * Do the strings first
1346 for (ts
= tstr
; ts
->name
!= NULL
; ts
++)
1347 if (strcmp(ts
->name
, what
) == 0)
1350 if (ts
->name
!= NULL
) {
1351 terminal_alloc(el
, ts
, how
);
1352 terminal_setflags(el
);
1356 * Do the numeric ones second
1358 for (tv
= tval
; tv
->name
!= NULL
; tv
++)
1359 if (strcmp(tv
->name
, what
) == 0)
1362 if (tv
->name
!= NULL
)
1365 if (tv
== &tval
[T_pt
] || tv
== &tval
[T_km
] ||
1366 tv
== &tval
[T_am
] || tv
== &tval
[T_xn
]) {
1367 if (strcmp(how
, "yes") == 0)
1368 el
->el_terminal
.t_val
[tv
- tval
] = 1;
1369 else if (strcmp(how
, "no") == 0)
1370 el
->el_terminal
.t_val
[tv
- tval
] = 0;
1372 (void) fprintf(el
->el_errfile
,
1373 "" FSTR
": Bad value `%s'.\n", argv
[0], how
);
1376 terminal_setflags(el
);
1377 if (terminal_change_size(el
, Val(T_li
), Val(T_co
)) == -1)
1384 i
= strtol(how
, &ep
, 10);
1386 (void) fprintf(el
->el_errfile
,
1387 "" FSTR
": Bad value `%s'.\n", argv
[0], how
);
1390 el
->el_terminal
.t_val
[tv
- tval
] = (int) i
;
1391 el
->el_terminal
.t_size
.v
= Val(T_co
);
1392 el
->el_terminal
.t_size
.h
= Val(T_li
);
1393 if (tv
== &tval
[T_co
] || tv
== &tval
[T_li
])
1394 if (terminal_change_size(el
, Val(T_li
), Val(T_co
))
1402 /* terminal_gettc():
1403 * Get the current terminal characteristics
1407 terminal_gettc(EditLine
*el
, int argc
__attribute__((__unused__
)), char **argv
)
1409 const struct termcapstr
*ts
;
1410 const struct termcapval
*tv
;
1414 if (argv
== NULL
|| argv
[1] == NULL
|| argv
[2] == NULL
)
1421 * Do the strings first
1423 for (ts
= tstr
; ts
->name
!= NULL
; ts
++)
1424 if (strcmp(ts
->name
, what
) == 0)
1427 if (ts
->name
!= NULL
) {
1428 *(char **)how
= el
->el_terminal
.t_str
[ts
- tstr
];
1432 * Do the numeric ones second
1434 for (tv
= tval
; tv
->name
!= NULL
; tv
++)
1435 if (strcmp(tv
->name
, what
) == 0)
1438 if (tv
->name
== NULL
)
1441 if (tv
== &tval
[T_pt
] || tv
== &tval
[T_km
] ||
1442 tv
== &tval
[T_am
] || tv
== &tval
[T_xn
]) {
1443 static char yes
[] = "yes";
1444 static char no
[] = "no";
1445 if (el
->el_terminal
.t_val
[tv
- tval
])
1446 *(char **)how
= yes
;
1451 *(int *)how
= el
->el_terminal
.t_val
[tv
- tval
];
1456 /* terminal_echotc():
1457 * Print the termcap string out with variable substitution
1461 terminal_echotc(EditLine
*el
, int argc
__attribute__((__unused__
)),
1466 int arg_need
, arg_cols
, arg_rows
;
1467 int verbose
= 0, silent
= 0;
1469 static const char fmts
[] = "%s\n", fmtd
[] = "%d\n";
1470 const struct termcapstr
*t
;
1471 char buf
[TC_BUFSIZE
];
1476 if (argv
== NULL
|| argv
[1] == NULL
)
1480 if (argv
[0][0] == '-') {
1481 switch (argv
[0][1]) {
1489 /* stderror(ERR_NAME | ERR_TCUSAGE); */
1494 if (!*argv
|| *argv
[0] == '\0')
1496 if (Strcmp(*argv
, STR("tabs")) == 0) {
1497 (void) fprintf(el
->el_outfile
, fmts
, EL_CAN_TAB
? "yes" : "no");
1499 } else if (Strcmp(*argv
, STR("meta")) == 0) {
1500 (void) fprintf(el
->el_outfile
, fmts
, Val(T_km
) ? "yes" : "no");
1502 } else if (Strcmp(*argv
, STR("xn")) == 0) {
1503 (void) fprintf(el
->el_outfile
, fmts
, EL_HAS_MAGIC_MARGINS
?
1506 } else if (Strcmp(*argv
, STR("am")) == 0) {
1507 (void) fprintf(el
->el_outfile
, fmts
, EL_HAS_AUTO_MARGINS
?
1510 } else if (Strcmp(*argv
, STR("baud")) == 0) {
1511 (void) fprintf(el
->el_outfile
, fmtd
, (int)el
->el_tty
.t_speed
);
1513 } else if (Strcmp(*argv
, STR("rows")) == 0 ||
1514 Strcmp(*argv
, STR("lines")) == 0) {
1515 (void) fprintf(el
->el_outfile
, fmtd
, Val(T_li
));
1517 } else if (Strcmp(*argv
, STR("cols")) == 0) {
1518 (void) fprintf(el
->el_outfile
, fmtd
, Val(T_co
));
1522 * Try to use our local definition first
1525 for (t
= tstr
; t
->name
!= NULL
; t
++)
1527 ct_encode_string(*argv
, &el
->el_scratch
)) == 0) {
1528 scap
= el
->el_terminal
.t_str
[t
- tstr
];
1531 if (t
->name
== NULL
) {
1532 /* XXX: some systems' tgetstr needs non const */
1533 scap
= tgetstr(ct_encode_string(*argv
, &el
->el_scratch
), &area
);
1535 if (!scap
|| scap
[0] == '\0') {
1537 (void) fprintf(el
->el_errfile
,
1538 "echotc: Termcap parameter `" FSTR
"' not found.\n",
1543 * Count home many values we need for this capability.
1545 for (cap
= scap
, arg_need
= 0; *cap
; cap
++)
1565 * hpux has lot's of them...
1568 (void) fprintf(el
->el_errfile
,
1569 "echotc: Warning: unknown termcap %% `%c'.\n",
1571 /* This is bad, but I won't complain */
1578 if (*argv
&& *argv
[0]) {
1580 (void) fprintf(el
->el_errfile
,
1581 "echotc: Warning: Extra argument `" FSTR
"'.\n",
1585 terminal_tputs(el
, scap
, 1);
1589 if (!*argv
|| *argv
[0] == '\0') {
1591 (void) fprintf(el
->el_errfile
,
1592 "echotc: Warning: Missing argument.\n");
1596 i
= Strtol(*argv
, &ep
, 10);
1597 if (*ep
!= '\0' || i
< 0) {
1599 (void) fprintf(el
->el_errfile
,
1600 "echotc: Bad value `" FSTR
"' for rows.\n",
1606 if (*argv
&& *argv
[0]) {
1608 (void) fprintf(el
->el_errfile
,
1609 "echotc: Warning: Extra argument `" FSTR
1613 terminal_tputs(el
, tgoto(scap
, arg_cols
, arg_rows
), 1);
1616 /* This is wrong, but I will ignore it... */
1618 (void) fprintf(el
->el_errfile
,
1619 "echotc: Warning: Too many required arguments (%d).\n",
1624 if (!*argv
|| *argv
[0] == '\0') {
1626 (void) fprintf(el
->el_errfile
,
1627 "echotc: Warning: Missing argument.\n");
1630 i
= Strtol(*argv
, &ep
, 10);
1631 if (*ep
!= '\0' || i
< 0) {
1633 (void) fprintf(el
->el_errfile
,
1634 "echotc: Bad value `" FSTR
"' for cols.\n",
1640 if (!*argv
|| *argv
[0] == '\0') {
1642 (void) fprintf(el
->el_errfile
,
1643 "echotc: Warning: Missing argument.\n");
1646 i
= Strtol(*argv
, &ep
, 10);
1647 if (*ep
!= '\0' || i
< 0) {
1649 (void) fprintf(el
->el_errfile
,
1650 "echotc: Bad value `" FSTR
"' for rows.\n",
1657 (void) fprintf(el
->el_errfile
,
1658 "echotc: Bad value `" FSTR
"'.\n", *argv
);
1662 if (*argv
&& *argv
[0]) {
1664 (void) fprintf(el
->el_errfile
,
1665 "echotc: Warning: Extra argument `" FSTR
1669 terminal_tputs(el
, tgoto(scap
, arg_cols
, arg_rows
), arg_rows
);