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