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