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