Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
*************************** Motivation **********************************Many interactive programs read input line by line, but would like toprovide line editing and history functionality to the end-user thatruns the program.The input-edit package provides that functionality. As far as theprogrammer is concerned, the program only asks for the next lineof input. However, until the user presses the RETURN key they can useemacs-style line editing commands and can traverse the history of linespreviously typed.Other packages, such as GNU's readline, have greater capability but arealso substantially larger. Input-edit is small, since it uses neitherstdio nor any termcap features, and is also quite portable. It only uses\b to backspace and \007 to ring the bell on errors. Since it cannotedit multiple lines it scrolls long lines left and right on the same line.Input edit uses classic (not ANSI) C, and should run on any Unixsystem (BSD, SYSV or POSIX), PC's under DOS with MSC, TurboC or djgpp,PC's under OS/2 with gcc (EMX), or Vax/VMS. Porting the package to newsystems basicaly requires code to read a character when it is typed withoutechoing it, everything else should be OK.I have run the package on:DECstation 5000, Ultrix 4.3 with cc 2.1 and gcc 2.3.3Sun Sparc 2, SunOS 4.1.1, with ccSGI Iris, IRIX System V.3, with ccPC using DOS with MSCThe description below is broken into two parts, the end-user (editing)interface and the programmer interface. Send bug reports, fixes andenhancements to:Chris Thewalt (thewalt@ce.berkeley.edu)5/3/93Thanks to the following people who have provided enhancements and fixes:Ron Ueberschaer, Christoph Keller, Scott Schwartz, Steven List,DaviD W. Sanderson, Goran Bostrom, Michael Gleason, Glenn Kasten,Edin Hodzic, Eric J Bivona, Kai Uwe Rommel, Danny Quah, Ulrich BetzlerPS: I don't have, and don't want to add, a vi mode, sorry.************************** End-User Interface ***************************Entering printable keys generally inserts new text into the buffer (unlessin overwrite mode, see below). Other special keys can be used to modifythe text in the buffer. In the description of the keys below, ^n meansControl-n, or holding the CONTROL key down while pressing "n". Errorswill ring the terminal bell.^A/^E : Move cursor to beginning/end of the line.^F/^B : Move cursor forward/backward one character.ESC-F : Move cursor forward one word.ESC-B : Move cursor backward one word.^D : Delete the character under the cursor.^H, DEL : Delete the character to the left of the cursor.^K : Kill from the cursor to the end of line.^L : Redraw current line.^O : Toggle overwrite/insert mode. Initially in insert mode. Textadded in overwrite mode (including yanks) overwriteexisting text, while insert mode does not overwrite.^P/^N : Move to previous/next item on history list.^R/^S : Perform incremental reverse/forward search for string onthe history list. Typing normal characters adds to the currentsearch string and searches for a match. Typing ^R/^S marksthe start of a new search, and moves on to the next match.Typing ^H or DEL deletes the last character from the searchstring, and searches from the starting location of the last search.Therefore, repeated DEL's appear to unwind to the match nearestthe point at which the last ^R or ^S was typed. If DEL isrepeated until the search string is empty the search locationbegins from the start of the history list. Typing ESC orany other editing character accepts the current match andloads it into the buffer, terminating the search.^T : Toggle the characters under and to the left of the cursor.^U : Deletes the entire line^Y : Yank previously killed text back at current location. Note thatthis will overwrite or insert, depending on the current mode.TAB : By default adds spaces to buffer to get to next TAB stop(just after every 8th column), although this may be rebound by theprogrammer, as described below.NL, CR : returns current buffer to the program.DOS and ANSI terminal arrow key sequences are recognized, and act like:up : same as ^Pdown : same as ^Nleft : same as ^Bright : same as ^F************************** Programmer Interface ***************************The programmer accesses input-edit through these functions, and optionallythrough three additional function pointer hooks. The four functions are:char *getline(char *prompt)Prints the prompt and allows the user to edit the current line. Apointer to the line is returned when the user finishes bytyping a newline or a return. Unlike GNU readline, the returnedpointer points to a static buffer, so it should not be free'd, andthe buffer contains the newline character. The user enters anend-of-file by typing ^D on an empty line, in which case thefirst character of the returned buffer is '\0'. Getline neverreturns a NULL pointer. The getline functions sets terminal modesneeded to make it work, and resets them before returning to thecaller. The getline function also looks for characters that wouldgenerate a signal, and resets the terminal modes before raising thesignal condition. If the signal handler returns to getline,the screen is automatically redrawn and editing can continue.Getline now requires both the input and output stream be connectedto the terminal (not redirected) so the main program should checkto make sure this is true. If input or output have been redirectedthe main program should use buffered IO (stdio) rather thanthe slow 1 character read()s that getline uses.void gl_setwidth(int width)Set the width of the terminal to the specified width. The defaultwidth is 80 characters, so this function need only be called if thewidth of the terminal is not 80. Since horizontal scrolling iscontrolled by this parameter it is important to get it right.void gl_histadd(char *buf)The gl_histadd function checks to see if the buf is not empty orwhitespace, and also checks to make sure it is different thanthe last saved buffer to avoid repeats on the history list.If the buf is a new non-blank string a copy is made and saved onthe history list, so the caller can re-use the specified buf.void gl_strwidth(size_t (*func)())The gl_strwidth function allows the caller to supply a pointer toa prompt width calculation function (strlen by default). Thisallows the caller to embed escape sequences in the prompt and thentell getline how many screen spaces the prompt will take up.The main loop in testgl.c, included in this directory, shows how theinput-edit package can be used:extern char *getline();extern void gl_histadd();main(){char *p;do {p = getline("PROMPT>>>> ");gl_histadd(p);fputs(p, stdout);} while (*p != 0);}In order to allow the main program to have additional access to the buffer,to implement things such as completion or auto-indent modes, threefunction pointers can be bound to user functions to modify the buffer asdescribed below. By default gl_in_hook and gl_out_hook are set to NULL,and gl_tab_hook is bound to a function that inserts spaces until the nextlogical tab stop is reached. The user can reassign any of these pointersto other functions. Each of the functions bound to these hooks receivesthe current buffer as the first argument, and must return the location ofthe leftmost change made in the buffer. If the buffer isn't modified thefunctions should return -1. When the hook function returns the screen isupdated to reflect any changes made by the user function.int (*gl_in_hook)(char *buf)If gl_in_hook is non-NULL the function is called each time a newbuffer is loaded. It is called when getline is entered, with anempty buffer, it is called each time a new buffer is loaded fromthe history with ^P or ^N, and it is called when an incrementalsearch string is accepted (when the search is terminated). Thebuffer can be modified and will be redrawn upon return to getline().int (*gl_out_hook)(char *buf)If gl_out_hook is non-NULL it is called when a line has beencompleted by the user entering a newline or return. The bufferhanded to the hook does not yet have the newline appended. If thebuffer is modified the screen is redrawn before getline returns thebuffer to the caller.int (*gl_tab_hook)(char *buf, int prompt_width, int *cursor_loc)If gl_tab_hook is non-NULL, it is called whenever a tab is typed.In addition to receiving the buffer, the current prompt width isgiven (needed to do tabbing right) and a pointer to the cursoroffset is given, where a 0 offset means the first character in theline. Not only does the cursor_loc tell the programmer where theTAB was received, but it can be reset so that the cursor will endup at the specified location after the screen is redrawn.