home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / getline.zip / getline.c < prev    next >
C/C++ Source or Header  |  1994-07-14  |  31KB  |  1,169 lines

  1. #ifndef lint
  2. static char     rcsid[] =
  3. "$Id: getline.c,v 3.11 1993/12/02 15:54:31 thewalt Exp thewalt $";
  4. static char    *copyright = "Copyright (C) 1991, 1992, 1993, Chris Thewalt";
  5. #endif
  6.  
  7. /*
  8.  * Copyright (C) 1991, 1992, 1993 by Chris Thewalt (thewalt@ce.berkeley.edu)
  9.  *
  10.  * Permission to use, copy, modify, and distribute this software 
  11.  * for any purpose and without fee is hereby granted, provided
  12.  * that the above copyright notices appear in all copies and that both the
  13.  * copyright notice and this permission notice appear in supporting
  14.  * documentation.  This software is provided "as is" without express or
  15.  * implied warranty.
  16.  *
  17.  * Thanks to the following people who have provided enhancements and fixes:
  18.  *   Ron Ueberschaer, Christoph Keller, Scott Schwartz, Steven List,
  19.  *   DaviD W. Sanderson, Goran Bostrom, Michael Gleason, Glenn Kasten,
  20.  *   Edin Hodzic, Eric J Bivona, Kai Uwe Rommel, Danny Quah, Ulrich Betzler
  21.  */
  22.  
  23. #include       "getline.h"
  24. static int      gl_tab();  /* forward reference needed for gl_tab_hook */
  25.  
  26. /******************** imported interface *********************************/
  27.  
  28. #include <string.h>
  29. #include <ctype.h>
  30. #include <errno.h>
  31. #include <signal.h>
  32.  
  33. extern int      isatty();    
  34. extern void    *malloc();
  35. extern void     free();
  36.  
  37. /********************* exported interface ********************************/
  38.  
  39. char           *getline();        /* read a line of input */
  40. void            gl_setwidth();        /* specify width of screen */
  41. void            gl_histadd();        /* adds entries to hist */
  42. void        gl_strwidth();        /* to bind gl_strlen */
  43.  
  44. int         (*gl_in_hook)() = 0;
  45. int         (*gl_out_hook)() = 0;
  46. int         (*gl_tab_hook)() = gl_tab;
  47.  
  48. /******************** internal interface *********************************/
  49.  
  50. #define BUF_SIZE 1024
  51.  
  52. static int      gl_init_done = -1;    /* terminal mode flag  */
  53. static int      gl_termw = 80;        /* actual terminal width */
  54. static int      gl_scroll = 27;        /* width of EOL scrolling region */
  55. static int      gl_width = 0;        /* net size available for input */
  56. static int      gl_extent = 0;        /* how far to redraw, 0 means all */
  57. static int      gl_overwrite = 0;    /* overwrite mode */
  58. static int      gl_pos, gl_cnt = 0;     /* position and size of input */
  59. static char     gl_buf[BUF_SIZE];       /* input buffer */
  60. static char     gl_killbuf[BUF_SIZE]=""; /* killed text */
  61. static char    *gl_prompt;        /* to save the prompt string */
  62. static char     gl_intrc = 0;        /* keyboard SIGINT char */
  63. static char     gl_quitc = 0;        /* keyboard SIGQUIT char */
  64. static char     gl_suspc = 0;        /* keyboard SIGTSTP char */
  65. static char     gl_dsuspc = 0;        /* delayed SIGTSTP char */
  66. static int      gl_search_mode = 0;    /* search mode flag */
  67.  
  68. static void     gl_init();        /* prepare to edit a line */
  69. static void     gl_cleanup();        /* to undo gl_init */
  70. static void     gl_char_init();        /* get ready for no echo input */
  71. static void     gl_char_cleanup();    /* undo gl_char_init */
  72. static size_t     (*gl_strlen)() = (size_t(*)())strlen; 
  73.                     /* returns printable prompt width */
  74.  
  75. static void     gl_addchar();        /* install specified char */
  76. static void     gl_del();        /* del, either left (-1) or cur (0) */
  77. static void     gl_error();        /* write error msg and die */
  78. static void     gl_fixup();        /* fixup state variables and screen */
  79. static int      gl_getc();        /* read one char from terminal */
  80. static void     gl_kill();        /* delete to EOL */
  81. static void     gl_newline();        /* handle \n or \r */
  82. static void     gl_putc();        /* write one char to terminal */
  83. static void     gl_puts();        /* write a line to terminal */
  84. static void     gl_redraw();        /* issue \n and redraw all */
  85. static void     gl_transpose();        /* transpose two chars */
  86. static void     gl_yank();        /* yank killed text */
  87. static void     gl_word();        /* move a word */
  88.  
  89. static void     hist_init();    /* initializes hist pointers */
  90. static char    *hist_next();    /* return ptr to next item */
  91. static char    *hist_prev();    /* return ptr to prev item */
  92. static char    *hist_save();    /* makes copy of a string, without NL */
  93.  
  94. static void     search_addchar();    /* increment search string */
  95. static void     search_term();        /* reset with current contents */
  96. static void     search_back();        /* look back for current string */
  97. static void     search_forw();        /* look forw for current string */
  98.  
  99. /************************ nonportable part *********************************/
  100.  
  101. extern int      write();
  102. extern void     exit();
  103.  
  104. #ifdef unix
  105. #ifndef __unix__
  106. #define __unix__
  107. #endif /* not __unix__ */
  108. #endif /* unix */
  109.  
  110. #ifdef _IBMR2
  111. #ifndef __unix__
  112. #define __unix__
  113. #endif
  114. #endif
  115.  
  116. #ifdef __GO32__
  117. #include <pc.h>
  118. #undef MSDOS
  119. #undef __unix__
  120. #endif
  121.  
  122. #ifdef MSDOS
  123. #include <bios.h>
  124. #endif
  125.  
  126. #ifdef __IBMC__
  127. #include <io.h>
  128. #endif
  129.  
  130. #ifdef __unix__
  131. #ifndef __convexc__
  132. extern int      read();
  133. extern int      kill();
  134. extern int      ioctl();
  135. #endif /* not __convexc__ */
  136. #ifdef POSIX        /* use POSIX interface */
  137. #include <termios.h>
  138. struct termios  new_termios, old_termios;
  139. #else /* not POSIX */
  140. #include <sys/ioctl.h>
  141. #ifdef M_XENIX    /* does not really use bsd terminal interface */
  142. #undef TIOCSETN
  143. #endif /* M_XENIX */
  144. #ifdef TIOCSETN        /* use BSD interface */
  145. #include <sgtty.h>
  146. struct sgttyb   new_tty, old_tty;
  147. struct tchars   tch;
  148. struct ltchars  ltch;
  149. #else            /* use SYSV interface */
  150. #include <termio.h>
  151. struct termio   new_termio, old_termio;
  152. #endif /* TIOCSETN */
  153. #endif /* POSIX */
  154. #endif    /* __unix__ */
  155.  
  156. #ifdef vms
  157. #include <descrip.h>
  158. #include <ttdef.h>
  159. #include <iodef.h>
  160. #include unixio
  161.    
  162. static int   setbuff[2];             /* buffer to set terminal attributes */
  163. static short chan = -1;              /* channel to terminal */
  164. struct dsc$descriptor_s descrip;     /* VMS descriptor */
  165. #endif
  166.  
  167. static void
  168. gl_char_init()            /* turn off input echo */
  169. {
  170. #ifdef __unix__
  171. #ifdef POSIX
  172.     tcgetattr(0, &old_termios);
  173.     gl_intrc = old_termios.c_cc[VINTR];
  174.     gl_quitc = old_termios.c_cc[VQUIT];
  175. #ifdef VSUSP
  176.     gl_suspc = old_termios.c_cc[VSUSP];
  177. #endif
  178. #ifdef VDSUSP
  179.     gl_dsuspc = old_termios.c_cc[VDSUSP];
  180. #endif
  181.     new_termios = old_termios;
  182.     new_termios.c_iflag &= ~(BRKINT|ISTRIP|IXON|IXOFF);
  183.     new_termios.c_iflag |= (IGNBRK|IGNPAR);
  184.     new_termios.c_lflag &= ~(ICANON|ISIG|IEXTEN|ECHO);
  185.     new_termios.c_cc[VMIN] = 1;
  186.     new_termios.c_cc[VTIME] = 0;
  187.     tcsetattr(0, TCSANOW, &new_termios);
  188. #else                /* not POSIX */
  189. #ifdef TIOCSETN            /* BSD */
  190.     ioctl(0, TIOCGETC, &tch);
  191.     ioctl(0, TIOCGLTC, <ch);
  192.     gl_intrc = tch.t_intrc;
  193.     gl_quitc = tch.t_quitc;
  194.     gl_suspc = ltch.t_suspc;
  195.     gl_dsuspc = ltch.t_dsuspc;
  196.     ioctl(0, TIOCGETP, &old_tty);
  197.     new_tty = old_tty;
  198.     new_tty.sg_flags |= RAW;
  199.     new_tty.sg_flags &= ~ECHO;
  200.     ioctl(0, TIOCSETN, &new_tty);
  201. #else                /* SYSV */
  202.     ioctl(0, TCGETA, &old_termio);
  203.     gl_intrc = old_termio.c_cc[VINTR];
  204.     gl_quitc = old_termio.c_cc[VQUIT];
  205.     new_termio = old_termio;
  206.     new_termio.c_iflag &= ~(BRKINT|ISTRIP|IXON|IXOFF);
  207.     new_termio.c_iflag |= (IGNBRK|IGNPAR);
  208.     new_termio.c_lflag &= ~(ICANON|ISIG|ECHO);
  209.     new_termio.c_cc[VMIN] = 1;
  210.     new_termio.c_cc[VTIME] = 0;
  211.     ioctl(0, TCSETA, &new_termio);
  212. #endif /* TIOCSETN */
  213. #endif /* POSIX */
  214. #endif /* __unix__ */
  215.  
  216. #ifdef vms
  217.     descrip.dsc$w_length  = strlen("tt:");
  218.     descrip.dsc$b_dtype   = DSC$K_DTYPE_T;
  219.     descrip.dsc$b_class   = DSC$K_CLASS_S;
  220.     descrip.dsc$a_pointer = "tt:";
  221.     (void)sys$assign(&descrip,&chan,0,0);
  222.     (void)sys$qiow(0,chan,IO$_SENSEMODE,0,0,0,setbuff,8,0,0,0,0);
  223.     setbuff[1] |= TT$M_NOECHO;
  224.     (void)sys$qiow(0,chan,IO$_SETMODE,0,0,0,setbuff,8,0,0,0,0);
  225. #endif /* vms */
  226. }
  227.  
  228. static void
  229. gl_char_cleanup()        /* undo effects of gl_char_init */
  230. {
  231. #ifdef __unix__
  232. #ifdef POSIX 
  233.     tcsetattr(0, TCSANOW, &old_termios);
  234. #else             /* not POSIX */
  235. #ifdef TIOCSETN        /* BSD */
  236.     ioctl(0, TIOCSETN, &old_tty);
  237. #else            /* SYSV */
  238.     ioctl(0, TCSETA, &old_termio);
  239. #endif /* TIOCSETN */
  240. #endif /* POSIX */
  241. #endif /* __unix__ */
  242.  
  243. #ifdef vms
  244.     setbuff[1] &= ~TT$M_NOECHO;
  245.     (void)sys$qiow(0,chan,IO$_SETMODE,0,0,0,setbuff,8,0,0,0,0);
  246.     sys$dassgn(chan);
  247.     chan = -1;
  248. #endif 
  249. }
  250.  
  251. #if MSDOS || __GO32__ 
  252. int pc_keymap(c)
  253. int c;
  254. {
  255.     switch (c) {
  256.     case 72: c = 16;   /* up -> ^P */
  257.         break;
  258.     case 80: c = 14;   /* down -> ^N */
  259.         break;
  260.     case 75: c = 2;    /* left -> ^B */
  261.         break;
  262.     case 77: c = 6;    /* right -> ^F */
  263.         break;
  264.     default: c = 0;    /* make it garbage */
  265.     }
  266.     return c;
  267. }
  268. #endif /* MSDOS ||  __GO32__ */
  269.  
  270. #if __IBMC__ || __EMX__
  271. int pc_keymap(c)
  272. int c;
  273. {
  274.     switch (c) {
  275.     case 72: c = 16;   /* up -> ^P */
  276.         break;
  277.     case 80: c = 14;   /* down -> ^N */
  278.         break;
  279.     case 75: c = 2;    /* left -> ^B */
  280.         break;
  281.     case 77: c = 6;    /* right -> ^F */
  282.         break;
  283.     case 83: c = 4;    /* delete -> ^D */
  284.         break;
  285.     case 71: c = 1;    /* home -> ^A */
  286.         break;
  287.     case 79: c = 5;    /* end -> ^E */
  288.         break;
  289.     case 82: c =15;    /* insert -> ^O */
  290.         break;
  291.     default: c = 0;    /* make it garbage */
  292.     }
  293.     return c;
  294. }
  295. #endif /* __IBMC__ || __EMX__ */
  296.  
  297.  
  298.  
  299. static int
  300. gl_getc()
  301. /* get a character without echoing it to screen */
  302. {
  303.     int             c;
  304. #ifdef __unix__
  305.     char            ch;
  306. #endif
  307.  
  308. #ifdef __unix__
  309.     while ((c = read(0, &ch, 1)) == -1) {
  310.     if (errno != EINTR)
  311.         break;
  312.     }
  313.     c = (ch <= 0)? -1 : ch;
  314. #endif    /* __unix__ */
  315. #ifdef MSDOS
  316.     c = _bios_keybrd(_NKEYBRD_READ);
  317. #endif  /* MSDOS */
  318. #ifdef __GO32__
  319.     c = getkey () ;
  320.     if (c > 255) c = pc_keymap(c & 0377);
  321. #endif /* __GO32__ */
  322. #ifdef __TURBOC__
  323.     while(!bioskey(1))
  324.     ;
  325.     c = bioskey(0);
  326. #endif
  327. #if MSDOS || __TURBOC__
  328.     if ((c & 0377) == 224) {
  329.     c = pc_keymap((c >> 8) & 0377);
  330.     } else {
  331.     c &= 0377;
  332.     }
  333. #endif /* MSDOS || __TURBOC__ */
  334.  
  335. #ifdef __IBMC__
  336.  c=_getch();
  337.     if (c == 224 || c == 0) {
  338.         c = pc_keymap(_getch());
  339.     } else {
  340.         c &= 0377;
  341.     }
  342. #endif
  343.  
  344. #ifdef __EMX__
  345.     c = _read_kbd(0, 1, 0);
  346.     if (c == 224 || c == 0) {
  347.         c = pc_keymap(_read_kbd(0, 1, 0));
  348.     } else {
  349.         c &= 0377;
  350.     }
  351. #endif
  352. #ifdef vms
  353.     if(chan < 0) {
  354.        c='\0';
  355.     }
  356.     (void)sys$qiow(0,chan,IO$_TTYREADALL,0,0,0,&c,1,0,0,0,0);
  357.     c &= 0177;                        /* get a char */
  358. #endif
  359.     return c;
  360. }
  361.  
  362. static void
  363. gl_putc(c)
  364. int     c;
  365. {
  366.     char   ch = c;
  367.  
  368.     write(1, &ch, 1);
  369.     if (ch == '\n') {
  370.     ch = '\r';
  371.         write(1, &ch, 1);    /* RAW mode needs '\r', does not hurt */
  372.     }
  373. }
  374.  
  375. /******************** fairly portable part *********************************/
  376.  
  377. static void
  378. gl_puts(buf)
  379. char *buf;
  380. {
  381.     int len; 
  382.     
  383.     if (buf) {
  384.         len = strlen(buf);
  385.         write(1, buf, len);
  386.     }
  387. }
  388.  
  389. static void
  390. gl_error(buf)
  391. char *buf;
  392. {
  393.     int len = strlen(buf);
  394.  
  395.     gl_cleanup();
  396.     write(2, buf, len);
  397.     exit(1);
  398. }
  399.  
  400. static void
  401. gl_init()
  402. /* set up variables and terminal */
  403. {
  404.     if (gl_init_done < 0) {        /* -1 only on startup */
  405.         hist_init();
  406.     }
  407.     if (isatty(0) == 0 || isatty(1) == 0)
  408.     gl_error("\n*** Error: getline(): not interactive, use stdio.\n");
  409.     gl_char_init();
  410.     gl_init_done = 1;
  411. }
  412.  
  413. static void
  414. gl_cleanup()
  415. /* undo effects of gl_init, as necessary */
  416. {
  417.     if (gl_init_done > 0)
  418.         gl_char_cleanup();
  419.     gl_init_done = 0;
  420. }
  421.  
  422. void
  423. gl_setwidth(w)
  424. int  w;
  425. {
  426.     if (w > 20) {
  427.     gl_termw = w;
  428.     gl_scroll = w / 3;
  429.     } else {
  430.     gl_error("\n*** Error: minimum screen width is 21\n");
  431.     }
  432. }
  433.  
  434. char *
  435. getline(prompt)
  436. char *prompt;
  437. {
  438.     int             c, loc, tmp;
  439.  
  440. #ifdef __unix__
  441.     int                sig;
  442. #endif
  443.  
  444.     gl_init();    
  445.     gl_prompt = (prompt)? prompt : "";
  446.     gl_buf[0] = 0;
  447.     if (gl_in_hook)
  448.     gl_in_hook(gl_buf);
  449.     gl_fixup(gl_prompt, -2, BUF_SIZE);
  450.     while ((c = gl_getc()) >= 0) {
  451.     gl_extent = 0;      /* reset to full extent */
  452.     if (isprint(c)) {
  453.         if (gl_search_mode)
  454.            search_addchar(c);
  455.         else
  456.            gl_addchar(c);
  457.     } else {
  458.         if (gl_search_mode) {
  459.             if (c == '\033' || c == '\016' || c == '\020') {
  460.                 search_term();
  461.                 c = 0;             /* ignore the character */
  462.         } else if (c == '\010' || c == '\177') {
  463.             search_addchar(-1); /* unwind search string */
  464.             c = 0;
  465.         } else if (c != '\022' && c != '\023') {
  466.             search_term();    /* terminate and handle char */
  467.         }
  468.         }
  469.         switch (c) {
  470.           case '\n': case '\r':             /* newline */
  471.         gl_newline();
  472.         gl_cleanup();
  473.         return gl_buf;
  474.         /*NOTREACHED*/
  475.         break; 
  476.           case '\001': gl_fixup(gl_prompt, -1, 0);        /* ^A */
  477.         break;
  478.           case '\002': gl_fixup(gl_prompt, -1, gl_pos-1);    /* ^B */
  479.         break;
  480.           case '\004':                    /* ^D */
  481.         if (gl_cnt == 0) {
  482.             gl_buf[0] = 0;
  483.             gl_cleanup();
  484.             gl_putc('\n');
  485.             return gl_buf;
  486.         } else {
  487.             gl_del(0);
  488.         }
  489.         break;
  490.           case '\005': gl_fixup(gl_prompt, -1, gl_cnt);    /* ^E */
  491.         break;
  492.           case '\006': gl_fixup(gl_prompt, -1, gl_pos+1);    /* ^F */
  493.         break;
  494.           case '\010': case '\177': gl_del(-1);    /* ^H and DEL */
  495.         break;
  496.           case '\t':                        /* TAB */
  497.                 if (gl_tab_hook) {
  498.             tmp = gl_pos;
  499.                 loc = gl_tab_hook(gl_buf, gl_strlen(gl_prompt), &tmp);
  500.                 if (loc >= 0 || tmp != gl_pos)
  501.                     gl_fixup(gl_prompt, loc, tmp);
  502.                 }
  503.         break;
  504.           case '\013': gl_kill(gl_pos);            /* ^K */
  505.         break;
  506.           case '\014': gl_redraw();                /* ^L */
  507.         break;
  508.           case '\016':                     /* ^N */
  509.         strcpy(gl_buf, hist_next());
  510.                 if (gl_in_hook)
  511.                 gl_in_hook(gl_buf);
  512.         gl_fixup(gl_prompt, 0, BUF_SIZE);
  513.         break;
  514.           case '\017': gl_overwrite = !gl_overwrite;           /* ^O */
  515.         break;
  516.           case '\020':                     /* ^P */
  517.         strcpy(gl_buf, hist_prev());
  518.                 if (gl_in_hook)
  519.                 gl_in_hook(gl_buf);
  520.         gl_fixup(gl_prompt, 0, BUF_SIZE);
  521.         break;
  522.           case '\022': search_back(1);            /* ^R */
  523.         break;
  524.           case '\023': search_forw(1);            /* ^S */
  525.         break;
  526.           case '\024': gl_transpose();            /* ^T */
  527.         break;
  528.               case '\025': gl_kill(0);                /* ^U */
  529.         break;
  530.           case '\031': gl_yank();                /* ^Y */
  531.         break;
  532.           case '\033':                /* ansi arrow keys */
  533.         c = gl_getc();
  534.         if (c == '[') {
  535.             switch(c = gl_getc()) {
  536.               case 'A':                         /* up */
  537.                 strcpy(gl_buf, hist_prev());
  538.                         if (gl_in_hook)
  539.                         gl_in_hook(gl_buf);
  540.                 gl_fixup(gl_prompt, 0, BUF_SIZE);
  541.                 break;
  542.               case 'B':                             /* down */
  543.                 strcpy(gl_buf, hist_next());
  544.                         if (gl_in_hook)
  545.                         gl_in_hook(gl_buf);
  546.                 gl_fixup(gl_prompt, 0, BUF_SIZE);
  547.                 break;
  548.               case 'C': gl_fixup(gl_prompt, -1, gl_pos+1); /* right */
  549.                 break;
  550.               case 'D': gl_fixup(gl_prompt, -1, gl_pos-1); /* left */
  551.                 break;
  552.               default: gl_putc('\007');         /* who knows */
  553.                 break;
  554.             }
  555.         } else if (c == 'f' || c == 'F') {
  556.             gl_word(1);
  557.         } else if (c == 'b' || c == 'B') {
  558.             gl_word(-1);
  559.         } else
  560.             gl_putc('\007');
  561.         break;
  562.           default:        /* check for a terminal signal */
  563. #ifdef __unix__
  564.             if (c > 0) {    /* ignore 0 (reset above) */
  565.                 sig = 0;
  566. #ifdef SIGINT
  567.                 if (c == gl_intrc)
  568.                     sig = SIGINT;
  569. #endif
  570. #ifdef SIGQUIT
  571.                 if (c == gl_quitc)
  572.                     sig = SIGQUIT;
  573. #endif
  574. #ifdef SIGTSTP
  575.                 if (c == gl_suspc || c == gl_dsuspc)
  576.                     sig = SIGTSTP;
  577. #endif
  578.                     if (sig != 0) {
  579.                     gl_cleanup();
  580.                     kill(0, sig);
  581.                     gl_init();
  582.                     gl_redraw();
  583.             c = 0;
  584.             } 
  585.         }
  586. #endif /* __unix__ */
  587.                 if (c > 0)
  588.             gl_putc('\007');
  589.         break;
  590.         }
  591.     }
  592.     }
  593.     gl_cleanup();
  594.     gl_buf[0] = 0;
  595.     return gl_buf;
  596. }
  597.  
  598. static void
  599. gl_addchar(c)
  600. int c;
  601. /* adds the character c to the input buffer at current location */
  602. {
  603.     int  i;
  604.  
  605.     if (gl_cnt >= BUF_SIZE - 1)
  606.     gl_error("\n*** Error: getline(): input buffer overflow\n");
  607.     if (gl_overwrite == 0 || gl_pos == gl_cnt) {
  608.         for (i=gl_cnt; i >= gl_pos; i--)
  609.             gl_buf[i+1] = gl_buf[i];
  610.         gl_buf[gl_pos] = c;
  611.         gl_fixup(gl_prompt, gl_pos, gl_pos+1);
  612.     } else {
  613.     gl_buf[gl_pos] = c;
  614.     gl_extent = 1;
  615.         gl_fixup(gl_prompt, gl_pos, gl_pos+1);
  616.     }
  617. }
  618.  
  619. static void
  620. gl_yank()
  621. /* adds the kill buffer to the input buffer at current location */
  622. {
  623.     int  i, len;
  624.  
  625.     len = strlen(gl_killbuf);
  626.     if (len > 0) {
  627.     if (gl_overwrite == 0) {
  628.             if (gl_cnt + len >= BUF_SIZE - 1) 
  629.             gl_error("\n*** Error: getline(): input buffer overflow\n");
  630.             for (i=gl_cnt; i >= gl_pos; i--)
  631.                 gl_buf[i+len] = gl_buf[i];
  632.         for (i=0; i < len; i++)
  633.                 gl_buf[gl_pos+i] = gl_killbuf[i];
  634.             gl_fixup(gl_prompt, gl_pos, gl_pos+len);
  635.     } else {
  636.         if (gl_pos + len > gl_cnt) {
  637.                 if (gl_pos + len >= BUF_SIZE - 1) 
  638.                 gl_error("\n*** Error: getline(): input buffer overflow\n");
  639.         gl_buf[gl_pos + len] = 0;
  640.             }
  641.         for (i=0; i < len; i++)
  642.                 gl_buf[gl_pos+i] = gl_killbuf[i];
  643.         gl_extent = len;
  644.             gl_fixup(gl_prompt, gl_pos, gl_pos+len);
  645.     }
  646.     } else
  647.     gl_putc('\007');
  648. }
  649.  
  650. static void
  651. gl_transpose()
  652. /* switch character under cursor and to left of cursor */
  653. {
  654.     int    c;
  655.  
  656.     if (gl_pos > 0 && gl_cnt > gl_pos) {
  657.     c = gl_buf[gl_pos-1];
  658.     gl_buf[gl_pos-1] = gl_buf[gl_pos];
  659.     gl_buf[gl_pos] = c;
  660.     gl_extent = 2;
  661.     gl_fixup(gl_prompt, gl_pos-1, gl_pos);
  662.     } else
  663.     gl_putc('\007');
  664. }
  665.  
  666. static void
  667. gl_newline()
  668. /*
  669.  * Cleans up entire line before returning to caller. A \n is appended.
  670.  * If line longer than screen, we redraw starting at beginning
  671.  */
  672. {
  673.     int change = gl_cnt;
  674.     int len = gl_cnt;
  675.     int loc = gl_width - 5;    /* shifts line back to start position */
  676.  
  677.     if (gl_cnt >= BUF_SIZE - 1) 
  678.         gl_error("\n*** Error: getline(): input buffer overflow\n");
  679.     if (gl_out_hook) {
  680.     change = gl_out_hook(gl_buf);
  681.         len = strlen(gl_buf);
  682.     } 
  683.     if (loc > len)
  684.     loc = len;
  685.     gl_fixup(gl_prompt, change, loc);    /* must do this before appending \n */
  686.     gl_buf[len] = '\n';
  687.     gl_buf[len+1] = '\0';
  688.     gl_putc('\n');
  689. }
  690.  
  691. static void
  692. gl_del(loc)
  693. int loc;
  694. /*
  695.  * Delete a character.  The loc variable can be:
  696.  *    -1 : delete character to left of cursor
  697.  *     0 : delete character under cursor
  698.  */
  699. {
  700.     int i;
  701.  
  702.     if ((loc == -1 && gl_pos > 0) || (loc == 0 && gl_pos < gl_cnt)) {
  703.         for (i=gl_pos+loc; i < gl_cnt; i++)
  704.         gl_buf[i] = gl_buf[i+1];
  705.     gl_fixup(gl_prompt, gl_pos+loc, gl_pos+loc);
  706.     } else
  707.     gl_putc('\007');
  708. }
  709.  
  710. static void
  711. gl_kill(pos)
  712. int pos;
  713. /* delete from pos to the end of line */
  714. {
  715.     if (pos < gl_cnt) {
  716.     strcpy(gl_killbuf, gl_buf + pos);
  717.     gl_buf[pos] = '\0';
  718.     gl_fixup(gl_prompt, pos, pos);
  719.     } else
  720.     gl_putc('\007');
  721. }
  722.  
  723. static void
  724. gl_word(direction)
  725. int direction;
  726. /* move forward or backword one word */
  727. {
  728.     int pos = gl_pos;
  729.  
  730.     if (direction > 0) {        /* forward */
  731.         while (!isspace(gl_buf[pos]) && pos < gl_cnt) 
  732.         pos++;
  733.     while (isspace(gl_buf[pos]) && pos < gl_cnt)
  734.         pos++;
  735.     } else {                /* backword */
  736.     if (pos > 0)
  737.         pos--;
  738.     while (isspace(gl_buf[pos]) && pos > 0)
  739.         pos--;
  740.         while (!isspace(gl_buf[pos]) && pos > 0) 
  741.         pos--;
  742.     if (pos < gl_cnt && isspace(gl_buf[pos]))   /* move onto word */
  743.         pos++;
  744.     }
  745.     gl_fixup(gl_prompt, -1, pos);
  746. }
  747.  
  748. static void
  749. gl_redraw()
  750. /* emit a newline, reset and redraw prompt and current input line */
  751. {
  752.     if (gl_init_done > 0) {
  753.         gl_putc('\n');
  754.         gl_fixup(gl_prompt, -2, gl_pos);
  755.     }
  756. }
  757.  
  758. static void
  759. gl_fixup(prompt, change, cursor)
  760. char  *prompt;
  761. int    change, cursor;
  762. /*
  763.  * This function is used both for redrawing when input changes or for
  764.  * moving within the input line.  The parameters are:
  765.  *   prompt:  compared to last_prompt[] for changes;
  766.  *   change : the index of the start of changes in the input buffer,
  767.  *            with -1 indicating no changes, -2 indicating we're on
  768.  *            a new line, redraw everything.
  769.  *   cursor : the desired location of the cursor after the call.
  770.  *            A value of BUF_SIZE can be used  to indicate the cursor should
  771.  *            move just past the end of the input line.
  772.  */
  773. {
  774.     static int   gl_shift;    /* index of first on screen character */
  775.     static int   off_right;    /* true if more text right of screen */
  776.     static int   off_left;    /* true if more text left of screen */
  777.     static char  last_prompt[80] = "";
  778.     int          left = 0, right = -1;        /* bounds for redraw */
  779.     int          pad;        /* how much to erase at end of line */
  780.     int          backup;        /* how far to backup before fixing */
  781.     int          new_shift;     /* value of shift based on cursor */
  782.     int          extra;         /* adjusts when shift (scroll) happens */
  783.     int          i;
  784.     int          new_right = -1; /* alternate right bound, using gl_extent */
  785.     int          l1, l2;
  786.  
  787.     if (change == -2) {   /* reset */
  788.     gl_pos = gl_cnt = gl_shift = off_right = off_left = 0;
  789.     gl_putc('\r');
  790.     gl_puts(prompt);
  791.     strcpy(last_prompt, prompt);
  792.     change = 0;
  793.         gl_width = gl_termw - gl_strlen(prompt);
  794.     } else if (strcmp(prompt, last_prompt) != 0) {
  795.     l1 = gl_strlen(last_prompt);
  796.     l2 = gl_strlen(prompt);
  797.     gl_cnt = gl_cnt + l1 - l2;
  798.     strcpy(last_prompt, prompt);
  799.     gl_putc('\r');
  800.     gl_puts(prompt);
  801.     gl_pos = gl_shift;
  802.         gl_width = gl_termw - l2;
  803.     change = 0;
  804.     }
  805.     pad = (off_right)? gl_width - 1 : gl_cnt - gl_shift;   /* old length */
  806.     backup = gl_pos - gl_shift;
  807.     if (change >= 0) {
  808.         gl_cnt = strlen(gl_buf);
  809.         if (change > gl_cnt)
  810.         change = gl_cnt;
  811.     }
  812.     if (cursor > gl_cnt) {
  813.     if (cursor != BUF_SIZE)        /* BUF_SIZE means end of line */
  814.         gl_putc('\007');
  815.     cursor = gl_cnt;
  816.     }
  817.     if (cursor < 0) {
  818.     gl_putc('\007');
  819.     cursor = 0;
  820.     }
  821.     if (off_right || (off_left && cursor < gl_shift + gl_width - gl_scroll / 2))
  822.     extra = 2;            /* shift the scrolling boundary */
  823.     else 
  824.     extra = 0;
  825.     new_shift = cursor + extra + gl_scroll - gl_width;
  826.     if (new_shift > 0) {
  827.     new_shift /= gl_scroll;
  828.     new_shift *= gl_scroll;
  829.     } else
  830.     new_shift = 0;
  831.     if (new_shift != gl_shift) {    /* scroll occurs */
  832.     gl_shift = new_shift;
  833.     off_left = (gl_shift)? 1 : 0;
  834.     off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0;
  835.         left = gl_shift;
  836.     new_right = right = (off_right)? gl_shift + gl_width - 2 : gl_cnt;
  837.     } else if (change >= 0) {        /* no scroll, but text changed */
  838.     if (change < gl_shift + off_left) {
  839.         left = gl_shift;
  840.     } else {
  841.         left = change;
  842.         backup = gl_pos - change;
  843.     }
  844.     off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0;
  845.     right = (off_right)? gl_shift + gl_width - 2 : gl_cnt;
  846.     new_right = (gl_extent && (right > left + gl_extent))? 
  847.                  left + gl_extent : right;
  848.     }
  849.     pad -= (off_right)? gl_width - 1 : gl_cnt - gl_shift;
  850.     pad = (pad < 0)? 0 : pad;
  851.     if (left <= right) {        /* clean up screen */
  852.     for (i=0; i < backup; i++)
  853.         gl_putc('\b');
  854.     if (left == gl_shift && off_left) {
  855.         gl_putc('$');
  856.         left++;
  857.         }
  858.     for (i=left; i < new_right; i++)
  859.         gl_putc(gl_buf[i]);
  860.     gl_pos = new_right;
  861.     if (off_right && new_right == right) {
  862.         gl_putc('$');
  863.         gl_pos++;
  864.     } else { 
  865.         for (i=0; i < pad; i++)    /* erase remains of prev line */
  866.         gl_putc(' ');
  867.         gl_pos += pad;
  868.     }
  869.     }
  870.     i = gl_pos - cursor;        /* move to final cursor location */
  871.     if (i > 0) {
  872.     while (i--)
  873.        gl_putc('\b');
  874.     } else {
  875.     for (i=gl_pos; i < cursor; i++)
  876.         gl_putc(gl_buf[i]);
  877.     }
  878.     gl_pos = cursor;
  879. }
  880.  
  881. static int
  882. gl_tab(buf, offset, loc)
  883. char  *buf;
  884. int    offset;
  885. int   *loc;
  886. /* default tab handler, acts like tabstops every 8 cols */
  887. {
  888.     int i, count, len;
  889.  
  890.     len = strlen(buf);
  891.     count = 8 - (offset + *loc) % 8;
  892.     for (i=len; i >= *loc; i--)
  893.         buf[i+count] = buf[i];
  894.     for (i=0; i < count; i++)
  895.         buf[*loc+i] = ' ';
  896.     i = *loc;
  897.     *loc = i + count;
  898.     return i;
  899. }
  900.  
  901. /******************* strlen stuff **************************************/
  902.  
  903. void gl_strwidth(func)
  904. size_t (*func)();
  905. {
  906.     if (func != 0) {
  907.     gl_strlen = func;
  908.     }
  909. }
  910.  
  911. /******************* History stuff **************************************/
  912.  
  913. #ifndef HIST_SIZE
  914. #define HIST_SIZE 100
  915. #endif
  916.  
  917. static int      hist_pos = 0, hist_last = 0;
  918. static char    *hist_buf[HIST_SIZE];
  919.  
  920. static void
  921. hist_init()
  922. {
  923.     int i;
  924.  
  925.     hist_buf[0] = "";
  926.     for (i=1; i < HIST_SIZE; i++)
  927.     hist_buf[i] = (char *)0;
  928. }
  929.  
  930. void
  931. gl_histadd(buf)
  932. char *buf;
  933. {
  934.     static char *prev = 0;
  935.     char *p = buf;
  936.     int len;
  937.  
  938.     /* in case we call gl_histadd() before we call getline() */
  939.     if (gl_init_done < 0) {        /* -1 only on startup */
  940.         hist_init();
  941.         gl_init_done = 0;
  942.     }
  943.     while (*p == ' ' || *p == '\t' || *p == '\n') 
  944.     p++;
  945.     if (*p) {
  946.     len = strlen(buf);
  947.     if (strchr(p, '\n'))     /* previously line already has NL stripped */
  948.         len--;
  949.     if (prev == 0 || strlen(prev) != len || 
  950.                 strncmp(prev, buf, len) != 0) {
  951.             hist_buf[hist_last] = hist_save(buf);
  952.         prev = hist_buf[hist_last];
  953.             hist_last = (hist_last + 1) % HIST_SIZE;
  954.             if (hist_buf[hist_last] && *hist_buf[hist_last]) {
  955.             free(hist_buf[hist_last]);
  956.             }
  957.         hist_buf[hist_last] = "";
  958.     }
  959.     }
  960.     hist_pos = hist_last;
  961. }
  962.  
  963. static char *
  964. hist_prev()
  965. /* loads previous hist entry into input buffer, sticks on first */
  966. {
  967.     char *p = 0;
  968.     int   next = (hist_pos - 1 + HIST_SIZE) % HIST_SIZE;
  969.  
  970.     if (hist_buf[hist_pos] != 0 && next != hist_last) {
  971.         hist_pos = next;
  972.         p = hist_buf[hist_pos];
  973.     } 
  974.     if (p == 0) {
  975.     p = "";
  976.     gl_putc('\007');
  977.     }
  978.     return p;
  979. }
  980.  
  981. static char *
  982. hist_next()
  983. /* loads next hist entry into input buffer, clears on last */
  984. {
  985.     char *p = 0;
  986.  
  987.     if (hist_pos != hist_last) {
  988.         hist_pos = (hist_pos+1) % HIST_SIZE;
  989.     p = hist_buf[hist_pos];
  990.     } 
  991.     if (p == 0) {
  992.     p = "";
  993.     gl_putc('\007');
  994.     }
  995.     return p;
  996. }
  997.  
  998. static char *
  999. hist_save(p)
  1000. char *p;
  1001. /* makes a copy of the string */
  1002. {
  1003.     char *s = 0;
  1004.     int   len = strlen(p);
  1005.     char *nl = strchr(p, '\n');
  1006.  
  1007.     if (nl) {
  1008.         if ((s = malloc(len)) != 0) {
  1009.             strncpy(s, p, len-1);
  1010.         s[len-1] = 0;
  1011.     }
  1012.     } else {
  1013.         if ((s = malloc(len+1)) != 0) {
  1014.             strcpy(s, p);
  1015.         }
  1016.     }
  1017.     if (s == 0) 
  1018.     gl_error("\n*** Error: hist_save() failed on malloc\n");
  1019.     return s;
  1020. }
  1021.  
  1022. /******************* Search stuff **************************************/
  1023.  
  1024. static char  search_prompt[101];  /* prompt includes search string */
  1025. static char  search_string[100];
  1026. static int   search_pos = 0;      /* current location in search_string */
  1027. static int   search_forw_flg = 0; /* search direction flag */
  1028. static int   search_last = 0;      /* last match found */
  1029.  
  1030. static void  
  1031. search_update(c)
  1032. int c;
  1033. {
  1034.     if (c == 0) {
  1035.     search_pos = 0;
  1036.         search_string[0] = 0;
  1037.         search_prompt[0] = '?';
  1038.         search_prompt[1] = ' ';
  1039.         search_prompt[2] = 0;
  1040.     } else if (c > 0) {
  1041.         search_string[search_pos] = c;
  1042.         search_string[search_pos+1] = 0;
  1043.         search_prompt[search_pos] = c;
  1044.         search_prompt[search_pos+1] = '?';
  1045.         search_prompt[search_pos+2] = ' ';
  1046.         search_prompt[search_pos+3] = 0;
  1047.     search_pos++;
  1048.     } else {
  1049.     if (search_pos > 0) {
  1050.         search_pos--;
  1051.             search_string[search_pos] = 0;
  1052.             search_prompt[search_pos] = '?';
  1053.             search_prompt[search_pos+1] = ' ';
  1054.             search_prompt[search_pos+2] = 0;
  1055.     } else {
  1056.         gl_putc('\007');
  1057.         hist_pos = hist_last;
  1058.     }
  1059.     }
  1060. }
  1061.  
  1062. static void 
  1063. search_addchar(c)
  1064. int  c;
  1065. {
  1066.     char *loc;
  1067.  
  1068.     search_update(c);
  1069.     if (c < 0) {
  1070.     if (search_pos > 0) {
  1071.         hist_pos = search_last;
  1072.     } else {
  1073.         gl_buf[0] = 0;
  1074.         hist_pos = hist_last;
  1075.     }
  1076.     strcpy(gl_buf, hist_buf[hist_pos]);
  1077.     }
  1078.     if ((loc = strstr(gl_buf, search_string)) != 0) {
  1079.     gl_fixup(search_prompt, 0, loc - gl_buf);
  1080.     } else if (search_pos > 0) {
  1081.         if (search_forw_flg) {
  1082.         search_forw(0);
  1083.         } else {
  1084.         search_back(0);
  1085.         }
  1086.     } else {
  1087.     gl_fixup(search_prompt, 0, 0);
  1088.     }
  1089. }
  1090.  
  1091. static void     
  1092. search_term()
  1093. {
  1094.     gl_search_mode = 0;
  1095.     if (gl_buf[0] == 0)        /* not found, reset hist list */
  1096.         hist_pos = hist_last;
  1097.     if (gl_in_hook)
  1098.     gl_in_hook(gl_buf);
  1099.     gl_fixup(gl_prompt, 0, gl_pos);
  1100. }
  1101.  
  1102. static void     
  1103. search_back(new_search)
  1104. int new_search;
  1105. {
  1106.     int    found = 0;
  1107.     char  *p, *loc;
  1108.  
  1109.     search_forw_flg = 0;
  1110.     if (gl_search_mode == 0) {
  1111.     search_last = hist_pos = hist_last;    
  1112.     search_update(0);    
  1113.     gl_search_mode = 1;
  1114.         gl_buf[0] = 0;
  1115.     gl_fixup(search_prompt, 0, 0);
  1116.     } else if (search_pos > 0) {
  1117.     while (!found) {
  1118.         p = hist_prev();
  1119.         if (*p == 0) {        /* not found, done looking */
  1120.            gl_buf[0] = 0;
  1121.            gl_fixup(search_prompt, 0, 0);
  1122.            found = 1;
  1123.         } else if ((loc = strstr(p, search_string)) != 0) {
  1124.            strcpy(gl_buf, p);
  1125.            gl_fixup(search_prompt, 0, loc - p);
  1126.            if (new_search)
  1127.            search_last = hist_pos;
  1128.            found = 1;
  1129.         } 
  1130.     }
  1131.     } else {
  1132.         gl_putc('\007');
  1133.     }
  1134. }
  1135.  
  1136. static void     
  1137. search_forw(new_search)
  1138. int new_search;
  1139. {
  1140.     int    found = 0;
  1141.     char  *p, *loc;
  1142.  
  1143.     search_forw_flg = 1;
  1144.     if (gl_search_mode == 0) {
  1145.     search_last = hist_pos = hist_last;    
  1146.     search_update(0);    
  1147.     gl_search_mode = 1;
  1148.         gl_buf[0] = 0;
  1149.     gl_fixup(search_prompt, 0, 0);
  1150.     } else if (search_pos > 0) {
  1151.     while (!found) {
  1152.         p = hist_next();
  1153.         if (*p == 0) {        /* not found, done looking */
  1154.            gl_buf[0] = 0;
  1155.            gl_fixup(search_prompt, 0, 0);
  1156.            found = 1;
  1157.         } else if ((loc = strstr(p, search_string)) != 0) {
  1158.            strcpy(gl_buf, p);
  1159.            gl_fixup(search_prompt, 0, loc - p);
  1160.            if (new_search)
  1161.            search_last = hist_pos;
  1162.            found = 1;
  1163.         } 
  1164.     }
  1165.     } else {
  1166.         gl_putc('\007');
  1167.     }
  1168. }
  1169.