]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - lib/libedit/term.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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.46 2006/11/24 00:01:17 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
263
264 private FILE *term_outfile = NULL;      /* XXX: How do we fix that? */
265
266
267 /* term_setflags():
268  *      Set the terminal capability flags
269  */
270 private void
271 term_setflags(EditLine *el)
272 {
273         EL_FLAGS = 0;
274         if (el->el_tty.t_tabs)
275                 EL_FLAGS |= (Val(T_pt) && !Val(T_xt)) ? TERM_CAN_TAB : 0;
276
277         EL_FLAGS |= (Val(T_km) || Val(T_MT)) ? TERM_HAS_META : 0;
278         EL_FLAGS |= GoodStr(T_ce) ? TERM_CAN_CEOL : 0;
279         EL_FLAGS |= (GoodStr(T_dc) || GoodStr(T_DC)) ? TERM_CAN_DELETE : 0;
280         EL_FLAGS |= (GoodStr(T_im) || GoodStr(T_ic) || GoodStr(T_IC)) ?
281             TERM_CAN_INSERT : 0;
282         EL_FLAGS |= (GoodStr(T_up) || GoodStr(T_UP)) ? TERM_CAN_UP : 0;
283         EL_FLAGS |= Val(T_am) ? TERM_HAS_AUTO_MARGINS : 0;
284         EL_FLAGS |= Val(T_xn) ? TERM_HAS_MAGIC_MARGINS : 0;
285
286         if (GoodStr(T_me) && GoodStr(T_ue))
287                 EL_FLAGS |= (strcmp(Str(T_me), Str(T_ue)) == 0) ?
288                     TERM_CAN_ME : 0;
289         else
290                 EL_FLAGS &= ~TERM_CAN_ME;
291         if (GoodStr(T_me) && GoodStr(T_se))
292                 EL_FLAGS |= (strcmp(Str(T_me), Str(T_se)) == 0) ?
293                     TERM_CAN_ME : 0;
294
295
296 #ifdef DEBUG_SCREEN
297         if (!EL_CAN_UP) {
298                 (void) fprintf(el->el_errfile,
299                     "WARNING: Your terminal cannot move up.\n");
300                 (void) fprintf(el->el_errfile,
301                     "Editing may be odd for long lines.\n");
302         }
303         if (!EL_CAN_CEOL)
304                 (void) fprintf(el->el_errfile, "no clear EOL capability.\n");
305         if (!EL_CAN_DELETE)
306                 (void) fprintf(el->el_errfile, "no delete char capability.\n");
307         if (!EL_CAN_INSERT)
308                 (void) fprintf(el->el_errfile, "no insert char capability.\n");
309 #endif /* DEBUG_SCREEN */
310 }
311
312
313 /* term_init():
314  *      Initialize the terminal stuff
315  */
316 protected int
317 term_init(EditLine *el)
318 {
319
320         el->el_term.t_buf = (char *) el_malloc(TC_BUFSIZE);
321         if (el->el_term.t_buf == NULL)
322                 return (-1);
323         el->el_term.t_cap = (char *) el_malloc(TC_BUFSIZE);
324         if (el->el_term.t_cap == NULL)
325                 return (-1);
326         el->el_term.t_fkey = (fkey_t *) el_malloc(A_K_NKEYS * sizeof(fkey_t));
327         if (el->el_term.t_fkey == NULL)
328                 return (-1);
329         el->el_term.t_loc = 0;
330         el->el_term.t_str = (char **) el_malloc(T_str * sizeof(char *));
331         if (el->el_term.t_str == NULL)
332                 return (-1);
333         (void) memset(el->el_term.t_str, 0, T_str * sizeof(char *));
334         el->el_term.t_val = (int *) el_malloc(T_val * sizeof(int));
335         if (el->el_term.t_val == NULL)
336                 return (-1);
337         (void) memset(el->el_term.t_val, 0, T_val * sizeof(int));
338         term_outfile = el->el_outfile;
339         term_init_arrow(el);
340         (void) term_set(el, NULL);
341         return (0);
342 }
343
344 /* term_end():
345  *      Clean up the terminal stuff
346  */
347 protected void
348 term_end(EditLine *el)
349 {
350
351         el_free((ptr_t) el->el_term.t_buf);
352         el->el_term.t_buf = NULL;
353         el_free((ptr_t) el->el_term.t_cap);
354         el->el_term.t_cap = NULL;
355         el->el_term.t_loc = 0;
356         el_free((ptr_t) el->el_term.t_str);
357         el->el_term.t_str = NULL;
358         el_free((ptr_t) el->el_term.t_val);
359         el->el_term.t_val = NULL;
360         el_free((ptr_t) el->el_term.t_fkey);
361         el->el_term.t_fkey = NULL;
362         term_free_display(el);
363 }
364
365
366 /* term_alloc():
367  *      Maintain a string pool for termcap strings
368  */
369 private void
370 term_alloc(EditLine *el, const struct termcapstr *t, const char *cap)
371 {
372         char termbuf[TC_BUFSIZE];
373         int tlen, clen;
374         char **tlist = el->el_term.t_str;
375         char **tmp, **str = &tlist[t - tstr];
376
377         if (cap == NULL || *cap == '\0') {
378                 *str = NULL;
379                 return;
380         } else
381                 clen = strlen(cap);
382
383         tlen = *str == NULL ? 0 : strlen(*str);
384
385         /*
386          * New string is shorter; no need to allocate space
387          */
388         if (clen <= tlen) {
389                 if (*str)
390                         (void) strcpy(*str, cap);       /* XXX strcpy is safe */
391                 return;
392         }
393         /*
394          * New string is longer; see if we have enough space to append
395          */
396         if (el->el_term.t_loc + 3 < TC_BUFSIZE) {
397                                                 /* XXX strcpy is safe */
398                 (void) strcpy(*str = &el->el_term.t_buf[el->el_term.t_loc],
399                     cap);
400                 el->el_term.t_loc += clen + 1;  /* one for \0 */
401                 return;
402         }
403         /*
404          * Compact our buffer; no need to check compaction, cause we know it
405          * fits...
406          */
407         tlen = 0;
408         for (tmp = tlist; tmp < &tlist[T_str]; tmp++)
409                 if (*tmp != NULL && *tmp != '\0' && *tmp != *str) {
410                         char *ptr;
411
412                         for (ptr = *tmp; *ptr != '\0'; termbuf[tlen++] = *ptr++)
413                                 continue;
414                         termbuf[tlen++] = '\0';
415                 }
416         memcpy(el->el_term.t_buf, termbuf, TC_BUFSIZE);
417         el->el_term.t_loc = tlen;
418         if (el->el_term.t_loc + 3 >= TC_BUFSIZE) {
419                 (void) fprintf(el->el_errfile,
420                     "Out of termcap string space.\n");
421                 return;
422         }
423                                         /* XXX strcpy is safe */
424         (void) strcpy(*str = &el->el_term.t_buf[el->el_term.t_loc], cap);
425         el->el_term.t_loc += clen + 1;  /* one for \0 */
426         return;
427 }
428
429
430 /* term_rebuffer_display():
431  *      Rebuffer the display after the screen changed size
432  */
433 private int
434 term_rebuffer_display(EditLine *el)
435 {
436         coord_t *c = &el->el_term.t_size;
437
438         term_free_display(el);
439
440         c->h = Val(T_co);
441         c->v = Val(T_li);
442
443         if (term_alloc_display(el) == -1)
444                 return (-1);
445         return (0);
446 }
447
448
449 /* term_alloc_display():
450  *      Allocate a new display.
451  */
452 private int
453 term_alloc_display(EditLine *el)
454 {
455         int i;
456         char **b;
457         coord_t *c = &el->el_term.t_size;
458
459         b = (char **) el_malloc((size_t) (sizeof(char *) * (c->v + 1)));
460         if (b == NULL)
461                 return (-1);
462         for (i = 0; i < c->v; i++) {
463                 b[i] = (char *) el_malloc((size_t) (sizeof(char) * (c->h + 1)));
464                 if (b[i] == NULL) {
465                         while (--i >= 0)
466                                 el_free((ptr_t) b[i]);
467                         el_free((ptr_t) b);
468                         return (-1);
469                 }
470         }
471         b[c->v] = NULL;
472         el->el_display = b;
473
474         b = (char **) el_malloc((size_t) (sizeof(char *) * (c->v + 1)));
475         if (b == NULL)
476                 return (-1);
477         for (i = 0; i < c->v; i++) {
478                 b[i] = (char *) el_malloc((size_t) (sizeof(char) * (c->h + 1)));
479                 if (b[i] == NULL) {
480                         while (--i >= 0)
481                                 el_free((ptr_t) b[i]);
482                         el_free((ptr_t) b);
483                         return (-1);
484                 }
485         }
486         b[c->v] = NULL;
487         el->el_vdisplay = b;
488         return (0);
489 }
490
491
492 /* term_free_display():
493  *      Free the display buffers
494  */
495 private void
496 term_free_display(EditLine *el)
497 {
498         char **b;
499         char **bufp;
500
501         b = el->el_display;
502         el->el_display = NULL;
503         if (b != NULL) {
504                 for (bufp = b; *bufp != NULL; bufp++)
505                         el_free((ptr_t) * bufp);
506                 el_free((ptr_t) b);
507         }
508         b = el->el_vdisplay;
509         el->el_vdisplay = NULL;
510         if (b != NULL) {
511                 for (bufp = b; *bufp != NULL; bufp++)
512                         el_free((ptr_t) * bufp);
513                 el_free((ptr_t) b);
514         }
515 }
516
517
518 /* term_move_to_line():
519  *      move to line <where> (first line == 0)
520  *      as efficiently as possible
521  */
522 protected void
523 term_move_to_line(EditLine *el, int where)
524 {
525         int del;
526
527         if (where == el->el_cursor.v)
528                 return;
529
530         if (where > el->el_term.t_size.v) {
531 #ifdef DEBUG_SCREEN
532                 (void) fprintf(el->el_errfile,
533                     "term_move_to_line: where is ridiculous: %d\r\n", where);
534 #endif /* DEBUG_SCREEN */
535                 return;
536         }
537         if ((del = where - el->el_cursor.v) > 0) {
538                 while (del > 0) {
539                         if (EL_HAS_AUTO_MARGINS &&
540                             el->el_display[el->el_cursor.v][0] != '\0') {
541                                 /* move without newline */
542                                 term_move_to_char(el, el->el_term.t_size.h - 1);
543                                 term_overwrite(el,
544                                     &el->el_display[el->el_cursor.v][el->el_cursor.h],
545                                     1);
546                                 /* updates Cursor */
547                                 del--;
548                         } else {
549                                 if ((del > 1) && GoodStr(T_DO)) {
550                                         (void) tputs(tgoto(Str(T_DO), del, del),
551                                             del, term__putc);
552                                         del = 0;
553                                 } else {
554                                         for (; del > 0; del--)
555                                                 term__putc('\n');
556                                         /* because the \n will become \r\n */
557                                         el->el_cursor.h = 0;
558                                 }
559                         }
560                 }
561         } else {                /* del < 0 */
562                 if (GoodStr(T_UP) && (-del > 1 || !GoodStr(T_up)))
563                         (void) tputs(tgoto(Str(T_UP), -del, -del), -del,
564                             term__putc);
565                 else {
566                         if (GoodStr(T_up))
567                                 for (; del < 0; del++)
568                                         (void) tputs(Str(T_up), 1, term__putc);
569                 }
570         }
571         el->el_cursor.v = where;/* now where is here */
572 }
573
574
575 /* term_move_to_char():
576  *      Move to the character position specified
577  */
578 protected void
579 term_move_to_char(EditLine *el, int where)
580 {
581         int del, i;
582
583 mc_again:
584         if (where == el->el_cursor.h)
585                 return;
586
587         if (where > el->el_term.t_size.h) {
588 #ifdef DEBUG_SCREEN
589                 (void) fprintf(el->el_errfile,
590                     "term_move_to_char: where is riduculous: %d\r\n", where);
591 #endif /* DEBUG_SCREEN */
592                 return;
593         }
594         if (!where) {           /* if where is first column */
595                 term__putc('\r');       /* do a CR */
596                 el->el_cursor.h = 0;
597                 return;
598         }
599         del = where - el->el_cursor.h;
600
601         if ((del < -4 || del > 4) && GoodStr(T_ch))
602                 /* go there directly */
603                 (void) tputs(tgoto(Str(T_ch), where, where), where, term__putc);
604         else {
605                 if (del > 0) {  /* moving forward */
606                         if ((del > 4) && GoodStr(T_RI))
607                                 (void) tputs(tgoto(Str(T_RI), del, del),
608                                     del, term__putc);
609                         else {
610                                         /* if I can do tabs, use them */
611                                 if (EL_CAN_TAB) {
612                                         if ((el->el_cursor.h & 0370) !=
613                                             (where & 0370)) {
614                                                 /* if not within tab stop */
615                                                 for (i =
616                                                     (el->el_cursor.h & 0370);
617                                                     i < (where & 0370);
618                                                     i += 8)
619                                                         term__putc('\t');       
620                                                         /* then tab over */
621                                                 el->el_cursor.h = where & 0370;
622                                         }
623                                 }
624                                 /*
625                                  * it's usually cheaper to just write the
626                                  * chars, so we do.
627                                  */
628                                 /*
629                                  * NOTE THAT term_overwrite() WILL CHANGE
630                                  * el->el_cursor.h!!!
631                                  */
632                                 term_overwrite(el,
633                                     &el->el_display[el->el_cursor.v][el->el_cursor.h],
634                                     where - el->el_cursor.h);
635
636                         }
637                 } else {        /* del < 0 := moving backward */
638                         if ((-del > 4) && GoodStr(T_LE))
639                                 (void) tputs(tgoto(Str(T_LE), -del, -del),
640                                     -del, term__putc);
641                         else {  /* can't go directly there */
642                                 /*
643                                  * if the "cost" is greater than the "cost"
644                                  * from col 0
645                                  */
646                                 if (EL_CAN_TAB ?
647                                     ((unsigned int)-del >
648                                     (((unsigned int) where >> 3) +
649                                      (where & 07)))
650                                     : (-del > where)) {
651                                         term__putc('\r');       /* do a CR */
652                                         el->el_cursor.h = 0;
653                                         goto mc_again;  /* and try again */
654                                 }
655                                 for (i = 0; i < -del; i++)
656                                         term__putc('\b');
657                         }
658                 }
659         }
660         el->el_cursor.h = where;                /* now where is here */
661 }
662
663
664 /* term_overwrite():
665  *      Overstrike num characters
666  */
667 protected void
668 term_overwrite(EditLine *el, const char *cp, int n)
669 {
670         if (n <= 0)
671                 return;         /* catch bugs */
672
673         if (n > el->el_term.t_size.h) {
674 #ifdef DEBUG_SCREEN
675                 (void) fprintf(el->el_errfile,
676                     "term_overwrite: n is riduculous: %d\r\n", n);
677 #endif /* DEBUG_SCREEN */
678                 return;
679         }
680         do {
681                 term__putc(*cp++);
682                 el->el_cursor.h++;
683         } while (--n);
684
685         if (el->el_cursor.h >= el->el_term.t_size.h) {  /* wrap? */
686                 if (EL_HAS_AUTO_MARGINS) {      /* yes */
687                         el->el_cursor.h = 0;
688                         el->el_cursor.v++;
689                         if (EL_HAS_MAGIC_MARGINS) {
690                                 /* force the wrap to avoid the "magic"
691                                  * situation */
692                                 char c;
693                                 if ((c = el->el_display[el->el_cursor.v][el->el_cursor.h])
694                                     != '\0')
695                                         term_overwrite(el, &c, 1);
696                                 else
697                                         term__putc(' ');
698                                 el->el_cursor.h = 1;
699                         }
700                 } else          /* no wrap, but cursor stays on screen */
701                         el->el_cursor.h = el->el_term.t_size.h;
702         }
703 }
704
705
706 /* term_deletechars():
707  *      Delete num characters
708  */
709 protected void
710 term_deletechars(EditLine *el, int num)
711 {
712         if (num <= 0)
713                 return;
714
715         if (!EL_CAN_DELETE) {
716 #ifdef DEBUG_EDIT
717                 (void) fprintf(el->el_errfile, "   ERROR: cannot delete   \n");
718 #endif /* DEBUG_EDIT */
719                 return;
720         }
721         if (num > el->el_term.t_size.h) {
722 #ifdef DEBUG_SCREEN
723                 (void) fprintf(el->el_errfile,
724                     "term_deletechars: num is riduculous: %d\r\n", num);
725 #endif /* DEBUG_SCREEN */
726                 return;
727         }
728         if (GoodStr(T_DC))      /* if I have multiple delete */
729                 if ((num > 1) || !GoodStr(T_dc)) {      /* if dc would be more
730                                                          * expen. */
731                         (void) tputs(tgoto(Str(T_DC), num, num),
732                             num, term__putc);
733                         return;
734                 }
735         if (GoodStr(T_dm))      /* if I have delete mode */
736                 (void) tputs(Str(T_dm), 1, term__putc);
737
738         if (GoodStr(T_dc))      /* else do one at a time */
739                 while (num--)
740                         (void) tputs(Str(T_dc), 1, term__putc);
741
742         if (GoodStr(T_ed))      /* if I have delete mode */
743                 (void) tputs(Str(T_ed), 1, term__putc);
744 }
745
746
747 /* term_insertwrite():
748  *      Puts terminal in insert character mode or inserts num
749  *      characters in the line
750  */
751 protected void
752 term_insertwrite(EditLine *el, char *cp, int num)
753 {
754         if (num <= 0)
755                 return;
756         if (!EL_CAN_INSERT) {
757 #ifdef DEBUG_EDIT
758                 (void) fprintf(el->el_errfile, "   ERROR: cannot insert   \n");
759 #endif /* DEBUG_EDIT */
760                 return;
761         }
762         if (num > el->el_term.t_size.h) {
763 #ifdef DEBUG_SCREEN
764                 (void) fprintf(el->el_errfile,
765                     "StartInsert: num is riduculous: %d\r\n", num);
766 #endif /* DEBUG_SCREEN */
767                 return;
768         }
769         if (GoodStr(T_IC))      /* if I have multiple insert */
770                 if ((num > 1) || !GoodStr(T_ic)) {
771                                 /* if ic would be more expensive */
772                         (void) tputs(tgoto(Str(T_IC), num, num),
773                             num, term__putc);
774                         term_overwrite(el, cp, num);
775                                 /* this updates el_cursor.h */
776                         return;
777                 }
778         if (GoodStr(T_im) && GoodStr(T_ei)) {   /* if I have insert mode */
779                 (void) tputs(Str(T_im), 1, term__putc);
780
781                 el->el_cursor.h += num;
782                 do
783                         term__putc(*cp++);
784                 while (--num);
785
786                 if (GoodStr(T_ip))      /* have to make num chars insert */
787                         (void) tputs(Str(T_ip), 1, term__putc);
788
789                 (void) tputs(Str(T_ei), 1, term__putc);
790                 return;
791         }
792         do {
793                 if (GoodStr(T_ic))      /* have to make num chars insert */
794                         (void) tputs(Str(T_ic), 1, term__putc);
795                                         /* insert a char */
796
797                 term__putc(*cp++);
798
799                 el->el_cursor.h++;
800
801                 if (GoodStr(T_ip))      /* have to make num chars insert */
802                         (void) tputs(Str(T_ip), 1, term__putc);
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                 (void) tputs(Str(T_ce), 1, term__putc);
819         else {
820                 for (i = 0; i < num; i++)
821                         term__putc(' ');
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                 (void) tputs(Str(T_cl), Val(T_li), term__putc);
837         else if (GoodStr(T_ho) && GoodStr(T_cd)) {
838                 (void) tputs(Str(T_ho), Val(T_li), term__putc); /* home */
839                 /* clear to bottom of screen */
840                 (void) tputs(Str(T_cd), Val(T_li), term__putc);
841         } else {
842                 term__putc('\r');
843                 term__putc('\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                 (void) tputs(Str(T_bl), 1, term__putc);
857         else
858                 term__putc('\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                 (void) tputs(Str(T_cd), Val(T_li), term__putc);
871         else if (GoodStr(T_ce))
872                 (void) tputs(Str(T_ce), Val(T_li), term__putc);
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
1235 /* term__putc():
1236  *      Add a character
1237  */
1238 protected int
1239 term__putc(int c)
1240 {
1241
1242         return (fputc(c, term_outfile));
1243 }
1244
1245
1246 /* term__flush():
1247  *      Flush output
1248  */
1249 protected void
1250 term__flush(void)
1251 {
1252
1253         (void) fflush(term_outfile);
1254 }
1255
1256 /* term_writec():
1257  *      Write the given character out, in a human readable form
1258  */
1259 protected void
1260 term_writec(EditLine *el, int c)
1261 {
1262         char buf[8];
1263         int cnt = key__decode_char(buf, sizeof(buf), 0, c);
1264         buf[cnt] = '\0';
1265         term_overwrite(el, buf, cnt);
1266         term__flush();
1267 }
1268
1269
1270 /* term_telltc():
1271  *      Print the current termcap characteristics
1272  */
1273 protected int
1274 /*ARGSUSED*/
1275 term_telltc(EditLine *el, int argc __unused, 
1276     const char **argv __unused)
1277 {
1278         const struct termcapstr *t;
1279         char **ts;
1280         char upbuf[EL_BUFSIZ];
1281
1282         (void) fprintf(el->el_outfile, "\n\tYour terminal has the\n");
1283         (void) fprintf(el->el_outfile, "\tfollowing characteristics:\n\n");
1284         (void) fprintf(el->el_outfile, "\tIt has %d columns and %d lines\n",
1285             Val(T_co), Val(T_li));
1286         (void) fprintf(el->el_outfile,
1287             "\tIt has %s meta key\n", EL_HAS_META ? "a" : "no");
1288         (void) fprintf(el->el_outfile,
1289             "\tIt can%suse tabs\n", EL_CAN_TAB ? " " : "not ");
1290         (void) fprintf(el->el_outfile, "\tIt %s automatic margins\n",
1291             EL_HAS_AUTO_MARGINS ? "has" : "does not have");
1292         if (EL_HAS_AUTO_MARGINS)
1293                 (void) fprintf(el->el_outfile, "\tIt %s magic margins\n",
1294                     EL_HAS_MAGIC_MARGINS ? "has" : "does not have");
1295
1296         for (t = tstr, ts = el->el_term.t_str; t->name != NULL; t++, ts++) {
1297                 const char *ub;
1298                 if (*ts && **ts) {
1299                     (void) key__decode_str(*ts, upbuf, sizeof(upbuf), "");
1300                     ub = upbuf;
1301                 } else {
1302                     ub = "(empty)";
1303                 }
1304                 (void) fprintf(el->el_outfile, "\t%25s (%s) == %s\n",
1305                     t->long_name, t->name, ub);
1306         }
1307         (void) fputc('\n', el->el_outfile);
1308         return (0);
1309 }
1310
1311
1312 /* term_settc():
1313  *      Change the current terminal characteristics
1314  */
1315 protected int
1316 /*ARGSUSED*/
1317 term_settc(EditLine *el, int argc __unused,
1318     const char **argv)
1319 {
1320         const struct termcapstr *ts;
1321         const struct termcapval *tv;
1322         const char *what, *how;
1323
1324         if (argv == NULL || argv[1] == NULL || argv[2] == NULL)
1325                 return -1;
1326
1327         what = argv[1];
1328         how = argv[2];
1329
1330         /*
1331          * Do the strings first
1332          */
1333         for (ts = tstr; ts->name != NULL; ts++)
1334                 if (strcmp(ts->name, what) == 0)
1335                         break;
1336
1337         if (ts->name != NULL) {
1338                 term_alloc(el, ts, how);
1339                 term_setflags(el);
1340                 return 0;
1341         }
1342         /*
1343          * Do the numeric ones second
1344          */
1345         for (tv = tval; tv->name != NULL; tv++)
1346                 if (strcmp(tv->name, what) == 0)
1347                         break;
1348
1349         if (tv->name != NULL)
1350                 return -1;
1351
1352         if (tv == &tval[T_pt] || tv == &tval[T_km] ||
1353             tv == &tval[T_am] || tv == &tval[T_xn]) {
1354                 if (strcmp(how, "yes") == 0)
1355                         el->el_term.t_val[tv - tval] = 1;
1356                 else if (strcmp(how, "no") == 0)
1357                         el->el_term.t_val[tv - tval] = 0;
1358                 else {
1359                         (void) fprintf(el->el_errfile,
1360                             "%s: Bad value `%s'.\n", argv[0], how);
1361                         return -1;
1362                 }
1363                 term_setflags(el);
1364                 if (term_change_size(el, Val(T_li), Val(T_co)) == -1)
1365                         return -1;
1366                 return 0;
1367         } else {
1368                 long i;
1369                 char *ep;
1370
1371                 i = strtol(how, &ep, 10);
1372                 if (*ep != '\0') {
1373                         (void) fprintf(el->el_errfile,
1374                             "%s: Bad value `%s'.\n", argv[0], how);
1375                         return -1;
1376                 }
1377                 el->el_term.t_val[tv - tval] = (int) i;
1378                 el->el_term.t_size.v = Val(T_co);
1379                 el->el_term.t_size.h = Val(T_li);
1380                 if (tv == &tval[T_co] || tv == &tval[T_li])
1381                         if (term_change_size(el, Val(T_li), Val(T_co))
1382                             == -1)
1383                                 return -1;
1384                 return 0;
1385         }
1386 }
1387
1388
1389 /* term_gettc():
1390  *      Get the current terminal characteristics
1391  */
1392 protected int
1393 /*ARGSUSED*/
1394 term_gettc(EditLine *el, int argc __unused, char **argv)
1395 {
1396         const struct termcapstr *ts;
1397         const struct termcapval *tv;
1398         char *what;
1399         void *how;
1400
1401         if (argv == NULL || argv[1] == NULL || argv[2] == NULL)
1402                 return (-1);
1403
1404         what = argv[1];
1405         how = argv[2];
1406
1407         /*
1408          * Do the strings first
1409          */
1410         for (ts = tstr; ts->name != NULL; ts++)
1411                 if (strcmp(ts->name, what) == 0)
1412                         break;
1413
1414         if (ts->name != NULL) {
1415                 *(char **)how = el->el_term.t_str[ts - tstr];
1416                 return 0;
1417         }
1418         /*
1419          * Do the numeric ones second
1420          */
1421         for (tv = tval; tv->name != NULL; tv++)
1422                 if (strcmp(tv->name, what) == 0)
1423                         break;
1424
1425         if (tv->name == NULL)
1426                 return -1;
1427
1428         if (tv == &tval[T_pt] || tv == &tval[T_km] ||
1429             tv == &tval[T_am] || tv == &tval[T_xn]) {
1430                 static char yes[] = "yes";
1431                 static char no[] = "no";
1432                 if (el->el_term.t_val[tv - tval])
1433                         *(char **)how = yes;
1434                 else
1435                         *(char **)how = no;
1436                 return 0;
1437         } else {
1438                 *(int *)how = el->el_term.t_val[tv - tval];
1439                 return 0;
1440         }
1441 }
1442
1443 /* term_echotc():
1444  *      Print the termcap string out with variable substitution
1445  */
1446 protected int
1447 /*ARGSUSED*/
1448 term_echotc(EditLine *el, int argc __unused,
1449     const char **argv)
1450 {
1451         char *cap, *scap, *ep;
1452         int arg_need, arg_cols, arg_rows;
1453         int verbose = 0, silent = 0;
1454         char *area;
1455         static const char fmts[] = "%s\n", fmtd[] = "%d\n";
1456         const struct termcapstr *t;
1457         char buf[TC_BUFSIZE];
1458         long i;
1459
1460         area = buf;
1461
1462         if (argv == NULL || argv[1] == NULL)
1463                 return (-1);
1464         argv++;
1465
1466         if (argv[0][0] == '-') {
1467                 switch (argv[0][1]) {
1468                 case 'v':
1469                         verbose = 1;
1470                         break;
1471                 case 's':
1472                         silent = 1;
1473                         break;
1474                 default:
1475                         /* stderror(ERR_NAME | ERR_TCUSAGE); */
1476                         break;
1477                 }
1478                 argv++;
1479         }
1480         if (!*argv || *argv[0] == '\0')
1481                 return (0);
1482         if (strcmp(*argv, "tabs") == 0) {
1483                 (void) fprintf(el->el_outfile, fmts, EL_CAN_TAB ? "yes" : "no");
1484                 return (0);
1485         } else if (strcmp(*argv, "meta") == 0) {
1486                 (void) fprintf(el->el_outfile, fmts, Val(T_km) ? "yes" : "no");
1487                 return (0);
1488         } else if (strcmp(*argv, "xn") == 0) {
1489                 (void) fprintf(el->el_outfile, fmts, EL_HAS_MAGIC_MARGINS ?
1490                     "yes" : "no");
1491                 return (0);
1492         } else if (strcmp(*argv, "am") == 0) {
1493                 (void) fprintf(el->el_outfile, fmts, EL_HAS_AUTO_MARGINS ?
1494                     "yes" : "no");
1495                 return (0);
1496         } else if (strcmp(*argv, "baud") == 0) {
1497 #ifdef notdef
1498                 int i;
1499
1500                 for (i = 0; baud_rate[i].b_name != NULL; i++)
1501                         if (el->el_tty.t_speed == baud_rate[i].b_rate) {
1502                                 (void) fprintf(el->el_outfile, fmts,
1503                                     baud_rate[i].b_name);
1504                                 return (0);
1505                         }
1506                 (void) fprintf(el->el_outfile, fmtd, 0);
1507 #else
1508                 (void) fprintf(el->el_outfile, fmtd, (int)el->el_tty.t_speed);
1509 #endif
1510                 return (0);
1511         } else if (strcmp(*argv, "rows") == 0 || strcmp(*argv, "lines") == 0) {
1512                 (void) fprintf(el->el_outfile, fmtd, Val(T_li));
1513                 return (0);
1514         } else if (strcmp(*argv, "cols") == 0) {
1515                 (void) fprintf(el->el_outfile, fmtd, Val(T_co));
1516                 return (0);
1517         }
1518         /*
1519          * Try to use our local definition first
1520          */
1521         scap = NULL;
1522         for (t = tstr; t->name != NULL; t++)
1523                 if (strcmp(t->name, *argv) == 0) {
1524                         scap = el->el_term.t_str[t - tstr];
1525                         break;
1526                 }
1527         if (t->name == NULL) {
1528                 /* XXX: some systems' tgetstr needs non const */
1529                 scap = tgetstr(strchr(*argv, **argv), &area);
1530         }
1531         if (!scap || scap[0] == '\0') {
1532                 if (!silent)
1533                         (void) fprintf(el->el_errfile,
1534                             "echotc: Termcap parameter `%s' not found.\n",
1535                             *argv);
1536                 return (-1);
1537         }
1538         /*
1539          * Count home many values we need for this capability.
1540          */
1541         for (cap = scap, arg_need = 0; *cap; cap++)
1542                 if (*cap == '%')
1543                         switch (*++cap) {
1544                         case 'd':
1545                         case '2':
1546                         case '3':
1547                         case '.':
1548                         case '+':
1549                                 arg_need++;
1550                                 break;
1551                         case '%':
1552                         case '>':
1553                         case 'i':
1554                         case 'r':
1555                         case 'n':
1556                         case 'B':
1557                         case 'D':
1558                                 break;
1559                         default:
1560                                 /*
1561                                  * hpux has lot's of them...
1562                                  */
1563                                 if (verbose)
1564                                         (void) fprintf(el->el_errfile,
1565                                 "echotc: Warning: unknown termcap %% `%c'.\n",
1566                                             *cap);
1567                                 /* This is bad, but I won't complain */
1568                                 break;
1569                         }
1570
1571         switch (arg_need) {
1572         case 0:
1573                 argv++;
1574                 if (*argv && *argv[0]) {
1575                         if (!silent)
1576                                 (void) fprintf(el->el_errfile,
1577                                     "echotc: Warning: Extra argument `%s'.\n",
1578                                     *argv);
1579                         return (-1);
1580                 }
1581                 (void) tputs(scap, 1, term__putc);
1582                 break;
1583         case 1:
1584                 argv++;
1585                 if (!*argv || *argv[0] == '\0') {
1586                         if (!silent)
1587                                 (void) fprintf(el->el_errfile,
1588                                     "echotc: Warning: Missing argument.\n");
1589                         return (-1);
1590                 }
1591                 arg_cols = 0;
1592                 i = strtol(*argv, &ep, 10);
1593                 if (*ep != '\0' || i < 0) {
1594                         if (!silent)
1595                                 (void) fprintf(el->el_errfile,
1596                                     "echotc: Bad value `%s' for rows.\n",
1597                                     *argv);
1598                         return (-1);
1599                 }
1600                 arg_rows = (int) i;
1601                 argv++;
1602                 if (*argv && *argv[0]) {
1603                         if (!silent)
1604                                 (void) fprintf(el->el_errfile,
1605                                     "echotc: Warning: Extra argument `%s'.\n",
1606                                     *argv);
1607                         return (-1);
1608                 }
1609                 (void) tputs(tgoto(scap, arg_cols, arg_rows), 1, term__putc);
1610                 break;
1611         default:
1612                 /* This is wrong, but I will ignore it... */
1613                 if (verbose)
1614                         (void) fprintf(el->el_errfile,
1615                          "echotc: Warning: Too many required arguments (%d).\n",
1616                             arg_need);
1617                 /* FALLTHROUGH */
1618         case 2:
1619                 argv++;
1620                 if (!*argv || *argv[0] == '\0') {
1621                         if (!silent)
1622                                 (void) fprintf(el->el_errfile,
1623                                     "echotc: Warning: Missing argument.\n");
1624                         return (-1);
1625                 }
1626                 i = strtol(*argv, &ep, 10);
1627                 if (*ep != '\0' || i < 0) {
1628                         if (!silent)
1629                                 (void) fprintf(el->el_errfile,
1630                                     "echotc: Bad value `%s' for cols.\n",
1631                                     *argv);
1632                         return (-1);
1633                 }
1634                 arg_cols = (int) i;
1635                 argv++;
1636                 if (!*argv || *argv[0] == '\0') {
1637                         if (!silent)
1638                                 (void) fprintf(el->el_errfile,
1639                                     "echotc: Warning: Missing argument.\n");
1640                         return (-1);
1641                 }
1642                 i = strtol(*argv, &ep, 10);
1643                 if (*ep != '\0' || i < 0) {
1644                         if (!silent)
1645                                 (void) fprintf(el->el_errfile,
1646                                     "echotc: Bad value `%s' for rows.\n",
1647                                     *argv);
1648                         return (-1);
1649                 }
1650                 arg_rows = (int) i;
1651                 if (*ep != '\0') {
1652                         if (!silent)
1653                                 (void) fprintf(el->el_errfile,
1654                                     "echotc: Bad value `%s'.\n", *argv);
1655                         return (-1);
1656                 }
1657                 argv++;
1658                 if (*argv && *argv[0]) {
1659                         if (!silent)
1660                                 (void) fprintf(el->el_errfile,
1661                                     "echotc: Warning: Extra argument `%s'.\n",
1662                                     *argv);
1663                         return (-1);
1664                 }
1665                 (void) tputs(tgoto(scap, arg_cols, arg_rows), arg_rows,
1666                     term__putc);
1667                 break;
1668         }
1669         return (0);
1670 }