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