]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/conio.c
04edb8243a7c882e07b6caff6e57d469fa256438
[bacula/bacula] / bacula / src / console / conio.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2016 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    This notice must be preserved when any source code is 
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20       Generalized console input/output handler
21       A maintanable replacement for readline()
22
23          Updated for Bacula, Kern Sibbald, December MMIII
24
25       This code is in part derived from code that I wrote in
26       1981, so some of it is a bit old and could use a cleanup.
27
28 */
29
30 /*
31  * UTF-8
32  *  If the top bit of a UTF-8 string is 0 (8 bits), then it
33  *    is a normal ASCII character.
34  *  If the top two bits are 11 (i.e. (c & 0xC0) == 0xC0 then
35  *    it is the start of a series of chars (up to 5)
36  *  Each subsequent character starts with 10 (i.e. (c & 0xC0) == 0x80)
37  */
38
39
40 #ifdef  TEST_PROGRAM
41 #include <stdio.h>
42 #include <unistd.h>
43 #include <stdlib.h>
44 #include <signal.h>
45 #include <string.h>
46 #include <ctype.h>
47 #define HAVE_CONIO 1
48 #else
49
50 /* We are in Bacula */
51 #include "bacula.h"
52
53 #endif
54
55 #ifdef HAVE_CONIO
56
57 #include <curses.h>
58 #include <term.h>
59
60 #ifdef HAVE_SUN_OS
61 #ifndef _TERM_H
62 extern "C" int tgetent(void *, const char *);
63 extern "C" int tgetnum(const char *);
64 extern "C" char *tgetstr (const char*, char**);
65 # Note: the following on older (Solaris 10) systems
66 #  may need to be moved to after the #endif
67 extern "C" char *tgoto (const char *, int, int);
68 #endif
69 #elif defined(__sgi)
70 extern "C" int tgetent(char *, char *);
71 extern "C" int tgetnum(char id[2]);
72 extern "C" char *tgetstr(char id[2], char **);
73 extern "C" char *tgoto(char *, int, int);
74 #elif defined (__digital__) && defined (__unix__)
75 extern "C" int tgetent(void *, const char *);
76 extern "C" int tgetnum(const char *);
77 extern "C" char *tgetstr (const char*, char**);
78 extern "C" char *tgoto (const char *, int, int);
79 #endif
80 #include "func.h"
81
82
83 /* From termios library */
84 #if defined(HAVE_HPUX_OS) || defined(HAVE_AIX_OS)
85 static char *BC;
86 static char *UP;
87 #else
88 extern char *BC;
89 extern char *UP;
90 #endif
91
92 /* Forward referenced functions */
93 extern "C" {
94 static void sigintcatcher(int);
95 }
96
97 static void add_smap(char *str, int func);
98
99
100 /* Global variables */
101
102 static const char *t_up = "\n";                    /* scroll up character */
103 static const char *t_honk = "\007";                /* sound beep */
104 static char *t_il;                           /* insert line */
105 static char *t_dl;                           /* delete line */
106 static char *t_cs;                           /* clear screen */
107 static char *t_cl;                           /* clear line */
108 static int t_width = 79;                     /* terminal width */
109 static int t_height = 24;                    /* terminal height */
110 static int linsdel_ok = 0;              /* set if term has line insert & delete fncs */
111
112 static char *t_cm;                           /* cursor positioning */
113 static char *t_ti;                           /* init sequence */
114 static char *t_te;                           /* end sequence */
115 static char *t_do;                           /* down one line */
116 static char *t_sf;                           /* scroll screen one line up */
117
118 /* Keypad and Function Keys */
119 static char *kl;                             /* left key */
120 static char *kr;                             /* right */
121 static char *ku;                             /* up */
122 static char *kd;                             /* down */
123 static char *kh;                             /* home */
124 static char *kb;                             /* backspace */
125 static char *kD;                             /* delete key */
126 static char *kI;                             /* insert */
127 static char *kN;                             /* next page */
128 static char *kP;                             /* previous page */
129 static char *kH;                             /* home */
130 static char *kE;                             /* end */
131
132 #ifndef EOS
133 #define EOS  '\0'                     /* end of string terminator */
134 #endif
135
136 #define TRUE  1
137 #define FALSE 0
138 /*
139  * Stab entry. Input chars (str), the length, and the desired
140  *  func code.
141  */
142 typedef struct s_stab {
143    struct s_stab *next;
144    char *str;
145    int len;
146    int func;
147 } stab_t;
148
149 #define MAX_STAB 30
150
151 static stab_t **stab = NULL;                 /* array of stabs by length */
152 static int num_stab;                         /* size of stab array */
153
154 static bool old_term_params_set = false;
155 static struct termios old_term_params;
156
157 /* Maintain lines in a doubly linked circular pool of lines. Each line is
158    preceded by a header defined by the lstr structure */
159
160
161 struct lstr {                         /* line pool structure */
162    struct lstr *prevl;                /* link to previous line */
163    struct lstr *nextl;                /* link to next line */
164    long len;                          /* length of line+header */
165    char used;                         /* set if line valid */
166    char line;                         /* line is actually varying length */
167 };
168
169 #ifdef unix
170 #define POOLEN 128000                 /* bytes in line pool */
171 #else
172 #define POOLEN 500                    /* bytes in line pool */
173 #endif
174 char pool[POOLEN];                    /* line pool */
175 #define PHDRL ((int)sizeof(struct lstr))  /* length of line header */
176
177 static struct lstr *lptr;             /* current line pointer */
178 static struct lstr *slptr;            /* store line pointer */
179 static int cl, cp;
180 static char *getnext(), *getprev();
181 static int first = 1;
182 static int mode_insert = 1;
183 static int mode_wspace = 1;           /* words separated by spaces */
184
185
186 static short char_map[600]= {
187    0,                                  F_SOL,    /* ^a Line start */
188    F_PRVWRD, /* ^b Previous word */    F_BREAK,  /* ^C break */
189    F_DELCHR, /* ^D Delete character */ F_EOL,    /* ^e End of line */
190    F_CSRRGT, /* ^f Right */            F_TABBAK, /* ^G Back tab */
191    F_CSRLFT, /* ^H Left */             F_TAB,    /* ^I Tab */
192    F_CSRDWN, /* ^J Down */             F_DELEOL, /* ^K kill to eol */
193    F_CLRSCRN,/* ^L clear screen */     F_RETURN, /* ^M Carriage return */
194    F_RETURN, /* ^N enter line  */      F_CONCAT, /* ^O Concatenate lines */
195    F_CSRUP,  /* ^P cursor up */        F_TINS,   /* ^Q Insert character mode */
196    F_PAGUP,  /* ^R Page up */          F_CENTER, /* ^S Center text */
197    F_PAGDWN, /* ^T Page down */        F_DELSOL, /* ^U delete to start of line */
198    F_DELWRD, /* ^V Delete word */      F_PRVWRD, /* ^W Previous word */
199    F_NXTMCH, /* ^X Next match */       F_DELEOL, /* ^Y Delete to end of line */
200    F_BACKGND,/* ^Z Background */       0x1B,     /* ^[=ESC escape */
201    F_TENTRY, /* ^\ Entry mode */       F_PASTECB,/* ^]=paste clipboard */
202    F_HOME,   /* ^^ Home */             F_ERSLIN, /* ^_ Erase line */
203
204    ' ','!','"','#','$','%','&','\047',
205    '(',')','*','+','\054','-','.','/',
206    '0','1','2','3','4','5','6','7',
207    '8','9',':',';','<','=','>','?',
208    '@','A','B','C','D','E','F','G',
209    'H','I','J','K','L','M','N','O',
210    'P','Q','R','S','T','U','V','W',
211    'X','Y','Z','[','\\',']','^','_',
212    '\140','a','b','c','d','e','f','g',
213    'h','i','j','k','l','m','n','o',
214    'p','q','r','s','t','u','v','w',
215    'x','y','z','{','|','}','\176',F_ERSCHR  /* erase character */
216
217   };
218
219
220 /* Local variables */
221
222 #define CR '\r'                       /* carriage return */
223
224
225 /* Function Prototypes */
226
227 static unsigned int input_char(void);
228 static unsigned int t_gnc(void);
229 static void insert_space(char *curline, int line_len);
230 static void insert_hole(char *curline, int line_len);
231 static void forward(char *str, int str_len);
232 static void backup(char *curline);
233 static void delchr(int cnt, char *curline, int line_len);
234 static int iswordc(char c);
235 static int  next_word(char *ldb_buf);
236 static int  prev_word(char *ldb_buf);
237 static void prtcur(char *str);
238 static void poolinit(void);
239 static char * getnext(void);
240 static char * getprev(void);
241 static void putline(char *newl, int newlen);
242 static void t_honk_horn(void);
243 static void t_insert_line(void);
244 static void t_delete_line(void);
245 static void t_clrline(int pos, int width);
246 void t_sendl(const char *msg, int len);
247 void t_send(const char *msg);
248 void t_char(char c);
249 static void asclrs();
250 static void ascurs(int y, int x);
251
252 static void rawmode(FILE *input);
253 static void normode(void);
254 static unsigned t_getch();
255 static void asclrl(int pos, int width);
256 static void asinsl();
257 static void asdell();
258
259 int input_line(char *string, int length);
260 extern "C" {
261 void con_term();
262 }
263 void trapctlc();
264 int usrbrk();
265 void clrbrk();
266
267 void con_init(FILE *input)
268 {
269    atexit(con_term);
270    rawmode(input);
271    trapctlc();
272 }
273
274 /*
275  * Zed control keys
276  */
277 void con_set_zed_keys(void)
278 {
279    char_map[1]  = F_NXTWRD; /* ^A Next Word */
280    char_map[2]  = F_SPLIT;  /* ^B Split line */
281    char_map[3]  = F_EOI;    /* ^C Quit */
282    char_map[4]  = F_DELCHR; /* ^D Delete character */
283    char_map[5]  = F_EOF;    /* ^E End of file */
284    char_map[6]  = F_INSCHR; /* ^F Insert character */
285    char_map[7]  = F_TABBAK; /* ^G Back tab */
286    char_map[8]  = F_CSRLFT; /* ^H Left */
287    char_map[9]  = F_TAB;    /* ^I Tab */
288    char_map[10] = F_CSRDWN; /* ^J Down */
289    char_map[11] = F_CSRUP;  /* ^K Up */
290    char_map[12] = F_CSRRGT; /* ^L Right */
291    char_map[13] = F_RETURN; /* ^M Carriage return */
292    char_map[14] = F_EOL;    /* ^N End of line */
293    char_map[15] = F_CONCAT; /* ^O Concatenate lines */
294    char_map[16] = F_MARK;   /* ^P Set marker */
295    char_map[17] = F_TINS;   /* ^Q Insert character mode */
296    char_map[18] = F_PAGUP;  /* ^R Page up */
297    char_map[19] = F_CENTER; /* ^S Center text */
298    char_map[20] = F_PAGDWN; /* ^T Page down */
299    char_map[21] = F_SOL;    /* ^U Line start */
300    char_map[22] = F_DELWRD; /* ^V Delete word */
301    char_map[23] = F_PRVWRD; /* ^W Previous word */
302    char_map[24] = F_NXTMCH; /* ^X Next match */
303    char_map[25] = F_DELEOL; /* ^Y Delete to end of line */
304    char_map[26] = F_DELLIN; /* ^Z Delete line */
305    /* 27 = ESC */
306    char_map[28] = F_TENTRY; /* ^\ Entry mode */
307    char_map[29] = F_PASTECB;/* ^]=paste clipboard */
308    char_map[30] = F_HOME;   /* ^^ Home */
309    char_map[31] = F_ERSLIN; /* ^_ Erase line */
310
311 }
312
313 void con_term()
314 {
315    normode();
316 }
317
318 #ifdef TEST_PROGRAM
319 /*
320  * Guarantee that the string is properly terminated */
321 char *bstrncpy(char *dest, const char *src, int maxlen)
322 {
323    strncpy(dest, src, maxlen-1);
324    dest[maxlen-1] = 0;
325    return dest;
326 }
327 #endif
328
329
330 /*
331  * New style string mapping to function code
332  */
333 static unsigned do_smap(unsigned c)
334 {
335     char str[MAX_STAB];
336     int len = 0;
337     stab_t *tstab;
338     int i, found;
339     unsigned cm;
340
341     len = 1;
342     str[0] = c;
343     str[1] = 0;
344
345     cm = char_map[c];
346     if (cm == 0) {
347        return c;
348     } else {
349        c = cm;
350     }
351     for ( ;; ) {
352        found = 0;
353        for (i=len-1; i<MAX_STAB; i++) {
354           for (tstab=stab[i]; tstab; tstab=tstab->next) {
355              if (strncmp(str, tstab->str, len) == 0) {
356                 if (len == tstab->len) {
357                    return tstab->func;
358                 }
359                 found = 1;
360                 break;                /* found possibility continue searching */
361              }
362           }
363        }
364        if (!found) {
365           return len==1?c:0;
366        }
367        /* found partial match, so get next character and retry */
368        str[len++] = t_gnc();
369        str[len] = 0;
370     }
371 }
372
373 #ifdef DEBUG_x
374 static void dump_stab()
375 {
376     int i, j, c;
377     stab_t *tstab;
378     char buf[100];
379
380     for (i=0; i<MAX_STAB; i++) {
381        for (tstab=stab[i]; tstab; tstab=tstab->next) {
382           for (j=0; j<tstab->len; j++) {
383              c = tstab->str[j];
384              if (c < 0x20 || c > 0x7F) {
385                 sprintf(buf, " 0x%x ", c);
386                 t_send(buf);
387              } else {
388                 buf[0] = c;
389                 buf[1] = 0;
390                 t_sendl(buf, 1);
391              }
392           }
393           sprintf(buf, " func=%d len=%d\n\r", tstab->func, tstab->len);
394           t_send(buf);
395        }
396     }
397 }
398 #endif
399
400 /*
401  * New routine. Add string to string->func mapping table.
402  */
403 static void add_smap(char *str, int func)
404 {
405    stab_t *tstab;
406    int len;
407
408    if (!str) {
409       return;
410    }
411    len = strlen(str);
412    if (len == 0) {
413 /*    errmsg("String for func %d is zero length\n", func); */
414       return;
415    }
416    tstab = (stab_t *)malloc(sizeof(stab_t));
417    memset(tstab, 0, sizeof(stab_t));
418    tstab->len = len;
419    tstab->str = (char *)malloc(tstab->len + 1);
420    bstrncpy(tstab->str, str, tstab->len + 1);
421    tstab->func = func;
422    if (tstab->len > num_stab) {
423       printf("stab string too long %d. Max is %d\n", tstab->len, num_stab);
424       exit(1);
425    }
426    tstab->next = stab[tstab->len-1];
427    stab[tstab->len-1] = tstab;
428 /* printf("Add_smap tstab=%x len=%d func=%d tstab->next=%x\n\r", tstab, len,
429           func, tstab->next); */
430
431 }
432
433
434 /* Get the next character from the terminal - performs table lookup on
435    the character to do the desired translation */
436 static unsigned int
437 input_char()
438 {
439     unsigned c;
440
441     if ((c=t_gnc()) <= 599) {         /* IBM generates codes up to 260 */
442           c = do_smap(c);
443     } else if (c > 1000) {            /* stuffed function */
444        c -= 1000;                     /* convert back to function code */
445     }
446     if (c <= 0) {
447         t_honk_horn();
448     }
449     /* if we got a screen size escape sequence, read height, width */
450     if (c == F_SCRSIZ) {
451        t_gnc();   /*  - 0x20 = y */
452        t_gnc();   /*  - 0x20 = x */
453        c = input_char();
454     }
455     return c;
456 }
457
458
459 /* Get a complete input line */
460
461 int
462 input_line(char *string, int length)
463 {
464    char curline[2000];                /* edit buffer */
465    int noline;
466    unsigned c;
467    int more;
468    int i;
469
470     if (first) {
471        poolinit();                   /* build line pool */
472        first = 0;
473     }
474     noline = 1;                       /* no line fetched yet */
475     for (cl=cp=0; cl<length && cl<(int)sizeof(curline); ) {
476        if (usrbrk()) {
477           clrbrk();
478           break;
479        }
480        switch (c=input_char()) {
481        case F_RETURN:                /* CR */
482            t_sendl("\r\n", 2);       /* yes, print it and */
483            goto done;                /* get out */
484        case F_CLRSCRN:               /* clear screen */
485           asclrs();
486           t_sendl(curline, cl);
487           ascurs(0, cp);
488           break;
489        case F_CSRUP:
490            if (noline) {             /* no line fetched yet */
491                getnext();            /* getnext so getprev gets current */
492                noline = 0;           /* we now have line */
493            }
494            bstrncpy(curline, getprev(), sizeof(curline));
495            prtcur(curline);
496            break;
497        case F_CSRDWN:
498            noline = 0;               /* mark line fetched */
499            bstrncpy(curline, getnext(), sizeof(curline));
500            prtcur(curline);
501            break;
502        case F_INSCHR:
503            insert_space(curline, sizeof(curline));
504            break;
505        case F_DELCHR:
506            delchr(1, curline, sizeof(curline));       /* delete one character */
507            break;
508        case F_CSRLFT:                /* Backspace */
509            backup(curline);
510            break;
511        case F_CSRRGT:
512            forward(curline, sizeof(curline));
513            break;
514        case F_ERSCHR:                /* Rubout */
515            backup(curline);
516            delchr(1, curline, sizeof(curline));
517            if (cp == 0) {
518               t_char(' ');
519               t_char(0x8);
520            }
521            break;
522        case F_DELEOL:
523            t_clrline(0, t_width);
524            if (cl > cp)
525                cl = cp;
526            break;
527        case F_NXTWRD:
528            i = next_word(curline);
529            while (i--) {
530               forward(curline, sizeof(curline));
531            }
532            break;
533        case F_PRVWRD:
534            i = prev_word(curline);
535            while (i--) {
536               backup(curline);
537            }
538            break;
539        case F_DELWRD:
540            delchr(next_word(curline), curline, sizeof(curline)); /* delete word */
541            break;
542        case F_NXTMCH:                /* Ctl-X */
543            if (cl==0) {
544                *string = EOS;        /* terminate string */
545                return(c);            /* give it to him */
546            }
547            /* Note fall through */
548        case F_DELLIN:
549        case F_ERSLIN:
550            while (cp > 0) {
551               backup(curline);      /* backup to beginning of line */
552            }
553            t_clrline(0, t_width);     /* erase line */
554            cp = 0;
555            cl = 0;                   /* reset cursor counter */
556            t_char(' ');
557            t_char(0x8);
558            break;
559        case F_SOL:
560            while (cp > 0) {
561               backup(curline);
562            }
563            break;
564        case F_EOL:
565            while (cp < cl) {
566                forward(curline, sizeof(curline));
567            }
568            while (cp > cl) {
569                backup(curline);
570            }
571            break;
572        case F_TINS:                  /* toggle insert mode */
573            mode_insert = !mode_insert;  /* flip bit */
574            break;
575        default:
576            if (c > 255) {            /* function key hit */
577                if (cl==0) {          /* if first character then */
578                   *string = EOS;     /* terminate string */
579                   return c;          /* return it */
580                }
581                t_honk_horn();        /* complain */
582            } else {
583                if ((c & 0xC0) == 0xC0) {
584                   if ((c & 0xFC) == 0xFC) {
585                      more = 5;
586                   } else if ((c & 0xF8) == 0xF8) {
587                      more = 4;
588                   } else if ((c & 0xF0) == 0xF0) {
589                      more = 3;
590                   } else if ((c & 0xE0) == 0xE0) {
591                      more = 2;
592                   } else {
593                      more = 1;
594                   }
595                } else {
596                   more = 0;
597                }
598                if (mode_insert) {
599                   insert_space(curline, sizeof(curline));
600                }
601                curline[cp++] = c;    /* store character in line being built */
602                t_char(c);      /* echo character to terminal */
603                while (more--) {
604                   c= input_char();
605                   insert_hole(curline, sizeof(curline));
606                   curline[cp++] = c;    /* store character in line being built */
607                   t_char(c);      /* echo character to terminal */
608                }
609                if (cp > cl) {
610                   cl = cp;           /* keep current length */
611                   curline[cp] = 0;
612                }
613            }
614            break;
615        }                             /* end switch */
616     }
617 /* If we fall through here rather than goto done, the line is too long
618    simply return what we have now. */
619 done:
620    curline[cl++] = EOS;              /* terminate */
621    bstrncpy(string,curline,length);           /* return line to caller */
622    /* Save non-blank lines. Note, put line zaps curline */
623    if (curline[0] != EOS) {
624       putline(curline,cl);            /* save line for posterity */
625    }
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-- && cl > 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 + 7) { /* split buffer */
870        nptr = (struct lstr *)((char *)lptr + newlen + PHDRL);
871        /* Appropriate byte alignment - for Intel 2 byte, but on
872           Sparc we need 8 byte alignment, so we always do 8 */
873        if (((long unsigned)nptr & 7) != 0) { /* test eight byte alignment */
874            p = (char *)nptr;
875            nptr = (struct lstr *)((((long unsigned) p) & ~7) + 8);
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    /* Note (char *)casting is due to really stupid compiler warnings */
992    t_width = tgetnum((char *)"co") - 1;
993    t_height = tgetnum((char *)"li");
994    BC = NULL;
995    UP = NULL;
996    t_cm = (char *)tgetstr((char *)"cm", &term_buffer);
997    t_cs = (char *)tgetstr((char *)"cl", &term_buffer); /* clear screen */
998    t_cl = (char *)tgetstr((char *)"ce", &term_buffer); /* clear line */
999    t_dl = (char *)tgetstr((char *)"dl", &term_buffer); /* delete line */
1000    t_il = (char *)tgetstr((char *)"al", &term_buffer); /* insert line */
1001    t_honk = (char *)tgetstr((char *)"bl", &term_buffer); /* beep */
1002    t_ti = (char *)tgetstr((char *)"ti", &term_buffer);
1003    t_te = (char *)tgetstr((char *)"te", &term_buffer);
1004    t_up = (char *)tgetstr((char *)"up", &term_buffer);
1005    t_do = (char *)tgetstr((char *)"do", &term_buffer);
1006    t_sf = (char *)tgetstr((char *)"sf", &term_buffer);
1007
1008    num_stab = MAX_STAB;                  /* get default stab size */
1009    stab = (stab_t **)malloc(sizeof(stab_t *) * num_stab);
1010    memset(stab, 0, sizeof(stab_t *) * num_stab);
1011
1012    /* Key bindings */
1013    kl = (char *)tgetstr((char *)"kl", &term_buffer);
1014    kr = (char *)tgetstr((char *)"kr", &term_buffer);
1015    ku = (char *)tgetstr((char *)"ku", &term_buffer);
1016    kd = (char *)tgetstr((char *)"kd", &term_buffer);
1017    kh = (char *)tgetstr((char *)"kh", &term_buffer);
1018    kb = (char *)tgetstr((char *)"kb", &term_buffer);
1019    kD = (char *)tgetstr((char *)"kD", &term_buffer);
1020    kI = (char *)tgetstr((char *)"kI", &term_buffer);
1021    kN = (char *)tgetstr((char *)"kN", &term_buffer);
1022    kP = (char *)tgetstr((char *)"kP", &term_buffer);
1023    kH = (char *)tgetstr((char *)"kH", &term_buffer);
1024    kE = (char *)tgetstr((char *)"kE", &term_buffer);
1025
1026    add_smap(kl, F_CSRLFT);
1027    add_smap(kr, F_CSRRGT);
1028    add_smap(ku, F_CSRUP);
1029    add_smap(kd, F_CSRDWN);
1030    add_smap(kI, F_TINS);
1031    add_smap(kN, F_PAGDWN);
1032    add_smap(kP, F_PAGUP);
1033    add_smap(kH, F_HOME);
1034    add_smap(kE, F_EOF);
1035
1036
1037    add_esc_smap("[A",   F_CSRUP);
1038    add_esc_smap("[B",   F_CSRDWN);
1039    add_esc_smap("[C",   F_CSRRGT);
1040    add_esc_smap("[D",   F_CSRLFT);
1041    add_esc_smap("[1~",  F_HOME);
1042    add_esc_smap("[2~",  F_TINS);
1043    add_esc_smap("[3~",  F_DELCHR);
1044    add_esc_smap("[4~",  F_EOF);
1045    add_esc_smap("f",    F_NXTWRD);
1046    add_esc_smap("b",    F_PRVWRD);
1047 }
1048
1049
1050 /* Restore tty mode */
1051 static void normode()
1052 {
1053    if (old_term_params_set) {
1054       tcsetattr(0, TCSANOW, &old_term_params);
1055       old_term_params_set = false;
1056    }
1057 }
1058
1059 /* Get next character from terminal/script file/unget buffer */
1060 static unsigned
1061 t_gnc()
1062 {
1063     return t_getch();
1064 }
1065
1066
1067 /* Get next character from OS */
1068 static unsigned t_getch(void)
1069 {
1070    unsigned char c;
1071
1072    if (read(0, &c, 1) != 1) {
1073       c = 0;
1074    }
1075    return (unsigned)c;
1076 }
1077
1078 /* Send message to terminal - primitive routine */
1079 void
1080 t_sendl(const char *msg, int len)
1081 {
1082    write(1, msg, len);
1083 }
1084
1085 void
1086 t_send(const char *msg)
1087 {
1088    if (msg == NULL) {
1089       return;
1090    }
1091    t_sendl(msg, strlen(msg));    /* faster than one char at time */
1092 }
1093
1094 /* Send single character to terminal - primitive routine - */
1095 void
1096 t_char(char c)
1097 {
1098    (void)write(1, &c, 1);
1099 }
1100
1101
1102 static int brkflg = 0;              /* set on user break */
1103
1104 /* Routine to return true if user types break */
1105 int usrbrk()
1106 {
1107    return brkflg;
1108 }
1109
1110 /* Clear break flag */
1111 void clrbrk()
1112 {
1113    brkflg = 0;
1114
1115 }
1116
1117 /* Interrupt caught here */
1118 static void sigintcatcher(int sig)
1119 {
1120    brkflg++;
1121    if (brkflg > 3) {
1122       normode();
1123       exit(1);
1124    }
1125    signal(SIGINT, sigintcatcher);
1126 }
1127
1128
1129 /* Trap Ctl-C */
1130 void trapctlc()
1131 {
1132    signal(SIGINT, sigintcatcher);
1133 }
1134
1135
1136 /* ASCLRL() -- Clear to end of line from current position */
1137 static void asclrl(int pos, int width)
1138 {
1139    int i;
1140
1141    if (t_cl) {
1142        t_send(t_cl);                 /* use clear to eol function */
1143        return;
1144    }
1145    if (pos==1 && linsdel_ok) {
1146        t_delete_line();              /* delete line */
1147        t_insert_line();              /* reinsert it */
1148        return;
1149    }
1150    for (i=1; i<=width-pos+1; i++)
1151        t_char(' ');                  /* last resort, blank it out */
1152    for (i=1; i<=width-pos+1; i++)    /* backspace to original position */
1153        t_char(0x8);
1154    return;
1155
1156 }
1157
1158
1159 /* ASCURS -- Set cursor position */
1160 static void ascurs(int y, int x)
1161 {
1162    t_send((char *)tgoto(t_cm, x, y));
1163 }
1164
1165
1166 /* ASCLRS -- Clear whole screen */
1167 static void asclrs()
1168 {
1169    ascurs(0,0);
1170    t_send(t_cs);
1171 }
1172
1173
1174
1175 /* ASINSL -- insert new line after cursor */
1176 static void asinsl()
1177 {
1178    t_clrline(0, t_width);
1179    t_send(t_il);                      /* insert before */
1180 }
1181
1182 /* ASDELL -- Delete line at cursor */
1183 static void asdell()
1184 {
1185    t_send(t_dl);
1186 }
1187
1188 #endif