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