home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume28 / input-edit / part01 next >
Text File  |  1992-02-23  |  43KB  |  1,394 lines

  1. Newsgroups: comp.sources.misc
  2. From: thewalt@canuck.CE.Berkeley.EDU (Chris Thewalt)
  3. Subject:  v28i056:  input-edit - C input functions for line editing with history, Part01/01
  4. Message-ID: <1992Feb18.152653.20566@sparky.imd.sterling.com>
  5. X-Md4-Signature: d07f13a59deb6e202103e5a937ce1dad
  6. Date: Tue, 18 Feb 1992 15:26:53 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: thewalt@canuck.CE.Berkeley.EDU (Chris Thewalt)
  10. Posting-number: Volume 28, Issue 56
  11. Archive-name: input-edit/part01
  12. Environment: UNIX, MS-DOS
  13. Supersedes: input-edit: Volume 25, Issue 56
  14.  
  15. This is a complete repost of the input-edit package posted in volume
  16. 25 and patched in volume 26.
  17.  
  18. Motivation:
  19.  
  20.   Many interactive programs read input line by line, but would like to
  21.   provide line editing and history functionality to the end-user that 
  22.   runs the program.
  23.  
  24.   The input-edit package provides that functionality.  As far as the 
  25.   programmer is concerned, the program only asks for the next line
  26.   of input. However, until the user presses the RETURN key they can use
  27.   emacs-style line editing commands and can traverse the history of lines
  28.   previously typed.
  29.  
  30.   Other packages, such as GNU's readline, have greater capability but are
  31.   also substantially larger.  Input-edit is small, since it uses neither
  32.   stdio nor any termcap features, and is also quite portable.  It only uses
  33.   \b to backspace and \007 to ring the bell on errors.  Since it cannot
  34.   edit multiple lines it scrolls long lines left and right on the same line.
  35.  
  36. Environment:
  37.   
  38.   Input edit uses classic (not ANSI) C, and should run on any Unix 
  39.   system (BSD or SYSV), PC's with the MSC compiler, or Vax/VMS.
  40.   
  41.   Porting the package to new systems basicaly requires code to read a
  42.   character when it is typed without echoing it, everything else should be OK.
  43.  
  44.   I have run the package on:
  45.  
  46.     DECstation 5000, Ultrix 4.2 with cc and gcc
  47.     Sun Sparc 2, SunOS 4.1.1, with cc
  48.     SGI Iris, IRIX System V.3, with cc
  49.     PC, DRDOS 5.0, with MSC 6.0
  50.     (I haven't tested the VMS code)
  51.  
  52. Changes since last release:
  53.  
  54.   * The user no longer calls gl_init() and gl_cleanup(), getline() sets
  55.     required terminal modes on entry and resets before returning.  This
  56.     was necessary to capture changes in terminal modes that the main
  57.     program might be making.
  58.   * Getline() now looks to see which characters are bound to signal
  59.     generation, and when these characters are seen getline() resets
  60.     terminal modes before passing on the signal.  If the signal handler
  61.     returns to getline(), the screen is automatically updated and editing
  62.     can continue.
  63.   * The toggle key for overwrite mode has been moved from ^G to ^O
  64.   * All code is now classic rather than ANSI C, so any compiler should
  65.     be able to handle it.
  66.   * ^Y now yanks back previously kill'ed (^K) text starting at the
  67.     current location.
  68.   * ^R/^S begin reverse and forward incremental searches through the
  69.     history list.
  70.   * The programmer must add buffers onto the history list by calling
  71.     gl_addhist(char *buffer).  This function makes copies of the buffer
  72.     and adds them to the history list if the buffer is not a blank line
  73.     and if the buffer is different than the last item saved (so the
  74.     program need not check for these conditions)
  75.   * The main program can specify the screen width to use with a call to
  76.     gl_setwidth(int width)
  77.   * Getline now insists that both the input and output are connected to
  78.     a terminal. If this is not the case, an error message is written and
  79.     the program is terminated.  The main program should check for this
  80.     case and use buffered IO (stdio) for non-interactive sessions.
  81.  
  82. #!/bin/sh
  83. # This is a shell archive (produced by shar 3.49)
  84. # To extract the files from this archive, save it to a file, remove
  85. # everything above the "!/bin/sh" line above, and type "sh file_name".
  86. #
  87. # made 02/11/1992 16:24 UTC by thewalt@canuck.CE.Berkeley.EDU
  88. # Source directory /usr/users/thewalt/sw/getline
  89. #
  90. # existing files will NOT be overwritten unless -c is specified
  91. #
  92. # This shar contains:
  93. # length  mode       name
  94. # ------ ---------- ------------------------------------------
  95. #   8280 -rw-r--r-- README
  96. #   1608 -rw-r--r-- CHANGES
  97. #  26245 -rw-r--r-- getline.c
  98. #    309 -rw-r--r-- testgl.c
  99. #
  100. # ============= README ==============
  101. if test -f 'README' -a X"$1" != X"-c"; then
  102.     echo 'x - skipping README (File already exists)'
  103. else
  104. echo 'x - extracting README (Text)'
  105. sed 's/^X//' << 'SHAR_EOF' > 'README' &&
  106. *************************** Motivation **********************************
  107. X
  108. Many interactive programs read input line by line, but would like to
  109. provide line editing and history functionality to the end-user that 
  110. runs the program.
  111. X
  112. The input-edit package provides that functionality.  As far as the 
  113. programmer is concerned, the program only asks for the next line
  114. of input. However, until the user presses the RETURN key they can use
  115. emacs-style line editing commands and can traverse the history of lines
  116. previously typed.
  117. X
  118. Other packages, such as GNU's readline, have greater capability but are
  119. also substantially larger.  Input-edit is small, since it uses neither
  120. stdio nor any termcap features, and is also quite portable.  It only uses
  121. \b to backspace and \007 to ring the bell on errors.  Since it cannot
  122. edit multiple lines it scrolls long lines left and right on the same line.
  123. X
  124. Input edit uses classic (not ANSI) C, and should run on any Unix 
  125. system (BSD or SYSV), PC's with the MSC compiler, or Vax/VMS (untested by me).  
  126. Porting the package to new systems basicaly requires code to read a
  127. character when it is typed without echoing it, everything else should be OK.
  128. X
  129. I have run the package on:
  130. X
  131. X    DECstation 5000, Ultrix 4.2 with cc and gcc
  132. X    Sun Sparc 2, SunOS 4.1.1, with cc
  133. X    SGI Iris, IRIX System V.3, with cc
  134. X    PC, DRDOS 5.0, with MSC 6.0
  135. X
  136. The description below is broken into two parts, the end-user (editing) 
  137. interface and the programmer interface.  Send bug reports, fixes and 
  138. enhancements to:
  139. X
  140. Chris Thewalt (thewalt@ce.berkeley.edu)   
  141. 2/4/92
  142. X
  143. PS: I don't have, and don't want to add, a vi mode, sorry.
  144. X
  145. ************************** End-User Interface ***************************
  146. X
  147. Entering printable keys generally inserts new text into the buffer (unless
  148. in overwrite mode, see below).  Other special keys can be used to modify
  149. the text in the buffer.  In the description of the keys below, ^n means
  150. Control-n, or holding the CONTROL key down while pressing "n".  Errors
  151. will ring the terminal bell.
  152. X
  153. ^A/^E    : Move cursor to beginning/end of the line.
  154. ^F/^B   : Move cursor forward/backward one character.
  155. ^D    : Delete the character under the cursor.
  156. ^H, DEL : Delete the character to the left of the cursor.
  157. ^K    : Kill from the cursor to the end of line.
  158. ^L    : Redraw current line.
  159. ^O    : Toggle overwrite/insert mode. Initially in insert mode. Text
  160. X      added in overwrite mode (including yanks) overwrite
  161. X      existing text, while insert mode does not overwrite.
  162. ^P/^N   : Move to previous/next item on history list.
  163. ^R/^S   : Perform incremental reverse/forward search for string on
  164. X      the history list.  Typing normal characters adds to the current
  165. X      search string and searches for a match. Typing ^R/^S marks
  166. X      the start of a new search, and moves on to the next match.
  167. X      Typing ^H or DEL deletes the last character from the search 
  168. X      string, and searches from the starting location of the last search.  
  169. X      Therefore, repeated DEL's appear to unwind to the match nearest 
  170. X      the point at which the last ^R or ^S was typed.  If DEL is 
  171. X      repeated until the search string is empty the search location 
  172. X      begins from the start of the history list.  Typing ESC or 
  173. X      any other editing character accepts the current match and 
  174. X      loads it into the buffer, terminating the search.
  175. ^T    : Toggle the characters under and to the left of the cursor.
  176. ^Y    : Yank previously killed text back at current location.  Note that
  177. X      this will overwrite or insert, depending on the current mode.
  178. TAB    : By default adds spaces to buffer to get to next TAB stop 
  179. X      (just after every 8th column), although this may be rebound by the 
  180. X      programmer, as described below.
  181. NL, CR  : returns current buffer to the program.
  182. X
  183. DOS and ANSI terminal arrow key sequences are recognized, and act like:
  184. X
  185. X  up    : same as ^P
  186. X  down  : same as ^N
  187. X  left  : same as ^B
  188. X  right : same as ^F
  189. X
  190. ************************** Programmer Interface ***************************
  191. X
  192. The programmer accesses input-edit through three functions, and optionally
  193. through three additional function pointer hooks.  The three functions are:
  194. X
  195. char *getline(char *prompt)
  196. X
  197. X    Prints the prompt and allows the user to edit the current line. A
  198. X    pointer to the line is returned when the user finishes by
  199. X    typing a newline or a return.  Unlike GNU readline, the returned
  200. X    pointer points to a static buffer, so it should not be free'd, and
  201. X    the buffer contains the newline character.  The user enters an
  202. X    end-of-file by typing ^D on an empty line, in which case the
  203. X    first character of the returned buffer is '\0'.  Getline never
  204. X    returns a NULL pointer.  The getline functions sets terminal modes
  205. X    needed to make it work, and resets them before returning to the
  206. X    caller.  The getline function also looks for characters that would
  207. X    generate a signal, and resets the terminal modes before raising the
  208. X    signal condition.  If the signal handler returns to getline, 
  209. X    the screen is automatically redrawn and editing can continue.
  210. X    Getline now requires both the input and output stream be connected
  211. X    to the terminal (not redirected) so the main program should check
  212. X    to make sure this is true.  If input or output have been redirected
  213. X    the main program should use buffered IO (stdio) rather than
  214. X    the slow 1 character read()s that getline uses.
  215. X
  216. void gl_setwidth(int width)
  217. X
  218. X        Set the width of the terminal to the specified width. The default
  219. X    width is 80 characters, so this function need only be called if the
  220. X    width of the terminal is not 80.  Since horizontal scrolling is
  221. X    controlled by this parameter it is important to get it right.
  222. X
  223. void gl_histadd(char *buf)
  224. X
  225. X    The gl_histadd function checks to see if the buf is not empty or
  226. X    whitespace, and also checks to make sure it is different than
  227. X    the last saved buffer to avoid repeats on the history list.
  228. X    If the buf is a new non-blank string a copy is made and saved on
  229. X    the history list, so the caller can re-use the specified buf.
  230. X
  231. The main loop in testgl.c, included in this directory, shows how the
  232. input-edit package can be used:
  233. X
  234. extern char *getline();
  235. extern void  gl_histadd();
  236. main()
  237. {
  238. X    char *p;
  239. X    do {
  240. X    p = getline("PROMPT>>>> ");
  241. X    gl_histadd(p);
  242. X    fputs(p, stdout);
  243. X    } while (*p != 0);
  244. }
  245. X
  246. In order to allow the main program to have additional access to the buffer,
  247. to implement things such as completion or auto-indent modes, three
  248. function pointers can be bound to user functions to modify the buffer as
  249. described below.  By default gl_in_hook and gl_out_hook are set to NULL,
  250. and gl_tab_hook is bound to a function that inserts spaces until the next
  251. logical tab stop is reached.  The user can reassign any of these pointers
  252. to other functions.  Each of the functions bound to these hooks receives
  253. the current buffer as the first argument, and must return the location of
  254. the leftmost change made in the buffer.  If the buffer isn't modified the
  255. functions should return -1.  When the hook function returns the screen is
  256. updated to reflect any changes made by the user function.
  257. X
  258. int (*gl_in_hook)(char *buf)
  259. X
  260. X    If gl_in_hook is non-NULL the function is called each time a new 
  261. X    buffer is loaded. It is called when getline is entered, with an
  262. X    empty buffer, it is called each time a new buffer is loaded from
  263. X    the history with ^P or ^N, and it is called when an incremental
  264. X    search string is accepted (when the search is terminated). The
  265. X    buffer can be modified and will be redrawn upon return to getline(). 
  266. X
  267. int (*gl_out_hook)(char *buf)
  268. X
  269. X    If gl_out_hook is non-NULL it is called when a line has been
  270. X    completed by the user entering a newline or return. The buffer
  271. X    handed to the hook does not yet have the newline appended. If the
  272. X    buffer is modified the screen is redrawn before getline returns the
  273. X    buffer to the caller.
  274. X
  275. int (*gl_tab_hook)(char *buf, int prompt_width, int *cursor_loc)
  276. X
  277. X    If gl_tab_hook is non-NULL, it is called whenever a tab is typed.
  278. X    In addition to receiving the buffer, the current prompt width is
  279. X    given (needed to do tabbing right) and a pointer to the cursor
  280. X    offset is given, where a 0 offset means the first character in the
  281. X    line.  Not only does the cursor_loc tell the programmer where the
  282. X    TAB was received, but it can be reset so that the cursor will end
  283. X    up at the specified location after the screen is redrawn.
  284. SHAR_EOF
  285. chmod 0644 README ||
  286. echo 'restore of README failed'
  287. Wc_c="`wc -c < 'README'`"
  288. test 8280 -eq "$Wc_c" ||
  289.     echo 'README: original size 8280, current size' "$Wc_c"
  290. fi
  291. # ============= CHANGES ==============
  292. if test -f 'CHANGES' -a X"$1" != X"-c"; then
  293.     echo 'x - skipping CHANGES (File already exists)'
  294. else
  295. echo 'x - extracting CHANGES (Text)'
  296. sed 's/^X//' << 'SHAR_EOF' > 'CHANGES' &&
  297. Changes from last release (see the README file for more details):
  298. X
  299. * The user no longer calls gl_init() and gl_cleanup(), getline() sets
  300. X  required terminal modes on entry and resets before returning.  This
  301. X  was necessary to capture changes in terminal modes that the main
  302. X  program might be making.
  303. * Getline() now looks to see which characters are bound to signal
  304. X  generation, and when these characters are seen getline() resets
  305. X  terminal modes before passing on the signal.  If the signal handler
  306. X  returns to getline(), the screen is automatically updated and editing
  307. X  can continue.
  308. * The toggle key for overwrite mode has been moved from ^G to ^O
  309. * All code is now classic rather than ANSI C, so any compiler should
  310. X  be able to handle it.
  311. * ^Y now yanks back previously kill'ed (^K) text starting at the
  312. X  current location.
  313. * ^R/^S begin reverse and forward incremental searches through the
  314. X  history list.
  315. * The programmer must add buffers onto the history list by calling
  316. X  gl_addhist(char *buffer).  This function makes copies of the buffer
  317. X  and adds them to the history list if the buffer is not a blank line
  318. X  and if the buffer is different than the last item saved (so the
  319. X  program need not check for these conditions)
  320. * The main program can specify the screen width to use with a call to
  321. X  gl_setwidth(int width)
  322. * Getline now insists that both the input and output are connected to
  323. X  a terminal. If this is not the case, an error message is written and
  324. X  the program is terminated.  The main program should check for this
  325. X  case and use buffered IO (stdio) for non-interactive sessions.
  326. X
  327. SHAR_EOF
  328. chmod 0644 CHANGES ||
  329. echo 'restore of CHANGES failed'
  330. Wc_c="`wc -c < 'CHANGES'`"
  331. test 1608 -eq "$Wc_c" ||
  332.     echo 'CHANGES: original size 1608, current size' "$Wc_c"
  333. fi
  334. # ============= getline.c ==============
  335. if test -f 'getline.c' -a X"$1" != X"-c"; then
  336.     echo 'x - skipping getline.c (File already exists)'
  337. else
  338. echo 'x - extracting getline.c (Text)'
  339. sed 's/^X//' << 'SHAR_EOF' > 'getline.c' &&
  340. #ifndef lint
  341. static char     rcsid[] =
  342. "$Id: getline.c,v 3.1 1992/02/11 16:15:16 thewalt Exp thewalt $";
  343. static char    *copyright = "Copyright (C) 1991, 1992, Chris Thewalt";
  344. #endif
  345. X
  346. /*
  347. X * Copyright (C) 1991, 1992 by Chris Thewalt (thewalt@ce.berkeley.edu)
  348. X *
  349. X * Permission to use, copy, modify, and distribute this software 
  350. X * for any purpose and without fee is hereby granted, provided
  351. X * that the above copyright notices appear in all copies and that both the
  352. X * copyright notice and this permission notice appear in supporting
  353. X * documentation.  This software is provided "as is" without express or
  354. X * implied warranty.
  355. X */
  356. X
  357. static int      gl_tab();  /* forward reference needed for gl_tab_hook */
  358. X
  359. /********************* exported interface ********************************/
  360. X
  361. char           *getline();        /* read a line of input */
  362. void            gl_setwidth();        /* specify width of screen */
  363. void            gl_histadd();        /* adds entries to hist */
  364. X
  365. int         (*gl_in_hook)() = 0;
  366. int         (*gl_out_hook)() = 0;
  367. int         (*gl_tab_hook)() = gl_tab;
  368. X
  369. /******************** imported interface *********************************/
  370. X
  371. #include <string.h>
  372. #include <ctype.h>
  373. #include <errno.h>
  374. #include <signal.h>
  375. X
  376. extern int      isatty();    
  377. extern void    *malloc();
  378. extern void     free();
  379. extern int      kill();    
  380. X
  381. /******************** internal interface *********************************/
  382. X
  383. #define BUF_SIZE 1024
  384. X
  385. static int      gl_init_done = -1;    /* terminal mode flag  */
  386. static int      gl_termw = 80;        /* actual terminal width */
  387. static int      gl_scroll = 27;        /* width of EOL scrolling region */
  388. static int      gl_width = 0;        /* net size available for input */
  389. static int      gl_extent = 0;        /* how far to redraw, 0 means all */
  390. static int      gl_overwrite = 0;    /* overwrite mode */
  391. static int      gl_pos, gl_cnt = 0;     /* position and size of input */
  392. static char     gl_buf[BUF_SIZE];       /* input buffer */
  393. static char     gl_killbuf[BUF_SIZE]=""; /* killed text */
  394. static char    *gl_prompt;        /* to save the prompt string */
  395. static char     gl_intrc = 0;        /* keyboard SIGINT char */
  396. static char     gl_quitc = 0;        /* keyboard SIGQUIT char */
  397. static char     gl_suspc = 0;        /* keyboard SIGTSTP char */
  398. static char     gl_dsuspc = 0;        /* delayed SIGTSTP char */
  399. static int      gl_search_mode = 0;    /* search mode flag */
  400. X
  401. static void     gl_init();        /* prepare to edit a line */
  402. static void     gl_cleanup();        /* to undo gl_init */
  403. static void     gl_char_init();        /* get ready for no echo input */
  404. static void     gl_char_cleanup();    /* undo gl_char_init */
  405. X
  406. static void     gl_addchar();        /* install specified char */
  407. static void     gl_del();        /* del, either left (-1) or cur (0) */
  408. static void     gl_error();        /* write error msg and die */
  409. static void     gl_fixup();        /* fixup state variables and screen */
  410. static int      gl_getc();        /* read one char from terminal */
  411. static void     gl_kill();        /* delete to EOL */
  412. static void     gl_newline();        /* handle \n or \r */
  413. static void     gl_putc();        /* write one char to terminal */
  414. static void     gl_puts();        /* write a line to terminal */
  415. static void     gl_redraw();        /* issue \n and redraw all */
  416. static void     gl_transpose();        /* transpose two chars */
  417. static void     gl_yank();        /* yank killed text */
  418. X
  419. static void     hist_init();    /* initializes hist pointers */
  420. static char    *hist_next();    /* return ptr to next item */
  421. static char    *hist_prev();    /* return ptr to prev item */
  422. static char    *hist_save();    /* makes copy of a string, without NL */
  423. X
  424. static void     search_addchar();    /* increment search string */
  425. static void     search_term();        /* reset with current contents */
  426. static void     search_back();        /* look back for current string */
  427. static void     search_forw();        /* look forw for current string */
  428. X
  429. /************************ nonportable part *********************************/
  430. X
  431. extern int      write();
  432. extern void     exit();
  433. X
  434. #ifdef MSDOS
  435. #include <bios.h>
  436. #endif
  437. X
  438. #ifdef unix
  439. extern int      read();
  440. extern int      ioctl();
  441. #include <sys/ioctl.h>
  442. X
  443. #ifdef TIOCGETP        /* use BSD interface if possible */
  444. #include <sgtty.h>
  445. struct sgttyb   new_tty, old_tty;
  446. struct tchars   tch;
  447. struct ltchars  ltch;
  448. #else
  449. #ifdef SIGTSTP        /* need POSIX interface to handle SUSP */
  450. #include <termios.h>
  451. struct termios  new_termios, old_termios;
  452. #else             /* use SYSV interface */
  453. #include <termio.h>
  454. struct termio   new_termio, old_termio;
  455. #endif
  456. #endif
  457. #endif    /* unix */
  458. X
  459. #ifdef vms
  460. #include <descrip.h>
  461. #include <ttdef.h>
  462. #include <iodef.h>
  463. #include unixio
  464. X   
  465. static int   setbuff[2];             /* buffer to set terminal attributes */
  466. static short chan = -1;              /* channel to terminal */
  467. struct dsc$descriptor_s descrip;     /* VMS descriptor */
  468. #endif
  469. X
  470. static void
  471. gl_char_init()            /* turn off input echo */
  472. {
  473. #ifdef unix
  474. #ifdef TIOCGETP            /* BSD */
  475. X    ioctl(0, TIOCGETC, &tch);
  476. X    ioctl(0, TIOCGLTC, <ch);
  477. X    gl_intrc = tch.t_intrc;
  478. X    gl_quitc = tch.t_quitc;
  479. X    gl_suspc = ltch.t_suspc;
  480. X    gl_dsuspc = ltch.t_dsuspc;
  481. X    ioctl(0, TIOCGETP, &old_tty);
  482. X    new_tty = old_tty;
  483. X    new_tty.sg_flags |= RAW;
  484. X    new_tty.sg_flags &= ~ECHO;
  485. X    ioctl(0, TIOCSETP, &new_tty);
  486. #else
  487. #ifdef SIGTSTP            /* POSIX */
  488. X    tcgetattr(0, &old_termios);
  489. X    gl_intrc = old_termios.c_cc[VINTR];
  490. X    gl_quitc = old_termios.c_cc[VQUIT];
  491. #ifdef VSUSP
  492. X    gl_suspc = old_termios.c_cc[VSUSP];
  493. #endif
  494. #ifdef VDSUSP
  495. X    gl_dsuspc = old_termios.c_cc[VDSUSP];
  496. #endif
  497. X    new_termios = old_termios;
  498. X    new_termios.c_iflag &= ~(BRKINT|ISTRIP|IXON|IXOFF);
  499. X    new_termios.c_iflag |= (IGNBRK|IGNPAR);
  500. X    new_termios.c_lflag &= ~(ICANON|ISIG|IEXTEN|ECHO);
  501. X    new_termios.c_cc[VMIN] = 1;
  502. X    new_termios.c_cc[VTIME] = 0;
  503. X    ioctl(0, TCSETA, &new_termios);
  504. #else                /* SYSV */
  505. X    ioctl(0, TCGETA, &old_termio);
  506. X    gl_intrc = old_termio.c_cc[VINTR];
  507. X    gl_quitc = old_termio.c_cc[VQUIT];
  508. X    new_termio = old_termio;
  509. X    new_termio.c_iflag &= ~(BRKINT|ISTRIP|IXON|IXOFF);
  510. X    new_termio.c_iflag |= (IGNBRK|IGNPAR);
  511. X    new_termio.c_lflag &= ~(ICANON|ISIG|ECHO);
  512. X    new_termio.c_cc[VMIN] = 1;
  513. X    new_termio.c_cc[VTIME] = 0;
  514. X    ioctl(0, TCSETA, &new_termio);
  515. #endif 
  516. #endif
  517. #endif /* unix */
  518. X
  519. #ifdef vms
  520. X    descrip.dsc$w_length  = strlen("tt:");
  521. X    descrip.dsc$b_dtype   = DSC$K_DTYPE_T;
  522. X    descrip.dsc$b_class   = DSC$K_CLASS_S;
  523. X    descrip.dsc$a_pointer = "tt:";
  524. X    (void)sys$assign(&descrip,&chan,0,0);
  525. X    (void)sys$qiow(0,chan,IO$_SENSEMODE,0,0,0,setbuff,8,0,0,0,0);
  526. X    setbuff[1] |= TT$M_NOECHO;
  527. X    (void)sys$qiow(0,chan,IO$_SETMODE,0,0,0,setbuff,8,0,0,0,0);
  528. #endif /* vms */
  529. }
  530. X
  531. static void
  532. gl_char_cleanup()        /* undo effects of gl_char_init */
  533. {
  534. #ifdef unix
  535. #ifdef TIOCSETP        /* BSD */
  536. X    ioctl(0, TIOCSETP, &old_tty);
  537. #else
  538. #ifdef SIGTSTP        /* POSIX */
  539. X    tcsetattr(0, TCSANOW, &old_termios);
  540. #else            /* SYSV */
  541. X    ioctl(0, TCSETA, &old_termio);
  542. #endif
  543. #endif
  544. #endif /* unix */
  545. X
  546. #ifdef vms
  547. X    setbuff[1] &= ~TT$M_NOECHO;
  548. X    (void)sys$qiow(0,chan,IO$_SETMODE,0,0,0,setbuff,8,0,0,0,0);
  549. X    sys$dassgn(chan);
  550. X    chan = -1;
  551. #endif 
  552. }
  553. X
  554. static int
  555. gl_getc()
  556. /* get a character without echoing it to screen */
  557. {
  558. X    int             c;
  559. X    char            ch;
  560. X
  561. #ifdef unix
  562. X    c = (read(0, &ch, 1) > 0)? ch : -1;
  563. #endif
  564. #ifdef MSDOS
  565. X    c = _bios_keybrd(_NKEYBRD_READ);
  566. X    if ((c & 0377) == 224) {
  567. X    switch (c = (c >> 8) & 0377) {
  568. X      case 72: c = 16;   /* up -> ^P */
  569. X        break;
  570. X          case 80: c = 14;   /* down -> ^N */
  571. X        break;
  572. X      case 75: c = 2;    /* left -> ^B */
  573. X        break;
  574. X          case 77: c = 6;    /* right -> ^F */
  575. X        break;
  576. X      default: c = 0;    /* make it garbage */
  577. X    }
  578. X    } else {
  579. X    c = c & 0377;
  580. X    }
  581. #endif
  582. #ifdef vms
  583. X    if(chan < 0) {
  584. X       c='\0';
  585. X    }
  586. X    (void)sys$qiow(0,chan,IO$_TTYREADALL,0,0,0,&c,1,0,0,0,0);
  587. X    c &= 0177;                        /* get a char */
  588. #endif
  589. X    return c;
  590. }
  591. X
  592. static void
  593. gl_putc(c)
  594. int     c;
  595. {
  596. X    char   ch = c;
  597. X
  598. X    write(1, &ch, 1);
  599. #ifdef unix
  600. #ifdef TIOCSETP        /* BSD in RAW mode, map NL to NL,CR */
  601. X    if (ch == '\n') {
  602. X    ch = '\r';
  603. X        write(1, &ch, 1);
  604. X    }
  605. #endif
  606. #endif
  607. }
  608. X
  609. /******************** fairly portable part *********************************/
  610. X
  611. static void
  612. gl_puts(buf)
  613. char *buf;
  614. {
  615. X    int len = strlen(buf);
  616. X
  617. X    write(1, buf, len);
  618. }
  619. X
  620. static void
  621. gl_error(buf)
  622. char *buf;
  623. {
  624. X    int len = strlen(buf);
  625. X
  626. X    gl_cleanup();
  627. X    write(2, buf, len);
  628. X    exit(1);
  629. }
  630. X
  631. static void
  632. gl_init()
  633. /* set up variables and terminal */
  634. {
  635. X    if (gl_init_done < 0) {        /* -1 only on startup */
  636. X        hist_init();
  637. X    }
  638. X    if (isatty(0) == 0 || isatty(1) == 0)
  639. X    gl_error("\n*** Error: getline(): not interactive, use stdio.\n");
  640. X    gl_char_init();
  641. X    gl_init_done = 1;
  642. }
  643. X
  644. static void
  645. gl_cleanup()
  646. /* undo effects of gl_init, as necessary */
  647. {
  648. X    if (gl_init_done > 0)
  649. X        gl_char_cleanup();
  650. X    gl_init_done = 0;
  651. }
  652. X
  653. void
  654. gl_setwidth(w)
  655. int  w;
  656. {
  657. X    if (w > 20) {
  658. X    gl_termw = w;
  659. X    gl_scroll = w / 3;
  660. X    } else {
  661. X    gl_error("\n*** Error: minimum screen width is 21\n");
  662. X    }
  663. }
  664. X
  665. char *
  666. getline(prompt)
  667. char *prompt;
  668. {
  669. X    int             c, loc, tmp;
  670. X    int                sig;
  671. X
  672. X    gl_init();    
  673. X    gl_prompt = (prompt)? prompt : "";
  674. X    gl_buf[0] = 0;
  675. X    if (gl_in_hook)
  676. X    gl_in_hook(gl_buf);
  677. X    gl_fixup(gl_prompt, -2, BUF_SIZE);
  678. X    while ((c = gl_getc()) >= 0) {
  679. X    gl_extent = 0;      /* reset to full extent */
  680. X    if (isprint(c)) {
  681. X        if (gl_search_mode)
  682. X           search_addchar(c);
  683. X        else
  684. X           gl_addchar(c);
  685. X    } else {
  686. X        if (gl_search_mode) {
  687. X            if (c == '\033' || c == '\016' || c == '\020') {
  688. X                search_term();
  689. X                c = 0;             /* ignore the character */
  690. X        } else if (c == '\010' || c == '\177') {
  691. X            search_addchar(-1); /* unwind search string */
  692. X            c = 0;
  693. X        } else if (c != '\022' && c != '\023') {
  694. X            search_term();    /* terminate and handle char */
  695. X        }
  696. X        }
  697. X        switch (c) {
  698. X          case '\n': case '\r':             /* newline */
  699. X        gl_newline();
  700. X        gl_cleanup();
  701. X        return gl_buf;
  702. X        /*NOTREACHED*/
  703. X        break; 
  704. X          case '\001': gl_fixup(gl_prompt, -1, 0);        /* ^A */
  705. X        break;
  706. X          case '\002': gl_fixup(gl_prompt, -1, gl_pos-1);    /* ^B */
  707. X        break;
  708. X          case '\004':                    /* ^D */
  709. X        if (gl_cnt == 0) {
  710. X            gl_buf[0] = 0;
  711. X            gl_cleanup();
  712. X            gl_putc('\n');
  713. X            return gl_buf;
  714. X        } else {
  715. X            gl_del(0);
  716. X        }
  717. X        break;
  718. X          case '\005': gl_fixup(gl_prompt, -1, gl_cnt);    /* ^E */
  719. X        break;
  720. X          case '\006': gl_fixup(gl_prompt, -1, gl_pos+1);    /* ^F */
  721. X        break;
  722. X          case '\010': case '\177': gl_del(-1);    /* ^H and DEL */
  723. X        break;
  724. X          case '\t':                        /* TAB */
  725. X                if (gl_tab_hook) {
  726. X            tmp = gl_pos;
  727. X                loc = gl_tab_hook(gl_buf, strlen(gl_prompt), &tmp);
  728. X                if (loc >= 0 || tmp != gl_pos)
  729. X                    gl_fixup(gl_prompt, loc, tmp);
  730. X                }
  731. X        break;
  732. X          case '\013': gl_kill();                /* ^K */
  733. X        break;
  734. X          case '\014': gl_redraw();                /* ^L */
  735. X        break;
  736. X          case '\016':                     /* ^N */
  737. X        strcpy(gl_buf, hist_next());
  738. X                if (gl_in_hook)
  739. X                gl_in_hook(gl_buf);
  740. X        gl_fixup(gl_prompt, 0, BUF_SIZE);
  741. X        break;
  742. X          case '\017': gl_overwrite = !gl_overwrite;           /* ^O */
  743. X        break;
  744. X          case '\020':                     /* ^P */
  745. X        strcpy(gl_buf, hist_prev());
  746. X                if (gl_in_hook)
  747. X                gl_in_hook(gl_buf);
  748. X        gl_fixup(gl_prompt, 0, BUF_SIZE);
  749. X        break;
  750. X          case '\022': search_back(1);            /* ^R */
  751. X        break;
  752. X          case '\023': search_forw(1);            /* ^S */
  753. X        break;
  754. X          case '\024': gl_transpose();            /* ^T */
  755. X        break;
  756. X          case '\031': gl_yank();                /* ^Y */
  757. X        break;
  758. X          case '\033':                /* ansi arrow keys */
  759. X        c = gl_getc();
  760. X        if (c == '[') {
  761. X            switch(c = gl_getc()) {
  762. X              case 'A':                         /* up */
  763. X                strcpy(gl_buf, hist_prev());
  764. X                        if (gl_in_hook)
  765. X                        gl_in_hook(gl_buf);
  766. X                gl_fixup(gl_prompt, 0, BUF_SIZE);
  767. X                break;
  768. X              case 'B':                             /* down */
  769. X                strcpy(gl_buf, hist_next());
  770. X                        if (gl_in_hook)
  771. X                        gl_in_hook(gl_buf);
  772. X                gl_fixup(gl_prompt, 0, BUF_SIZE);
  773. X                break;
  774. X              case 'C': gl_fixup(gl_prompt, -1, gl_pos+1); /* right */
  775. X                break;
  776. X              case 'D': gl_fixup(gl_prompt, -1, gl_pos-1); /* left */
  777. X                break;
  778. X              default: gl_putc('\007');         /* who knows */
  779. X                break;
  780. X            }
  781. X        } else
  782. X            gl_putc('\007');
  783. X        break;
  784. X          default:        /* check for a terminal signal */
  785. #ifdef unix
  786. X            if (c > 0) {    /* ignore 0 (reset above) */
  787. X                sig = 0;
  788. #ifdef SIGINT
  789. X                if (c == gl_intrc)
  790. X                    sig = SIGINT;
  791. #endif
  792. #ifdef SIGQUIT
  793. X                if (c == gl_quitc)
  794. X                    sig = SIGQUIT;
  795. #endif
  796. #ifdef SIGTSTP
  797. X                if (c == gl_suspc || c == gl_dsuspc)
  798. X                    sig = SIGTSTP;
  799. #endif
  800. X                    if (sig != 0) {
  801. X                    gl_cleanup();
  802. X                    kill(0, sig);
  803. X                    gl_init();
  804. X                    gl_redraw();
  805. X            c = 0;
  806. X            } 
  807. X        }
  808. #endif /* unix */
  809. X                if (c > 0)
  810. X            gl_putc('\007');
  811. X        break;
  812. X        }
  813. X    }
  814. X    }
  815. X    gl_cleanup();
  816. X    gl_buf[0] = 0;
  817. X    return gl_buf;
  818. }
  819. X
  820. static void
  821. gl_addchar(c)
  822. int c;
  823. /* adds the character c to the input buffer at current location */
  824. {
  825. X    int  i;
  826. X
  827. X    if (gl_cnt >= BUF_SIZE - 1)
  828. X    gl_error("\n*** Error: getline(): input buffer overflow\n");
  829. X    if (gl_overwrite == 0 || gl_pos == gl_cnt) {
  830. X        for (i=gl_cnt; i >= gl_pos; i--)
  831. X            gl_buf[i+1] = gl_buf[i];
  832. X        gl_buf[gl_pos] = c;
  833. X        gl_fixup(gl_prompt, gl_pos, gl_pos+1);
  834. X    } else {
  835. X    gl_buf[gl_pos] = c;
  836. X    gl_extent = 1;
  837. X        gl_fixup(gl_prompt, gl_pos, gl_pos+1);
  838. X    }
  839. }
  840. X
  841. static void
  842. gl_yank()
  843. /* adds the kill buffer to the input buffer at current location */
  844. {
  845. X    int  i, len;
  846. X
  847. X    len = strlen(gl_killbuf);
  848. X    if (len > 0) {
  849. X    if (gl_overwrite == 0) {
  850. X            if (gl_cnt + len >= BUF_SIZE - 1) 
  851. X            gl_error("\n*** Error: getline(): input buffer overflow\n");
  852. X            for (i=gl_cnt; i >= gl_pos; i--)
  853. X                gl_buf[i+len] = gl_buf[i];
  854. X        for (i=0; i < len; i++)
  855. X                gl_buf[gl_pos+i] = gl_killbuf[i];
  856. X            gl_fixup(gl_prompt, gl_pos, gl_pos+len);
  857. X    } else {
  858. X        if (gl_pos + len > gl_cnt) {
  859. X                if (gl_pos + len >= BUF_SIZE - 1) 
  860. X                gl_error("\n*** Error: getline(): input buffer overflow\n");
  861. X        gl_buf[gl_pos + len] = 0;
  862. X            }
  863. X        for (i=0; i < len; i++)
  864. X                gl_buf[gl_pos+i] = gl_killbuf[i];
  865. X        gl_extent = len;
  866. X            gl_fixup(gl_prompt, gl_pos, gl_pos+len);
  867. X    }
  868. X    } else
  869. X    gl_putc('\007');
  870. }
  871. X
  872. static void
  873. gl_transpose()
  874. /* switch character under cursor and to left of cursor */
  875. {
  876. X    int    c;
  877. X
  878. X    if (gl_pos > 0 && gl_cnt > gl_pos) {
  879. X    c = gl_buf[gl_pos-1];
  880. X    gl_buf[gl_pos-1] = gl_buf[gl_pos];
  881. X    gl_buf[gl_pos] = c;
  882. X    gl_extent = 2;
  883. X    gl_fixup(gl_prompt, gl_pos-1, gl_pos);
  884. X    } else
  885. X    gl_putc('\007');
  886. }
  887. X
  888. static void
  889. gl_newline()
  890. /*
  891. X * Cleans up entire line before returning to caller. A \n is appended.
  892. X * If line longer than screen, we redraw starting at beginning
  893. X */
  894. {
  895. X    int change = gl_cnt;
  896. X    int len = gl_cnt;
  897. X    int loc = gl_width - 5;    /* shifts line back to start position */
  898. X
  899. X    if (gl_cnt >= BUF_SIZE - 1) 
  900. X        gl_error("\n*** Error: getline(): input buffer overflow\n");
  901. X    if (gl_out_hook) {
  902. X    change = gl_out_hook(gl_buf);
  903. X        len = strlen(gl_buf);
  904. X    } 
  905. X    if (loc > len)
  906. X    loc = len;
  907. X    gl_fixup(gl_prompt, change, loc);    /* must do this before appending \n */
  908. X    gl_buf[len] = '\n';
  909. X    gl_buf[len+1] = '\0';
  910. X    gl_putc('\n');
  911. }
  912. X
  913. static void
  914. gl_del(loc)
  915. int loc;
  916. /*
  917. X * Delete a character.  The loc variable can be:
  918. X *    -1 : delete character to left of cursor
  919. X *     0 : delete character under cursor
  920. X */
  921. {
  922. X    int i;
  923. X
  924. X    if ((loc == -1 && gl_pos > 0) || (loc == 0 && gl_pos < gl_cnt)) {
  925. X        for (i=gl_pos+loc; i < gl_cnt; i++)
  926. X        gl_buf[i] = gl_buf[i+1];
  927. X    gl_fixup(gl_prompt, gl_pos+loc, gl_pos+loc);
  928. X    } else
  929. X    gl_putc('\007');
  930. }
  931. X
  932. static void
  933. gl_kill()
  934. /* delete from current position to the end of line */
  935. {
  936. X    if (gl_pos < gl_cnt) {
  937. X    strcpy(gl_killbuf, gl_buf + gl_pos);
  938. X    gl_buf[gl_pos] = '\0';
  939. X    gl_fixup(gl_prompt, gl_pos, gl_pos);
  940. X    } else
  941. X    gl_putc('\007');
  942. }
  943. X
  944. static void
  945. gl_redraw()
  946. /* emit a newline, reset and redraw prompt and current input line */
  947. {
  948. X    if (gl_init_done > 0) {
  949. X        gl_putc('\n');
  950. X        gl_fixup(gl_prompt, -2, gl_pos);
  951. X    }
  952. }
  953. X
  954. static void
  955. gl_fixup(prompt, change, cursor)
  956. char  *prompt;
  957. int    change, cursor;
  958. /*
  959. X * This function is used both for redrawing when input changes or for
  960. X * moving within the input line.  The parameters are:
  961. X *   prompt:  compared to last_prompt[] for changes;
  962. X *   change : the index of the start of changes in the input buffer,
  963. X *            with -1 indicating no changes, -2 indicating we're on
  964. X *            a new line, redraw everything.
  965. X *   cursor : the desired location of the cursor after the call.
  966. X *            A value of BUF_SIZE can be used  to indicate the cursor should
  967. X *            move just past the end of the input line.
  968. X */
  969. {
  970. X    static int   gl_shift;    /* index of first on screen character */
  971. X    static int   off_right;    /* true if more text right of screen */
  972. X    static int   off_left;    /* true if more text left of screen */
  973. X    static char  last_prompt[80] = "";
  974. X    int          left = 0, right = -1;        /* bounds for redraw */
  975. X    int          pad;        /* how much to erase at end of line */
  976. X    int          backup;        /* how far to backup before fixing */
  977. X    int          new_shift;     /* value of shift based on cursor */
  978. X    int          extra;         /* adjusts when shift (scroll) happens */
  979. X    int          i;
  980. X    int          new_right = -1; /* alternate right bound, using gl_extent */
  981. X    int          l1, l2;
  982. X
  983. X    if (change == -2) {   /* reset */
  984. X    gl_pos = gl_cnt = gl_shift = off_right = off_left = 0;
  985. X    gl_puts(prompt);
  986. X    strcpy(last_prompt, prompt);
  987. X    change = 0;
  988. X        gl_width = gl_termw - strlen(prompt);
  989. X    } else if (strcmp(prompt, last_prompt) != 0) {
  990. X    l1 = strlen(last_prompt);
  991. X    l2 = strlen(prompt);
  992. X    gl_cnt = gl_cnt + l1 - l2;
  993. X    strcpy(last_prompt, prompt);
  994. X        backup = gl_pos - gl_shift + l1;
  995. X    for (i=0; i < backup; i++)
  996. X        gl_putc('\b');
  997. X    gl_puts(prompt);
  998. X    gl_pos = gl_shift;
  999. X        gl_width = gl_termw - l2;
  1000. X    change = 0;
  1001. X    }
  1002. X    pad = (off_right)? gl_width - 1 : gl_cnt - gl_shift;   /* old length */
  1003. X    backup = gl_pos - gl_shift;
  1004. X    if (change >= 0) {
  1005. X        gl_cnt = strlen(gl_buf);
  1006. X        if (change > gl_cnt)
  1007. X        change = gl_cnt;
  1008. X    }
  1009. X    if (cursor > gl_cnt) {
  1010. X    if (cursor != BUF_SIZE)        /* BUF_SIZE means end of line */
  1011. X        gl_putc('\007');
  1012. X    cursor = gl_cnt;
  1013. X    }
  1014. X    if (cursor < 0) {
  1015. X    gl_putc('\007');
  1016. X    cursor = 0;
  1017. X    }
  1018. X    if (off_right || (off_left && cursor < gl_shift + gl_width - gl_scroll / 2))
  1019. X    extra = 2;            /* shift the scrolling boundary */
  1020. X    else 
  1021. X    extra = 0;
  1022. X    new_shift = cursor + extra + gl_scroll - gl_width;
  1023. X    if (new_shift > 0) {
  1024. X    new_shift /= gl_scroll;
  1025. X    new_shift *= gl_scroll;
  1026. X    } else
  1027. X    new_shift = 0;
  1028. X    if (new_shift != gl_shift) {    /* scroll occurs */
  1029. X    gl_shift = new_shift;
  1030. X    off_left = (gl_shift)? 1 : 0;
  1031. X    off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0;
  1032. X        left = gl_shift;
  1033. X    new_right = right = (off_right)? gl_shift + gl_width - 2 : gl_cnt;
  1034. X    } else if (change >= 0) {        /* no scroll, but text changed */
  1035. X    if (change < gl_shift + off_left) {
  1036. X        left = gl_shift;
  1037. X    } else {
  1038. X        left = change;
  1039. X        backup = gl_pos - change;
  1040. X    }
  1041. X    off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0;
  1042. X    right = (off_right)? gl_shift + gl_width - 2 : gl_cnt;
  1043. X    new_right = (gl_extent && (right > left + gl_extent))? 
  1044. X                 left + gl_extent : right;
  1045. X    }
  1046. X    pad -= (off_right)? gl_width - 1 : gl_cnt - gl_shift;
  1047. X    pad = (pad < 0)? 0 : pad;
  1048. X    if (left <= right) {        /* clean up screen */
  1049. X    for (i=0; i < backup; i++)
  1050. X        gl_putc('\b');
  1051. X    if (left == gl_shift && off_left) {
  1052. X        gl_putc('$');
  1053. X        left++;
  1054. X        }
  1055. X    for (i=left; i < new_right; i++)
  1056. X        gl_putc(gl_buf[i]);
  1057. X    gl_pos = new_right;
  1058. X    if (off_right && new_right == right) {
  1059. X        gl_putc('$');
  1060. X        gl_pos++;
  1061. X    } else { 
  1062. X        for (i=0; i < pad; i++)    /* erase remains of prev line */
  1063. X        gl_putc(' ');
  1064. X        gl_pos += pad;
  1065. X    }
  1066. X    }
  1067. X    i = gl_pos - cursor;        /* move to final cursor location */
  1068. X    if (i > 0) {
  1069. X    while (i--)
  1070. X       gl_putc('\b');
  1071. X    } else {
  1072. X    for (i=gl_pos; i < cursor; i++)
  1073. X        gl_putc(gl_buf[i]);
  1074. X    }
  1075. X    gl_pos = cursor;
  1076. }
  1077. X
  1078. static int
  1079. gl_tab(buf, offset, loc)
  1080. char  *buf;
  1081. int    offset;
  1082. int   *loc;
  1083. /* default tab handler, acts like tabstops every 8 cols */
  1084. {
  1085. X    int i, count, len;
  1086. X
  1087. X    len = strlen(buf);
  1088. X    count = 8 - (offset + *loc) % 8;
  1089. X    for (i=len; i >= *loc; i--)
  1090. X        buf[i+count] = buf[i];
  1091. X    for (i=0; i < count; i++)
  1092. X        buf[*loc+i] = ' ';
  1093. X    i = *loc;
  1094. X    *loc = i + count;
  1095. X    return i;
  1096. }
  1097. X
  1098. /******************* History stuff **************************************/
  1099. X
  1100. #ifndef HIST_SIZE
  1101. #define HIST_SIZE 100
  1102. #endif
  1103. X
  1104. static int      hist_pos = 0, hist_last = 0;
  1105. static char    *hist_buf[HIST_SIZE];
  1106. X
  1107. static void
  1108. hist_init()
  1109. {
  1110. X    int i;
  1111. X
  1112. X    hist_buf[0] = "";
  1113. X    for (i=1; i < HIST_SIZE; i++)
  1114. X    hist_buf[i] = (char *)0;
  1115. }
  1116. X
  1117. void
  1118. gl_histadd(buf)
  1119. char *buf;
  1120. {
  1121. X    static char *prev = 0;
  1122. X    char *p = buf;
  1123. X    int len;
  1124. X
  1125. X    while (*p == ' ' || *p == '\t' || *p == '\n') 
  1126. X    p++;
  1127. X    if (*p) {
  1128. X    len = strlen(buf);
  1129. X    if (strchr(p, '\n'))     /* previously line already has NL stripped */
  1130. X        len--;
  1131. X    if (prev == 0 || strlen(prev) != len || 
  1132. X                strncmp(prev, buf, len) != 0) {
  1133. X            hist_buf[hist_last] = hist_save(buf);
  1134. X        prev = hist_buf[hist_last];
  1135. X            hist_last = (hist_last + 1) % HIST_SIZE;
  1136. X            if (hist_buf[hist_last] && *hist_buf[hist_last]) {
  1137. X            free(hist_buf[hist_last]);
  1138. X            }
  1139. X        hist_buf[hist_last] = "";
  1140. X    }
  1141. X    }
  1142. X    hist_pos = hist_last;
  1143. }
  1144. X
  1145. static char *
  1146. hist_prev()
  1147. /* loads previous hist entry into input buffer, sticks on first */
  1148. {
  1149. X    char *p = 0;
  1150. X    int   next = (hist_pos - 1 + HIST_SIZE) % HIST_SIZE;
  1151. X
  1152. X    if (hist_buf[hist_pos] != 0 && next != hist_last) {
  1153. X        hist_pos = next;
  1154. X        p = hist_buf[hist_pos];
  1155. X    } 
  1156. X    if (p == 0) {
  1157. X    p = "";
  1158. X    gl_putc('\007');
  1159. X    }
  1160. X    return p;
  1161. }
  1162. X
  1163. static char *
  1164. hist_next()
  1165. /* loads next hist entry into input buffer, clears on last */
  1166. {
  1167. X    char *p = 0;
  1168. X
  1169. X    if (hist_pos != hist_last) {
  1170. X        hist_pos = (hist_pos+1) % HIST_SIZE;
  1171. X    p = hist_buf[hist_pos];
  1172. X    } 
  1173. X    if (p == 0) {
  1174. X    p = "";
  1175. X    gl_putc('\007');
  1176. X    }
  1177. X    return p;
  1178. }
  1179. X
  1180. static char *
  1181. hist_save(p)
  1182. char *p;
  1183. /* makes a copy of the string */
  1184. {
  1185. X    char *s = 0;
  1186. X    int   len = strlen(p);
  1187. X    char *nl = strchr(p, '\n');
  1188. X
  1189. X    if (nl) {
  1190. X        if ((s = malloc(len)) != 0) {
  1191. X            strncpy(s, p, len-1);
  1192. X        s[len-1] = 0;
  1193. X    }
  1194. X    } else {
  1195. X        if ((s = malloc(len+1)) != 0) {
  1196. X            strcpy(s, p);
  1197. X        }
  1198. X    }
  1199. X    if (s == 0) 
  1200. X    gl_error("\n*** Error: hist_save() failed on malloc\n");
  1201. X    return s;
  1202. }
  1203. X
  1204. /******************* Search stuff **************************************/
  1205. X
  1206. static char  search_prompt[101];  /* prompt includes search string */
  1207. static char  search_string[100];
  1208. static int   search_pos = 0;      /* current location in search_string */
  1209. static int   search_forw_flg = 0; /* search direction flag */
  1210. static int   search_last = 0;      /* last match found */
  1211. X
  1212. static void  
  1213. search_update(c)
  1214. int c;
  1215. {
  1216. X    if (c == 0) {
  1217. X    search_pos = 0;
  1218. X        search_string[0] = 0;
  1219. X        search_prompt[0] = '?';
  1220. X        search_prompt[1] = ' ';
  1221. X        search_prompt[2] = 0;
  1222. X    } else if (c > 0) {
  1223. X        search_string[search_pos] = c;
  1224. X        search_string[search_pos+1] = 0;
  1225. X        search_prompt[search_pos] = c;
  1226. X        search_prompt[search_pos+1] = '?';
  1227. X        search_prompt[search_pos+2] = ' ';
  1228. X        search_prompt[search_pos+3] = 0;
  1229. X    search_pos++;
  1230. X    } else {
  1231. X    if (search_pos > 0) {
  1232. X        search_pos--;
  1233. X            search_string[search_pos] = 0;
  1234. X            search_prompt[search_pos] = '?';
  1235. X            search_prompt[search_pos+1] = ' ';
  1236. X            search_prompt[search_pos+2] = 0;
  1237. X    } else {
  1238. X        gl_putc('\007');
  1239. X        hist_pos = hist_last;
  1240. X    }
  1241. X    }
  1242. }
  1243. X
  1244. static void 
  1245. search_addchar(c)
  1246. int  c;
  1247. {
  1248. X    char *loc;
  1249. X
  1250. X    search_update(c);
  1251. X    if (c < 0) {
  1252. X    if (search_pos > 0) {
  1253. X        hist_pos = search_last;
  1254. X    } else {
  1255. X        gl_buf[0] = 0;
  1256. X        hist_pos = hist_last;
  1257. X    }
  1258. X    strcpy(gl_buf, hist_buf[hist_pos]);
  1259. X    }
  1260. X    if ((loc = strstr(gl_buf, search_string)) != 0) {
  1261. X    gl_fixup(search_prompt, 0, loc - gl_buf);
  1262. X    } else if (search_pos > 0) {
  1263. X        if (search_forw_flg) {
  1264. X        search_forw(0);
  1265. X        } else {
  1266. X        search_back(0);
  1267. X        }
  1268. X    } else {
  1269. X    gl_fixup(search_prompt, 0, 0);
  1270. X    }
  1271. }
  1272. X
  1273. static void     
  1274. search_term()
  1275. {
  1276. X    gl_search_mode = 0;
  1277. X    if (gl_buf[0] == 0)        /* not found, reset hist list */
  1278. X        hist_pos = hist_last;
  1279. X    if (gl_in_hook)
  1280. X    gl_in_hook(gl_buf);
  1281. X    gl_fixup(gl_prompt, 0, gl_pos);
  1282. }
  1283. X
  1284. static void     
  1285. search_back(new_search)
  1286. int new_search;
  1287. {
  1288. X    int    found = 0;
  1289. X    char  *p, *loc;
  1290. X
  1291. X    search_forw_flg = 0;
  1292. X    if (gl_search_mode == 0) {
  1293. X    search_last = hist_pos = hist_last;    
  1294. X    search_update(0);    
  1295. X    gl_search_mode = 1;
  1296. X        gl_buf[0] = 0;
  1297. X    gl_fixup(search_prompt, 0, 0);
  1298. X    } else if (search_pos > 0) {
  1299. X    while (!found) {
  1300. X        p = hist_prev();
  1301. X        if (*p == 0) {        /* not found, done looking */
  1302. X           gl_buf[0] = 0;
  1303. X           gl_fixup(search_prompt, 0, 0);
  1304. X           found = 1;
  1305. X        } else if ((loc = strstr(p, search_string)) != 0) {
  1306. X           strcpy(gl_buf, p);
  1307. X           gl_fixup(search_prompt, 0, loc - p);
  1308. X           if (new_search)
  1309. X           search_last = hist_pos;
  1310. X           found = 1;
  1311. X        } 
  1312. X    }
  1313. X    } else {
  1314. X        gl_putc('\007');
  1315. X    }
  1316. }
  1317. X
  1318. static void     
  1319. search_forw(new_search)
  1320. int new_search;
  1321. {
  1322. X    int    found = 0;
  1323. X    char  *p, *loc;
  1324. X
  1325. X    search_forw_flg = 1;
  1326. X    if (gl_search_mode == 0) {
  1327. X    search_last = hist_pos = hist_last;    
  1328. X    search_update(0);    
  1329. X    gl_search_mode = 1;
  1330. X        gl_buf[0] = 0;
  1331. X    gl_fixup(search_prompt, 0, 0);
  1332. X    } else if (search_pos > 0) {
  1333. X    while (!found) {
  1334. X        p = hist_next();
  1335. X        if (*p == 0) {        /* not found, done looking */
  1336. X           gl_buf[0] = 0;
  1337. X           gl_fixup(search_prompt, 0, 0);
  1338. X           found = 1;
  1339. X        } else if ((loc = strstr(p, search_string)) != 0) {
  1340. X           strcpy(gl_buf, p);
  1341. X           gl_fixup(search_prompt, 0, loc - p);
  1342. X           if (new_search)
  1343. X           search_last = hist_pos;
  1344. X           found = 1;
  1345. X        } 
  1346. X    }
  1347. X    } else {
  1348. X        gl_putc('\007');
  1349. X    }
  1350. }
  1351. SHAR_EOF
  1352. chmod 0644 getline.c ||
  1353. echo 'restore of getline.c failed'
  1354. Wc_c="`wc -c < 'getline.c'`"
  1355. test 26245 -eq "$Wc_c" ||
  1356.     echo 'getline.c: original size 26245, current size' "$Wc_c"
  1357. fi
  1358. # ============= testgl.c ==============
  1359. if test -f 'testgl.c' -a X"$1" != X"-c"; then
  1360.     echo 'x - skipping testgl.c (File already exists)'
  1361. else
  1362. echo 'x - extracting testgl.c (Text)'
  1363. sed 's/^X//' << 'SHAR_EOF' > 'testgl.c' &&
  1364. #include <stdio.h>
  1365. X
  1366. extern char *getline();
  1367. extern void  gl_histadd();
  1368. extern void  exit();
  1369. X
  1370. main()
  1371. /* 
  1372. X * just echo user input lines, letting user edit them and move through
  1373. X * history list
  1374. X */
  1375. {
  1376. X    char *p;
  1377. X
  1378. X    do {
  1379. X    p = getline("PROMPT>>>> ");
  1380. X    gl_histadd(p);
  1381. X    fputs(p, stdout);
  1382. X    } while (*p != 0);
  1383. }
  1384. SHAR_EOF
  1385. chmod 0644 testgl.c ||
  1386. echo 'restore of testgl.c failed'
  1387. Wc_c="`wc -c < 'testgl.c'`"
  1388. test 309 -eq "$Wc_c" ||
  1389.     echo 'testgl.c: original size 309, current size' "$Wc_c"
  1390. fi
  1391. exit 0
  1392.  
  1393. exit 0 # Just in case...
  1394.