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