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