]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/conio.c
Switch from GPLv2 to AGPLv3
[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    int len;
150    int func;
151    char *str;
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        int y, x;
457        y = t_gnc() - 0x20;        /* y */
458        x = t_gnc() - 0x20;        /* x */
459        c = input_char();
460     }
461     return c;
462 }
463
464
465 /* Get a complete input line */
466
467 int
468 input_line(char *string, int length)
469 {
470    char curline[2000];                /* edit buffer */
471    int noline;
472    unsigned c;
473    int more;
474    int i;
475
476     if (first) {
477        poolinit();                   /* build line pool */
478        first = 0;
479     }
480     noline = 1;                       /* no line fetched yet */
481     for (cl=cp=0; cl<length && cl<(int)sizeof(curline); ) {
482        if (usrbrk()) {
483           clrbrk();
484           break;
485        }
486        switch (c=input_char()) {
487        case F_RETURN:                /* CR */
488            t_sendl("\r\n", 2);       /* yes, print it and */
489            goto done;                /* get out */
490        case F_CLRSCRN:               /* clear screen */
491           asclrs();
492           t_sendl(curline, cl);
493           ascurs(0, cp);
494           break;
495        case F_CSRUP:
496            if (noline) {             /* no line fetched yet */
497                getnext();            /* getnext so getprev gets current */
498                noline = 0;           /* we now have line */
499            }
500            bstrncpy(curline, getprev(), sizeof(curline));
501            prtcur(curline);
502            break;
503        case F_CSRDWN:
504            noline = 0;               /* mark line fetched */
505            bstrncpy(curline, getnext(), sizeof(curline));
506            prtcur(curline);
507            break;
508        case F_INSCHR:
509            insert_space(curline, sizeof(curline));
510            break;
511        case F_DELCHR:
512            delchr(1, curline, sizeof(curline));       /* delete one character */
513            break;
514        case F_CSRLFT:                /* Backspace */
515            backup(curline);
516            break;
517        case F_CSRRGT:
518            forward(curline, sizeof(curline));
519            break;
520        case F_ERSCHR:                /* Rubout */
521            backup(curline);
522            delchr(1, curline, sizeof(curline));
523            if (cp == 0) {
524               t_char(' ');
525               t_char(0x8);
526            }
527            break;
528        case F_DELEOL:
529            t_clrline(0, t_width);
530            if (cl > cp)
531                cl = cp;
532            break;
533        case F_NXTWRD:
534            i = next_word(curline);
535            while (i--) {
536               forward(curline, sizeof(curline));
537            }
538            break;
539        case F_PRVWRD:
540            i = prev_word(curline);
541            while (i--) {
542               backup(curline);
543            }
544            break;
545        case F_DELWRD:
546            delchr(next_word(curline), curline, sizeof(curline)); /* delete word */
547            break;
548        case F_NXTMCH:                /* Ctl-X */
549            if (cl==0) {
550                *string = EOS;        /* terminate string */
551                return(c);            /* give it to him */
552            }
553            /* Note fall through */
554        case F_DELLIN:
555        case F_ERSLIN:
556            while (cp > 0) {
557               backup(curline);      /* backup to beginning of line */
558            }
559            t_clrline(0, t_width);     /* erase line */
560            cp = 0;
561            cl = 0;                   /* reset cursor counter */
562            t_char(' ');
563            t_char(0x8);
564            break;
565        case F_SOL:
566            while (cp > 0) {
567               backup(curline);
568            }
569            break;
570        case F_EOL:
571            while (cp < cl) {
572                forward(curline, sizeof(curline));
573            }
574            while (cp > cl) {
575                backup(curline);
576            }
577            break;
578        case F_TINS:                  /* toggle insert mode */
579            mode_insert = !mode_insert;  /* flip bit */
580            break;
581        default:
582            if (c > 255) {            /* function key hit */
583                if (cl==0) {          /* if first character then */
584                   *string = EOS;     /* terminate string */
585                   return c;          /* return it */
586                }
587                t_honk_horn();        /* complain */
588            } else {
589                if ((c & 0xC0) == 0xC0) {
590                   if ((c & 0xFC) == 0xFC) {
591                      more = 5;
592                   } else if ((c & 0xF8) == 0xF8) {
593                      more = 4;
594                   } else if ((c & 0xF0) == 0xF0) {
595                      more = 3;
596                   } else if ((c & 0xE0) == 0xE0) {
597                      more = 2;
598                   } else {
599                      more = 1;
600                   }
601                } else {
602                   more = 0;
603                }
604                if (mode_insert) {
605                   insert_space(curline, sizeof(curline));
606                }
607                curline[cp++] = c;    /* store character in line being built */
608                t_char(c);      /* echo character to terminal */
609                while (more--) {
610                   c= input_char();
611                   insert_hole(curline, sizeof(curline));
612                   curline[cp++] = c;    /* store character in line being built */
613                   t_char(c);      /* echo character to terminal */
614                }
615                if (cp > cl) {
616                   cl = cp;           /* keep current length */
617                   curline[cp] = 0;
618                }
619            }
620            break;
621        }                             /* end switch */
622     }
623 /* If we fall through here rather than goto done, the line is too long
624    simply return what we have now. */
625 done:
626    curline[cl++] = EOS;              /* terminate */
627    bstrncpy(string,curline,length);           /* return line to caller */
628    /* Save non-blank lines. Note, put line zaps curline */
629    if (curline[0] != EOS) {
630       putline(curline,cl);            /* save line for posterity */
631    }
632    return 0;                         /* give it to him/her */
633 }
634
635 /* Insert a space at the current cursor position */
636 static void
637 insert_space(char *curline, int curline_len)
638 {
639    int i;
640
641    if (cp >= cl || cl+1 > curline_len) {
642       return;
643    }
644    /* Note! source and destination overlap */
645    memmove(&curline[cp+1],&curline[cp],i=cl-cp);
646    cl++;
647    curline[cp] = ' ';
648    i = 0;
649    while (cl > cp) {
650       forward(curline, curline_len);
651       i++;
652    }
653    while (i--) {
654       backup(curline);
655    }
656 }
657
658
659 static void
660 insert_hole(char *curline, int curline_len)
661 {
662    int i;
663
664    if (cp > cl || cl+1 > curline_len) {
665       return;
666    }
667    /* Note! source and destination overlap */
668    memmove(&curline[cp+1], &curline[cp], i=cl-cp);
669    cl++;
670    curline[cl] = 0;
671 }
672
673
674 /* Move cursor forward keeping characters under it */
675 static void
676 forward(char *str, int str_len)
677 {
678    if (cp > str_len) {
679       return;
680    }
681    if (cp >= cl) {
682        t_char(' ');
683        str[cp+1] = ' ';
684        str[cp+2] = 0;
685    } else {
686        t_char(str[cp]);
687        if ((str[cp] & 0xC0) == 0xC0) {
688           cp++;
689           while ((str[cp] & 0xC0) == 0x80) {
690              t_char(str[cp]);
691              cp++;
692           }
693           cp--;
694        }
695    }
696    cp++;
697 }
698
699 /* How many characters under the cursor */
700 static int
701 char_count(int cptr, char *str)
702 {
703    int cnt = 1;
704    if (cptr > cl) {
705       return 0;
706    }
707    if ((str[cptr] & 0xC0) == 0xC0) {
708       cptr++;
709       while ((str[cptr] & 0xC0) == 0x80) {
710          cnt++;
711          cptr++;
712       }
713    }
714    return cnt;
715 }
716
717 /* Backup cursor keeping characters under it */
718 static void
719 backup(char *str)
720 {
721     if (cp == 0) {
722        return;
723     }
724     while ((str[cp] & 0xC0) == 0x80) {
725        cp--;
726     }
727     t_char('\010');
728     cp--;
729 }
730
731 /* Delete the character under the cursor */
732 static void
733 delchr(int del, char *curline, int line_len)
734 {
735    int i, cnt;
736
737    if (cp > cl || del == 0) {
738       return;
739    }
740    while (del-- && cl > 0) {
741       cnt = char_count(cp, curline);
742       if ((i=cl-cp-cnt) > 0) {
743          memcpy(&curline[cp], &curline[cp+cnt], i);
744       }
745       cl -= cnt;
746       curline[cl] = EOS;
747       t_clrline(0, t_width);
748       i = 0;
749       while (cl > cp) {
750          forward(curline, line_len);
751          i++;
752       }
753       while (i--) {
754          backup(curline);
755       }
756    }
757 }
758
759 /* Determine if character is part of a word */
760 static int
761 iswordc(char c)
762 {
763    if (mode_wspace)
764       return !isspace(c);
765    if (c >= '0' && c <= '9')
766       return true;
767    if (c == '$' || c == '%')
768       return true;
769    return isalpha(c);
770 }
771
772 /* Return number of characters to get to next word */
773 static int
774 next_word(char *ldb_buf)
775 {
776    int ncp;
777
778    if (cp > cl)
779       return 0;
780    ncp = cp;
781    for ( ; ncp<cl && iswordc(*(ldb_buf+ncp)); ncp++) ;
782    for ( ; ncp<cl && !iswordc(*(ldb_buf+ncp)); ncp++) ;
783    return ncp-cp;
784 }
785
786 /* Return number of characters to get to previous word */
787 static int
788 prev_word(char *ldb_buf)
789 {
790     int ncp, i;
791
792     if (cp == 0)                      /* if at begin of line stop now */
793         return 0;
794     if (cp > cl)                      /* if past eol start at eol */
795         ncp=cl+1;
796     else
797         ncp = cp;
798     /* backup to end of previous word - i.e. skip special chars */
799     for (i=ncp-1; i && !iswordc(*(ldb_buf+i)); i--) ;
800     if (i == 0) {                     /* at beginning of line? */
801         return cp;                    /* backup to beginning */
802     }
803     /* now move back through word to beginning of word */
804     for ( ; i && iswordc(*(ldb_buf+i)); i--) ;
805     ncp = i+1;                        /* position to first char of word */
806     if (i==0 && iswordc(*ldb_buf))    /* check for beginning of line */
807         ncp = 0;
808     return cp-ncp;                    /* return count */
809 }
810
811 /* Display new current line */
812 static void
813 prtcur(char *str)
814 {
815     while (cp > 0) {
816        backup(str);
817     }
818     t_clrline(0,t_width);
819     cp = cl = strlen(str);
820     t_sendl(str, cl);
821 }
822
823
824 /* Initialize line pool. Split pool into two pieces. */
825 static void
826 poolinit()
827 {
828    slptr = lptr = (struct lstr *)pool;
829    lptr->nextl = lptr;
830    lptr->prevl = lptr;
831    lptr->used = 1;
832    lptr->line = 0;
833    lptr->len = POOLEN;
834 }
835
836
837 /* Return pointer to next line in the pool and advance current line pointer */
838 static char *
839 getnext()
840 {
841    do {                              /* find next used line */
842       lptr = lptr->nextl;
843    } while (!lptr->used);
844    return (char *)&lptr->line;
845 }
846
847 /* Return pointer to previous line in the pool */
848 static char *
849 getprev()
850 {
851    do {                              /* find previous used line */
852       lptr = lptr->prevl;
853    } while (!lptr->used);
854    return (char *)&lptr->line;
855 }
856
857 static void
858 putline(char *newl, int newlen)
859 {
860    struct lstr *nptr;                /* points to next line */
861    char *p;
862
863    lptr = slptr;                     /* get ptr to last line stored */
864    lptr = lptr->nextl;               /* advance pointer */
865    if ((char *)lptr-pool+newlen+PHDRL > POOLEN) { /* not enough room */
866        lptr->used = 0;               /* delete line */
867        lptr = (struct lstr *)pool;   /* start at beginning of buffer */
868    }
869    while (lptr->len < newlen+PHDRL) { /* concatenate buffers */
870        nptr = lptr->nextl;           /* point to next line */
871        lptr->nextl = nptr->nextl;    /* unlink it from list */
872        nptr->nextl->prevl = lptr;
873        lptr->len += nptr->len;
874    }
875    if (lptr->len > newlen + 2 * PHDRL) { /* split buffer */
876        nptr = (struct lstr *)((char *)lptr + newlen + PHDRL);
877        /* Appropriate byte alignment - normally 2 byte, but on
878           sparc we need 4 byte alignment, so we always do 4 */
879        if (((long unsigned)nptr & 3) != 0) { /* test four byte alignment */
880            p = (char *)nptr;
881            nptr = (struct lstr *)((((long unsigned) p) & ~3) + 4);
882        }
883        nptr->len = lptr->len - ((char *)nptr - (char *)lptr);
884        lptr->len -= nptr->len;
885        nptr->nextl = lptr->nextl;    /* link in new buffer */
886        lptr->nextl->prevl = nptr;
887        lptr->nextl = nptr;
888        nptr->prevl = lptr;
889        nptr->used = 0;
890    }
891    memcpy(&lptr->line,newl,newlen);
892    lptr->used = 1;                   /* mark line used */
893    slptr = lptr;                     /* save as stored line */
894 }
895
896 #ifdef  DEBUGOUT
897 static void
898 dump(struct lstr *ptr, char *msg)
899 {
900     printf("%s buf=%x nextl=%x prevl=%x len=%d used=%d\n",
901         msg,ptr,ptr->nextl,ptr->prevl,ptr->len,ptr->used);
902     if (ptr->used)
903         printf("line=%s\n",&ptr->line);
904 }
905 #endif  /* DEBUGOUT */
906
907
908 /* Honk horn on terminal */
909 static void
910 t_honk_horn()
911 {
912    t_send(t_honk);
913 }
914
915 /* Insert line on terminal */
916 static void
917 t_insert_line()
918 {
919    asinsl();
920 }
921
922 /* Delete line from terminal */
923 static void
924 t_delete_line()
925 {
926    asdell();
927 }
928
929 /* clear line from pos to width */
930 static void
931 t_clrline(int pos, int width)
932 {
933     asclrl(pos, width);           /* clear to end of line */
934 }
935
936 /* Helper function to add string preceded by
937  *  ESC to smap table */
938 static void add_esc_smap(const char *str, int func)
939 {
940    char buf[1000];
941    buf[0] = 0x1B;                     /* esc */
942    bstrncpy(buf+1, str, sizeof(buf)-1);
943    add_smap(buf, func);
944 }
945
946 /* Set raw mode on terminal file.  Basically, get the terminal into a
947    mode in which all characters can be read as they are entered.  CBREAK
948    mode is not sufficient.
949  */
950 static void rawmode(FILE *input)
951 {
952    struct termios t;
953    static char term_buf[2048];
954    static char *term_buffer = term_buf;
955    char *termtype = (char *)getenv("TERM");
956
957    /* Make sure we are dealing with a terminal */
958    if (!isatty(fileno(input))) {
959       return;
960    }
961    if (tcgetattr(0, &old_term_params) != 0) {
962       printf("conio: Cannot tcgetattr()\n");
963       exit(1);
964    }
965    old_term_params_set = true;
966    t = old_term_params;
967    t.c_cc[VMIN] = 1; /* satisfy read after 1 char */
968    t.c_cc[VTIME] = 0;
969    t.c_iflag &= ~(BRKINT | IGNPAR | PARMRK | INPCK |
970                   ISTRIP | ICRNL | IXON | IXOFF | INLCR | IGNCR);
971    t.c_iflag |= IGNBRK;
972    t.c_oflag |= ONLCR;
973    t.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ICANON |
974                   NOFLSH | TOSTOP);
975    tcflush(0, TCIFLUSH);
976    if (tcsetattr(0, TCSANOW, &t) == -1) {
977       printf("Cannot tcsetattr()\n");
978    }
979
980    /* Defaults, the main program can override these */
981    signal(SIGQUIT, SIG_IGN);
982    signal(SIGHUP, SIG_IGN);
983    signal(SIGINT, sigintcatcher);
984    signal(SIGWINCH, SIG_IGN);
985
986    if (!termtype) {
987       printf("Cannot get terminal type.\n");
988       normode();
989       exit(1);
990    }
991    if (tgetent(term_buffer, termtype) < 0) {
992       printf("Cannot get terminal termcap entry.\n");
993       normode();
994       exit(1);
995    }
996    t_width = t_height = -1;
997    /* Note (char *)casting is due to really stupid compiler warnings */
998    t_width = tgetnum((char *)"co") - 1;
999    t_height = tgetnum((char *)"li");
1000    BC = NULL;
1001    UP = NULL;
1002    t_cm = (char *)tgetstr((char *)"cm", &term_buffer);
1003    t_cs = (char *)tgetstr((char *)"cl", &term_buffer); /* clear screen */
1004    t_cl = (char *)tgetstr((char *)"ce", &term_buffer); /* clear line */
1005    t_dl = (char *)tgetstr((char *)"dl", &term_buffer); /* delete line */
1006    t_il = (char *)tgetstr((char *)"al", &term_buffer); /* insert line */
1007    t_honk = (char *)tgetstr((char *)"bl", &term_buffer); /* beep */
1008    t_ti = (char *)tgetstr((char *)"ti", &term_buffer);
1009    t_te = (char *)tgetstr((char *)"te", &term_buffer);
1010    t_up = (char *)tgetstr((char *)"up", &term_buffer);
1011    t_do = (char *)tgetstr((char *)"do", &term_buffer);
1012    t_sf = (char *)tgetstr((char *)"sf", &term_buffer);
1013
1014    num_stab = MAX_STAB;                  /* get default stab size */
1015    stab = (stab_t **)malloc(sizeof(stab_t *) * num_stab);
1016    memset(stab, 0, sizeof(stab_t *) * num_stab);
1017
1018    /* Key bindings */
1019    kl = (char *)tgetstr((char *)"kl", &term_buffer);
1020    kr = (char *)tgetstr((char *)"kr", &term_buffer);
1021    ku = (char *)tgetstr((char *)"ku", &term_buffer);
1022    kd = (char *)tgetstr((char *)"kd", &term_buffer);
1023    kh = (char *)tgetstr((char *)"kh", &term_buffer);
1024    kb = (char *)tgetstr((char *)"kb", &term_buffer);
1025    kD = (char *)tgetstr((char *)"kD", &term_buffer);
1026    kI = (char *)tgetstr((char *)"kI", &term_buffer);
1027    kN = (char *)tgetstr((char *)"kN", &term_buffer);
1028    kP = (char *)tgetstr((char *)"kP", &term_buffer);
1029    kH = (char *)tgetstr((char *)"kH", &term_buffer);
1030    kE = (char *)tgetstr((char *)"kE", &term_buffer);
1031
1032    add_smap(kl, F_CSRLFT);
1033    add_smap(kr, F_CSRRGT);
1034    add_smap(ku, F_CSRUP);
1035    add_smap(kd, F_CSRDWN);
1036    add_smap(kI, F_TINS);
1037    add_smap(kN, F_PAGDWN);
1038    add_smap(kP, F_PAGUP);
1039    add_smap(kH, F_HOME);
1040    add_smap(kE, F_EOF);
1041
1042
1043    add_esc_smap("[A",   F_CSRUP);
1044    add_esc_smap("[B",   F_CSRDWN);
1045    add_esc_smap("[C",   F_CSRRGT);
1046    add_esc_smap("[D",   F_CSRLFT);
1047    add_esc_smap("[1~",  F_HOME);
1048    add_esc_smap("[2~",  F_TINS);
1049    add_esc_smap("[3~",  F_DELCHR);
1050    add_esc_smap("[4~",  F_EOF);
1051    add_esc_smap("f",    F_NXTWRD);
1052    add_esc_smap("b",    F_PRVWRD);
1053 }
1054
1055
1056 /* Restore tty mode */
1057 static void normode()
1058 {
1059    if (old_term_params_set) {
1060       tcsetattr(0, TCSANOW, &old_term_params);
1061       old_term_params_set = false;
1062    }
1063 }
1064
1065 /* Get next character from terminal/script file/unget buffer */
1066 static unsigned
1067 t_gnc()
1068 {
1069     return t_getch();
1070 }
1071
1072
1073 /* Get next character from OS */
1074 static unsigned t_getch(void)
1075 {
1076    unsigned char c;
1077
1078    if (read(0, &c, 1) != 1) {
1079       c = 0;
1080    }
1081    return (unsigned)c;
1082 }
1083
1084 /* Send message to terminal - primitive routine */
1085 void
1086 t_sendl(const char *msg, int len)
1087 {
1088    write(1, msg, len);
1089 }
1090
1091 void
1092 t_send(const char *msg)
1093 {
1094    if (msg == NULL) {
1095       return;
1096    }
1097    t_sendl(msg, strlen(msg));    /* faster than one char at time */
1098 }
1099
1100 /* Send single character to terminal - primitive routine - */
1101 void
1102 t_char(char c)
1103 {
1104    (void)write(1, &c, 1);
1105 }
1106
1107
1108 static int brkflg = 0;              /* set on user break */
1109
1110 /* Routine to return true if user types break */
1111 int usrbrk()
1112 {
1113    return brkflg;
1114 }
1115
1116 /* Clear break flag */
1117 void clrbrk()
1118 {
1119    brkflg = 0;
1120
1121 }
1122
1123 /* Interrupt caught here */
1124 static void sigintcatcher(int sig)
1125 {
1126    brkflg++;
1127    if (brkflg > 3) {
1128       normode();
1129       exit(1);
1130    }
1131    signal(SIGINT, sigintcatcher);
1132 }
1133
1134
1135 /* Trap Ctl-C */
1136 void trapctlc()
1137 {
1138    signal(SIGINT, sigintcatcher);
1139 }
1140
1141
1142 /* ASCLRL() -- Clear to end of line from current position */
1143 static void asclrl(int pos, int width)
1144 {
1145    int i;
1146
1147    if (t_cl) {
1148        t_send(t_cl);                 /* use clear to eol function */
1149        return;
1150    }
1151    if (pos==1 && linsdel_ok) {
1152        t_delete_line();              /* delete line */
1153        t_insert_line();              /* reinsert it */
1154        return;
1155    }
1156    for (i=1; i<=width-pos+1; i++)
1157        t_char(' ');                  /* last resort, blank it out */
1158    for (i=1; i<=width-pos+1; i++)    /* backspace to original position */
1159        t_char(0x8);
1160    return;
1161
1162 }
1163
1164
1165 /* ASCURS -- Set cursor position */
1166 static void ascurs(int y, int x)
1167 {
1168    t_send((char *)tgoto(t_cm, x, y));
1169 }
1170
1171
1172 /* ASCLRS -- Clear whole screen */
1173 static void asclrs()
1174 {
1175    ascurs(0,0);
1176    t_send(t_cs);
1177 }
1178
1179
1180
1181 /* ASINSL -- insert new line after cursor */
1182 static void asinsl()
1183 {
1184    t_clrline(0, t_width);
1185    t_send(t_il);                      /* insert before */
1186 }
1187
1188 /* ASDELL -- Delete line at cursor */
1189 static void asdell()
1190 {
1191    t_send(t_dl);
1192 }