]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/conio.c
Fix conio.h problem on Solaris
[bacula/bacula] / bacula / src / console / conio.c
1 /*        
2       Generalized console input/output handler                     
3       A maintanable replacement for readline()
4
5          Kern Sibbald, December MMIII
6
7       This code is in part derived from code that I wrote in
8       1981, so some of it is a bit old and could use a cleanup.
9          
10 */
11 /*
12    Copyright (C) 1981-2004 Kern Sibbald and John Walker
13                  Yes, that is 1981 no error.
14
15    This program is free software; you can redistribute it and/or
16    modify it under the terms of the GNU General Public License as
17    published by the Free Software Foundation; either version 2 of
18    the License, or (at your option) any later version.
19
20    This program is distributed in the hope that it will be useful,
21    but WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23    General Public License for more details.
24
25    You should have received a copy of the GNU General Public
26    License along with this program; if not, write to the Free
27    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
28    MA 02111-1307, USA.
29
30  */
31
32
33 #ifdef  TEST_PROGRAM
34 #include <stdio.h>
35 #include <unistd.h>
36 #include <stdlib.h>
37 #include <signal.h>
38 #include <string.h>
39 #include <ctype.h> 
40 #else
41
42 /* We are in Bacula */
43 #include "bacula.h"
44
45 #endif
46
47 #include <termios.h>
48 #ifdef HAVE_SUN_OS
49 extern "C" int tgetent(void *, const char *);
50 extern "C" int tgetnum(const char *);
51 extern "C" char *tgetstr (const char*, char**);
52 extern "C" char *tgoto (const char *, int, int);
53 #else
54 #include <termcap.h>
55 #endif
56 #include "func.h"
57
58
59 /* From termios library */
60 extern char *BC;
61 extern char *UP;
62
63 /* Forward referenced functions */
64 static void sigintcatcher(int);
65 static void add_smap(char *str, int func);
66
67
68 /* Global variables */
69
70 static char *t_up = "\n";                    /* scroll up character */
71 static char *t_honk = "\007";                /* sound beep */
72 static char *t_il;                           /* insert line */
73 static char *t_dl;                           /* delete line */
74 static char *t_cs;                           /* clear screen */
75 static char *t_cl;                           /* clear line */
76 static int t_width = 79;                     /* terminal width */
77 static int t_height = 24;                    /* terminal height */
78 static int linsdel_ok = 0;              /* set if term has line insert & delete fncs */
79
80 static char *t_cm;                           /* cursor positioning */
81 static char *t_ti;                           /* init sequence */
82 static char *t_te;                           /* end sequence */
83 static char *t_do;                           /* down one line */
84 static char *t_sf;                           /* scroll screen one line up */
85
86 /* Keypad and Function Keys */
87 static char *kl;                             /* left key */
88 static char *kr;                             /* right */
89 static char *ku;                             /* up */
90 static char *kd;                             /* down */
91 static char *kh;                             /* home */
92 static char *kb;                             /* backspace */
93 static char *kD;                             /* delete key */
94 static char *kI;                             /* insert */
95 static char *kN;                             /* next page */
96 static char *kP;                             /* previous page */
97 static char *kH;                             /* home */
98 static char *kE;                             /* end */
99
100 #ifndef EOS
101 #define EOS  '\0'                     /* end of string terminator */
102 #endif
103
104 #define TRUE  1
105 #define FALSE 0
106 /*
107  * Stab entry. Input chars (str), the length, and the desired
108  *  func code.
109  */
110 typedef struct s_stab {
111    struct s_stab *next;
112    int len;
113    int func;
114    char *str;
115 } stab_t;
116
117 #define MAX_STAB 30
118
119 static stab_t **stab = NULL;                 /* array of stabs by length */
120 static int num_stab;                         /* size of stab array */
121
122 static bool old_term_params_set = false;
123 static struct termios old_term_params;
124
125 /* Maintain lines in a doubly linked circular pool of lines. Each line is
126    preceded by a header defined by the lstr structure */
127
128
129 struct lstr {                         /* line pool structure */
130    struct lstr *prevl;                /* link to previous line */
131    struct lstr *nextl;                /* link to next line */
132    long len;                          /* length of line+header */
133    char used;                         /* set if line valid */
134    char line;                         /* line is actually varying length */
135 };
136
137 #ifdef unix
138 #define POOLEN 128000                 /* bytes in line pool */
139 #else
140 #define POOLEN 500                    /* bytes in line pool */
141 #endif
142 char pool[POOLEN];                    /* line pool */
143 #define PHDRL ((int)sizeof(struct lstr))  /* length of line header */
144
145 static struct lstr *lptr;             /* current line pointer */
146 static struct lstr *slptr;            /* store line pointer */
147 static int cl, cp;
148 static char *getnext(), *getprev();
149 static int first = 1;
150 static int mode_insert = 0;
151 static int mode_wspace = 1;           /* words separated by spaces */
152
153
154 static short char_map[600]= {
155    0,                                  F_SOL,    /* ^a Line start */
156    F_PRVWRD, /* ^b Previous word */    F_BREAK,  /* ^C break */
157    F_DELCHR, /* ^D Delete character */ F_EOL,    /* ^e End of line */
158    F_CSRRGT, /* ^f Right */            F_TABBAK, /* ^G Back tab */
159    F_CSRLFT, /* ^H Left */             F_TAB,    /* ^I Tab */
160    F_CSRDWN, /* ^J Down */             F_DELEOL, /* ^K kill to eol */
161    F_CLRSCRN,/* ^L clear screen */     F_RETURN, /* ^M Carriage return */
162    F_RETURN, /* ^N enter line  */      F_CONCAT, /* ^O Concatenate lines */
163    F_CSRUP,  /* ^P cursor up */        F_TINS,   /* ^Q Insert character mode */
164    F_PAGUP,  /* ^R Page up */          F_CENTER, /* ^S Center text */
165    F_PAGDWN, /* ^T Page down */        F_DELSOL, /* ^U delete to start of line */
166    F_DELWRD, /* ^V Delete word */      F_PRVWRD, /* ^W Previous word */
167    F_NXTMCH, /* ^X Next match */       F_DELEOL, /* ^Y Delete to end of line */
168    F_BACKGND,/* ^Z Background */       0x1B,     /* ^[=ESC escape */
169    F_TENTRY, /* ^\ Entry mode */       F_PASTECB,/* ^]=paste clipboard */
170    F_HOME,   /* ^^ Home */             F_ERSLIN, /* ^_ Erase line */
171
172    ' ','!','"','#','$','%','&','\047',
173    '(',')','*','+','\054','-','.','/',
174    '0','1','2','3','4','5','6','7',
175    '8','9',':',';','<','=','>','?',
176    '@','A','B','C','D','E','F','G',
177    'H','I','J','K','L','M','N','O',
178    'P','Q','R','S','T','U','V','W',
179    'X','Y','Z','[','\\',']','^','_',
180    '\140','a','b','c','d','e','f','g',
181    'h','i','j','k','l','m','n','o',
182    'p','q','r','s','t','u','v','w',
183    'x','y','z','{','|','}','\176',F_ERSCHR  /* erase character */
184
185   };
186
187
188 /* Local variables */
189
190 #define CR '\r'                       /* carriage return */
191
192
193 /* Function Prototypes */
194
195 static int input_char(void);
196 static int t_gnc(void);
197 static void insert_space(char *curline, int line_len);
198 static void forward(int i, char *str, int str_len);
199 static void backup(int i);
200 static void delchr(int cnt, char *curline, int line_len);
201 static int iswordc(char c);
202 static int  next_word(char *ldb_buf);
203 static int  prev_word(char *ldb_buf);
204 static void prtcur(char *str);
205 static void poolinit(void);
206 static char * getnext(void);
207 static char * getprev(void);
208 static void putline(char *newl, int newlen);
209 static void t_honk_horn(void);
210 static void t_insert_line(void);
211 static void t_delete_line(void);
212 static void t_clrline(int pos, int width);
213 void t_sendl(char *msg, int len);
214 void t_send(char *msg);
215 void t_char(char c);
216 static void asclrs();
217 static void ascurs(int y, int x);
218
219 static void rawmode(FILE *input);
220 static void normode(void);
221 static int t_getch();
222 static void asclrl(int pos, int width);
223 static void asinsl();
224 static void asdell();
225
226 int input_line(char *string, int length);
227 void con_term();
228 void trapctlc();
229 int usrbrk();
230 void clrbrk();
231     
232 void con_init(FILE *input)
233 {
234    atexit(con_term); 
235    rawmode(input);
236    trapctlc();
237 }
238
239 /*
240  * Zed control keys
241  */
242 void con_set_zed_keys(void)
243 {
244    char_map[1]  = F_NXTWRD; /* ^A Next Word */
245    char_map[2]  = F_SPLIT;  /* ^B Split line */
246    char_map[3]  = F_EOI;    /* ^C Quit */
247    char_map[4]  = F_DELCHR; /* ^D Delete character */
248    char_map[5]  = F_EOF;    /* ^E End of file */
249    char_map[6]  = F_INSCHR; /* ^F Insert character */
250    char_map[7]  = F_TABBAK; /* ^G Back tab */
251    char_map[8]  = F_CSRLFT; /* ^H Left */
252    char_map[9]  = F_TAB;    /* ^I Tab */
253    char_map[10] = F_CSRDWN; /* ^J Down */
254    char_map[11] = F_CSRUP;  /* ^K Up */
255    char_map[12] = F_CSRRGT; /* ^L Right */
256    char_map[13] = F_RETURN; /* ^M Carriage return */
257    char_map[14] = F_EOL;    /* ^N End of line */
258    char_map[15] = F_CONCAT; /* ^O Concatenate lines */
259    char_map[16] = F_MARK;   /* ^P Set marker */
260    char_map[17] = F_TINS;   /* ^Q Insert character mode */
261    char_map[18] = F_PAGUP;  /* ^R Page up */
262    char_map[19] = F_CENTER; /* ^S Center text */
263    char_map[20] = F_PAGDWN; /* ^T Page down */
264    char_map[21] = F_SOL;    /* ^U Line start */
265    char_map[22] = F_DELWRD; /* ^V Delete word */
266    char_map[23] = F_PRVWRD; /* ^W Previous word */
267    char_map[24] = F_NXTMCH; /* ^X Next match */
268    char_map[25] = F_DELEOL; /* ^Y Delete to end of line */
269    char_map[26] = F_DELLIN; /* ^Z Delete line */
270    /* 27 = ESC */
271    char_map[28] = F_TENTRY; /* ^\ Entry mode */
272    char_map[29] = F_PASTECB;/* ^]=paste clipboard */
273    char_map[30] = F_HOME;   /* ^^ Home */
274    char_map[31] = F_ERSLIN; /* ^_ Erase line */
275
276 }
277
278 void con_term()
279 {
280    normode();
281 }
282
283 #ifdef TEST_PROGRAM
284 /*
285  * Guarantee that the string is properly terminated */
286 char *bstrncpy(char *dest, const char *src, int maxlen)
287 {
288    strncpy(dest, src, maxlen-1);
289    dest[maxlen-1] = 0;
290    return dest;
291 }
292 #endif
293
294
295 /*
296  * New style string mapping to function code
297  */
298 static int do_smap(int c)
299 {
300     char str[MAX_STAB];
301     int len = 0; 
302     stab_t *tstab;
303     int i, found;
304
305     len = 1;
306     str[0] = c;
307     str[1] = 0;
308
309     if (c != 27) {
310        c = char_map[c];
311     }
312     if (c <= 0) {
313        return c;
314     }
315     for ( ;; ) {
316        found = 0;
317        for (i=len-1; i<MAX_STAB; i++) {
318           for (tstab=stab[i]; tstab; tstab=tstab->next) {
319              if (strncmp(str, tstab->str, len) == 0) {
320                 if (len == tstab->len) {
321                    return tstab->func;
322                 }
323                 found = 1;
324                 break;                /* found possibility continue searching */
325              }
326           }
327        }
328        if (!found) {
329           return len==1?c:0;
330        }
331        /* found partial match, so get next character and retry */
332        str[len++] = t_gnc();
333        str[len] = 0;
334     }
335 }
336
337 #ifdef DEBUG_x
338 static void dump_stab()
339 {
340     int i, j, c;
341     stab_t *tstab;
342     char buf[100];
343
344     for (i=0; i<MAX_STAB; i++) {
345        for (tstab=stab[i]; tstab; tstab=tstab->next) {
346           for (j=0; j<tstab->len; j++) {
347              c = tstab->str[j];
348              if (c < 0x20 || c > 0x7F) {
349                 sprintf(buf, " 0x%x ", c);
350                 t_send(buf);
351              } else {
352                 buf[0] = c;
353                 buf[1] = 0;
354                 t_sendl(buf, 1);
355              }
356           }
357           sprintf(buf, " func=%d len=%d\n\r", tstab->func, tstab->len);
358           t_send(buf);
359        }
360     }
361 }
362 #endif
363
364 /*
365  * New routine. Add string to string->func mapping table.
366  */
367 static void add_smap(char *str, int func)
368 {
369    stab_t *tstab;
370    int len;
371   
372    if (!str) {
373       return;
374    }
375    len = strlen(str);
376    if (len == 0) {
377 /*    errmsg("String for func %d is zero length\n", func); */
378       return;
379    }
380    tstab = (stab_t *)malloc(sizeof(stab_t));
381    memset(tstab, 0, sizeof(stab_t));
382    tstab->len = len;
383    tstab->str = (char *)malloc(tstab->len + 1);
384    bstrncpy(tstab->str, str, tstab->len + 1);
385    tstab->func = func;
386    if (tstab->len > num_stab) {
387       printf("stab string too long %d. Max is %d\n", tstab->len, num_stab);
388       exit(1);
389    }
390    tstab->next = stab[tstab->len-1];
391    stab[tstab->len-1] = tstab;
392 /* printf("Add_smap tstab=%x len=%d func=%d tstab->next=%x\n\r", tstab, len, 
393           func, tstab->next); */
394
395 }
396
397
398 /* Get the next character from the terminal - performs table lookup on
399    the character to do the desired translation */
400 static int
401 input_char()
402 {
403     int c;
404
405     if ((c=t_gnc()) <= 599) {         /* IBM generates codes up to 260 */
406           c = do_smap(c);
407     } else if (c > 1000) {            /* stuffed function */
408        c -= 1000;                     /* convert back to function code */
409     }
410     if (c <= 0) {
411         t_honk_horn();
412     }
413     /* if we got a screen size escape sequence, read height, width */
414     if (c == F_SCRSIZ) {
415        int y, x;
416        y = t_gnc() - 0x20;        /* y */
417        x = t_gnc() - 0x20;        /* x */
418        c = input_char();
419     }
420     return c;
421 }
422
423
424 /* Get a complete input line */
425
426 int
427 input_line(char *string, int length)
428 {
429    char curline[2000];                /* edit buffer */
430    int noline;
431    int c;
432
433     if (first) {
434        poolinit();                   /* build line pool */
435        first = 0;
436     }
437     noline = 1;                       /* no line fetched yet */
438     for (cl=cp=0; cl<length && cl<(int)sizeof(curline); ) {
439        if (usrbrk()) {
440           clrbrk();
441           break;
442        }
443        switch (c=(int)input_char()) {
444        case F_RETURN:                /* CR */
445            t_sendl("\r\n", 2);       /* yes, print it and */
446            goto done;                /* get out */
447        case F_CLRSCRN:               /* clear screen */
448           asclrs();
449           t_sendl(curline, cl);
450           ascurs(0, cp);
451           break;
452        case F_CSRUP:
453            if (noline) {             /* no line fetched yet */
454                getnext();            /* getnext so getprev gets current */
455                noline = 0;           /* we now have line */
456            }
457            bstrncpy(curline, getprev(), sizeof(curline));
458            prtcur(curline);
459            break;
460        case F_CSRDWN:
461            noline = 0;               /* mark line fetched */
462            bstrncpy(curline, getnext(), sizeof(curline));
463            prtcur(curline);
464            break;
465        case F_INSCHR:
466            insert_space(curline, sizeof(curline));
467            break;
468        case F_DELCHR:
469            delchr(1, curline, sizeof(curline));       /* delete one character */
470            break;
471        case F_CSRLFT:                /* Backspace */
472            backup(1);
473            break;
474        case F_CSRRGT:
475            forward(1,curline, sizeof(curline));
476            break;
477        case F_ERSCHR:                /* Rubout */
478            backup(1);
479            delchr(1, curline, sizeof(curline));
480            break;
481        case F_DELEOL:
482            t_clrline(0, t_width);
483            if (cl > cp)
484                cl = cp;
485            break;
486        case F_NXTWRD:
487            forward(next_word(curline),curline, sizeof(curline));
488            break;
489        case F_PRVWRD:
490            backup(prev_word(curline));
491            break;
492        case F_DELWRD:
493            delchr(next_word(curline), curline, sizeof(curline)); /* delete word */
494            break;
495        case F_NXTMCH:                /* Ctl-X */
496            if (cl==0) {
497                *string = EOS;        /* terminate string */
498                return(c);            /* give it to him */
499            }
500            /* Note fall through */
501        case F_DELLIN:
502        case F_ERSLIN:
503            backup(cp);               /* backup to beginning of line */
504            t_clrline(0,t_width);     /* erase line */
505            cp = 0;
506            cl = 0;                   /* reset cursor counter */
507            break;
508        case F_SOL:
509            backup(cp);
510            break;
511        case F_EOL:
512            while (cp < cl) {
513                forward(1,curline, sizeof(curline));
514            }
515            while (cp > cl) {
516                backup(1);
517            }
518            break;
519        case F_TINS:                  /* toggle insert mode */
520            mode_insert = !mode_insert;  /* flip bit */
521            break;
522        default:
523            if (c > 255) {            /* function key hit */
524                if (cl==0) {          /* if first character then */
525                   *string = EOS;     /* terminate string */
526                   return c;          /* return it */
527                }
528                t_honk_horn();        /* complain */
529            } else {
530                if (mode_insert) {
531                   insert_space(curline, sizeof(curline));
532                }
533                curline[cp++] = c;    /* store character in line being built */
534                t_char((char)c);      /* echo character to terminal */
535                if (cp > cl) {
536                   cl = cp;           /* keep current length */
537                }
538            }
539            break;
540        }                             /* end switch */
541     }
542 /* If we fall through here rather than goto done, the line is too long
543    simply return what we have now. */
544 done:
545    curline[cl++] = EOS;              /* terminate */
546    bstrncpy(string,curline,length);           /* return line to caller */
547    /* Note, put line zaps curline */
548    putline(curline,cl);              /* save line for posterity */
549    return 0;                         /* give it to him/her */
550 }
551
552 /* Insert a space at the current cursor position */
553 static void
554 insert_space(char *curline, int curline_len)
555 {
556    int i;
557
558    if (cp > cl || cl+1 > curline_len) return;
559    /* Note! source and destination overlap */
560    memmove(&curline[cp+1],&curline[cp],i=cl-cp);
561    cl++;
562    i++;
563    curline[cp] = ' ';
564    forward(i,curline, curline_len);
565    backup(i);
566 }
567
568
569 /* Move cursor forward keeping characters under it */
570 static void
571 forward(int i, char *str, int str_len)
572 {
573    while (i--) {
574       if (cp > str_len) {
575          return;
576       }
577       if (cp>=cl) {
578           t_char(' ');
579           str[cp+1] = ' ';
580       } else {
581           t_char(str[cp]);
582       }
583       cp++;
584    }
585 }
586
587 /* Backup cursor keeping characters under it */
588 static void
589 backup(int i)
590 {
591     for ( ; i && cp; i--,cp--) {
592        t_char('\010');
593     }
594 }
595
596 /* Delete the character under the cursor */
597 static void
598 delchr(int cnt, char *curline, int line_len) 
599 {
600    register int i;
601
602    if (cp > cl)
603       return;
604    if ((i=cl-cp-cnt+1) > 0) {
605       memcpy(&curline[cp], &curline[cp+cnt],i);
606    }
607    curline[cl -= cnt] = EOS;
608    t_clrline(0,t_width);
609    if (cl > cp) {
610       forward(i=cl-cp,curline, line_len);
611       backup(i);
612    }
613 }
614
615 /* Determine if character is part of a word */
616 static int
617 iswordc(char c)
618 {
619    if (mode_wspace)
620       return !isspace(c);
621    if (c >= '0' && c <= '9')
622       return TRUE;
623    if (c == '$' || c == '%')
624       return TRUE;
625    return isalpha(c);
626 }
627
628 /* Return number of characters to get to next word */
629 static int
630 next_word(char *ldb_buf)
631 {
632    int ncp;
633
634    if (cp > cl)
635       return 0;
636    ncp = cp;
637    for ( ; ncp<cl && iswordc(*(ldb_buf+ncp)); ncp++) ;
638    for ( ; ncp<cl && !iswordc(*(ldb_buf+ncp)); ncp++) ;
639    return ncp-cp;
640 }
641
642 /* Return number of characters to get to previous word */
643 static int
644 prev_word(char *ldb_buf)
645 {
646     int ncp, i;
647
648     if (cp == 0)                      /* if at begin of line stop now */
649         return 0;
650     if (cp > cl)                      /* if past eol start at eol */
651         ncp=cl+1;
652     else
653         ncp = cp;
654     /* backup to end of previous word - i.e. skip special chars */
655     for (i=ncp-1; i && !iswordc(*(ldb_buf+i)); i--) ;
656     if (i == 0) {                     /* at beginning of line? */
657         return cp;                    /* backup to beginning */
658     }
659     /* now move back through word to beginning of word */
660     for ( ; i && iswordc(*(ldb_buf+i)); i--) ;
661     ncp = i+1;                        /* position to first char of word */
662     if (i==0 && iswordc(*ldb_buf))    /* check for beginning of line */
663         ncp = 0;
664     return cp-ncp;                    /* return count */
665 }
666
667 /* Display new current line */
668 static void
669 prtcur(char *str)
670 {
671     backup(cp);
672     t_clrline(0,t_width);
673     cp = cl = strlen(str);
674     t_sendl(str,cl);
675 }
676
677
678 /* Initialize line pool. Split pool into two pieces. */
679 static void
680 poolinit()
681 {
682    slptr = lptr = (struct lstr *)pool;
683    lptr->nextl = lptr;
684    lptr->prevl = lptr;
685    lptr->used = 1;
686    lptr->line = 0;
687    lptr->len = POOLEN;
688 }
689
690
691 /* Return pointer to next line in the pool and advance current line pointer */
692 static char *
693 getnext()
694 {
695    do {                              /* find next used line */
696       lptr = lptr->nextl;
697    } while (!lptr->used);
698    return (char *)&lptr->line;
699 }
700
701 /* Return pointer to previous line in the pool */
702 static char *
703 getprev()
704 {
705    do {                              /* find previous used line */
706       lptr = lptr->prevl;
707    } while (!lptr->used);
708    return (char *)&lptr->line;
709 }
710
711 static void
712 putline(char *newl, int newlen)
713 {
714    struct lstr *nptr;                /* points to next line */
715    char *p;
716
717    lptr = slptr;                     /* get ptr to last line stored */
718    lptr = lptr->nextl;               /* advance pointer */
719    if ((char *)lptr-pool+newlen+PHDRL > POOLEN) { /* not enough room */
720        lptr->used = 0;               /* delete line */
721        lptr = (struct lstr *)pool;   /* start at beginning of buffer */
722    }
723    while (lptr->len < newlen+PHDRL) { /* concatenate buffers */
724        nptr = lptr->nextl;           /* point to next line */
725        lptr->nextl = nptr->nextl;    /* unlink it from list */
726        nptr->nextl->prevl = lptr;
727        lptr->len += nptr->len;
728    }
729    if (lptr->len > newlen + 2 * PHDRL) { /* split buffer */
730        nptr = (struct lstr *)((char *)lptr + newlen + PHDRL);
731        /* Appropriate byte alignment - normally 2 byte, but on
732           sparc we need 4 byte alignment, so we always do 4 */
733        if (((long unsigned)nptr & 3) != 0) { /* test four byte alignment */
734            p = (char *)nptr;
735            nptr = (struct lstr *)((((long unsigned) p) & ~3) + 4);
736        }
737        nptr->len = lptr->len - ((char *)nptr - (char *)lptr);
738        lptr->len -= nptr->len;
739        nptr->nextl = lptr->nextl;    /* link in new buffer */
740        lptr->nextl->prevl = nptr;
741        lptr->nextl = nptr;
742        nptr->prevl = lptr;
743        nptr->used = 0;
744    }
745    memcpy(&lptr->line,newl,newlen);
746    lptr->used = 1;                   /* mark line used */
747    slptr = lptr;                     /* save as stored line */
748 }
749
750 #ifdef  DEBUGOUT
751 static void
752 dump(struct lstr *ptr, char *msg)
753 {
754     printf("%s buf=%x nextl=%x prevl=%x len=%d used=%d\n",
755         msg,ptr,ptr->nextl,ptr->prevl,ptr->len,ptr->used);
756     if (ptr->used)
757         printf("line=%s\n",&ptr->line);
758 }
759 #endif  /* DEBUGOUT */
760
761
762 /* Honk horn on terminal */
763 static void
764 t_honk_horn()
765 {
766    t_send(t_honk);
767 }
768
769 /* Insert line on terminal */
770 static void
771 t_insert_line()
772 {
773    asinsl();
774 }
775
776 /* Delete line from terminal */
777 static void
778 t_delete_line()
779 {
780    asdell();
781 }
782
783 /* clear line from pos to width */
784 static void
785 t_clrline(int pos, int width)
786 {
787     asclrl(pos, width);           /* clear to end of line */
788 }
789
790 /* Helper function to add string preceded by 
791  *  ESC to smap table */
792 static void add_esc_smap(char *str, int func)
793 {
794    char buf[1000];
795    buf[0] = 0x1B;                     /* esc */
796    bstrncpy(buf+1, str, sizeof(buf)-1);
797    add_smap(buf, func);
798 }
799
800 /* Set raw mode on terminal file.  Basically, get the terminal into a
801    mode in which all characters can be read as they are entered.  CBREAK
802    mode is not sufficient.
803  */
804 static void rawmode(FILE *input)
805 {
806    struct termios t;
807    static char term_buf[2048];
808    static char *term_buffer = term_buf;
809    char *termtype = (char *)getenv("TERM");
810
811    /* Make sure we are dealing with a terminal */
812    if (!isatty(fileno(input))) {
813       return;
814    }
815    if (tcgetattr(0, &old_term_params) != 0) {
816       printf("conio: Cannot tcgetattr()\n");
817       exit(1);
818    }
819    old_term_params_set = true;
820    t = old_term_params;                         
821    t.c_cc[VMIN] = 1; /* satisfy read after 1 char */
822    t.c_cc[VTIME] = 0;
823    t.c_iflag &= ~(BRKINT | IGNPAR | PARMRK | INPCK | 
824                   ISTRIP | ICRNL | IXON | IXOFF | INLCR | IGNCR);     
825    t.c_iflag |= IGNBRK;
826    t.c_oflag |= ONLCR;
827    t.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ICANON |
828                   NOFLSH | TOSTOP);
829    tcflush(0, TCIFLUSH);
830    if (tcsetattr(0, TCSANOW, &t) == -1) {
831       printf("Cannot tcsetattr()\n");
832    }
833
834    /* Defaults, the main program can override these */
835    signal(SIGQUIT, SIG_IGN);
836    signal(SIGHUP, SIG_IGN);
837 // signal(SIGSTOP, SIG_IGN);
838    signal(SIGINT, sigintcatcher);
839    signal(SIGWINCH, SIG_IGN);   
840    signal(SIGQUIT, SIG_IGN);
841    signal(SIGCHLD, SIG_IGN);
842 // signal(SIGTSTP, SIG_IGN);
843
844    if (!termtype) {
845       printf("Cannot get terminal type.\n");
846       normode();
847       exit(1);
848    }
849    if (tgetent(term_buffer, termtype) < 0) {
850       printf("Cannot get terminal termcap entry.\n");
851       normode();
852       exit(1);
853    }
854    t_width = t_height = -1;
855    t_width = tgetnum("co") - 1;
856    t_height = tgetnum("li");
857    BC = NULL;
858    UP = NULL;
859    t_cm = (char *)tgetstr("cm", &term_buffer);
860    t_cs = (char *)tgetstr("cl", &term_buffer); /* clear screen */
861    t_cl = (char *)tgetstr("ce", &term_buffer); /* clear line */
862    t_dl = (char *)tgetstr("dl", &term_buffer); /* delete line */
863    t_il = (char *)tgetstr("al", &term_buffer); /* insert line */
864    t_honk = (char *)tgetstr("bl", &term_buffer); /* beep */
865    t_ti = (char *)tgetstr("ti", &term_buffer);
866    t_te = (char *)tgetstr("te", &term_buffer);
867    t_up = (char *)tgetstr("up", &term_buffer);
868    t_do = (char *)tgetstr("do", &term_buffer);
869    t_sf = (char *)tgetstr("sf", &term_buffer);
870
871    num_stab = MAX_STAB;                  /* get default stab size */
872    stab = (stab_t **)malloc(sizeof(stab_t *) * num_stab);
873    memset(stab, 0, sizeof(stab_t *) * num_stab);
874
875    /* Key bindings */
876    kl = (char *)tgetstr("kl", &term_buffer);
877    kr = (char *)tgetstr("kr", &term_buffer);
878    ku = (char *)tgetstr("ku", &term_buffer);
879    kd = (char *)tgetstr("kd", &term_buffer);
880    kh = (char *)tgetstr("kh", &term_buffer);
881    kb = (char *)tgetstr("kb", &term_buffer);
882    kD = (char *)tgetstr("kD", &term_buffer);
883    kI = (char *)tgetstr("kI", &term_buffer);
884    kN = (char *)tgetstr("kN", &term_buffer);
885    kP = (char *)tgetstr("kP", &term_buffer);
886    kH = (char *)tgetstr("kH", &term_buffer);
887    kE = (char *)tgetstr("kE", &term_buffer);
888
889    add_smap(kl, F_CSRLFT);
890    add_smap(kr, F_CSRRGT);
891    add_smap(ku, F_CSRUP);
892    add_smap(kd, F_CSRDWN);
893    add_smap(kI, F_TINS);
894    add_smap(kN, F_PAGDWN);
895    add_smap(kP, F_PAGUP);
896    add_smap(kH, F_HOME);
897    add_smap(kE, F_EOF);
898
899
900    add_esc_smap("[A",   F_CSRUP);
901    add_esc_smap("[B",   F_CSRDWN);
902    add_esc_smap("[C",   F_CSRRGT);
903    add_esc_smap("[D",   F_CSRLFT);
904    add_esc_smap("[1~",  F_HOME);
905    add_esc_smap("[2~",  F_TINS);
906    add_esc_smap("[3~",  F_DELCHR);
907    add_esc_smap("[4~",  F_EOF);
908    add_esc_smap("f",    F_NXTWRD);
909    add_esc_smap("b",    F_PRVWRD);
910 }
911
912
913 /* Restore tty mode */
914 static void normode()
915 {
916    if (old_term_params_set) {
917       tcsetattr(0, TCSANOW, &old_term_params);
918       old_term_params_set = false;
919    }
920 }
921
922 /* Get next character from terminal/script file/unget buffer */
923 static int
924 t_gnc()
925 {
926     return t_getch();
927 }
928
929
930 /* Get next character from OS */
931 static int t_getch(void)
932 {
933    char c;
934
935    if (read(0, &c, 1) != 1) {
936       c = 0;
937    }
938    return (int)c;   
939 }
940     
941 /* Send message to terminal - primitive routine */
942 void
943 t_sendl(char *msg, int len)
944 {
945    write(1, msg, len);
946 }
947
948 void
949 t_send(char *msg)
950 {
951    if (msg == NULL) {
952       return;
953    }
954    t_sendl(msg, strlen(msg));    /* faster than one char at time */
955 }
956
957 /* Send single character to terminal - primitive routine - */
958 void
959 t_char(char c)
960 {
961    write(1, &c, 1);
962 }
963
964
965 static int brkflg = 0;              /* set on user break */
966
967 /* Routine to return true if user types break */
968 int usrbrk()
969 {
970    return brkflg;
971 }
972
973 /* Clear break flag */
974 void clrbrk()
975 {
976    brkflg = 0;
977
978 }
979
980 /* Interrupt caught here */
981 static void sigintcatcher(int sig)
982 {
983    brkflg++;
984    if (brkflg > 3) {
985       normode();
986       exit(1);
987    }
988    signal(SIGINT, sigintcatcher);
989 }
990
991
992 /* Trap Ctl-C */
993 void trapctlc()
994 {
995    signal(SIGINT, sigintcatcher);
996 }
997
998
999 /* ASCLRL() -- Clear to end of line from current position */
1000 static void asclrl(int pos, int width) 
1001 {
1002    int i;
1003
1004    if (t_cl) {
1005        t_send(t_cl);                 /* use clear to eol function */
1006        return;
1007    }
1008    if (pos==1 && linsdel_ok) {
1009        t_delete_line();              /* delete line */
1010        t_insert_line();              /* reinsert it */
1011        return;
1012    }
1013    for (i=1; i<=width-pos+1; i++)
1014        t_char(' ');                  /* last resort, blank it out */
1015    for (i=1; i<=width-pos+1; i++)    /* backspace to original position */
1016        t_char(0x8);
1017    return;
1018   
1019 }
1020
1021
1022 /* ASCURS -- Set cursor position */
1023 static void ascurs(int y, int x)
1024 {
1025    t_send((char *)tgoto(t_cm, x, y));
1026 }
1027                                                                                         
1028
1029 /* ASCLRS -- Clear whole screen */
1030 static void asclrs() 
1031 {
1032    ascurs(0,0);
1033    t_send(t_cs);
1034 }
1035
1036
1037
1038 /* ASINSL -- insert new line after cursor */
1039 static void asinsl()
1040 {
1041    t_clrline(0, t_width);
1042    t_send(t_il);                      /* insert before */
1043 }
1044
1045 /* ASDELL -- Delete line at cursor */
1046 static void asdell()
1047 {
1048    t_send(t_dl);
1049 }