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