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