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