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