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