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