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