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