]> CyberLeo.Net >> Repos - FreeBSD/stable/8.git/blob - lib/libedit/term.c
MFC r309748 (by glebius):
[FreeBSD/stable/8.git] / lib / libedit / term.c
1 /*-
2  * Copyright (c) 1992, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Christos Zoulas of Cornell University.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  *      $NetBSD: term.c,v 1.56 2009/12/28 21:54:21 christos Exp $
33  */
34
35 #if !defined(lint) && !defined(SCCSID)
36 static char sccsid[] = "@(#)term.c      8.2 (Berkeley) 4/30/95";
37 #endif /* not lint && not SCCSID */
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
40
41 /*
42  * term.c: Editor/termcap-curses interface
43  *         We have to declare a static variable here, since the
44  *         termcap putchar routine does not take an argument!
45  */
46 #include <stdio.h>
47 #include <signal.h>
48 #include <string.h>
49 #include <stdlib.h>
50 #include <unistd.h>
51 #include <termcap.h>
52 #include <curses.h>
53 #include <ncurses.h>
54 #include <term.h>
55 #include <sys/types.h>
56 #include <sys/ioctl.h>
57
58 #include "el.h"
59
60 /*
61  * IMPORTANT NOTE: these routines are allowed to look at the current screen
62  * and the current possition assuming that it is correct.  If this is not
63  * true, then the update will be WRONG!  This is (should be) a valid
64  * assumption...
65  */
66
67 #define TC_BUFSIZE      2048
68
69 #define GoodStr(a)      (el->el_term.t_str[a] != NULL && \
70                             el->el_term.t_str[a][0] != '\0')
71 #define Str(a)          el->el_term.t_str[a]
72 #define Val(a)          el->el_term.t_val[a]
73
74 #ifdef notdef
75 private const struct {
76         const char *b_name;
77         int b_rate;
78 } baud_rate[] = {
79 #ifdef B0
80         { "0", B0 },
81 #endif
82 #ifdef B50
83         { "50", B50 },
84 #endif
85 #ifdef B75
86         { "75", B75 },
87 #endif
88 #ifdef B110
89         { "110", B110 },
90 #endif
91 #ifdef B134
92         { "134", B134 },
93 #endif
94 #ifdef B150
95         { "150", B150 },
96 #endif
97 #ifdef B200
98         { "200", B200 },
99 #endif
100 #ifdef B300
101         { "300", B300 },
102 #endif
103 #ifdef B600
104         { "600", B600 },
105 #endif
106 #ifdef B900
107         { "900", B900 },
108 #endif
109 #ifdef B1200
110         { "1200", B1200 },
111 #endif
112 #ifdef B1800
113         { "1800", B1800 },
114 #endif
115 #ifdef B2400
116         { "2400", B2400 },
117 #endif
118 #ifdef B3600
119         { "3600", B3600 },
120 #endif
121 #ifdef B4800
122         { "4800", B4800 },
123 #endif
124 #ifdef B7200
125         { "7200", B7200 },
126 #endif
127 #ifdef B9600
128         { "9600", B9600 },
129 #endif
130 #ifdef EXTA
131         { "19200", EXTA },
132 #endif
133 #ifdef B19200
134         { "19200", B19200 },
135 #endif
136 #ifdef EXTB
137         { "38400", EXTB },
138 #endif
139 #ifdef B38400
140         { "38400", B38400 },
141 #endif
142         { NULL, 0 }
143 };
144 #endif
145
146 private const struct termcapstr {
147         const char *name;
148         const char *long_name;
149 } tstr[] = {
150 #define T_al    0
151         { "al", "add new blank line" },
152 #define T_bl    1
153         { "bl", "audible bell" },
154 #define T_cd    2
155         { "cd", "clear to bottom" },
156 #define T_ce    3
157         { "ce", "clear to end of line" },
158 #define T_ch    4
159         { "ch", "cursor to horiz pos" },
160 #define T_cl    5
161         { "cl", "clear screen" },
162 #define T_dc    6
163         { "dc", "delete a character" },
164 #define T_dl    7
165         { "dl", "delete a line" },
166 #define T_dm    8
167         { "dm", "start delete mode" },
168 #define T_ed    9
169         { "ed", "end delete mode" },
170 #define T_ei    10
171         { "ei", "end insert mode" },
172 #define T_fs    11
173         { "fs", "cursor from status line" },
174 #define T_ho    12
175         { "ho", "home cursor" },
176 #define T_ic    13
177         { "ic", "insert character" },
178 #define T_im    14
179         { "im", "start insert mode" },
180 #define T_ip    15
181         { "ip", "insert padding" },
182 #define T_kd    16
183         { "kd", "sends cursor down" },
184 #define T_kl    17
185         { "kl", "sends cursor left" },
186 #define T_kr    18
187         { "kr", "sends cursor right" },
188 #define T_ku    19
189         { "ku", "sends cursor up" },
190 #define T_md    20
191         { "md", "begin bold" },
192 #define T_me    21
193         { "me", "end attributes" },
194 #define T_nd    22
195         { "nd", "non destructive space" },
196 #define T_se    23
197         { "se", "end standout" },
198 #define T_so    24
199         { "so", "begin standout" },
200 #define T_ts    25
201         { "ts", "cursor to status line" },
202 #define T_up    26
203         { "up", "cursor up one" },
204 #define T_us    27
205         { "us", "begin underline" },
206 #define T_ue    28
207         { "ue", "end underline" },
208 #define T_vb    29
209         { "vb", "visible bell" },
210 #define T_DC    30
211         { "DC", "delete multiple chars" },
212 #define T_DO    31
213         { "DO", "cursor down multiple" },
214 #define T_IC    32
215         { "IC", "insert multiple chars" },
216 #define T_LE    33
217         { "LE", "cursor left multiple" },
218 #define T_RI    34
219         { "RI", "cursor right multiple" },
220 #define T_UP    35
221         { "UP", "cursor up multiple" },
222 #define T_kh    36
223         { "kh", "send cursor home" },
224 #define T_at7   37
225         { "@7", "send cursor end" },
226 #define T_str   38
227         { NULL, NULL }
228 };
229
230 private const struct termcapval {
231         const char *name;
232         const char *long_name;
233 } tval[] = {
234 #define T_am    0
235         { "am", "has automatic margins" },
236 #define T_pt    1
237         { "pt", "has physical tabs" },
238 #define T_li    2
239         { "li", "Number of lines" },
240 #define T_co    3
241         { "co", "Number of columns" },
242 #define T_km    4
243         { "km", "Has meta key" },
244 #define T_xt    5
245         { "xt", "Tab chars destructive" },
246 #define T_xn    6
247         { "xn", "newline ignored at right margin" },
248 #define T_MT    7
249         { "MT", "Has meta key" },                       /* XXX? */
250 #define T_val   8
251         { NULL, NULL, }
252 };
253 /* do two or more of the attributes use me */
254
255 private void    term_setflags(EditLine *);
256 private int     term_rebuffer_display(EditLine *);
257 private void    term_free_display(EditLine *);
258 private int     term_alloc_display(EditLine *);
259 private void    term_alloc(EditLine *, const struct termcapstr *, const char *);
260 private void    term_init_arrow(EditLine *);
261 private void    term_reset_arrow(EditLine *);
262 private int     term_putc(int);
263 private void    term_tputs(EditLine *, const char *, int);
264
265 #ifdef _REENTRANT
266 private pthread_mutex_t term_mutex = PTHREAD_MUTEX_INITIALIZER;
267 #endif
268 private FILE *term_outfile = NULL;
269
270
271 /* term_setflags():
272  *      Set the terminal capability flags
273  */
274 private void
275 term_setflags(EditLine *el)
276 {
277         EL_FLAGS = 0;
278         if (el->el_tty.t_tabs)
279                 EL_FLAGS |= (Val(T_pt) && !Val(T_xt)) ? TERM_CAN_TAB : 0;
280
281         EL_FLAGS |= (Val(T_km) || Val(T_MT)) ? TERM_HAS_META : 0;
282         EL_FLAGS |= GoodStr(T_ce) ? TERM_CAN_CEOL : 0;
283         EL_FLAGS |= (GoodStr(T_dc) || GoodStr(T_DC)) ? TERM_CAN_DELETE : 0;
284         EL_FLAGS |= (GoodStr(T_im) || GoodStr(T_ic) || GoodStr(T_IC)) ?
285             TERM_CAN_INSERT : 0;
286         EL_FLAGS |= (GoodStr(T_up) || GoodStr(T_UP)) ? TERM_CAN_UP : 0;
287         EL_FLAGS |= Val(T_am) ? TERM_HAS_AUTO_MARGINS : 0;
288         EL_FLAGS |= Val(T_xn) ? TERM_HAS_MAGIC_MARGINS : 0;
289
290         if (GoodStr(T_me) && GoodStr(T_ue))
291                 EL_FLAGS |= (strcmp(Str(T_me), Str(T_ue)) == 0) ?
292                     TERM_CAN_ME : 0;
293         else
294                 EL_FLAGS &= ~TERM_CAN_ME;
295         if (GoodStr(T_me) && GoodStr(T_se))
296                 EL_FLAGS |= (strcmp(Str(T_me), Str(T_se)) == 0) ?
297                     TERM_CAN_ME : 0;
298
299
300 #ifdef DEBUG_SCREEN
301         if (!EL_CAN_UP) {
302                 (void) fprintf(el->el_errfile,
303                     "WARNING: Your terminal cannot move up.\n");
304                 (void) fprintf(el->el_errfile,
305                     "Editing may be odd for long lines.\n");
306         }
307         if (!EL_CAN_CEOL)
308                 (void) fprintf(el->el_errfile, "no clear EOL capability.\n");
309         if (!EL_CAN_DELETE)
310                 (void) fprintf(el->el_errfile, "no delete char capability.\n");
311         if (!EL_CAN_INSERT)
312                 (void) fprintf(el->el_errfile, "no insert char capability.\n");
313 #endif /* DEBUG_SCREEN */
314 }
315
316 /* term_init():
317  *      Initialize the terminal stuff
318  */
319 protected int
320 term_init(EditLine *el)
321 {
322
323         el->el_term.t_buf = (char *) el_malloc(TC_BUFSIZE);
324         if (el->el_term.t_buf == NULL)
325                 return (-1);
326         el->el_term.t_cap = (char *) el_malloc(TC_BUFSIZE);
327         if (el->el_term.t_cap == NULL)
328                 return (-1);
329         el->el_term.t_fkey = (fkey_t *) el_malloc(A_K_NKEYS * sizeof(fkey_t));
330         if (el->el_term.t_fkey == NULL)
331                 return (-1);
332         el->el_term.t_loc = 0;
333         el->el_term.t_str = (char **) el_malloc(T_str * sizeof(char *));
334         if (el->el_term.t_str == NULL)
335                 return (-1);
336         (void) memset(el->el_term.t_str, 0, T_str * sizeof(char *));
337         el->el_term.t_val = (int *) el_malloc(T_val * sizeof(int));
338         if (el->el_term.t_val == NULL)
339                 return (-1);
340         (void) memset(el->el_term.t_val, 0, T_val * sizeof(int));
341         term_init_arrow(el);
342         (void) term_set(el, NULL);
343         return (0);
344 }
345
346 /* term_end():
347  *      Clean up the terminal stuff
348  */
349 protected void
350 term_end(EditLine *el)
351 {
352
353         el_free((ptr_t) el->el_term.t_buf);
354         el->el_term.t_buf = NULL;
355         el_free((ptr_t) el->el_term.t_cap);
356         el->el_term.t_cap = NULL;
357         el->el_term.t_loc = 0;
358         el_free((ptr_t) el->el_term.t_str);
359         el->el_term.t_str = NULL;
360         el_free((ptr_t) el->el_term.t_val);
361         el->el_term.t_val = NULL;
362         el_free((ptr_t) el->el_term.t_fkey);
363         el->el_term.t_fkey = NULL;
364         term_free_display(el);
365 }
366
367
368 /* term_alloc():
369  *      Maintain a string pool for termcap strings
370  */
371 private void
372 term_alloc(EditLine *el, const struct termcapstr *t, const char *cap)
373 {
374         char termbuf[TC_BUFSIZE];
375         size_t tlen, clen;
376         char **tlist = el->el_term.t_str;
377         char **tmp, **str = &tlist[t - tstr];
378
379         if (cap == NULL || *cap == '\0') {
380                 *str = NULL;
381                 return;
382         } else
383                 clen = strlen(cap);
384
385         tlen = *str == NULL ? 0 : strlen(*str);
386
387         /*
388          * New string is shorter; no need to allocate space
389          */
390         if (clen <= tlen) {
391                 if (*str)
392                         (void) strcpy(*str, cap);       /* XXX strcpy is safe */
393                 return;
394         }
395         /*
396          * New string is longer; see if we have enough space to append
397          */
398         if (el->el_term.t_loc + 3 < TC_BUFSIZE) {
399                                                 /* XXX strcpy is safe */
400                 (void) strcpy(*str = &el->el_term.t_buf[el->el_term.t_loc],
401                     cap);
402                 el->el_term.t_loc += (int)clen + 1;     /* one for \0 */
403                 return;
404         }
405         /*
406          * Compact our buffer; no need to check compaction, cause we know it
407          * fits...
408          */
409         tlen = 0;
410         for (tmp = tlist; tmp < &tlist[T_str]; tmp++)
411                 if (*tmp != NULL && *tmp != '\0' && *tmp != *str) {
412                         char *ptr;
413
414                         for (ptr = *tmp; *ptr != '\0'; termbuf[tlen++] = *ptr++)
415                                 continue;
416                         termbuf[tlen++] = '\0';
417                 }
418         memcpy(el->el_term.t_buf, termbuf, TC_BUFSIZE);
419         el->el_term.t_loc = (int)tlen;
420         if (el->el_term.t_loc + 3 >= TC_BUFSIZE) {
421                 (void) fprintf(el->el_errfile,
422                     "Out of termcap string space.\n");
423                 return;
424         }
425                                         /* XXX strcpy is safe */
426         (void) strcpy(*str = &el->el_term.t_buf[el->el_term.t_loc], cap);
427         el->el_term.t_loc += (int)clen + 1;     /* one for \0 */
428         return;
429 }
430
431
432 /* term_rebuffer_display():
433  *      Rebuffer the display after the screen changed size
434  */
435 private int
436 term_rebuffer_display(EditLine *el)
437 {
438         coord_t *c = &el->el_term.t_size;
439
440         term_free_display(el);
441
442         c->h = Val(T_co);
443         c->v = Val(T_li);
444
445         if (term_alloc_display(el) == -1)
446                 return (-1);
447         return (0);
448 }
449
450
451 /* term_alloc_display():
452  *      Allocate a new display.
453  */
454 private int
455 term_alloc_display(EditLine *el)
456 {
457         int i;
458         char **b;
459         coord_t *c = &el->el_term.t_size;
460
461         b = (char **) el_malloc((size_t) (sizeof(char *) * (c->v + 1)));
462         if (b == NULL)
463                 return (-1);
464         for (i = 0; i < c->v; i++) {
465                 b[i] = (char *) el_malloc((size_t) (sizeof(char) * (c->h + 1)));
466                 if (b[i] == NULL) {
467                         while (--i >= 0)
468                                 el_free((ptr_t) b[i]);
469                         el_free((ptr_t) b);
470                         return (-1);
471                 }
472         }
473         b[c->v] = NULL;
474         el->el_display = b;
475
476         b = (char **) el_malloc((size_t) (sizeof(char *) * (c->v + 1)));
477         if (b == NULL)
478                 return (-1);
479         for (i = 0; i < c->v; i++) {
480                 b[i] = (char *) el_malloc((size_t) (sizeof(char) * (c->h + 1)));
481                 if (b[i] == NULL) {
482                         while (--i >= 0)
483                                 el_free((ptr_t) b[i]);
484                         el_free((ptr_t) b);
485                         return (-1);
486                 }
487         }
488         b[c->v] = NULL;
489         el->el_vdisplay = b;
490         return (0);
491 }
492
493
494 /* term_free_display():
495  *      Free the display buffers
496  */
497 private void
498 term_free_display(EditLine *el)
499 {
500         char **b;
501         char **bufp;
502
503         b = el->el_display;
504         el->el_display = NULL;
505         if (b != NULL) {
506                 for (bufp = b; *bufp != NULL; bufp++)
507                         el_free((ptr_t) * bufp);
508                 el_free((ptr_t) b);
509         }
510         b = el->el_vdisplay;
511         el->el_vdisplay = NULL;
512         if (b != NULL) {
513                 for (bufp = b; *bufp != NULL; bufp++)
514                         el_free((ptr_t) * bufp);
515                 el_free((ptr_t) b);
516         }
517 }
518
519
520 /* term_move_to_line():
521  *      move to line <where> (first line == 0)
522  *      as efficiently as possible
523  */
524 protected void
525 term_move_to_line(EditLine *el, int where)
526 {
527         int del;
528
529         if (where == el->el_cursor.v)
530                 return;
531
532         if (where > el->el_term.t_size.v) {
533 #ifdef DEBUG_SCREEN
534                 (void) fprintf(el->el_errfile,
535                     "term_move_to_line: where is ridiculous: %d\r\n", where);
536 #endif /* DEBUG_SCREEN */
537                 return;
538         }
539         if ((del = where - el->el_cursor.v) > 0) {
540                 while (del > 0) {
541                         if (EL_HAS_AUTO_MARGINS &&
542                             el->el_display[el->el_cursor.v][0] != '\0') {
543                                 /* move without newline */
544                                 term_move_to_char(el, el->el_term.t_size.h - 1);
545                                 term_overwrite(el, &el->el_display
546                                     [el->el_cursor.v][el->el_cursor.h],
547                                     (size_t)(el->el_term.t_size.h -
548                                     el->el_cursor.h));
549                                 /* updates Cursor */
550                                 del--;
551                         } else {
552                                 if ((del > 1) && GoodStr(T_DO)) {
553                                         term_tputs(el, tgoto(Str(T_DO), del,
554                                             del), del);
555                                         del = 0;
556                                 } else {
557                                         for (; del > 0; del--)
558                                                 term__putc(el, '\n');
559                                         /* because the \n will become \r\n */
560                                         el->el_cursor.h = 0;
561                                 }
562                         }
563                 }
564         } else {                /* del < 0 */
565                 if (GoodStr(T_UP) && (-del > 1 || !GoodStr(T_up)))
566                         term_tputs(el, tgoto(Str(T_UP), -del, -del), -del);
567                 else {
568                         if (GoodStr(T_up))
569                                 for (; del < 0; del++)
570                                         term_tputs(el, Str(T_up), 1);
571                 }
572         }
573         el->el_cursor.v = where;/* now where is here */
574 }
575
576
577 /* term_move_to_char():
578  *      Move to the character position specified
579  */
580 protected void
581 term_move_to_char(EditLine *el, int where)
582 {
583         int del, i;
584
585 mc_again:
586         if (where == el->el_cursor.h)
587                 return;
588
589         if (where > el->el_term.t_size.h) {
590 #ifdef DEBUG_SCREEN
591                 (void) fprintf(el->el_errfile,
592                     "term_move_to_char: where is riduculous: %d\r\n", where);
593 #endif /* DEBUG_SCREEN */
594                 return;
595         }
596         if (!where) {           /* if where is first column */
597                 term__putc(el, '\r');   /* do a CR */
598                 el->el_cursor.h = 0;
599                 return;
600         }
601         del = where - el->el_cursor.h;
602
603         if ((del < -4 || del > 4) && GoodStr(T_ch))
604                 /* go there directly */
605                 term_tputs(el, tgoto(Str(T_ch), where, where), where);
606         else {
607                 if (del > 0) {  /* moving forward */
608                         if ((del > 4) && GoodStr(T_RI))
609                                 term_tputs(el, tgoto(Str(T_RI), del, del), del);
610                         else {
611                                         /* if I can do tabs, use them */
612                                 if (EL_CAN_TAB) {
613                                         if ((el->el_cursor.h & 0370) !=
614                                             (where & ~0x7)) {
615                                                 /* if not within tab stop */
616                                                 for (i =
617                                                     (el->el_cursor.h & 0370);
618                                                     i < (where & ~0x7);
619                                                     i += 8)
620                                                         term__putc(el, '\t');   
621                                                         /* then tab over */
622                                                 el->el_cursor.h = where & ~0x7;
623                                         }
624                                 }
625                                 /*
626                                  * it's usually cheaper to just write the
627                                  * chars, so we do.
628                                  */
629                                 /*
630                                  * NOTE THAT term_overwrite() WILL CHANGE
631                                  * el->el_cursor.h!!!
632                                  */
633                                 term_overwrite(el, &el->el_display[
634                                     el->el_cursor.v][el->el_cursor.h],
635                                     (size_t)(where - el->el_cursor.h));
636
637                         }
638                 } else {        /* del < 0 := moving backward */
639                         if ((-del > 4) && GoodStr(T_LE))
640                                 term_tputs(el, tgoto(Str(T_LE), -del, -del),
641                                     -del);
642                         else {  /* can't go directly there */
643                                 /*
644                                  * if the "cost" is greater than the "cost"
645                                  * from col 0
646                                  */
647                                 if (EL_CAN_TAB ?
648                                     ((unsigned int)-del >
649                                     (((unsigned int) where >> 3) +
650                                      (where & 07)))
651                                     : (-del > where)) {
652                                         term__putc(el, '\r');   /* do a CR */
653                                         el->el_cursor.h = 0;
654                                         goto mc_again;  /* and try again */
655                                 }
656                                 for (i = 0; i < -del; i++)
657                                         term__putc(el, '\b');
658                         }
659                 }
660         }
661         el->el_cursor.h = where;                /* now where is here */
662 }
663
664
665 /* term_overwrite():
666  *      Overstrike num characters
667  */
668 protected void
669 term_overwrite(EditLine *el, const char *cp, size_t n)
670 {
671         if (n == 0)
672                 return;
673
674         if (n > (size_t)el->el_term.t_size.h) {
675 #ifdef DEBUG_SCREEN
676                 (void) fprintf(el->el_errfile,
677                     "term_overwrite: n is riduculous: %d\r\n", n);
678 #endif /* DEBUG_SCREEN */
679                 return;
680         }
681
682         do {
683                 term__putc(el, *cp++);
684                 el->el_cursor.h++;
685         } while (--n);
686
687         if (el->el_cursor.h >= el->el_term.t_size.h) {  /* wrap? */
688                 if (EL_HAS_AUTO_MARGINS) {      /* yes */
689                         el->el_cursor.h = 0;
690                         el->el_cursor.v++;
691                         if (EL_HAS_MAGIC_MARGINS) {
692                                 /* force the wrap to avoid the "magic"
693                                  * situation */
694                                 char c;
695                                 if ((c = el->el_display[el->el_cursor.v][el->el_cursor.h])
696                                     != '\0')
697                                         term_overwrite(el, &c, 1);
698                                 else {
699                                         term__putc(el, ' ');
700                                         el->el_cursor.h = 1;
701                                 }
702                         }
703                 } else          /* no wrap, but cursor stays on screen */
704                         el->el_cursor.h = el->el_term.t_size.h - 1;
705         }
706 }
707
708
709 /* term_deletechars():
710  *      Delete num characters
711  */
712 protected void
713 term_deletechars(EditLine *el, int num)
714 {
715         if (num <= 0)
716                 return;
717
718         if (!EL_CAN_DELETE) {
719 #ifdef DEBUG_EDIT
720                 (void) fprintf(el->el_errfile, "   ERROR: cannot delete   \n");
721 #endif /* DEBUG_EDIT */
722                 return;
723         }
724         if (num > el->el_term.t_size.h) {
725 #ifdef DEBUG_SCREEN
726                 (void) fprintf(el->el_errfile,
727                     "term_deletechars: num is riduculous: %d\r\n", num);
728 #endif /* DEBUG_SCREEN */
729                 return;
730         }
731         if (GoodStr(T_DC))      /* if I have multiple delete */
732                 if ((num > 1) || !GoodStr(T_dc)) {      /* if dc would be more
733                                                          * expen. */
734                         term_tputs(el, tgoto(Str(T_DC), num, num), num);
735                         return;
736                 }
737         if (GoodStr(T_dm))      /* if I have delete mode */
738                 term_tputs(el, Str(T_dm), 1);
739
740         if (GoodStr(T_dc))      /* else do one at a time */
741                 while (num--)
742                         term_tputs(el, Str(T_dc), 1);
743
744         if (GoodStr(T_ed))      /* if I have delete mode */
745                 term_tputs(el, Str(T_ed), 1);
746 }
747
748
749 /* term_insertwrite():
750  *      Puts terminal in insert character mode or inserts num
751  *      characters in the line
752  */
753 protected void
754 term_insertwrite(EditLine *el, char *cp, int num)
755 {
756         if (num <= 0)
757                 return;
758         if (!EL_CAN_INSERT) {
759 #ifdef DEBUG_EDIT
760                 (void) fprintf(el->el_errfile, "   ERROR: cannot insert   \n");
761 #endif /* DEBUG_EDIT */
762                 return;
763         }
764         if (num > el->el_term.t_size.h) {
765 #ifdef DEBUG_SCREEN
766                 (void) fprintf(el->el_errfile,
767                     "StartInsert: num is riduculous: %d\r\n", num);
768 #endif /* DEBUG_SCREEN */
769                 return;
770         }
771         if (GoodStr(T_IC))      /* if I have multiple insert */
772                 if ((num > 1) || !GoodStr(T_ic)) {
773                                 /* if ic would be more expensive */
774                         term_tputs(el, tgoto(Str(T_IC), num, num), num);
775                         term_overwrite(el, cp, (size_t)num);
776                                 /* this updates el_cursor.h */
777                         return;
778                 }
779         if (GoodStr(T_im) && GoodStr(T_ei)) {   /* if I have insert mode */
780                 term_tputs(el, Str(T_im), 1);
781
782                 el->el_cursor.h += num;
783                 do
784                         term__putc(el, *cp++);
785                 while (--num);
786
787                 if (GoodStr(T_ip))      /* have to make num chars insert */
788                         term_tputs(el, Str(T_ip), 1);
789
790                 term_tputs(el, Str(T_ei), 1);
791                 return;
792         }
793         do {
794                 if (GoodStr(T_ic))      /* have to make num chars insert */
795                         term_tputs(el, Str(T_ic), 1);
796
797                 term__putc(el, *cp++);
798
799                 el->el_cursor.h++;
800
801                 if (GoodStr(T_ip))      /* have to make num chars insert */
802                         term_tputs(el, Str(T_ip), 1);
803                                         /* pad the inserted char */
804
805         } while (--num);
806 }
807
808
809 /* term_clear_EOL():
810  *      clear to end of line.  There are num characters to clear
811  */
812 protected void
813 term_clear_EOL(EditLine *el, int num)
814 {
815         int i;
816
817         if (EL_CAN_CEOL && GoodStr(T_ce))
818                 term_tputs(el, Str(T_ce), 1);
819         else {
820                 for (i = 0; i < num; i++)
821                         term__putc(el, ' ');
822                 el->el_cursor.h += num; /* have written num spaces */
823         }
824 }
825
826
827 /* term_clear_screen():
828  *      Clear the screen
829  */
830 protected void
831 term_clear_screen(EditLine *el)
832 {                               /* clear the whole screen and home */
833
834         if (GoodStr(T_cl))
835                 /* send the clear screen code */
836                 term_tputs(el, Str(T_cl), Val(T_li));
837         else if (GoodStr(T_ho) && GoodStr(T_cd)) {
838                 term_tputs(el, Str(T_ho), Val(T_li));   /* home */
839                 /* clear to bottom of screen */
840                 term_tputs(el, Str(T_cd), Val(T_li));
841         } else {
842                 term__putc(el, '\r');
843                 term__putc(el, '\n');
844         }
845 }
846
847
848 /* term_beep():
849  *      Beep the way the terminal wants us
850  */
851 protected void
852 term_beep(EditLine *el)
853 {
854         if (GoodStr(T_bl))
855                 /* what termcap says we should use */
856                 term_tputs(el, Str(T_bl), 1);
857         else
858                 term__putc(el, '\007'); /* an ASCII bell; ^G */
859 }
860
861
862 #ifdef notdef
863 /* term_clear_to_bottom():
864  *      Clear to the bottom of the screen
865  */
866 protected void
867 term_clear_to_bottom(EditLine *el)
868 {
869         if (GoodStr(T_cd))
870                 term_tputs(el, Str(T_cd), Val(T_li));
871         else if (GoodStr(T_ce))
872                 term_tputs(el, Str(T_ce), Val(T_li));
873 }
874 #endif
875
876 protected void
877 term_get(EditLine *el, const char **term)
878 {
879         *term = el->el_term.t_name;
880 }
881
882
883 /* term_set():
884  *      Read in the terminal capabilities from the requested terminal
885  */
886 protected int
887 term_set(EditLine *el, const char *term)
888 {
889         int i;
890         char buf[TC_BUFSIZE];
891         char *area;
892         const struct termcapstr *t;
893         sigset_t oset, nset;
894         int lins, cols;
895
896         (void) sigemptyset(&nset);
897         (void) sigaddset(&nset, SIGWINCH);
898         (void) sigprocmask(SIG_BLOCK, &nset, &oset);
899
900         area = buf;
901
902
903         if (term == NULL)
904                 term = getenv("TERM");
905
906         if (!term || !term[0])
907                 term = "dumb";
908
909         if (strcmp(term, "emacs") == 0)
910                 el->el_flags |= EDIT_DISABLED;
911
912         memset(el->el_term.t_cap, 0, TC_BUFSIZE);
913
914         i = tgetent(el->el_term.t_cap, term);
915
916         if (i <= 0) {
917                 if (i == -1)
918                         (void) fprintf(el->el_errfile,
919                             "Cannot read termcap database;\n");
920                 else if (i == 0)
921                         (void) fprintf(el->el_errfile,
922                             "No entry for terminal type \"%s\";\n", term);
923                 (void) fprintf(el->el_errfile,
924                     "using dumb terminal settings.\n");
925                 Val(T_co) = 80; /* do a dumb terminal */
926                 Val(T_pt) = Val(T_km) = Val(T_li) = 0;
927                 Val(T_xt) = Val(T_MT);
928                 for (t = tstr; t->name != NULL; t++)
929                         term_alloc(el, t, NULL);
930         } else {
931                 /* auto/magic margins */
932                 Val(T_am) = tgetflag("am");
933                 Val(T_xn) = tgetflag("xn");
934                 /* Can we tab */
935                 Val(T_pt) = tgetflag("pt");
936                 Val(T_xt) = tgetflag("xt");
937                 /* do we have a meta? */
938                 Val(T_km) = tgetflag("km");
939                 Val(T_MT) = tgetflag("MT");
940                 /* Get the size */
941                 Val(T_co) = tgetnum("co");
942                 Val(T_li) = tgetnum("li");
943                 for (t = tstr; t->name != NULL; t++) {
944                         /* XXX: some systems' tgetstr needs non const */
945                         term_alloc(el, t, tgetstr(strchr(t->name, *t->name),
946                             &area));
947                 }
948         }
949
950         if (Val(T_co) < 2)
951                 Val(T_co) = 80; /* just in case */
952         if (Val(T_li) < 1)
953                 Val(T_li) = 24;
954
955         el->el_term.t_size.v = Val(T_co);
956         el->el_term.t_size.h = Val(T_li);
957
958         term_setflags(el);
959
960                                 /* get the correct window size */
961         (void) term_get_size(el, &lins, &cols);
962         if (term_change_size(el, lins, cols) == -1)
963                 return (-1);
964         (void) sigprocmask(SIG_SETMASK, &oset, NULL);
965         term_bind_arrow(el);
966         el->el_term.t_name = term;
967         return (i <= 0 ? -1 : 0);
968 }
969
970
971 /* term_get_size():
972  *      Return the new window size in lines and cols, and
973  *      true if the size was changed.
974  */
975 protected int
976 term_get_size(EditLine *el, int *lins, int *cols)
977 {
978
979         *cols = Val(T_co);
980         *lins = Val(T_li);
981
982 #ifdef TIOCGWINSZ
983         {
984                 struct winsize ws;
985                 if (ioctl(el->el_infd, TIOCGWINSZ, (ioctl_t) & ws) != -1) {
986                         if (ws.ws_col)
987                                 *cols = ws.ws_col;
988                         if (ws.ws_row)
989                                 *lins = ws.ws_row;
990                 }
991         }
992 #endif
993 #ifdef TIOCGSIZE
994         {
995                 struct ttysize ts;
996                 if (ioctl(el->el_infd, TIOCGSIZE, (ioctl_t) & ts) != -1) {
997                         if (ts.ts_cols)
998                                 *cols = ts.ts_cols;
999                         if (ts.ts_lines)
1000                                 *lins = ts.ts_lines;
1001                 }
1002         }
1003 #endif
1004         return (Val(T_co) != *cols || Val(T_li) != *lins);
1005 }
1006
1007
1008 /* term_change_size():
1009  *      Change the size of the terminal
1010  */
1011 protected int
1012 term_change_size(EditLine *el, int lins, int cols)
1013 {
1014         /*
1015          * Just in case
1016          */
1017         Val(T_co) = (cols < 2) ? 80 : cols;
1018         Val(T_li) = (lins < 1) ? 24 : lins;
1019
1020         /* re-make display buffers */
1021         if (term_rebuffer_display(el) == -1)
1022                 return (-1);
1023         re_clear_display(el);
1024         return (0);
1025 }
1026
1027
1028 /* term_init_arrow():
1029  *      Initialize the arrow key bindings from termcap
1030  */
1031 private void
1032 term_init_arrow(EditLine *el)
1033 {
1034         fkey_t *arrow = el->el_term.t_fkey;
1035
1036         arrow[A_K_DN].name = "down";
1037         arrow[A_K_DN].key = T_kd;
1038         arrow[A_K_DN].fun.cmd = ED_NEXT_HISTORY;
1039         arrow[A_K_DN].type = XK_CMD;
1040
1041         arrow[A_K_UP].name = "up";
1042         arrow[A_K_UP].key = T_ku;
1043         arrow[A_K_UP].fun.cmd = ED_PREV_HISTORY;
1044         arrow[A_K_UP].type = XK_CMD;
1045
1046         arrow[A_K_LT].name = "left";
1047         arrow[A_K_LT].key = T_kl;
1048         arrow[A_K_LT].fun.cmd = ED_PREV_CHAR;
1049         arrow[A_K_LT].type = XK_CMD;
1050
1051         arrow[A_K_RT].name = "right";
1052         arrow[A_K_RT].key = T_kr;
1053         arrow[A_K_RT].fun.cmd = ED_NEXT_CHAR;
1054         arrow[A_K_RT].type = XK_CMD;
1055
1056         arrow[A_K_HO].name = "home";
1057         arrow[A_K_HO].key = T_kh;
1058         arrow[A_K_HO].fun.cmd = ED_MOVE_TO_BEG;
1059         arrow[A_K_HO].type = XK_CMD;
1060
1061         arrow[A_K_EN].name = "end";
1062         arrow[A_K_EN].key = T_at7;
1063         arrow[A_K_EN].fun.cmd = ED_MOVE_TO_END;
1064         arrow[A_K_EN].type = XK_CMD;
1065 }
1066
1067
1068 /* term_reset_arrow():
1069  *      Reset arrow key bindings
1070  */
1071 private void
1072 term_reset_arrow(EditLine *el)
1073 {
1074         fkey_t *arrow = el->el_term.t_fkey;
1075         static const char strA[] = {033, '[', 'A', '\0'};
1076         static const char strB[] = {033, '[', 'B', '\0'};
1077         static const char strC[] = {033, '[', 'C', '\0'};
1078         static const char strD[] = {033, '[', 'D', '\0'};
1079         static const char strH[] = {033, '[', 'H', '\0'};
1080         static const char strF[] = {033, '[', 'F', '\0'};
1081         static const char str1[] = {033, '[', '1', '~', '\0'};
1082         static const char str4[] = {033, '[', '4', '~', '\0'};
1083         static const char stOA[] = {033, 'O', 'A', '\0'};
1084         static const char stOB[] = {033, 'O', 'B', '\0'};
1085         static const char stOC[] = {033, 'O', 'C', '\0'};
1086         static const char stOD[] = {033, 'O', 'D', '\0'};
1087         static const char stOH[] = {033, 'O', 'H', '\0'};
1088         static const char stOF[] = {033, 'O', 'F', '\0'};
1089
1090         key_add(el, strA, &arrow[A_K_UP].fun, arrow[A_K_UP].type);
1091         key_add(el, strB, &arrow[A_K_DN].fun, arrow[A_K_DN].type);
1092         key_add(el, strC, &arrow[A_K_RT].fun, arrow[A_K_RT].type);
1093         key_add(el, strD, &arrow[A_K_LT].fun, arrow[A_K_LT].type);
1094         key_add(el, strH, &arrow[A_K_HO].fun, arrow[A_K_HO].type);
1095         key_add(el, strF, &arrow[A_K_EN].fun, arrow[A_K_EN].type);
1096         key_add(el, str1, &arrow[A_K_HO].fun, arrow[A_K_HO].type);
1097         key_add(el, str4, &arrow[A_K_EN].fun, arrow[A_K_EN].type);
1098         key_add(el, stOA, &arrow[A_K_UP].fun, arrow[A_K_UP].type);
1099         key_add(el, stOB, &arrow[A_K_DN].fun, arrow[A_K_DN].type);
1100         key_add(el, stOC, &arrow[A_K_RT].fun, arrow[A_K_RT].type);
1101         key_add(el, stOD, &arrow[A_K_LT].fun, arrow[A_K_LT].type);
1102         key_add(el, stOH, &arrow[A_K_HO].fun, arrow[A_K_HO].type);
1103         key_add(el, stOF, &arrow[A_K_EN].fun, arrow[A_K_EN].type);
1104
1105         if (el->el_map.type == MAP_VI) {
1106                 key_add(el, &strA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type);
1107                 key_add(el, &strB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type);
1108                 key_add(el, &strC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type);
1109                 key_add(el, &strD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type);
1110                 key_add(el, &strH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type);
1111                 key_add(el, &strF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type);
1112                 key_add(el, &str1[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type);
1113                 key_add(el, &str4[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type);
1114                 key_add(el, &stOA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type);
1115                 key_add(el, &stOB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type);
1116                 key_add(el, &stOC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type);
1117                 key_add(el, &stOD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type);
1118                 key_add(el, &stOH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type);
1119                 key_add(el, &stOF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type);
1120         }
1121 }
1122
1123
1124 /* term_set_arrow():
1125  *      Set an arrow key binding
1126  */
1127 protected int
1128 term_set_arrow(EditLine *el, const char *name, key_value_t *fun, int type)
1129 {
1130         fkey_t *arrow = el->el_term.t_fkey;
1131         int i;
1132
1133         for (i = 0; i < A_K_NKEYS; i++)
1134                 if (strcmp(name, arrow[i].name) == 0) {
1135                         arrow[i].fun = *fun;
1136                         arrow[i].type = type;
1137                         return (0);
1138                 }
1139         return (-1);
1140 }
1141
1142
1143 /* term_clear_arrow():
1144  *      Clear an arrow key binding
1145  */
1146 protected int
1147 term_clear_arrow(EditLine *el, const char *name)
1148 {
1149         fkey_t *arrow = el->el_term.t_fkey;
1150         int i;
1151
1152         for (i = 0; i < A_K_NKEYS; i++)
1153                 if (strcmp(name, arrow[i].name) == 0) {
1154                         arrow[i].type = XK_NOD;
1155                         return (0);
1156                 }
1157         return (-1);
1158 }
1159
1160
1161 /* term_print_arrow():
1162  *      Print the arrow key bindings
1163  */
1164 protected void
1165 term_print_arrow(EditLine *el, const char *name)
1166 {
1167         int i;
1168         fkey_t *arrow = el->el_term.t_fkey;
1169
1170         for (i = 0; i < A_K_NKEYS; i++)
1171                 if (*name == '\0' || strcmp(name, arrow[i].name) == 0)
1172                         if (arrow[i].type != XK_NOD)
1173                                 key_kprint(el, arrow[i].name, &arrow[i].fun,
1174                                     arrow[i].type);
1175 }
1176
1177
1178 /* term_bind_arrow():
1179  *      Bind the arrow keys
1180  */
1181 protected void
1182 term_bind_arrow(EditLine *el)
1183 {
1184         el_action_t *map;
1185         const el_action_t *dmap;
1186         int i, j;
1187         char *p;
1188         fkey_t *arrow = el->el_term.t_fkey;
1189
1190         /* Check if the components needed are initialized */
1191         if (el->el_term.t_buf == NULL || el->el_map.key == NULL)
1192                 return;
1193
1194         map = el->el_map.type == MAP_VI ? el->el_map.alt : el->el_map.key;
1195         dmap = el->el_map.type == MAP_VI ? el->el_map.vic : el->el_map.emacs;
1196
1197         term_reset_arrow(el);
1198
1199         for (i = 0; i < A_K_NKEYS; i++) {
1200                 p = el->el_term.t_str[arrow[i].key];
1201                 if (p && *p) {
1202                         j = (unsigned char) *p;
1203                         /*
1204                          * Assign the arrow keys only if:
1205                          *
1206                          * 1. They are multi-character arrow keys and the user
1207                          *    has not re-assigned the leading character, or
1208                          *    has re-assigned the leading character to be
1209                          *        ED_SEQUENCE_LEAD_IN
1210                          * 2. They are single arrow keys pointing to an
1211                          *    unassigned key.
1212                          */
1213                         if (arrow[i].type == XK_NOD)
1214                                 key_clear(el, map, p);
1215                         else {
1216                                 if (p[1] && (dmap[j] == map[j] ||
1217                                         map[j] == ED_SEQUENCE_LEAD_IN)) {
1218                                         key_add(el, p, &arrow[i].fun,
1219                                             arrow[i].type);
1220                                         map[j] = ED_SEQUENCE_LEAD_IN;
1221                                 } else if (map[j] == ED_UNASSIGNED) {
1222                                         key_clear(el, map, p);
1223                                         if (arrow[i].type == XK_CMD)
1224                                                 map[j] = arrow[i].fun.cmd;
1225                                         else
1226                                                 key_add(el, p, &arrow[i].fun,
1227                                                     arrow[i].type);
1228                                 }
1229                         }
1230                 }
1231         }
1232 }
1233
1234 /* term_putc():
1235  *      Add a character
1236  */
1237 private int
1238 term_putc(int c)
1239 {
1240
1241         if (term_outfile == NULL)
1242                 return -1;
1243         return fputc(c, term_outfile);
1244 }
1245
1246 private void
1247 term_tputs(EditLine *el, const char *cap, int affcnt)
1248 {
1249 #ifdef _REENTRANT
1250         pthread_mutex_lock(&term_mutex);
1251 #endif
1252         term_outfile = el->el_outfile;
1253         (void)tputs(cap, affcnt, term_putc);
1254 #ifdef _REENTRANT
1255         pthread_mutex_unlock(&term_mutex);
1256 #endif
1257 }
1258
1259 /* term__putc():
1260  *      Add a character
1261  */
1262 protected int
1263 term__putc(EditLine *el, int c)
1264 {
1265
1266         return fputc(c, el->el_outfile);
1267 }
1268
1269 /* term__flush():
1270  *      Flush output
1271  */
1272 protected void
1273 term__flush(EditLine *el)
1274 {
1275
1276         (void) fflush(el->el_outfile);
1277 }
1278
1279 /* term_writec():
1280  *      Write the given character out, in a human readable form
1281  */
1282 protected void
1283 term_writec(EditLine *el, int c)
1284 {
1285         char buf[8];
1286         size_t cnt = key__decode_char(buf, sizeof(buf), 0, c);
1287         buf[cnt] = '\0';
1288         term_overwrite(el, buf, (size_t)cnt);
1289         term__flush(el);
1290 }
1291
1292
1293 /* term_telltc():
1294  *      Print the current termcap characteristics
1295  */
1296 protected int
1297 /*ARGSUSED*/
1298 term_telltc(EditLine *el, int argc __unused, 
1299     const char **argv __unused)
1300 {
1301         const struct termcapstr *t;
1302         char **ts;
1303         char upbuf[EL_BUFSIZ];
1304
1305         (void) fprintf(el->el_outfile, "\n\tYour terminal has the\n");
1306         (void) fprintf(el->el_outfile, "\tfollowing characteristics:\n\n");
1307         (void) fprintf(el->el_outfile, "\tIt has %d columns and %d lines\n",
1308             Val(T_co), Val(T_li));
1309         (void) fprintf(el->el_outfile,
1310             "\tIt has %s meta key\n", EL_HAS_META ? "a" : "no");
1311         (void) fprintf(el->el_outfile,
1312             "\tIt can%suse tabs\n", EL_CAN_TAB ? " " : "not ");
1313         (void) fprintf(el->el_outfile, "\tIt %s automatic margins\n",
1314             EL_HAS_AUTO_MARGINS ? "has" : "does not have");
1315         if (EL_HAS_AUTO_MARGINS)
1316                 (void) fprintf(el->el_outfile, "\tIt %s magic margins\n",
1317                     EL_HAS_MAGIC_MARGINS ? "has" : "does not have");
1318
1319         for (t = tstr, ts = el->el_term.t_str; t->name != NULL; t++, ts++) {
1320                 const char *ub;
1321                 if (*ts && **ts) {
1322                     (void) key__decode_str(*ts, upbuf, sizeof(upbuf), "");
1323                     ub = upbuf;
1324                 } else {
1325                     ub = "(empty)";
1326                 }
1327                 (void) fprintf(el->el_outfile, "\t%25s (%s) == %s\n",
1328                     t->long_name, t->name, ub);
1329         }
1330         (void) fputc('\n', el->el_outfile);
1331         return (0);
1332 }
1333
1334
1335 /* term_settc():
1336  *      Change the current terminal characteristics
1337  */
1338 protected int
1339 /*ARGSUSED*/
1340 term_settc(EditLine *el, int argc __unused,
1341     const char **argv)
1342 {
1343         const struct termcapstr *ts;
1344         const struct termcapval *tv;
1345         const char *what, *how;
1346
1347         if (argv == NULL || argv[1] == NULL || argv[2] == NULL)
1348                 return -1;
1349
1350         what = argv[1];
1351         how = argv[2];
1352
1353         /*
1354          * Do the strings first
1355          */
1356         for (ts = tstr; ts->name != NULL; ts++)
1357                 if (strcmp(ts->name, what) == 0)
1358                         break;
1359
1360         if (ts->name != NULL) {
1361                 term_alloc(el, ts, how);
1362                 term_setflags(el);
1363                 return 0;
1364         }
1365         /*
1366          * Do the numeric ones second
1367          */
1368         for (tv = tval; tv->name != NULL; tv++)
1369                 if (strcmp(tv->name, what) == 0)
1370                         break;
1371
1372         if (tv->name != NULL)
1373                 return -1;
1374
1375         if (tv == &tval[T_pt] || tv == &tval[T_km] ||
1376             tv == &tval[T_am] || tv == &tval[T_xn]) {
1377                 if (strcmp(how, "yes") == 0)
1378                         el->el_term.t_val[tv - tval] = 1;
1379                 else if (strcmp(how, "no") == 0)
1380                         el->el_term.t_val[tv - tval] = 0;
1381                 else {
1382                         (void) fprintf(el->el_errfile,
1383                             "%s: Bad value `%s'.\n", argv[0], how);
1384                         return -1;
1385                 }
1386                 term_setflags(el);
1387                 if (term_change_size(el, Val(T_li), Val(T_co)) == -1)
1388                         return -1;
1389                 return 0;
1390         } else {
1391                 long i;
1392                 char *ep;
1393
1394                 i = strtol(how, &ep, 10);
1395                 if (*ep != '\0') {
1396                         (void) fprintf(el->el_errfile,
1397                             "%s: Bad value `%s'.\n", argv[0], how);
1398                         return -1;
1399                 }
1400                 el->el_term.t_val[tv - tval] = (int) i;
1401                 el->el_term.t_size.v = Val(T_co);
1402                 el->el_term.t_size.h = Val(T_li);
1403                 if (tv == &tval[T_co] || tv == &tval[T_li])
1404                         if (term_change_size(el, Val(T_li), Val(T_co))
1405                             == -1)
1406                                 return -1;
1407                 return 0;
1408         }
1409 }
1410
1411
1412 /* term_gettc():
1413  *      Get the current terminal characteristics
1414  */
1415 protected int
1416 /*ARGSUSED*/
1417 term_gettc(EditLine *el, int argc __unused, char **argv)
1418 {
1419         const struct termcapstr *ts;
1420         const struct termcapval *tv;
1421         char *what;
1422         void *how;
1423
1424         if (argv == NULL || argv[1] == NULL || argv[2] == NULL)
1425                 return (-1);
1426
1427         what = argv[1];
1428         how = argv[2];
1429
1430         /*
1431          * Do the strings first
1432          */
1433         for (ts = tstr; ts->name != NULL; ts++)
1434                 if (strcmp(ts->name, what) == 0)
1435                         break;
1436
1437         if (ts->name != NULL) {
1438                 *(char **)how = el->el_term.t_str[ts - tstr];
1439                 return 0;
1440         }
1441         /*
1442          * Do the numeric ones second
1443          */
1444         for (tv = tval; tv->name != NULL; tv++)
1445                 if (strcmp(tv->name, what) == 0)
1446                         break;
1447
1448         if (tv->name == NULL)
1449                 return -1;
1450
1451         if (tv == &tval[T_pt] || tv == &tval[T_km] ||
1452             tv == &tval[T_am] || tv == &tval[T_xn]) {
1453                 static char yes[] = "yes";
1454                 static char no[] = "no";
1455                 if (el->el_term.t_val[tv - tval])
1456                         *(char **)how = yes;
1457                 else
1458                         *(char **)how = no;
1459                 return 0;
1460         } else {
1461                 *(int *)how = el->el_term.t_val[tv - tval];
1462                 return 0;
1463         }
1464 }
1465
1466 /* term_echotc():
1467  *      Print the termcap string out with variable substitution
1468  */
1469 protected int
1470 /*ARGSUSED*/
1471 term_echotc(EditLine *el, int argc __unused,
1472     const char **argv)
1473 {
1474         char *cap, *scap, *ep;
1475         int arg_need, arg_cols, arg_rows;
1476         int verbose = 0, silent = 0;
1477         char *area;
1478         static const char fmts[] = "%s\n", fmtd[] = "%d\n";
1479         const struct termcapstr *t;
1480         char buf[TC_BUFSIZE];
1481         long i;
1482
1483         area = buf;
1484
1485         if (argv == NULL || argv[1] == NULL)
1486                 return (-1);
1487         argv++;
1488
1489         if (argv[0][0] == '-') {
1490                 switch (argv[0][1]) {
1491                 case 'v':
1492                         verbose = 1;
1493                         break;
1494                 case 's':
1495                         silent = 1;
1496                         break;
1497                 default:
1498                         /* stderror(ERR_NAME | ERR_TCUSAGE); */
1499                         break;
1500                 }
1501                 argv++;
1502         }
1503         if (!*argv || *argv[0] == '\0')
1504                 return (0);
1505         if (strcmp(*argv, "tabs") == 0) {
1506                 (void) fprintf(el->el_outfile, fmts, EL_CAN_TAB ? "yes" : "no");
1507                 return (0);
1508         } else if (strcmp(*argv, "meta") == 0) {
1509                 (void) fprintf(el->el_outfile, fmts, Val(T_km) ? "yes" : "no");
1510                 return (0);
1511         } else if (strcmp(*argv, "xn") == 0) {
1512                 (void) fprintf(el->el_outfile, fmts, EL_HAS_MAGIC_MARGINS ?
1513                     "yes" : "no");
1514                 return (0);
1515         } else if (strcmp(*argv, "am") == 0) {
1516                 (void) fprintf(el->el_outfile, fmts, EL_HAS_AUTO_MARGINS ?
1517                     "yes" : "no");
1518                 return (0);
1519         } else if (strcmp(*argv, "baud") == 0) {
1520 #ifdef notdef
1521                 int i;
1522
1523                 for (i = 0; baud_rate[i].b_name != NULL; i++)
1524                         if (el->el_tty.t_speed == baud_rate[i].b_rate) {
1525                                 (void) fprintf(el->el_outfile, fmts,
1526                                     baud_rate[i].b_name);
1527                                 return (0);
1528                         }
1529                 (void) fprintf(el->el_outfile, fmtd, 0);
1530 #else
1531                 (void) fprintf(el->el_outfile, fmtd, (int)el->el_tty.t_speed);
1532 #endif
1533                 return (0);
1534         } else if (strcmp(*argv, "rows") == 0 || strcmp(*argv, "lines") == 0) {
1535                 (void) fprintf(el->el_outfile, fmtd, Val(T_li));
1536                 return (0);
1537         } else if (strcmp(*argv, "cols") == 0) {
1538                 (void) fprintf(el->el_outfile, fmtd, Val(T_co));
1539                 return (0);
1540         }
1541         /*
1542          * Try to use our local definition first
1543          */
1544         scap = NULL;
1545         for (t = tstr; t->name != NULL; t++)
1546                 if (strcmp(t->name, *argv) == 0) {
1547                         scap = el->el_term.t_str[t - tstr];
1548                         break;
1549                 }
1550         if (t->name == NULL) {
1551                 /* XXX: some systems' tgetstr needs non const */
1552                 scap = tgetstr(strchr(*argv, **argv), &area);
1553         }
1554         if (!scap || scap[0] == '\0') {
1555                 if (!silent)
1556                         (void) fprintf(el->el_errfile,
1557                             "echotc: Termcap parameter `%s' not found.\n",
1558                             *argv);
1559                 return (-1);
1560         }
1561         /*
1562          * Count home many values we need for this capability.
1563          */
1564         for (cap = scap, arg_need = 0; *cap; cap++)
1565                 if (*cap == '%')
1566                         switch (*++cap) {
1567                         case 'd':
1568                         case '2':
1569                         case '3':
1570                         case '.':
1571                         case '+':
1572                                 arg_need++;
1573                                 break;
1574                         case '%':
1575                         case '>':
1576                         case 'i':
1577                         case 'r':
1578                         case 'n':
1579                         case 'B':
1580                         case 'D':
1581                                 break;
1582                         default:
1583                                 /*
1584                                  * hpux has lot's of them...
1585                                  */
1586                                 if (verbose)
1587                                         (void) fprintf(el->el_errfile,
1588                                 "echotc: Warning: unknown termcap %% `%c'.\n",
1589                                             *cap);
1590                                 /* This is bad, but I won't complain */
1591                                 break;
1592                         }
1593
1594         switch (arg_need) {
1595         case 0:
1596                 argv++;
1597                 if (*argv && *argv[0]) {
1598                         if (!silent)
1599                                 (void) fprintf(el->el_errfile,
1600                                     "echotc: Warning: Extra argument `%s'.\n",
1601                                     *argv);
1602                         return (-1);
1603                 }
1604                 term_tputs(el, scap, 1);
1605                 break;
1606         case 1:
1607                 argv++;
1608                 if (!*argv || *argv[0] == '\0') {
1609                         if (!silent)
1610                                 (void) fprintf(el->el_errfile,
1611                                     "echotc: Warning: Missing argument.\n");
1612                         return (-1);
1613                 }
1614                 arg_cols = 0;
1615                 i = strtol(*argv, &ep, 10);
1616                 if (*ep != '\0' || i < 0) {
1617                         if (!silent)
1618                                 (void) fprintf(el->el_errfile,
1619                                     "echotc: Bad value `%s' for rows.\n",
1620                                     *argv);
1621                         return (-1);
1622                 }
1623                 arg_rows = (int) i;
1624                 argv++;
1625                 if (*argv && *argv[0]) {
1626                         if (!silent)
1627                                 (void) fprintf(el->el_errfile,
1628                                     "echotc: Warning: Extra argument `%s'.\n",
1629                                     *argv);
1630                         return (-1);
1631                 }
1632                 term_tputs(el, tgoto(scap, arg_cols, arg_rows), 1);
1633                 break;
1634         default:
1635                 /* This is wrong, but I will ignore it... */
1636                 if (verbose)
1637                         (void) fprintf(el->el_errfile,
1638                          "echotc: Warning: Too many required arguments (%d).\n",
1639                             arg_need);
1640                 /* FALLTHROUGH */
1641         case 2:
1642                 argv++;
1643                 if (!*argv || *argv[0] == '\0') {
1644                         if (!silent)
1645                                 (void) fprintf(el->el_errfile,
1646                                     "echotc: Warning: Missing argument.\n");
1647                         return (-1);
1648                 }
1649                 i = strtol(*argv, &ep, 10);
1650                 if (*ep != '\0' || i < 0) {
1651                         if (!silent)
1652                                 (void) fprintf(el->el_errfile,
1653                                     "echotc: Bad value `%s' for cols.\n",
1654                                     *argv);
1655                         return (-1);
1656                 }
1657                 arg_cols = (int) i;
1658                 argv++;
1659                 if (!*argv || *argv[0] == '\0') {
1660                         if (!silent)
1661                                 (void) fprintf(el->el_errfile,
1662                                     "echotc: Warning: Missing argument.\n");
1663                         return (-1);
1664                 }
1665                 i = strtol(*argv, &ep, 10);
1666                 if (*ep != '\0' || i < 0) {
1667                         if (!silent)
1668                                 (void) fprintf(el->el_errfile,
1669                                     "echotc: Bad value `%s' for rows.\n",
1670                                     *argv);
1671                         return (-1);
1672                 }
1673                 arg_rows = (int) i;
1674                 if (*ep != '\0') {
1675                         if (!silent)
1676                                 (void) fprintf(el->el_errfile,
1677                                     "echotc: Bad value `%s'.\n", *argv);
1678                         return (-1);
1679                 }
1680                 argv++;
1681                 if (*argv && *argv[0]) {
1682                         if (!silent)
1683                                 (void) fprintf(el->el_errfile,
1684                                     "echotc: Warning: Extra argument `%s'.\n",
1685                                     *argv);
1686                         return (-1);
1687                 }
1688                 term_tputs(el, tgoto(scap, arg_cols, arg_rows), arg_rows);
1689                 break;
1690         }
1691         return (0);
1692 }