home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume37 / input-edit / part01 next >
Encoding:
Text File  |  1993-05-15  |  54.1 KB  |  1,832 lines

  1. Newsgroups: comp.sources.misc
  2. From: thewalt@canuck.CE.Berkeley.EDU (Chris Thewalt)
  3. Subject: v37i050: input-edit - C input functions for line editing with history, Part01/01
  4. Message-ID: <1993May11.175323.3487@sparky.imd.sterling.com>
  5. X-Md4-Signature: cc7c9d447bfc709116845a170f28e21f
  6. Date: Tue, 11 May 1993 17:53:23 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: thewalt@canuck.CE.Berkeley.EDU (Chris Thewalt)
  10. Posting-number: Volume 37, Issue 50
  11. Archive-name: input-edit/part01
  12. Environment: unix, vms, dos, posix
  13. Supersedes: input-edit: Volume 28, Issue 56
  14.  
  15. Changes since last release:
  16.     * type-ahead saved in BSD mode (was OK in SYSV and POSIX)
  17.     * fixed POSIX mode bug and enabled termios use if POSIX defined.
  18.     * allow caller to supply a prompt width calculation function so that the
  19.       caller can embed escape sequences into the prompt (see gl_strwidth in
  20.       the man page).
  21.     * added a getline.h header file for inclusion into the caller.
  22.     * man page added, thanks to DaviD W. Sanderson (dws@cs.wisc.edu)
  23.  
  24. Description:
  25.     Motivation: Many interactive programs read input line by line, but 
  26.     would like to provide line editing and history functionality to the 
  27.     end-user that runs the program.
  28.  
  29.     The input-edit package provides that functionality.  As far as the 
  30.     programmer is concerned, the program only asks for the next line
  31.     of input. However, until the user presses the RETURN key they can use
  32.     emacs-style line editing commands and can traverse the history of lines
  33.     previously typed.
  34.  
  35.     Other packages, such as GNU's readline, have greater capability but are
  36.     also substantially larger.  Input-edit is small, since it uses neither
  37.     stdio nor any termcap features, and is also quite portable.  It only uses
  38.     \b to backspace and \007 to ring the bell on errors.  Since it cannot
  39.     edit multiple lines it scrolls long lines left and right on the same line.
  40.  
  41.     Input edit uses classic (not ANSI) C, and should run on any Unix 
  42.     system (BSD, SYSV or POSIX), PC's with the MSC compiler, or 
  43.     Vax/VMS (untested by me).  Porting the package to new systems basicaly 
  44.     requires code to read a character when it is typed without echoing it, 
  45.     everything else should be OK.
  46.  
  47.     I have run the package on:
  48.  
  49.     DECstation 5000, Ultrix 4.3 with cc 2.1 and gcc 2.3.3
  50.     Sun Sparc 2, SunOS 4.1.1, with cc
  51.     SGI Iris, IRIX System V.3, with cc
  52.     PC, DRDOS 5.0, with MSC 6.0
  53.  
  54.     Chris Thewalt (thewalt@ce.berkeley.edu)   
  55.     5/3/93
  56.  
  57.     PS: I don't have, and don't want to add, a vi mode, sorry.
  58. ---------
  59. #! /bin/sh
  60. # This is a shell archive.  Remove anything before this line, then feed it
  61. # into a shell via "sh file" or similar.  To overwrite existing files,
  62. # type "sh file -c".
  63. # Contents:  README CHANGES Makefile getline.3 getline.c getline.h
  64. #   testgl.c
  65. # Wrapped by kent@sparky on Tue May 11 12:48:34 1993
  66. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin ; export PATH
  67. echo If this archive is complete, you will see the following message:
  68. echo '          "shar: End of archive 1 (of 1)."'
  69. if test -f 'README' -a "${1}" != "-c" ; then 
  70.   echo shar: Will not clobber existing file \"'README'\"
  71. else
  72.   echo shar: Extracting \"'README'\" \(8595 characters\)
  73.   sed "s/^X//" >'README' <<'END_OF_FILE'
  74. X*************************** Motivation **********************************
  75. X
  76. XMany interactive programs read input line by line, but would like to
  77. Xprovide line editing and history functionality to the end-user that 
  78. Xruns the program.
  79. X
  80. XThe input-edit package provides that functionality.  As far as the 
  81. Xprogrammer is concerned, the program only asks for the next line
  82. Xof input. However, until the user presses the RETURN key they can use
  83. Xemacs-style line editing commands and can traverse the history of lines
  84. Xpreviously typed.
  85. X
  86. XOther packages, such as GNU's readline, have greater capability but are
  87. Xalso substantially larger.  Input-edit is small, since it uses neither
  88. Xstdio nor any termcap features, and is also quite portable.  It only uses
  89. X\b to backspace and \007 to ring the bell on errors.  Since it cannot
  90. Xedit multiple lines it scrolls long lines left and right on the same line.
  91. X
  92. XInput edit uses classic (not ANSI) C, and should run on any Unix 
  93. Xsystem (BSD, SYSV or POSIX), PC's with the MSC compiler, or 
  94. XVax/VMS (untested by me).  Porting the package to new systems basicaly 
  95. Xrequires code to read a character when it is typed without echoing it, 
  96. Xeverything else should be OK.
  97. X
  98. XI have run the package on:
  99. X
  100. X    DECstation 5000, Ultrix 4.3 with cc 2.1 and gcc 2.3.3
  101. X    Sun Sparc 2, SunOS 4.1.1, with cc
  102. X    SGI Iris, IRIX System V.3, with cc
  103. X    PC, DRDOS 5.0, with MSC 6.0
  104. X
  105. XThe description below is broken into two parts, the end-user (editing) 
  106. Xinterface and the programmer interface.  Send bug reports, fixes and 
  107. Xenhancements to:
  108. X
  109. XChris Thewalt (thewalt@ce.berkeley.edu)   
  110. X5/3/93
  111. X
  112. XPS: I don't have, and don't want to add, a vi mode, sorry.
  113. X
  114. X************************** End-User Interface ***************************
  115. X
  116. XEntering printable keys generally inserts new text into the buffer (unless
  117. Xin overwrite mode, see below).  Other special keys can be used to modify
  118. Xthe text in the buffer.  In the description of the keys below, ^n means
  119. XControl-n, or holding the CONTROL key down while pressing "n".  Errors
  120. Xwill ring the terminal bell.
  121. X
  122. X^A/^E    : Move cursor to beginning/end of the line.
  123. X^F/^B   : Move cursor forward/backward one character.
  124. X^D    : Delete the character under the cursor.
  125. X^H, DEL : Delete the character to the left of the cursor.
  126. X^K    : Kill from the cursor to the end of line.
  127. X^L    : Redraw current line.
  128. X^O    : Toggle overwrite/insert mode. Initially in insert mode. Text
  129. X      added in overwrite mode (including yanks) overwrite
  130. X      existing text, while insert mode does not overwrite.
  131. X^P/^N   : Move to previous/next item on history list.
  132. X^R/^S   : Perform incremental reverse/forward search for string on
  133. X      the history list.  Typing normal characters adds to the current
  134. X      search string and searches for a match. Typing ^R/^S marks
  135. X      the start of a new search, and moves on to the next match.
  136. X      Typing ^H or DEL deletes the last character from the search 
  137. X      string, and searches from the starting location of the last search.  
  138. X      Therefore, repeated DEL's appear to unwind to the match nearest 
  139. X      the point at which the last ^R or ^S was typed.  If DEL is 
  140. X      repeated until the search string is empty the search location 
  141. X      begins from the start of the history list.  Typing ESC or 
  142. X      any other editing character accepts the current match and 
  143. X      loads it into the buffer, terminating the search.
  144. X^T    : Toggle the characters under and to the left of the cursor.
  145. X^Y    : Yank previously killed text back at current location.  Note that
  146. X      this will overwrite or insert, depending on the current mode.
  147. XTAB    : By default adds spaces to buffer to get to next TAB stop 
  148. X      (just after every 8th column), although this may be rebound by the 
  149. X      programmer, as described below.
  150. XNL, CR  : returns current buffer to the program.
  151. X
  152. XDOS and ANSI terminal arrow key sequences are recognized, and act like:
  153. X
  154. X  up    : same as ^P
  155. X  down  : same as ^N
  156. X  left  : same as ^B
  157. X  right : same as ^F
  158. X
  159. X************************** Programmer Interface ***************************
  160. X
  161. XThe programmer accesses input-edit through these functions, and optionally
  162. Xthrough three additional function pointer hooks.  The four functions are:
  163. X
  164. Xchar *getline(char *prompt)
  165. X
  166. X    Prints the prompt and allows the user to edit the current line. A
  167. X    pointer to the line is returned when the user finishes by
  168. X    typing a newline or a return.  Unlike GNU readline, the returned
  169. X    pointer points to a static buffer, so it should not be free'd, and
  170. X    the buffer contains the newline character.  The user enters an
  171. X    end-of-file by typing ^D on an empty line, in which case the
  172. X    first character of the returned buffer is '\0'.  Getline never
  173. X    returns a NULL pointer.  The getline functions sets terminal modes
  174. X    needed to make it work, and resets them before returning to the
  175. X    caller.  The getline function also looks for characters that would
  176. X    generate a signal, and resets the terminal modes before raising the
  177. X    signal condition.  If the signal handler returns to getline, 
  178. X    the screen is automatically redrawn and editing can continue.
  179. X    Getline now requires both the input and output stream be connected
  180. X    to the terminal (not redirected) so the main program should check
  181. X    to make sure this is true.  If input or output have been redirected
  182. X    the main program should use buffered IO (stdio) rather than
  183. X    the slow 1 character read()s that getline uses.
  184. X
  185. Xvoid gl_setwidth(int width)
  186. X
  187. X        Set the width of the terminal to the specified width. The default
  188. X    width is 80 characters, so this function need only be called if the
  189. X    width of the terminal is not 80.  Since horizontal scrolling is
  190. X    controlled by this parameter it is important to get it right.
  191. X
  192. Xvoid gl_histadd(char *buf)
  193. X
  194. X    The gl_histadd function checks to see if the buf is not empty or
  195. X    whitespace, and also checks to make sure it is different than
  196. X    the last saved buffer to avoid repeats on the history list.
  197. X    If the buf is a new non-blank string a copy is made and saved on
  198. X    the history list, so the caller can re-use the specified buf.
  199. X
  200. Xvoid gl_strwidth(size_t (*func)())
  201. X    The gl_strwidth function allows the caller to supply a pointer to 
  202. X    a prompt width calculation function (strlen by default). This
  203. X    allows the caller to embed escape sequences in the prompt and then
  204. X    tell getline how many screen spaces the prompt will take up.
  205. X
  206. XThe main loop in testgl.c, included in this directory, shows how the
  207. Xinput-edit package can be used:
  208. X
  209. Xextern char *getline();
  210. Xextern void  gl_histadd();
  211. Xmain()
  212. X{
  213. X    char *p;
  214. X    do {
  215. X    p = getline("PROMPT>>>> ");
  216. X    gl_histadd(p);
  217. X    fputs(p, stdout);
  218. X    } while (*p != 0);
  219. X}
  220. X
  221. XIn order to allow the main program to have additional access to the buffer,
  222. Xto implement things such as completion or auto-indent modes, three
  223. Xfunction pointers can be bound to user functions to modify the buffer as
  224. Xdescribed below.  By default gl_in_hook and gl_out_hook are set to NULL,
  225. Xand gl_tab_hook is bound to a function that inserts spaces until the next
  226. Xlogical tab stop is reached.  The user can reassign any of these pointers
  227. Xto other functions.  Each of the functions bound to these hooks receives
  228. Xthe current buffer as the first argument, and must return the location of
  229. Xthe leftmost change made in the buffer.  If the buffer isn't modified the
  230. Xfunctions should return -1.  When the hook function returns the screen is
  231. Xupdated to reflect any changes made by the user function.
  232. X
  233. Xint (*gl_in_hook)(char *buf)
  234. X
  235. X    If gl_in_hook is non-NULL the function is called each time a new 
  236. X    buffer is loaded. It is called when getline is entered, with an
  237. X    empty buffer, it is called each time a new buffer is loaded from
  238. X    the history with ^P or ^N, and it is called when an incremental
  239. X    search string is accepted (when the search is terminated). The
  240. X    buffer can be modified and will be redrawn upon return to getline(). 
  241. X
  242. Xint (*gl_out_hook)(char *buf)
  243. X
  244. X    If gl_out_hook is non-NULL it is called when a line has been
  245. X    completed by the user entering a newline or return. The buffer
  246. X    handed to the hook does not yet have the newline appended. If the
  247. X    buffer is modified the screen is redrawn before getline returns the
  248. X    buffer to the caller.
  249. X
  250. Xint (*gl_tab_hook)(char *buf, int prompt_width, int *cursor_loc)
  251. X
  252. X    If gl_tab_hook is non-NULL, it is called whenever a tab is typed.
  253. X    In addition to receiving the buffer, the current prompt width is
  254. X    given (needed to do tabbing right) and a pointer to the cursor
  255. X    offset is given, where a 0 offset means the first character in the
  256. X    line.  Not only does the cursor_loc tell the programmer where the
  257. X    TAB was received, but it can be reset so that the cursor will end
  258. X    up at the specified location after the screen is redrawn.
  259. END_OF_FILE
  260.   if test 8595 -ne `wc -c <'README'`; then
  261.     echo shar: \"'README'\" unpacked with wrong size!
  262.   fi
  263.   # end of 'README'
  264. fi
  265. if test -f 'CHANGES' -a "${1}" != "-c" ; then 
  266.   echo shar: Will not clobber existing file \"'CHANGES'\"
  267. else
  268.   echo shar: Extracting \"'CHANGES'\" \(2073 characters\)
  269.   sed "s/^X//" >'CHANGES' <<'END_OF_FILE'
  270. XChanges from last release (v28i056 in comp.sources.misc)
  271. X
  272. X* type-ahead saved in BSD mode (was OK in SYSV and POSIX)
  273. X* fixed POSIX mode bug and enabled termios use if POSIX defined.
  274. X* allow caller to supply a prompt width calculation function so that the
  275. X  caller can embed escape sequences into the prompt (see gl_strwidth in
  276. X  the man page).
  277. X* added a getline.h header file for inclusion into the caller.
  278. X* man page added, thanks to DaviD W. Sanderson (dws@cs.wisc.edu)
  279. X
  280. X
  281. XChanges from previous release (v25i056 and patch v26i092)
  282. X
  283. X* The user no longer calls gl_init() and gl_cleanup(), getline() sets
  284. X  required terminal modes on entry and resets before returning.  This
  285. X  was necessary to capture changes in terminal modes that the main
  286. X  program might be making.
  287. X* Getline() now looks to see which characters are bound to signal
  288. X  generation, and when these characters are seen getline() resets
  289. X  terminal modes before passing on the signal.  If the signal handler
  290. X  returns to getline(), the screen is automatically updated and editing
  291. X  can continue.
  292. X* The toggle key for overwrite mode has been moved from ^G to ^O
  293. X* All code is now classic rather than ANSI C, so any compiler should
  294. X  be able to handle it.
  295. X* ^Y now yanks back previously kill'ed (^K) text starting at the
  296. X  current location.
  297. X* ^R/^S begin reverse and forward incremental searches through the
  298. X  history list.
  299. X* The programmer must add buffers onto the history list by calling
  300. X  gl_addhist(char *buffer).  This function makes copies of the buffer
  301. X  and adds them to the history list if the buffer is not a blank line
  302. X  and if the buffer is different than the last item saved (so the
  303. X  program need not check for these conditions)
  304. X* The main program can specify the screen width to use with a call to
  305. X  gl_setwidth(int width)
  306. X* Getline now insists that both the input and output are connected to
  307. X  a terminal. If this is not the case, an error message is written and
  308. X  the program is terminated.  The main program should check for this
  309. X  case and use buffered IO (stdio) for non-interactive sessions.
  310. X
  311. END_OF_FILE
  312.   if test 2073 -ne `wc -c <'CHANGES'`; then
  313.     echo shar: \"'CHANGES'\" unpacked with wrong size!
  314.   fi
  315.   # end of 'CHANGES'
  316. fi
  317. if test -f 'Makefile' -a "${1}" != "-c" ; then 
  318.   echo shar: Will not clobber existing file \"'Makefile'\"
  319. else
  320.   echo shar: Extracting \"'Makefile'\" \(182 characters\)
  321.   sed "s/^X//" >'Makefile' <<'END_OF_FILE'
  322. XTARGET = testgl
  323. XOBJS   = testgl.o getline.o
  324. X
  325. XCC     = gcc
  326. XCFLAGS = -O 
  327. XLDFLAGS= -s
  328. X
  329. X$(TARGET): $(OBJS)
  330. X    $(CC) $(LDFLAGS) $(CFLAGS) -o testgl $(OBJS)
  331. X
  332. Xclean:
  333. X    rm -f $(OBJS) $(TARGET)
  334. END_OF_FILE
  335.   if test 182 -ne `wc -c <'Makefile'`; then
  336.     echo shar: \"'Makefile'\" unpacked with wrong size!
  337.   fi
  338.   # end of 'Makefile'
  339. fi
  340. if test -f 'getline.3' -a "${1}" != "-c" ; then 
  341.   echo shar: Will not clobber existing file \"'getline.3'\"
  342. else
  343.   echo shar: Extracting \"'getline.3'\" \(9501 characters\)
  344.   sed "s/^X//" >'getline.3' <<'END_OF_FILE'
  345. X.\" Note that in silly ol' [nt]roff, even trailing white space is
  346. X.\" significant.  I went through and eliminated it.
  347. X.\" I adopted a convention of using a bold 'getline' when referring to
  348. X.\" the whole package, but an italic 'getline' when referring to the
  349. X.\" specific function.
  350. X.\" Note that in [nt]roff that "-" is a hyphen, while "\-" is a dash.
  351. X.\" I adjusted some source text lines to keep them short (I keep line
  352. X.\" numbers turned on in vi, so I only have 72 cols w/o wrapping).
  353. X.\" It's too bad that getline() doesn't realloc() the buffer as
  354. X.\" necessary.  Then it could have an unlimited input line length.
  355. X.\" Note that .RI et al are limited in how many args they can take.
  356. X.\" I corrected gl_addhist to gl_histadd, which is what is actually
  357. X.\" used!  Perhaps it really should be gl_addhist, to preserve the
  358. X.\" gl_<verb><object> pattern in the other function names.
  359. X.\" I tried to rephrase certain sections to avoid the passive voice.
  360. X.\" I find the active voice much easier to understand, since I can tell
  361. X.\" more easily what acts on what.
  362. X.TH GETLINE 3
  363. X.SH NAME
  364. Xgetline \- command-line editing library with history
  365. X.SH SYNOPSIS
  366. X.RI "char *getline(char *" prompt );
  367. X.PP
  368. X.RI "void gl_histadd(char *" line );
  369. X.br
  370. X.RI "void gl_setwidth(int " width );
  371. X.br
  372. X.RI "void gl_strwidth(int " (*width_func)() );
  373. X.PP
  374. X.RI "extern int (*gl_in_hook)(char *" buf );
  375. X.br
  376. X.RI "extern int (*gl_out_hook)(char *" buf );
  377. X.br
  378. X.RI "extern int (*gl_tab_hook)(char *" buf ,
  379. X.RI "int " prompt_width ", int *" cursor_loc );
  380. X.SH DESCRIPTION
  381. XThe
  382. X.B getline
  383. Xpackage is a set of library routines that implement
  384. Xan editable command-line history.
  385. X.PP
  386. X.B "Programming Interface"
  387. X.br
  388. X.I getline
  389. Xreturns a pointer to a line of text read from the user,
  390. Xprompting the user with the specified
  391. X.IR prompt .
  392. XThe pointer refers to a static buffer allocated by the
  393. X.B getline
  394. Xpackage.
  395. XClients may assume that the pointer
  396. X.I getline
  397. Xreturns is always the same, and is never NULL.
  398. XThe buffer
  399. X.I getline
  400. Xreturns to the caller contains the terminating newline character,
  401. Xexcept on end of file,
  402. Xin which case the first character in the buffer is 0
  403. X.RB ( NUL ).
  404. XFile descriptors 0 and 1 must be connected to the terminal
  405. X(not redirected),
  406. Xso the caller should check for this condition (using
  407. X.IR isatty (\|))
  408. Xand call stdio routines if the session is not interactive.
  409. X.PP
  410. X.I gl_histadd
  411. Xadds the given
  412. X.I line
  413. Xto the
  414. X.B getline
  415. Xhistory list if the
  416. X.I line
  417. Xis not empty and if it is different from the last line
  418. Xin the history list
  419. X(so the caller need not check for these conditions).
  420. X.I gl_histadd
  421. Xmakes its own copies of all the lines it adds to the history list.
  422. XThis is so the caller can reuse the buffer it supplies to
  423. X.IR gl_histadd .
  424. X.PP
  425. X.I gl_setwidth
  426. Xspecifies the terminal
  427. X.I width
  428. Xto use for horizontal scrolling.
  429. XThe default value is 80 columns,
  430. Xand it is important to properly specify the
  431. X.I width
  432. Xor lines may wrap inadvertently.
  433. X.PP
  434. X.I gl_strwidth
  435. Xallows the application program to supply a prompt string width calculation
  436. Xfunction that returns the number of screen positions used by the argument
  437. Xstring.  
  438. XBy default strlen is used, but if the prompt contains escape sequences the user
  439. Xcan bind a function that returns the actual number of screen postitions
  440. Xused by the argument string, not including the escape characters.
  441. X.PP
  442. XIn addition to the function call interface,
  443. X.B getline
  444. Xhas three externally accessible function pointers
  445. Xthat act as hooks if bound to user-defined functions.
  446. X.B getline
  447. Xsupplies each of the functions with a pointer to the current buffer
  448. Xas the first argument,
  449. Xand expects the return value to be the index
  450. Xof the first change the function made in the buffer
  451. X(or \-1 if the function did not alter the buffer).
  452. XAfter the functions return,
  453. X.B getline
  454. Xupdates the screen as necessary.
  455. X.\"-------
  456. X.\" DWS comment --
  457. X.\"-------
  458. XNote that the functions may not alter the size of the buffer.
  459. XIndeed, they do not even know how large the buffer is!
  460. X.PP
  461. X.I getline
  462. Xcalls
  463. X.I gl_in_hook
  464. X(initially NULL)
  465. Xeach time it loads a new buffer.
  466. XMore precisely, this is
  467. X.TP
  468. X\(bu
  469. Xat the first call to
  470. X.I getline
  471. X(with an empty buffer)
  472. X.TP
  473. X\(bu
  474. Xeach time the user enters a new buffer from the history list (with
  475. X.B ^P
  476. Xor
  477. X.BR ^N )
  478. X.TP
  479. X\(bu
  480. Xwhen the user accepts an incremental search string
  481. X(when the user terminates the search).
  482. X.PP
  483. X.I getline
  484. Xcalls
  485. X.I gl_out_hook
  486. X(initially NULL)
  487. Xwhen a line has been completed by the user entering a
  488. X.B NEWLINE
  489. X.RB (\| ^J \|)
  490. Xor
  491. X.B RETURN
  492. X.RB (\| ^M \|).
  493. XThe buffer
  494. X.I gl_out_hook
  495. Xsees does not yet have the newline appended, and
  496. X.I gl_out_hook
  497. Xshould not add a newline.
  498. X.PP
  499. X.I getline
  500. Xcalls
  501. X.I gl_tab_hook
  502. Xwhenever the user types a
  503. X.BR TAB .
  504. XIn addition to the buffer,
  505. X.I getline
  506. Xsupplies the current
  507. X.I prompt_width
  508. X(presumably needed for tabbing calculations) and
  509. X.IR cursor_loc ,
  510. Xa pointer to the cursor location.
  511. X.RI ( *cursor_loc
  512. X\(eq 0 corresponds to the first character in the buffer)
  513. X.I *cursor_loc
  514. Xtells
  515. X.I gl_tab_hook
  516. Xwhere the
  517. X.B TAB
  518. Xwas typed.
  519. XNote that when it redraws the screen,
  520. X.I getline
  521. Xwill honor any change
  522. X.I gl_tab_hook
  523. Xmay make to
  524. X.IR *cursor_loc .
  525. X.\"-------
  526. X.\" DWS comment --
  527. X.\"-------
  528. XNote also that
  529. X.I prompt_width
  530. Xmay not correspond to the actual width of
  531. X.I prompt
  532. Xon the screen if
  533. X.I prompt
  534. Xcontains escape sequences.
  535. X.I gl_tab_hook
  536. Xis initially bound to the
  537. X.B getline
  538. Xinternal static function
  539. X.IR gl_tab ,
  540. Xwhich acts like a normal
  541. X.B TAB
  542. Xkey by inserting spaces.
  543. X.PP
  544. X.B "User Interface"
  545. X.br
  546. X.\"-------
  547. X.\" I adapted the prologue to this section from the ksh man page (dws).
  548. X.\"-------
  549. XTo edit, the user moves the cursor to the point needing correction and
  550. Xthen inserts or deletes characters or words as needed.
  551. XAll the editing commands are control characters,
  552. Xwhich typed by holding the
  553. XCTRL key down while typing another character.
  554. XControl characters are indicated below as the caret
  555. X.RB (\| ^ \|)
  556. Xfollowed by another character,
  557. Xsuch as
  558. X.BR ^A .
  559. X.PP
  560. XAll edit commands operate from any place on the line,
  561. Xnot just at the beginning.
  562. X.PP
  563. XThese are the
  564. X.I getline
  565. Xkey bindings.
  566. X.\"-------
  567. X.\" Tt    - max width of tag
  568. X.\" Tw    - max width of tag + spacing to the paragraph
  569. X.\" Tp    - special .TP, with the best indent for the editing command
  570. X.\"    descriptions.
  571. X.\"    The first version of Tp prints the tags left-justified.
  572. X.\"    The second version of Tp prints the tags as follows:
  573. X.\"    If one argument is given, it is printed right-justified.
  574. X.\"    If two arguments are given, the first is printed left-justified
  575. X.\"    and the second is printed right-justified.
  576. X.\"-------
  577. X.nr Tt \w'BACKSPACE'
  578. X.nr Tw \w'BACKSPACE\0\0\0'
  579. X.\" .de Tp
  580. X.\" .TP \n(Twu
  581. X.\" \fB\\$1\fR
  582. X.\" ..
  583. X.de Tp
  584. X.TP \n(Twu
  585. X.if \\n(.$=1 \h@\n(Ttu-\w'\fB\\$1\fR'u@\fB\\$1\fR
  586. X.if \\n(.$=2 \fB\\$1\fR\h@\n(Ttu-\w'\fB\\$1\\$2\fR'u@\fB\\$2\fR
  587. X..
  588. X.PP
  589. X.\"-------
  590. X.\" Set interparagraph spacing to zero so binding descriptions are
  591. X.\" kept together.
  592. X.\"-------
  593. X.PD 0
  594. X.Tp "^A"
  595. XMove cursor to beginning of line.
  596. X.Tp "^B"
  597. XMove cursor left (back) 1 column.
  598. X.Tp "^D"
  599. XDelete the character under the cursor.
  600. X.Tp "^E"
  601. XMove cursor to end of line.
  602. X.Tp "^F"
  603. XMove point right (forward) 1 column.
  604. X.Tp "^H"
  605. XDelete the character left of the cursor.@
  606. X.Tp "^I"
  607. XJump to next tab stop (may be redefined by the program).
  608. X.Tp "^J"
  609. XReturn the current line.
  610. X.Tp "^K"
  611. XKill from cursor to the end of the line (see
  612. X.BR "^Y" \|).
  613. X.Tp "^L"
  614. XRedisplay current line.
  615. X.Tp "^M"
  616. XReturn the current line.
  617. X.Tp "^N"
  618. XFetches next line from the history list.
  619. X.Tp "^O"
  620. XToggle overwrite/insert mode, initially in insert mode.
  621. X.Tp "^P"
  622. XFetches previous line from the history list.
  623. X.Tp "^R"
  624. XBegin a reverse incremental search through history list.
  625. XEach printing character typed adds to the search substring
  626. X(initially empty), and
  627. X.B getline
  628. Xfinds and displays the first matching location.
  629. XTyping
  630. X.B ^R
  631. Xagain marks the current starting location and begins a new
  632. Xsearch for the current substring.
  633. XTyping
  634. X.B ^H
  635. Xor
  636. X.B DEL
  637. Xdeletes the last character from the search string,
  638. Xand
  639. X.B getline
  640. Xrestarts the search from the last starting location.
  641. XRepeated
  642. X.B ^H
  643. Xor
  644. X.B DEL
  645. Xcharacters therefore appear to unwind the search to the match nearest
  646. Xthe point where the user last typed
  647. X.B ^R
  648. Xor
  649. X.BR ^S .
  650. XTyping
  651. X.B ^H
  652. Xor
  653. X.B DEL
  654. Xuntil the search string is empty causes
  655. X.B getline
  656. Xto reset the start of the search to the beginning of the history list.
  657. XTyping
  658. X.B ESC
  659. Xor any other editing character accepts the current match
  660. Xand terminates the search.
  661. X.Tp "^S"
  662. XBegin a forward incremental search through the history list.
  663. XThe behavior is like that of
  664. X.B ^R
  665. Xbut in the opposite direction through the history list.
  666. X.Tp "^T"
  667. XTranspose current and previous character.
  668. X.Tp "^Y"
  669. XYank previously killed
  670. X.RB (\| ^K \|)
  671. Xtext back at current location.
  672. X.Tp BACKSPACE
  673. XDelete the character left of the cursor.
  674. X.Tp DEL
  675. XDelete the character left of the cursor.
  676. X.Tp RETURN
  677. XReturn the current line.
  678. X.Tp TAB
  679. XJump to next tab stop (may be redefined by the program).
  680. X.\"-------
  681. X.\" Restore default interparagraph spacing.
  682. X.\"-------
  683. X.PD
  684. X.PP
  685. X.B getline
  686. Xrecognizes DOS and ANSI arrow keys.
  687. XThey cause the following actions:
  688. X.B up
  689. Xis the same as
  690. X.BR ^P ,
  691. X.B down
  692. Xis the same as
  693. X.BR ^N ,
  694. X.B left
  695. Xis the same as
  696. X.BR ^P ,
  697. Xand
  698. X.B right
  699. Xis the same as
  700. X.BR ^F .
  701. X.SH AUTHORS
  702. X.PP
  703. XProgram by
  704. XChristopher R. Thewalt (thewalt\|@ce.berkeley.edu)
  705. X.PP
  706. XOriginal man page by
  707. XDaviD W. Sanderson (dws\|@cs.wisc.edu)
  708. Xand Christopher R. Thewalt
  709. X.SH COPYRIGHT
  710. X\&
  711. X.br
  712. X.if n (C)
  713. X.if t \s+8\v'+2p'\fB\(co\fR\v'-2p'\s0
  714. X\s+2Copyright 1992,1993 by Christopher R. Thewalt and DaviD W. Sanderson\s0
  715. X(but freely redistributable)
  716. END_OF_FILE
  717.   if test 9501 -ne `wc -c <'getline.3'`; then
  718.     echo shar: \"'getline.3'\" unpacked with wrong size!
  719.   fi
  720.   # end of 'getline.3'
  721. fi
  722. if test -f 'getline.c' -a "${1}" != "-c" ; then 
  723.   echo shar: Will not clobber existing file \"'getline.c'\"
  724. else
  725.   echo shar: Extracting \"'getline.c'\" \(26628 characters\)
  726.   sed "s/^X//" >'getline.c' <<'END_OF_FILE'
  727. X#ifndef lint
  728. Xstatic char     rcsid[] =
  729. X"$Id: getline.c,v 3.7 1993/05/03 20:57:50 thewalt Exp thewalt $";
  730. Xstatic char    *copyright = "Copyright (C) 1991, 1992, 1993, Chris Thewalt";
  731. X#endif
  732. X
  733. X/*
  734. X * Copyright (C) 1991, 1992, 1993 by Chris Thewalt (thewalt@ce.berkeley.edu)
  735. X *
  736. X * Permission to use, copy, modify, and distribute this software 
  737. X * for any purpose and without fee is hereby granted, provided
  738. X * that the above copyright notices appear in all copies and that both the
  739. X * copyright notice and this permission notice appear in supporting
  740. X * documentation.  This software is provided "as is" without express or
  741. X * implied warranty.
  742. X */
  743. X
  744. X#include       "getline.h"
  745. Xstatic int      gl_tab();  /* forward reference needed for gl_tab_hook */
  746. X
  747. X/******************** imported interface *********************************/
  748. X
  749. X#include <string.h>
  750. X#include <ctype.h>
  751. X#include <errno.h>
  752. X#include <signal.h>
  753. X
  754. Xextern int      isatty();    
  755. Xextern void    *malloc();
  756. Xextern void     free();
  757. Xextern int      kill();    
  758. X
  759. X/********************* exported interface ********************************/
  760. X
  761. Xchar           *getline();        /* read a line of input */
  762. Xvoid            gl_setwidth();        /* specify width of screen */
  763. Xvoid            gl_histadd();        /* adds entries to hist */
  764. Xvoid        gl_strwidth();        /* to bind gl_strlen */
  765. X
  766. Xint         (*gl_in_hook)() = 0;
  767. Xint         (*gl_out_hook)() = 0;
  768. Xint         (*gl_tab_hook)() = gl_tab;
  769. X
  770. X/******************** internal interface *********************************/
  771. X
  772. X#define BUF_SIZE 1024
  773. X
  774. Xstatic int      gl_init_done = -1;    /* terminal mode flag  */
  775. Xstatic int      gl_termw = 80;        /* actual terminal width */
  776. Xstatic int      gl_scroll = 27;        /* width of EOL scrolling region */
  777. Xstatic int      gl_width = 0;        /* net size available for input */
  778. Xstatic int      gl_extent = 0;        /* how far to redraw, 0 means all */
  779. Xstatic int      gl_overwrite = 0;    /* overwrite mode */
  780. Xstatic int      gl_pos, gl_cnt = 0;     /* position and size of input */
  781. Xstatic char     gl_buf[BUF_SIZE];       /* input buffer */
  782. Xstatic char     gl_killbuf[BUF_SIZE]=""; /* killed text */
  783. Xstatic char    *gl_prompt;        /* to save the prompt string */
  784. Xstatic char     gl_intrc = 0;        /* keyboard SIGINT char */
  785. Xstatic char     gl_quitc = 0;        /* keyboard SIGQUIT char */
  786. Xstatic char     gl_suspc = 0;        /* keyboard SIGTSTP char */
  787. Xstatic char     gl_dsuspc = 0;        /* delayed SIGTSTP char */
  788. Xstatic int      gl_search_mode = 0;    /* search mode flag */
  789. X
  790. Xstatic void     gl_init();        /* prepare to edit a line */
  791. Xstatic void     gl_cleanup();        /* to undo gl_init */
  792. Xstatic void     gl_char_init();        /* get ready for no echo input */
  793. Xstatic void     gl_char_cleanup();    /* undo gl_char_init */
  794. Xstatic size_t     (*gl_strlen)() = (size_t(*)())strlen; 
  795. X                    /* returns printable prompt width */
  796. X
  797. Xstatic void     gl_addchar();        /* install specified char */
  798. Xstatic void     gl_del();        /* del, either left (-1) or cur (0) */
  799. Xstatic void     gl_error();        /* write error msg and die */
  800. Xstatic void     gl_fixup();        /* fixup state variables and screen */
  801. Xstatic int      gl_getc();        /* read one char from terminal */
  802. Xstatic void     gl_kill();        /* delete to EOL */
  803. Xstatic void     gl_newline();        /* handle \n or \r */
  804. Xstatic void     gl_putc();        /* write one char to terminal */
  805. Xstatic void     gl_puts();        /* write a line to terminal */
  806. Xstatic void     gl_redraw();        /* issue \n and redraw all */
  807. Xstatic void     gl_transpose();        /* transpose two chars */
  808. Xstatic void     gl_yank();        /* yank killed text */
  809. X
  810. Xstatic void     hist_init();    /* initializes hist pointers */
  811. Xstatic char    *hist_next();    /* return ptr to next item */
  812. Xstatic char    *hist_prev();    /* return ptr to prev item */
  813. Xstatic char    *hist_save();    /* makes copy of a string, without NL */
  814. X
  815. Xstatic void     search_addchar();    /* increment search string */
  816. Xstatic void     search_term();        /* reset with current contents */
  817. Xstatic void     search_back();        /* look back for current string */
  818. Xstatic void     search_forw();        /* look forw for current string */
  819. X
  820. X/************************ nonportable part *********************************/
  821. X
  822. Xextern int      write();
  823. Xextern void     exit();
  824. X
  825. X#ifdef MSDOS
  826. X#include <bios.h>
  827. X#endif
  828. X
  829. X#ifdef unix
  830. Xextern int      read();
  831. Xextern int      ioctl();
  832. X
  833. X#ifdef POSIX        /* use POSIX interface */
  834. X#include <termios.h>
  835. Xstruct termios  new_termios, old_termios;
  836. X#else /* not POSIX */
  837. X#include <sys/ioctl.h>
  838. X#ifdef TIOCSETN        /* use BSD interface */
  839. X#include <sgtty.h>
  840. Xstruct sgttyb   new_tty, old_tty;
  841. Xstruct tchars   tch;
  842. Xstruct ltchars  ltch;
  843. X#else            /* use SYSV interface */
  844. X#include <termio.h>
  845. Xstruct termio   new_termio, old_termio;
  846. X#endif /* TIOCSETN */
  847. X#endif /* POSIX */
  848. X#endif    /* unix */
  849. X
  850. X#ifdef vms
  851. X#include <descrip.h>
  852. X#include <ttdef.h>
  853. X#include <iodef.h>
  854. X#include unixio
  855. X   
  856. Xstatic int   setbuff[2];             /* buffer to set terminal attributes */
  857. Xstatic short chan = -1;              /* channel to terminal */
  858. Xstruct dsc$descriptor_s descrip;     /* VMS descriptor */
  859. X#endif
  860. X
  861. Xstatic void
  862. Xgl_char_init()            /* turn off input echo */
  863. X{
  864. X#ifdef unix
  865. X#ifdef POSIX
  866. X    tcgetattr(0, &old_termios);
  867. X    gl_intrc = old_termios.c_cc[VINTR];
  868. X    gl_quitc = old_termios.c_cc[VQUIT];
  869. X#ifdef VSUSP
  870. X    gl_suspc = old_termios.c_cc[VSUSP];
  871. X#endif
  872. X#ifdef VDSUSP
  873. X    gl_dsuspc = old_termios.c_cc[VDSUSP];
  874. X#endif
  875. X    new_termios = old_termios;
  876. X    new_termios.c_iflag &= ~(BRKINT|ISTRIP|IXON|IXOFF);
  877. X    new_termios.c_iflag |= (IGNBRK|IGNPAR);
  878. X    new_termios.c_lflag &= ~(ICANON|ISIG|IEXTEN|ECHO);
  879. X    new_termios.c_cc[VMIN] = 1;
  880. X    new_termios.c_cc[VTIME] = 0;
  881. X    tcsetattr(0, TCSANOW, &new_termios);
  882. X#else                /* not POSIX */
  883. X#ifdef TIOCSETN            /* BSD */
  884. X    ioctl(0, TIOCGETC, &tch);
  885. X    ioctl(0, TIOCGLTC, <ch);
  886. X    gl_intrc = tch.t_intrc;
  887. X    gl_quitc = tch.t_quitc;
  888. X    gl_suspc = ltch.t_suspc;
  889. X    gl_dsuspc = ltch.t_dsuspc;
  890. X    ioctl(0, TIOCGETP, &old_tty);
  891. X    new_tty = old_tty;
  892. X    new_tty.sg_flags |= RAW;
  893. X    new_tty.sg_flags &= ~ECHO;
  894. X    ioctl(0, TIOCSETN, &new_tty);
  895. X#else                /* SYSV */
  896. X    ioctl(0, TCGETA, &old_termio);
  897. X    gl_intrc = old_termio.c_cc[VINTR];
  898. X    gl_quitc = old_termio.c_cc[VQUIT];
  899. X    new_termio = old_termio;
  900. X    new_termio.c_iflag &= ~(BRKINT|ISTRIP|IXON|IXOFF);
  901. X    new_termio.c_iflag |= (IGNBRK|IGNPAR);
  902. X    new_termio.c_lflag &= ~(ICANON|ISIG|ECHO);
  903. X    new_termio.c_cc[VMIN] = 1;
  904. X    new_termio.c_cc[VTIME] = 0;
  905. X    ioctl(0, TCSETA, &new_termio);
  906. X#endif /* TIOCSETN */
  907. X#endif /* POSIX */
  908. X#endif /* unix */
  909. X
  910. X#ifdef vms
  911. X    descrip.dsc$w_length  = strlen("tt:");
  912. X    descrip.dsc$b_dtype   = DSC$K_DTYPE_T;
  913. X    descrip.dsc$b_class   = DSC$K_CLASS_S;
  914. X    descrip.dsc$a_pointer = "tt:";
  915. X    (void)sys$assign(&descrip,&chan,0,0);
  916. X    (void)sys$qiow(0,chan,IO$_SENSEMODE,0,0,0,setbuff,8,0,0,0,0);
  917. X    setbuff[1] |= TT$M_NOECHO;
  918. X    (void)sys$qiow(0,chan,IO$_SETMODE,0,0,0,setbuff,8,0,0,0,0);
  919. X#endif /* vms */
  920. X}
  921. X
  922. Xstatic void
  923. Xgl_char_cleanup()        /* undo effects of gl_char_init */
  924. X{
  925. X#ifdef unix
  926. X#ifdef POSIX 
  927. X    tcsetattr(0, TCSANOW, &old_termios);
  928. X#else             /* not POSIX */
  929. X#ifdef TIOCSETN        /* BSD */
  930. X    ioctl(0, TIOCSETN, &old_tty);
  931. X#else            /* SYSV */
  932. X    ioctl(0, TCSETA, &old_termio);
  933. X#endif /* TIOCSETN */
  934. X#endif /* POSIX */
  935. X#endif /* unix */
  936. X
  937. X#ifdef vms
  938. X    setbuff[1] &= ~TT$M_NOECHO;
  939. X    (void)sys$qiow(0,chan,IO$_SETMODE,0,0,0,setbuff,8,0,0,0,0);
  940. X    sys$dassgn(chan);
  941. X    chan = -1;
  942. X#endif 
  943. X}
  944. X
  945. Xstatic int
  946. Xgl_getc()
  947. X/* get a character without echoing it to screen */
  948. X{
  949. X    int             c;
  950. X    char            ch;
  951. X
  952. X#ifdef unix
  953. X    c = (read(0, &ch, 1) > 0)? ch : -1;
  954. X#endif
  955. X#ifdef MSDOS
  956. X    c = _bios_keybrd(_NKEYBRD_READ);
  957. X    if ((c & 0377) == 224) {
  958. X    switch (c = (c >> 8) & 0377) {
  959. X      case 72: c = 16;   /* up -> ^P */
  960. X        break;
  961. X          case 80: c = 14;   /* down -> ^N */
  962. X        break;
  963. X      case 75: c = 2;    /* left -> ^B */
  964. X        break;
  965. X          case 77: c = 6;    /* right -> ^F */
  966. X        break;
  967. X      default: c = 0;    /* make it garbage */
  968. X    }
  969. X    } else {
  970. X    c = c & 0377;
  971. X    }
  972. X#endif
  973. X#ifdef vms
  974. X    if(chan < 0) {
  975. X       c='\0';
  976. X    }
  977. X    (void)sys$qiow(0,chan,IO$_TTYREADALL,0,0,0,&c,1,0,0,0,0);
  978. X    c &= 0177;                        /* get a char */
  979. X#endif
  980. X    return c;
  981. X}
  982. X
  983. Xstatic void
  984. Xgl_putc(c)
  985. Xint     c;
  986. X{
  987. X    char   ch = c;
  988. X
  989. X    write(1, &ch, 1);
  990. X    if (ch == '\n') {
  991. X    ch = '\r';
  992. X        write(1, &ch, 1);    /* RAW mode needs '\r', does not hurt */
  993. X    }
  994. X}
  995. X
  996. X/******************** fairly portable part *********************************/
  997. X
  998. Xstatic void
  999. Xgl_puts(buf)
  1000. Xchar *buf;
  1001. X{
  1002. X    int len; 
  1003. X    
  1004. X    if (buf) {
  1005. X        len = strlen(buf);
  1006. X        write(1, buf, len);
  1007. X    }
  1008. X}
  1009. X
  1010. Xstatic void
  1011. Xgl_error(buf)
  1012. Xchar *buf;
  1013. X{
  1014. X    int len = strlen(buf);
  1015. X
  1016. X    gl_cleanup();
  1017. X    write(2, buf, len);
  1018. X    exit(1);
  1019. X}
  1020. X
  1021. Xstatic void
  1022. Xgl_init()
  1023. X/* set up variables and terminal */
  1024. X{
  1025. X    if (gl_init_done < 0) {        /* -1 only on startup */
  1026. X        hist_init();
  1027. X    }
  1028. X    if (isatty(0) == 0 || isatty(1) == 0)
  1029. X    gl_error("\n*** Error: getline(): not interactive, use stdio.\n");
  1030. X    gl_char_init();
  1031. X    gl_init_done = 1;
  1032. X}
  1033. X
  1034. Xstatic void
  1035. Xgl_cleanup()
  1036. X/* undo effects of gl_init, as necessary */
  1037. X{
  1038. X    if (gl_init_done > 0)
  1039. X        gl_char_cleanup();
  1040. X    gl_init_done = 0;
  1041. X}
  1042. X
  1043. Xvoid
  1044. Xgl_setwidth(w)
  1045. Xint  w;
  1046. X{
  1047. X    if (w > 20) {
  1048. X    gl_termw = w;
  1049. X    gl_scroll = w / 3;
  1050. X    } else {
  1051. X    gl_error("\n*** Error: minimum screen width is 21\n");
  1052. X    }
  1053. X}
  1054. X
  1055. Xchar *
  1056. Xgetline(prompt)
  1057. Xchar *prompt;
  1058. X{
  1059. X    int             c, loc, tmp;
  1060. X    int                sig;
  1061. X
  1062. X    gl_init();    
  1063. X    gl_prompt = (prompt)? prompt : "";
  1064. X    gl_buf[0] = 0;
  1065. X    if (gl_in_hook)
  1066. X    gl_in_hook(gl_buf);
  1067. X    gl_fixup(gl_prompt, -2, BUF_SIZE);
  1068. X    while ((c = gl_getc()) >= 0) {
  1069. X    gl_extent = 0;      /* reset to full extent */
  1070. X    if (isprint(c)) {
  1071. X        if (gl_search_mode)
  1072. X           search_addchar(c);
  1073. X        else
  1074. X           gl_addchar(c);
  1075. X    } else {
  1076. X        if (gl_search_mode) {
  1077. X            if (c == '\033' || c == '\016' || c == '\020') {
  1078. X                search_term();
  1079. X                c = 0;             /* ignore the character */
  1080. X        } else if (c == '\010' || c == '\177') {
  1081. X            search_addchar(-1); /* unwind search string */
  1082. X            c = 0;
  1083. X        } else if (c != '\022' && c != '\023') {
  1084. X            search_term();    /* terminate and handle char */
  1085. X        }
  1086. X        }
  1087. X        switch (c) {
  1088. X          case '\n': case '\r':             /* newline */
  1089. X        gl_newline();
  1090. X        gl_cleanup();
  1091. X        return gl_buf;
  1092. X        /*NOTREACHED*/
  1093. X        break; 
  1094. X          case '\001': gl_fixup(gl_prompt, -1, 0);        /* ^A */
  1095. X        break;
  1096. X          case '\002': gl_fixup(gl_prompt, -1, gl_pos-1);    /* ^B */
  1097. X        break;
  1098. X          case '\004':                    /* ^D */
  1099. X        if (gl_cnt == 0) {
  1100. X            gl_buf[0] = 0;
  1101. X            gl_cleanup();
  1102. X            gl_putc('\n');
  1103. X            return gl_buf;
  1104. X        } else {
  1105. X            gl_del(0);
  1106. X        }
  1107. X        break;
  1108. X          case '\005': gl_fixup(gl_prompt, -1, gl_cnt);    /* ^E */
  1109. X        break;
  1110. X          case '\006': gl_fixup(gl_prompt, -1, gl_pos+1);    /* ^F */
  1111. X        break;
  1112. X          case '\010': case '\177': gl_del(-1);    /* ^H and DEL */
  1113. X        break;
  1114. X          case '\t':                        /* TAB */
  1115. X                if (gl_tab_hook) {
  1116. X            tmp = gl_pos;
  1117. X                loc = gl_tab_hook(gl_buf, gl_strlen(gl_prompt), &tmp);
  1118. X                if (loc >= 0 || tmp != gl_pos)
  1119. X                    gl_fixup(gl_prompt, loc, tmp);
  1120. X                }
  1121. X        break;
  1122. X          case '\013': gl_kill();                /* ^K */
  1123. X        break;
  1124. X          case '\014': gl_redraw();                /* ^L */
  1125. X        break;
  1126. X          case '\016':                     /* ^N */
  1127. X        strcpy(gl_buf, hist_next());
  1128. X                if (gl_in_hook)
  1129. X                gl_in_hook(gl_buf);
  1130. X        gl_fixup(gl_prompt, 0, BUF_SIZE);
  1131. X        break;
  1132. X          case '\017': gl_overwrite = !gl_overwrite;           /* ^O */
  1133. X        break;
  1134. X          case '\020':                     /* ^P */
  1135. X        strcpy(gl_buf, hist_prev());
  1136. X                if (gl_in_hook)
  1137. X                gl_in_hook(gl_buf);
  1138. X        gl_fixup(gl_prompt, 0, BUF_SIZE);
  1139. X        break;
  1140. X          case '\022': search_back(1);            /* ^R */
  1141. X        break;
  1142. X          case '\023': search_forw(1);            /* ^S */
  1143. X        break;
  1144. X          case '\024': gl_transpose();            /* ^T */
  1145. X        break;
  1146. X          case '\031': gl_yank();                /* ^Y */
  1147. X        break;
  1148. X          case '\033':                /* ansi arrow keys */
  1149. X        c = gl_getc();
  1150. X        if (c == '[') {
  1151. X            switch(c = gl_getc()) {
  1152. X              case 'A':                         /* up */
  1153. X                strcpy(gl_buf, hist_prev());
  1154. X                        if (gl_in_hook)
  1155. X                        gl_in_hook(gl_buf);
  1156. X                gl_fixup(gl_prompt, 0, BUF_SIZE);
  1157. X                break;
  1158. X              case 'B':                             /* down */
  1159. X                strcpy(gl_buf, hist_next());
  1160. X                        if (gl_in_hook)
  1161. X                        gl_in_hook(gl_buf);
  1162. X                gl_fixup(gl_prompt, 0, BUF_SIZE);
  1163. X                break;
  1164. X              case 'C': gl_fixup(gl_prompt, -1, gl_pos+1); /* right */
  1165. X                break;
  1166. X              case 'D': gl_fixup(gl_prompt, -1, gl_pos-1); /* left */
  1167. X                break;
  1168. X              default: gl_putc('\007');         /* who knows */
  1169. X                break;
  1170. X            }
  1171. X        } else
  1172. X            gl_putc('\007');
  1173. X        break;
  1174. X          default:        /* check for a terminal signal */
  1175. X#ifdef unix
  1176. X            if (c > 0) {    /* ignore 0 (reset above) */
  1177. X                sig = 0;
  1178. X#ifdef SIGINT
  1179. X                if (c == gl_intrc)
  1180. X                    sig = SIGINT;
  1181. X#endif
  1182. X#ifdef SIGQUIT
  1183. X                if (c == gl_quitc)
  1184. X                    sig = SIGQUIT;
  1185. X#endif
  1186. X#ifdef SIGTSTP
  1187. X                if (c == gl_suspc || c == gl_dsuspc)
  1188. X                    sig = SIGTSTP;
  1189. X#endif
  1190. X                    if (sig != 0) {
  1191. X                    gl_cleanup();
  1192. X                    kill(0, sig);
  1193. X                    gl_init();
  1194. X                    gl_redraw();
  1195. X            c = 0;
  1196. X            } 
  1197. X        }
  1198. X#endif /* unix */
  1199. X                if (c > 0)
  1200. X            gl_putc('\007');
  1201. X        break;
  1202. X        }
  1203. X    }
  1204. X    }
  1205. X    gl_cleanup();
  1206. X    gl_buf[0] = 0;
  1207. X    return gl_buf;
  1208. X}
  1209. X
  1210. Xstatic void
  1211. Xgl_addchar(c)
  1212. Xint c;
  1213. X/* adds the character c to the input buffer at current location */
  1214. X{
  1215. X    int  i;
  1216. X
  1217. X    if (gl_cnt >= BUF_SIZE - 1)
  1218. X    gl_error("\n*** Error: getline(): input buffer overflow\n");
  1219. X    if (gl_overwrite == 0 || gl_pos == gl_cnt) {
  1220. X        for (i=gl_cnt; i >= gl_pos; i--)
  1221. X            gl_buf[i+1] = gl_buf[i];
  1222. X        gl_buf[gl_pos] = c;
  1223. X        gl_fixup(gl_prompt, gl_pos, gl_pos+1);
  1224. X    } else {
  1225. X    gl_buf[gl_pos] = c;
  1226. X    gl_extent = 1;
  1227. X        gl_fixup(gl_prompt, gl_pos, gl_pos+1);
  1228. X    }
  1229. X}
  1230. X
  1231. Xstatic void
  1232. Xgl_yank()
  1233. X/* adds the kill buffer to the input buffer at current location */
  1234. X{
  1235. X    int  i, len;
  1236. X
  1237. X    len = strlen(gl_killbuf);
  1238. X    if (len > 0) {
  1239. X    if (gl_overwrite == 0) {
  1240. X            if (gl_cnt + len >= BUF_SIZE - 1) 
  1241. X            gl_error("\n*** Error: getline(): input buffer overflow\n");
  1242. X            for (i=gl_cnt; i >= gl_pos; i--)
  1243. X                gl_buf[i+len] = gl_buf[i];
  1244. X        for (i=0; i < len; i++)
  1245. X                gl_buf[gl_pos+i] = gl_killbuf[i];
  1246. X            gl_fixup(gl_prompt, gl_pos, gl_pos+len);
  1247. X    } else {
  1248. X        if (gl_pos + len > gl_cnt) {
  1249. X                if (gl_pos + len >= BUF_SIZE - 1) 
  1250. X                gl_error("\n*** Error: getline(): input buffer overflow\n");
  1251. X        gl_buf[gl_pos + len] = 0;
  1252. X            }
  1253. X        for (i=0; i < len; i++)
  1254. X                gl_buf[gl_pos+i] = gl_killbuf[i];
  1255. X        gl_extent = len;
  1256. X            gl_fixup(gl_prompt, gl_pos, gl_pos+len);
  1257. X    }
  1258. X    } else
  1259. X    gl_putc('\007');
  1260. X}
  1261. X
  1262. Xstatic void
  1263. Xgl_transpose()
  1264. X/* switch character under cursor and to left of cursor */
  1265. X{
  1266. X    int    c;
  1267. X
  1268. X    if (gl_pos > 0 && gl_cnt > gl_pos) {
  1269. X    c = gl_buf[gl_pos-1];
  1270. X    gl_buf[gl_pos-1] = gl_buf[gl_pos];
  1271. X    gl_buf[gl_pos] = c;
  1272. X    gl_extent = 2;
  1273. X    gl_fixup(gl_prompt, gl_pos-1, gl_pos);
  1274. X    } else
  1275. X    gl_putc('\007');
  1276. X}
  1277. X
  1278. Xstatic void
  1279. Xgl_newline()
  1280. X/*
  1281. X * Cleans up entire line before returning to caller. A \n is appended.
  1282. X * If line longer than screen, we redraw starting at beginning
  1283. X */
  1284. X{
  1285. X    int change = gl_cnt;
  1286. X    int len = gl_cnt;
  1287. X    int loc = gl_width - 5;    /* shifts line back to start position */
  1288. X
  1289. X    if (gl_cnt >= BUF_SIZE - 1) 
  1290. X        gl_error("\n*** Error: getline(): input buffer overflow\n");
  1291. X    if (gl_out_hook) {
  1292. X    change = gl_out_hook(gl_buf);
  1293. X        len = strlen(gl_buf);
  1294. X    } 
  1295. X    if (loc > len)
  1296. X    loc = len;
  1297. X    gl_fixup(gl_prompt, change, loc);    /* must do this before appending \n */
  1298. X    gl_buf[len] = '\n';
  1299. X    gl_buf[len+1] = '\0';
  1300. X    gl_putc('\n');
  1301. X}
  1302. X
  1303. Xstatic void
  1304. Xgl_del(loc)
  1305. Xint loc;
  1306. X/*
  1307. X * Delete a character.  The loc variable can be:
  1308. X *    -1 : delete character to left of cursor
  1309. X *     0 : delete character under cursor
  1310. X */
  1311. X{
  1312. X    int i;
  1313. X
  1314. X    if ((loc == -1 && gl_pos > 0) || (loc == 0 && gl_pos < gl_cnt)) {
  1315. X        for (i=gl_pos+loc; i < gl_cnt; i++)
  1316. X        gl_buf[i] = gl_buf[i+1];
  1317. X    gl_fixup(gl_prompt, gl_pos+loc, gl_pos+loc);
  1318. X    } else
  1319. X    gl_putc('\007');
  1320. X}
  1321. X
  1322. Xstatic void
  1323. Xgl_kill()
  1324. X/* delete from current position to the end of line */
  1325. X{
  1326. X    if (gl_pos < gl_cnt) {
  1327. X    strcpy(gl_killbuf, gl_buf + gl_pos);
  1328. X    gl_buf[gl_pos] = '\0';
  1329. X    gl_fixup(gl_prompt, gl_pos, gl_pos);
  1330. X    } else
  1331. X    gl_putc('\007');
  1332. X}
  1333. X
  1334. Xstatic void
  1335. Xgl_redraw()
  1336. X/* emit a newline, reset and redraw prompt and current input line */
  1337. X{
  1338. X    if (gl_init_done > 0) {
  1339. X        gl_putc('\n');
  1340. X        gl_fixup(gl_prompt, -2, gl_pos);
  1341. X    }
  1342. X}
  1343. X
  1344. Xstatic void
  1345. Xgl_fixup(prompt, change, cursor)
  1346. Xchar  *prompt;
  1347. Xint    change, cursor;
  1348. X/*
  1349. X * This function is used both for redrawing when input changes or for
  1350. X * moving within the input line.  The parameters are:
  1351. X *   prompt:  compared to last_prompt[] for changes;
  1352. X *   change : the index of the start of changes in the input buffer,
  1353. X *            with -1 indicating no changes, -2 indicating we're on
  1354. X *            a new line, redraw everything.
  1355. X *   cursor : the desired location of the cursor after the call.
  1356. X *            A value of BUF_SIZE can be used  to indicate the cursor should
  1357. X *            move just past the end of the input line.
  1358. X */
  1359. X{
  1360. X    static int   gl_shift;    /* index of first on screen character */
  1361. X    static int   off_right;    /* true if more text right of screen */
  1362. X    static int   off_left;    /* true if more text left of screen */
  1363. X    static char  last_prompt[80] = "";
  1364. X    int          left = 0, right = -1;        /* bounds for redraw */
  1365. X    int          pad;        /* how much to erase at end of line */
  1366. X    int          backup;        /* how far to backup before fixing */
  1367. X    int          new_shift;     /* value of shift based on cursor */
  1368. X    int          extra;         /* adjusts when shift (scroll) happens */
  1369. X    int          i;
  1370. X    int          new_right = -1; /* alternate right bound, using gl_extent */
  1371. X    int          l1, l2;
  1372. X
  1373. X    if (change == -2) {   /* reset */
  1374. X    gl_pos = gl_cnt = gl_shift = off_right = off_left = 0;
  1375. X    gl_putc('\r');
  1376. X    gl_puts(prompt);
  1377. X    strcpy(last_prompt, prompt);
  1378. X    change = 0;
  1379. X        gl_width = gl_termw - gl_strlen(prompt);
  1380. X    } else if (strcmp(prompt, last_prompt) != 0) {
  1381. X    l1 = gl_strlen(last_prompt);
  1382. X    l2 = gl_strlen(prompt);
  1383. X    gl_cnt = gl_cnt + l1 - l2;
  1384. X    strcpy(last_prompt, prompt);
  1385. X    gl_putc('\r');
  1386. X    gl_puts(prompt);
  1387. X    gl_pos = gl_shift;
  1388. X        gl_width = gl_termw - l2;
  1389. X    change = 0;
  1390. X    }
  1391. X    pad = (off_right)? gl_width - 1 : gl_cnt - gl_shift;   /* old length */
  1392. X    backup = gl_pos - gl_shift;
  1393. X    if (change >= 0) {
  1394. X        gl_cnt = strlen(gl_buf);
  1395. X        if (change > gl_cnt)
  1396. X        change = gl_cnt;
  1397. X    }
  1398. X    if (cursor > gl_cnt) {
  1399. X    if (cursor != BUF_SIZE)        /* BUF_SIZE means end of line */
  1400. X        gl_putc('\007');
  1401. X    cursor = gl_cnt;
  1402. X    }
  1403. X    if (cursor < 0) {
  1404. X    gl_putc('\007');
  1405. X    cursor = 0;
  1406. X    }
  1407. X    if (off_right || (off_left && cursor < gl_shift + gl_width - gl_scroll / 2))
  1408. X    extra = 2;            /* shift the scrolling boundary */
  1409. X    else 
  1410. X    extra = 0;
  1411. X    new_shift = cursor + extra + gl_scroll - gl_width;
  1412. X    if (new_shift > 0) {
  1413. X    new_shift /= gl_scroll;
  1414. X    new_shift *= gl_scroll;
  1415. X    } else
  1416. X    new_shift = 0;
  1417. X    if (new_shift != gl_shift) {    /* scroll occurs */
  1418. X    gl_shift = new_shift;
  1419. X    off_left = (gl_shift)? 1 : 0;
  1420. X    off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0;
  1421. X        left = gl_shift;
  1422. X    new_right = right = (off_right)? gl_shift + gl_width - 2 : gl_cnt;
  1423. X    } else if (change >= 0) {        /* no scroll, but text changed */
  1424. X    if (change < gl_shift + off_left) {
  1425. X        left = gl_shift;
  1426. X    } else {
  1427. X        left = change;
  1428. X        backup = gl_pos - change;
  1429. X    }
  1430. X    off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0;
  1431. X    right = (off_right)? gl_shift + gl_width - 2 : gl_cnt;
  1432. X    new_right = (gl_extent && (right > left + gl_extent))? 
  1433. X                 left + gl_extent : right;
  1434. X    }
  1435. X    pad -= (off_right)? gl_width - 1 : gl_cnt - gl_shift;
  1436. X    pad = (pad < 0)? 0 : pad;
  1437. X    if (left <= right) {        /* clean up screen */
  1438. X    for (i=0; i < backup; i++)
  1439. X        gl_putc('\b');
  1440. X    if (left == gl_shift && off_left) {
  1441. X        gl_putc('$');
  1442. X        left++;
  1443. X        }
  1444. X    for (i=left; i < new_right; i++)
  1445. X        gl_putc(gl_buf[i]);
  1446. X    gl_pos = new_right;
  1447. X    if (off_right && new_right == right) {
  1448. X        gl_putc('$');
  1449. X        gl_pos++;
  1450. X    } else { 
  1451. X        for (i=0; i < pad; i++)    /* erase remains of prev line */
  1452. X        gl_putc(' ');
  1453. X        gl_pos += pad;
  1454. X    }
  1455. X    }
  1456. X    i = gl_pos - cursor;        /* move to final cursor location */
  1457. X    if (i > 0) {
  1458. X    while (i--)
  1459. X       gl_putc('\b');
  1460. X    } else {
  1461. X    for (i=gl_pos; i < cursor; i++)
  1462. X        gl_putc(gl_buf[i]);
  1463. X    }
  1464. X    gl_pos = cursor;
  1465. X}
  1466. X
  1467. Xstatic int
  1468. Xgl_tab(buf, offset, loc)
  1469. Xchar  *buf;
  1470. Xint    offset;
  1471. Xint   *loc;
  1472. X/* default tab handler, acts like tabstops every 8 cols */
  1473. X{
  1474. X    int i, count, len;
  1475. X
  1476. X    len = strlen(buf);
  1477. X    count = 8 - (offset + *loc) % 8;
  1478. X    for (i=len; i >= *loc; i--)
  1479. X        buf[i+count] = buf[i];
  1480. X    for (i=0; i < count; i++)
  1481. X        buf[*loc+i] = ' ';
  1482. X    i = *loc;
  1483. X    *loc = i + count;
  1484. X    return i;
  1485. X}
  1486. X
  1487. X/******************* strlen stuff **************************************/
  1488. X
  1489. Xvoid gl_strwidth(func)
  1490. Xsize_t (*func)();
  1491. X{
  1492. X    if (func != 0) {
  1493. X    gl_strlen = func;
  1494. X    }
  1495. X}
  1496. X
  1497. X/******************* History stuff **************************************/
  1498. X
  1499. X#ifndef HIST_SIZE
  1500. X#define HIST_SIZE 100
  1501. X#endif
  1502. X
  1503. Xstatic int      hist_pos = 0, hist_last = 0;
  1504. Xstatic char    *hist_buf[HIST_SIZE];
  1505. X
  1506. Xstatic void
  1507. Xhist_init()
  1508. X{
  1509. X    int i;
  1510. X
  1511. X    hist_buf[0] = "";
  1512. X    for (i=1; i < HIST_SIZE; i++)
  1513. X    hist_buf[i] = (char *)0;
  1514. X}
  1515. X
  1516. Xvoid
  1517. Xgl_histadd(buf)
  1518. Xchar *buf;
  1519. X{
  1520. X    static char *prev = 0;
  1521. X    char *p = buf;
  1522. X    int len;
  1523. X
  1524. X    while (*p == ' ' || *p == '\t' || *p == '\n') 
  1525. X    p++;
  1526. X    if (*p) {
  1527. X    len = strlen(buf);
  1528. X    if (strchr(p, '\n'))     /* previously line already has NL stripped */
  1529. X        len--;
  1530. X    if (prev == 0 || strlen(prev) != len || 
  1531. X                strncmp(prev, buf, len) != 0) {
  1532. X            hist_buf[hist_last] = hist_save(buf);
  1533. X        prev = hist_buf[hist_last];
  1534. X            hist_last = (hist_last + 1) % HIST_SIZE;
  1535. X            if (hist_buf[hist_last] && *hist_buf[hist_last]) {
  1536. X            free(hist_buf[hist_last]);
  1537. X            }
  1538. X        hist_buf[hist_last] = "";
  1539. X    }
  1540. X    }
  1541. X    hist_pos = hist_last;
  1542. X}
  1543. X
  1544. Xstatic char *
  1545. Xhist_prev()
  1546. X/* loads previous hist entry into input buffer, sticks on first */
  1547. X{
  1548. X    char *p = 0;
  1549. X    int   next = (hist_pos - 1 + HIST_SIZE) % HIST_SIZE;
  1550. X
  1551. X    if (hist_buf[hist_pos] != 0 && next != hist_last) {
  1552. X        hist_pos = next;
  1553. X        p = hist_buf[hist_pos];
  1554. X    } 
  1555. X    if (p == 0) {
  1556. X    p = "";
  1557. X    gl_putc('\007');
  1558. X    }
  1559. X    return p;
  1560. X}
  1561. X
  1562. Xstatic char *
  1563. Xhist_next()
  1564. X/* loads next hist entry into input buffer, clears on last */
  1565. X{
  1566. X    char *p = 0;
  1567. X
  1568. X    if (hist_pos != hist_last) {
  1569. X        hist_pos = (hist_pos+1) % HIST_SIZE;
  1570. X    p = hist_buf[hist_pos];
  1571. X    } 
  1572. X    if (p == 0) {
  1573. X    p = "";
  1574. X    gl_putc('\007');
  1575. X    }
  1576. X    return p;
  1577. X}
  1578. X
  1579. Xstatic char *
  1580. Xhist_save(p)
  1581. Xchar *p;
  1582. X/* makes a copy of the string */
  1583. X{
  1584. X    char *s = 0;
  1585. X    int   len = strlen(p);
  1586. X    char *nl = strchr(p, '\n');
  1587. X
  1588. X    if (nl) {
  1589. X        if ((s = malloc(len)) != 0) {
  1590. X            strncpy(s, p, len-1);
  1591. X        s[len-1] = 0;
  1592. X    }
  1593. X    } else {
  1594. X        if ((s = malloc(len+1)) != 0) {
  1595. X            strcpy(s, p);
  1596. X        }
  1597. X    }
  1598. X    if (s == 0) 
  1599. X    gl_error("\n*** Error: hist_save() failed on malloc\n");
  1600. X    return s;
  1601. X}
  1602. X
  1603. X/******************* Search stuff **************************************/
  1604. X
  1605. Xstatic char  search_prompt[101];  /* prompt includes search string */
  1606. Xstatic char  search_string[100];
  1607. Xstatic int   search_pos = 0;      /* current location in search_string */
  1608. Xstatic int   search_forw_flg = 0; /* search direction flag */
  1609. Xstatic int   search_last = 0;      /* last match found */
  1610. X
  1611. Xstatic void  
  1612. Xsearch_update(c)
  1613. Xint c;
  1614. X{
  1615. X    if (c == 0) {
  1616. X    search_pos = 0;
  1617. X        search_string[0] = 0;
  1618. X        search_prompt[0] = '?';
  1619. X        search_prompt[1] = ' ';
  1620. X        search_prompt[2] = 0;
  1621. X    } else if (c > 0) {
  1622. X        search_string[search_pos] = c;
  1623. X        search_string[search_pos+1] = 0;
  1624. X        search_prompt[search_pos] = c;
  1625. X        search_prompt[search_pos+1] = '?';
  1626. X        search_prompt[search_pos+2] = ' ';
  1627. X        search_prompt[search_pos+3] = 0;
  1628. X    search_pos++;
  1629. X    } else {
  1630. X    if (search_pos > 0) {
  1631. X        search_pos--;
  1632. X            search_string[search_pos] = 0;
  1633. X            search_prompt[search_pos] = '?';
  1634. X            search_prompt[search_pos+1] = ' ';
  1635. X            search_prompt[search_pos+2] = 0;
  1636. X    } else {
  1637. X        gl_putc('\007');
  1638. X        hist_pos = hist_last;
  1639. X    }
  1640. X    }
  1641. X}
  1642. X
  1643. Xstatic void 
  1644. Xsearch_addchar(c)
  1645. Xint  c;
  1646. X{
  1647. X    char *loc;
  1648. X
  1649. X    search_update(c);
  1650. X    if (c < 0) {
  1651. X    if (search_pos > 0) {
  1652. X        hist_pos = search_last;
  1653. X    } else {
  1654. X        gl_buf[0] = 0;
  1655. X        hist_pos = hist_last;
  1656. X    }
  1657. X    strcpy(gl_buf, hist_buf[hist_pos]);
  1658. X    }
  1659. X    if ((loc = strstr(gl_buf, search_string)) != 0) {
  1660. X    gl_fixup(search_prompt, 0, loc - gl_buf);
  1661. X    } else if (search_pos > 0) {
  1662. X        if (search_forw_flg) {
  1663. X        search_forw(0);
  1664. X        } else {
  1665. X        search_back(0);
  1666. X        }
  1667. X    } else {
  1668. X    gl_fixup(search_prompt, 0, 0);
  1669. X    }
  1670. X}
  1671. X
  1672. Xstatic void     
  1673. Xsearch_term()
  1674. X{
  1675. X    gl_search_mode = 0;
  1676. X    if (gl_buf[0] == 0)        /* not found, reset hist list */
  1677. X        hist_pos = hist_last;
  1678. X    if (gl_in_hook)
  1679. X    gl_in_hook(gl_buf);
  1680. X    gl_fixup(gl_prompt, 0, gl_pos);
  1681. X}
  1682. X
  1683. Xstatic void     
  1684. Xsearch_back(new_search)
  1685. Xint new_search;
  1686. X{
  1687. X    int    found = 0;
  1688. X    char  *p, *loc;
  1689. X
  1690. X    search_forw_flg = 0;
  1691. X    if (gl_search_mode == 0) {
  1692. X    search_last = hist_pos = hist_last;    
  1693. X    search_update(0);    
  1694. X    gl_search_mode = 1;
  1695. X        gl_buf[0] = 0;
  1696. X    gl_fixup(search_prompt, 0, 0);
  1697. X    } else if (search_pos > 0) {
  1698. X    while (!found) {
  1699. X        p = hist_prev();
  1700. X        if (*p == 0) {        /* not found, done looking */
  1701. X           gl_buf[0] = 0;
  1702. X           gl_fixup(search_prompt, 0, 0);
  1703. X           found = 1;
  1704. X        } else if ((loc = strstr(p, search_string)) != 0) {
  1705. X           strcpy(gl_buf, p);
  1706. X           gl_fixup(search_prompt, 0, loc - p);
  1707. X           if (new_search)
  1708. X           search_last = hist_pos;
  1709. X           found = 1;
  1710. X        } 
  1711. X    }
  1712. X    } else {
  1713. X        gl_putc('\007');
  1714. X    }
  1715. X}
  1716. X
  1717. Xstatic void     
  1718. Xsearch_forw(new_search)
  1719. Xint new_search;
  1720. X{
  1721. X    int    found = 0;
  1722. X    char  *p, *loc;
  1723. X
  1724. X    search_forw_flg = 1;
  1725. X    if (gl_search_mode == 0) {
  1726. X    search_last = hist_pos = hist_last;    
  1727. X    search_update(0);    
  1728. X    gl_search_mode = 1;
  1729. X        gl_buf[0] = 0;
  1730. X    gl_fixup(search_prompt, 0, 0);
  1731. X    } else if (search_pos > 0) {
  1732. X    while (!found) {
  1733. X        p = hist_next();
  1734. X        if (*p == 0) {        /* not found, done looking */
  1735. X           gl_buf[0] = 0;
  1736. X           gl_fixup(search_prompt, 0, 0);
  1737. X           found = 1;
  1738. X        } else if ((loc = strstr(p, search_string)) != 0) {
  1739. X           strcpy(gl_buf, p);
  1740. X           gl_fixup(search_prompt, 0, loc - p);
  1741. X           if (new_search)
  1742. X           search_last = hist_pos;
  1743. X           found = 1;
  1744. X        } 
  1745. X    }
  1746. X    } else {
  1747. X        gl_putc('\007');
  1748. X    }
  1749. X}
  1750. END_OF_FILE
  1751.   if test 26628 -ne `wc -c <'getline.c'`; then
  1752.     echo shar: \"'getline.c'\" unpacked with wrong size!
  1753.   fi
  1754.   # end of 'getline.c'
  1755. fi
  1756. if test -f 'getline.h' -a "${1}" != "-c" ; then 
  1757.   echo shar: Will not clobber existing file \"'getline.h'\"
  1758. else
  1759.   echo shar: Extracting \"'getline.h'\" \(480 characters\)
  1760.   sed "s/^X//" >'getline.h' <<'END_OF_FILE'
  1761. X#ifndef GETLINE_H
  1762. X#define GETLINE_H
  1763. X
  1764. X/* unix systems can #define POSIX to use termios, otherwise 
  1765. X * the bsd or sysv interface will be used 
  1766. X */
  1767. X
  1768. Xchar           *getline();        /* read a line of input */
  1769. Xvoid            gl_setwidth();        /* specify width of screen */
  1770. Xvoid            gl_histadd();        /* adds entries to hist */
  1771. Xvoid        gl_strwidth();        /* to bind gl_strlen */
  1772. X
  1773. Xextern int     (*gl_in_hook)();
  1774. Xextern int     (*gl_out_hook)();
  1775. Xextern int    (*gl_tab_hook)();
  1776. X
  1777. X#endif /* GETLINE_H */
  1778. END_OF_FILE
  1779.   if test 480 -ne `wc -c <'getline.h'`; then
  1780.     echo shar: \"'getline.h'\" unpacked with wrong size!
  1781.   fi
  1782.   # end of 'getline.h'
  1783. fi
  1784. if test -f 'testgl.c' -a "${1}" != "-c" ; then 
  1785.   echo shar: Will not clobber existing file \"'testgl.c'\"
  1786. else
  1787.   echo shar: Extracting \"'testgl.c'\" \(309 characters\)
  1788.   sed "s/^X//" >'testgl.c' <<'END_OF_FILE'
  1789. X#include <stdio.h>
  1790. X
  1791. Xextern char *getline();
  1792. Xextern void  gl_histadd();
  1793. Xextern void  exit();
  1794. X
  1795. Xmain()
  1796. X/* 
  1797. X * just echo user input lines, letting user edit them and move through
  1798. X * history list
  1799. X */
  1800. X{
  1801. X    char *p;
  1802. X
  1803. X    do {
  1804. X    p = getline("PROMPT>>>> ");
  1805. X    gl_histadd(p);
  1806. X    fputs(p, stdout);
  1807. X    } while (*p != 0);
  1808. X}
  1809. END_OF_FILE
  1810.   if test 309 -ne `wc -c <'testgl.c'`; then
  1811.     echo shar: \"'testgl.c'\" unpacked with wrong size!
  1812.   fi
  1813.   # end of 'testgl.c'
  1814. fi
  1815. echo shar: End of archive 1 \(of 1\).
  1816. cp /dev/null ark1isdone
  1817. MISSING=""
  1818. for I in 1 ; do
  1819.     if test ! -f ark${I}isdone ; then
  1820.     MISSING="${MISSING} ${I}"
  1821.     fi
  1822. done
  1823. if test "${MISSING}" = "" ; then
  1824.     echo You have the archive.
  1825.     rm -f ark[1-9]isdone
  1826. else
  1827.     echo You still must unpack the following archives:
  1828.     echo "        " ${MISSING}
  1829. fi
  1830. exit 0
  1831. exit 0 # Just in case...
  1832.