tcsh with a command line editor (2 of 5)

Paul Placeway paul at osu-dbs.UUCP
Sat Apr 14 05:27:34 AEST 1984


.
The following code is my changes to Ken Greer's tcsh.  I have added a visual
mini-editor to the shell, and cleaned up the expansion routines some what.
note that this is the 4.1 version.  When we get 4.2 up I'll repost the new
changes.

Please send any changes back to me so that I can update our version.

Note: this is part 2 of 5, you need all of the parts to make tcsh.

					Paul W. Placeway
					The Ohio State University IRCC
					(UUCP: cbosgd!osu-dbs!paul)
					(CSNet: paul at ohio-state)

================ cut here ================
: This is a shar archive.  Extract with sh, not csh.
echo x - inputl.c
cat > inputl.c << '!Funky!Stuff!'
/* @(#)inputl.c	1.1  (Ohio State)  6/24/83  Paul Placeway */

/* 
 * $Compile: cc -DTEST -o foobar -O %f -ltermcap
 */

/*****************************************************************
 * inputl() -- get a promped line from the keyboard with input editing
 * 
 * SYNOPSIS:
 * 	char *
 *	inputl(prompt, string, stringlen)
 *	char *prompt;
 *	char *string;
 *	int stringlen;		length of the above string
 *
 *	ilsetup (instr, outstr)
 *	short instr, outstr;	input and output streams - normaly 0 & 1
 *
 *	int
 *	ilpushback(string)
 *	char *string;
 *
 * DESCRIPTION:
 *	Inputl reads a string from the terminal and allows editing by the
 *	user.  The editing is done by control keys and special use keys such
 *	as DELETE or BACKSPACE.  A list of the controls follows:
 *
 *	^@		Nothing (becuase some parts won't cope with it)
 *
 *	^A		Moves the cursor to the begining of the line (after
 *			the prompt)
 *
 *	^B		Moves the cursor back one space.  Beeps if it is
 *			allready in column one.
 *
 *	^C		Sends a SIGINTR to the current process (from the tty
 *			driver)
 *
 *	^D		Deletes the character that is over the cursor.
 *
 *	^E		Moves the cursor to the end of the line.
 *
 *	^F		Moves the cursor forward one space.  Beeps if at the
 *			end of the line.
 *
 *	^G		Erase entire line and start over.
 *
 *	^H	BS	Same as ^B
 *
 *	^I	TAB	Inserts a tab character
 *
 *	^J	LF	Moves the cursor to the end of the line, inserts a
 *			newline character, and sends the line.
 *
 *	^K		Delete all characters from the current cursor
 *			possition to the end inclusive and save them in a
 *			special buffer to be called back later (see ^Y).
 *
 *	^L		Put a ^L at the end of the line and send it (without
 *			echoing a newline).
 *
 *	^M	RETURN	Same as ^J
 *
 *	^N		Move to the next following word (i.e. skip to the
 *			end of the current whitespace, then skip to the end
 *			of the non-whitespace)
 *
 *	^O		Flush tty output until the next tty input request
 *			(tty driver).
 *
 *	^P		Same as ^N but go backwards.
 *
 *	^Q		Restart output. (see ^S)
 *
 *	^R		Redisplay the current line and move the cursor to
 *			it's original place.
 *
 *	^S		Stop the tty's output.
 *
 *	^T		Transpose previous two characters.
 *
 *	^U		Same as ^K but kill to the beginning of the line
 *			exclusive.
 *
 *	^V		Clear the screen and redisplay the line
 *
 *	^W		Delete the previous word.
 *
 *	^X		Delete the next word (see ^W).
 *
 *	^Y		Yank the characters saved (by ^K or ^U) as if they
 *			were typed on the keyboard.
 *
 *	^Z		Send a SIGTSTP to the current process (tty driver).
 *
 *	^[	ESC	put a ESC at the end of the line and send it
 *			(without echoing a newline)
 *
 *	^\		send a SIGQUIT to the current process (tty driver)
 *
 *	^]		Send a SIGTSTP when the program attempts to read the
 *			NULL. (tty driver) This is usually immeaditly
 *			because of CBREAK mode, but the assignment is left
 *			so that cooked mode uses it.
 *
 *	^^		Insert the next character regardless of it's
 *			meaning.
 *
 *	^_		Put an end of file mark at the end and send the
 *			line. (tty driver)
 *
 *	^?	DELETE	Delete the character immeaditely to the left of the
 *			cursor.  Beeps if the cursor is at the beginning of
 *			the line.
 *
 *	ALL OTHERS	All other characters insert themselfs into the line.
 *
 *	  Ilpushback pushes the string onto the input line exactly as if it
 *	had been typed on the console.  The next call to inputl will print
 *	the string and put the cursor at the end of the line.  If control
 *	characters are passed to ilpushback, they will act as if they had
 *	been quoted.
 *
 * NOTE
 *	This program is in the public domain.  It may be copied and 
 *	modified, but not sold or included in another product which
 *	is sold.
 *
 *	If you change anything, PLEASE send me mail describing your changes
 *	so that I can include them in later versions.  A diff listing would
 *	be very helpful, also.
 *
 *	Also, please send any bug reports to me.  Thanks,
 *
 *					Paul W. Placeway
 *					The Ohio State University IRCC
 *					(UUCP: cbosgd!osu-dbs!paul)
 *					(CSNet: paul at ohio-state)
 *
 *****************************************************************
 */

#define CSH
#define MAKE_INPUTL
				/* so that some defines get done */
#include "inputl.h"

/*
    Interface to termcap library.
*/

static char    *ClearEOL;
static char    *ClearScr;
static char    *DeleteCh;
static char    *StartIns;
static char    *EndIns;
static char    *FwdCh;
static char    *BkdCh;


static char input_line[1024];	/* what i'm getting */
static char kill_buf[1024];	/* for ^K, ^U, ^Y */
static char last_inp[1024];	/* for matching input compleation and ^@ */
static int  endptr;		/* the last char in the line */
static int  cursor;		/* The current cursor posisition (0 = beg) */
static int  killbuf_len = 0;	/* Length of the kill buffer string */
static int  lastin_len = 0;	/* for last input saving */
static int  matching_length = 0;   /* for command compleation */

static int  pushed_input = FALSE;  /* weather some input has been pushed on */
static char *prompt;		/* a global pointer to the prompt */
static int  prompt_length;	/* how long the prompt given is */
static int  edit_flags;		/* flags that effect the cursor
				   posisition and other things */
static int  do_redisplay = FALSE;	/* redraw what you have now */
static int  has_been_setup = FALSE;	/* for autosetup of the terminal */
static int  tty_driver_not_setup = TRUE; /* for autosetup of the tty driver */
static int  keybindings[256];	/* the currently assigned keys */
static int  bindings_been_inited = FALSE;	/* for setup of bindings */
						/* the first time */

#ifdef CSH

extern short SHIN, SHOUT;

#else

static short SHIN = 0;		/* for input and output munging */
static short SHOUT = 1;

#endif

/* vars for the tty driver */    

struct sgttyb old_term;
struct sgttyb new_term;
struct sgttyb none_term;
struct sgttyb test_term;	/* for testing wether the modes have been */
				/* changed on us so we can compensate */

struct tchars  old_tchars;      /* INT, QUIT, XON, XOFF, EOF, BRK */
struct tchars  new_tchars;
struct tchars  none_tchars;

struct ltchars old_ltchars;	/* SUSP, DSTOP, RPRNT, FLUSH, WERASE, LNEXT */
struct ltchars new_ltchars;
struct ltchars none_ltchars;

long old_modwd;			/* both for testing if things have changed */
long test_modwd;		/* on us during proccessing */

/* 
 * inputl.c  --	get an input line from the terminal using full line editing
 *		and take the line on an escape, control-V , return, 
 *		or line feed.
 */

char
inputl(prompt_str, string_to_get, string_to_get_length)
char *prompt_str;		/* prompt string */
char *string_to_get;
int string_to_get_length;	/* max length of the input line */
{
    register int i;
    char c;
    int doing_input;
    char temp_c1;
    char temp_c2;
    int old_cursor;
    int quoted;
    int outputchar();
    char retval;		/* return value - char typed to send line */

    if (isatty (SHIN) == 0) {
	if (read (SHIN, string_to_get, 512) <= 0) {
	    string_to_get[0] = -1;
	    string_to_get[1] = '\0';
	}
	return ('\n');
    }
    
    if (has_been_setup == FALSE) {
        ilsetup();
    }
    
    prompt = prompt_str;
    prompt_length = strlen (prompt);
    if (prompt_length > 70) prompt_length = 70;

    set_term();			/* turn on CBREAK and NOECHO */

    if (pushed_input == FALSE) {
	if (ClearEOL != 0) {
	    write (SHOUT, "\r", 1);		/* start at the beg of line */
	    write (SHOUT, prompt, prompt_length);   /* write the prompt */
	    tputs (ClearEOL, 0, outputchar);
	} else {
	    write (SHOUT, prompt, prompt_length);   /* write the prompt */
	}
	cursor = 0;		/* start cursor off at beginning */
	endptr = 0;
    } else {
	if (do_redisplay == FALSE) {
	    for (i = matching_length; i < endptr; i++) {
		PrintChar (input_line[i], i+1);
	    }
	}
	pushed_input = FALSE;
    }
    

    doing_input = TRUE;

    if (do_redisplay == TRUE) {
	Redisplay();
	do_redisplay = FALSE;
    }

    quoted = FALSE;

    while (doing_input) {
	if (read (SHIN, &c, 1) != 1) {		/* get an input character */
	    input_line[0] = -1;		/* fool it into thinking that the */
	    endptr = 1;			/* end-of-file char was typed */
	    break;
	}
	if (quoted == TRUE) {
	    quoted = FALSE;
	    Insert(c);
	    set_term();		/* reset the special characters */
	}
	else switch (keybindings[c]) {
			/* and parse it for kbrd commands */

	    case YANK_LAST_INPUT:	/* ^@ -- YANK_LAST_INPUT */
		for (i = 0; i < lastin_len; i++) {
		    Insert(last_inp[i]);
		}
		break;
		
	    case BEGINNING_OF_LINE:	/* ^A -- BEGINNING_OF_LINE */
		MoveCursor(cursor, 0);
		cursor = 0;
		break;
		
	    case BACKWARD_CHAR:	/* ^B -- BACKWARD_CHAR */
		if (cursor != 0) {
		    MoveCursor(cursor, cursor - 1);
		    cursor--;
		}
		else beep();
		break;
		
	    case TTY_SIGINTR:	/* ^C -- SIGINTR (tty driver) */
		break;
		
	    case DELETE_CHAR_FORWARD:	/* ^D -- DELETE_CHAR_FORWARD */
		Delete();
		break;
		
	    case END_OF_LINE:	/* ^E -- END_OF_LINE */
		MoveCursor(cursor, endptr);
		cursor = endptr;
		break;
		
	    case FORWARD_CHAR:	/* ^F -- FORWARD_CHAR */
		if (cursor != endptr) {
		    MoveCursor(cursor, cursor +1);  /* move the cursor fwd */
		    cursor++;			  /* by writing its current */
		}				   /* character */
		else beep();	/* at end-of-line */
		break;
		
	    case START_OVER:	/* ^G -- START_OVER */
		ClearLine();
		(void) write (SHOUT, prompt, prompt_length);
		endptr = 0;
		cursor = 0;
		break;
		
	    case KILL_TO_END:	/* ^K -- KILL_TO_END */
		if (cursor == endptr) {
		    beep();
		    break;
		}
		else {
		    killbuf_len = endptr - cursor;
		    for (i = 0; i < killbuf_len; i++) {
		        kill_buf[i] = input_line[cursor + i];
		    }
		    endptr = cursor;
		    if (ClearEOL != 0) {
			tputs (ClearEOL, 0, outputchar);
		    }
		    else {
			Redisplay();
		    }
		    break;
		}
		
		
	    case SEND_EOF:	/* ^_ -- TTY_EOF */
		c = -1;		/* sends an EOF */
		/* fall through to */
	    case SEND_LINE:	/* ^V -- SEND_LINE */
		input_line[endptr] = '\0';	/* just in case */
		doing_input = FALSE;		/* send it */
		MoveCursor(cursor, endptr);	/* make sure your at end */
		retval = c;			/* set the return value */
		break;

	    case NL_SEND_LINE:	/* ^J LF -- NL_SEND_LINE */
		if (endptr > 0) {		/* save the input for ^H */
		    lastin_len = endptr;
		    for (i = 0; i <=lastin_len; i++) {
		        last_inp[i] = input_line[i];
		    }
		}
		input_line[endptr] = '\n';
		retval = '\n';		/* like the name says: NL... */
		doing_input = FALSE;		/* send it */
		if ((c == '\n') || (c == '\r')) {
		    outputchar('\n');
		}
		break;
		
	    case FORWARD_WORD:	/* ^N -- FORWARD_WORD */
		old_cursor = cursor;
		while ((IsInWord (input_line[cursor]) == 0)
		    && (cursor != endptr)) {
		    ++cursor;
		}
		while ((IsInWord (input_line[cursor]))
		    && (cursor != endptr)) {
		    ++cursor;
		}
		MoveCursor(old_cursor, cursor);
		break;

	    case ERR:		/* beep on this key */
		beep();		/* fall through to... */
	    case NOP:		/* no operation */
		break;

	    case TTY_FLUSH_OUTPUT:	/* ^O -- TTY_FLUSH_OUTPUT */
		break;

	    case BACKWARD_WORD:		/* ^P -- BACKWARD_WORD */
		old_cursor = cursor;
		while ((IsInWord (input_line[cursor-1]) == 0)
		    && (cursor != 0)) {
		    --cursor;
		}
		while ((IsInWord (input_line[cursor-1]))
		    && (cursor != 0)) {
		    --cursor;
		}
		MoveCursor(old_cursor, cursor);
		break;

	    case TTY_START_OUTPUT:	/* ^Q -- START_OUTPUT (tty driver) */
		break;
		
	    case REDISPLAY:	/* ^R -- REDISPLAY */
		Redisplay();
		break;
		
	    case TTY_STOP_OUTPUT:	/* ^S -- STOP_OUTPUT */
		break;

	    case TRANSPOSE_CHARS:	/* ^T -- TRANSPOSE_PREVIOUS_TWO */
		if (cursor >= 2) {
		    temp_c2 = input_line[cursor - 2];
		    temp_c1 = input_line[cursor - 1];
		    DelPrev();
		    DelPrev();
		    Insert(temp_c1);
		    Insert(temp_c2);
		}
		else beep();
		break;
		
	    case KILL_TO_BEGINNING:	/* ^U -- KILL_TO_BEGINNING */
		if (cursor == 0) {
		    beep();
		    break;
		}
		else {
		    killbuf_len = cursor;
		    for (i = 0; i < cursor; i++) {
		        kill_buf[i] = input_line[i];
		    }
		    while (cursor != 0) {
		        DelPrev();
		    }
		    break;
		}

	    case CLEAR_SCREEN:	/* ^L -- CLEAR_SCREEN */
		ClearScreen();
		Redisplay();
		break;

	    case DELETE_WORD_BACKWARD:	/* ^W -- DELETE_WORD_BACKWARD */
		while ((IsInWord (input_line[cursor-1]) == 0)
		    && (cursor != 0)) {
		    DelPrev();
		}
		while ((IsInWord (input_line[cursor-1])) 
		    && (cursor != 0)) {
		    DelPrev();
		}
		break;

	    case DELETE_WORD_FORWARD:	/* ^X -- DELETE_WORD_FORWARD */
		while ((IsInWord (input_line[cursor]) == 0)
		    && (cursor != endptr)) {
		    Delete();
		}
		while ((IsInWord (input_line[cursor]))
		    && (cursor != endptr)) {
		    Delete();
		}
		break;

	    case YANK_KILLBUFFER:	/* ^Y -- YANK_KILLBUFFER */
		for (i = 0; i < killbuf_len; i++) {
		    Insert(kill_buf[i]);
		}
		break;

	    case TTY_SIGTSUSP:	/* ^Z -- TTY_SIGTSUSP */
		break;

	    case TTY_SIGQUIT:	/* ^\ -- TTY_SIGQUIT */
		break;

	    case TTY_DSUSP:	/* ^] -- TTY_DSUSP */
		break;

	    case QUOTE_NEXT:	/* ^^ -- QUOTE_NEXT */
		quoted = TRUE;
		nospecial();	/* this is gross -- turn off all the chars */
		break;

	    case DELETE_CHAR_BACKWARD:	/* ^? DEL -- DELETE_CHAR_BACKWARD */
		DelPrev();
		break;

	    case AUTO_INSERT:	/* it's a normal character, insert it */
		Insert(c);
		break;

	    default:		/* it's garbage, ignore it */
		break;
		
	    }			/* end of switch (c) */

	}			/* end of while (doing_input) */

	    
    input_line[endptr+1] = NULL;
    reset_term();
    
    strncpy(string_to_get, input_line, string_to_get_length- 1);
    string_to_get[string_to_get_length] = '\0';
				/* string_to_get = input_line */
    return (retval);
}


/************************************************************/

#ifdef CSH

ilsetup () {

#else

ilsetup(Shin, Shout)
short Shin, Shout;		/* SHIN = input stream, SHOUT = output str. */
{

#endif
    static  char    bp[1024];
    static  char    buffer[1024];
    char    *area = buffer;
    char    *getenv();
    char    *tgetstr();
    char    *name;
    int     retcode;

#ifndef CSH
    SHIN = Shin;
    SHOUT = Shout;
#endif

    name = getenv("TERM");

    retcode = tgetent(bp, name);

    switch(retcode) {

        case -1:
            printf("can't open termcap file.\n");
            exit(1);
            break;

        case 0:
            printf("No termcap entry for %s, using \"dumb\"\n", name);
            if ((tgetent(bp, "dumb")) == -1) {
		printf("can't open termcap file.\n");
		exit (1);
	    }
            break;

    }

    StartIns  = tgetstr("im", &area);
    EndIns    = tgetstr("ei", &area);
    DeleteCh  = tgetstr("dc", &area);
    ClearEOL  = tgetstr("ce", &area);
    ClearScr  = tgetstr("cl", &area);
    FwdCh     = tgetstr("nd", &area);
    BkdCh     = tgetstr("bc", &area);

    InitBindings ();

    has_been_setup = TRUE;

}

static
InitBindings ()
{
    register int i;

    if (bindings_been_inited) return;

    get_tty_special ();		/* get the old chars */

    for (i=0; i<128; i++) {	/* assign all the keys */
	DoBindToKey (AUTO_INSERT, i);
    }
    DoBindToKey(BEGINNING_OF_LINE, 001);
    DoBindToKey(BACKWARD_CHAR, 002);
    DoBindToKey(TTY_SIGINTR, 003);
    DoBindToKey(DELETE_CHAR_FORWARD, 004);
    DoBindToKey(END_OF_LINE, 005);
    DoBindToKey(FORWARD_CHAR, 006);
    DoBindToKey(START_OVER, 007);
    DoBindToKey(YANK_LAST_INPUT, 010);
    DoBindToKey(KILL_TO_END, 013);
    DoBindToKey(SEND_EOF, 037);
    DoBindToKey(SEND_LINE, 000);
    DoBindToKey(SEND_LINE, 077);
    DoBindToKey(SEND_LINE, 033);
    DoBindToKey(NL_SEND_LINE, 012);
    DoBindToKey(NL_SEND_LINE, 015);
    DoBindToKey(FORWARD_WORD, 016);
    DoBindToKey(TTY_FLUSH_OUTPUT, 017);
    DoBindToKey(BACKWARD_WORD, 020);
    DoBindToKey(TTY_START_OUTPUT, 021);
    DoBindToKey(REDISPLAY, 022);
    DoBindToKey(TTY_STOP_OUTPUT, 023);
    DoBindToKey(TRANSPOSE_CHARS, 024);
    DoBindToKey(KILL_TO_BEGINNING, 025);
    DoBindToKey(CLEAR_SCREEN, 014);
    DoBindToKey(DELETE_WORD_BACKWARD, 027);
    DoBindToKey(DELETE_WORD_FORWARD, 030);
    DoBindToKey(YANK_KILLBUFFER, 031);
    DoBindToKey(TTY_SIGTSUSP, 032);
    DoBindToKey(TTY_SIGQUIT, 034);
    DoBindToKey(TTY_DSUSP, 035);
    DoBindToKey(QUOTE_NEXT, 026);	/* ^^ -> ^V (back to normal) */
    DoBindToKey(DELETE_CHAR_BACKWARD, 0177);

    reset_term ();		/* export bindings */
    bindings_been_inited = TRUE;
}

static
ClearLine() {
    int outputchar();

    if (ClearEOL != 0) {
	outputchar ('\r');
	tputs (ClearEOL, 0, outputchar);
    } else {
	outputchar ('\n');
    }
}

static
ClearScreen() {
    int outputchar();
    
    if (ClearScr != 0) {
	tputs (ClearScr, 0, outputchar);
    }
}


static
DelPrevChar() {
    register int distance;
    int Count();
    
    MoveCursor(cursor, cursor-1);
    distance = Count(cursor) - Count(cursor - 1);

    DelScrChar(distance);
}

static
DelChar() {
    register int distance;
    int Count();
    
    distance = Count(cursor + 1) - Count(cursor);

    DelScrChar(distance);
}

static
DelScrChar(distance)
int distance;
{
    register int i;
    int outputchar();

    if (DeleteCh == 0) {	/* do a huge pain */
	for (i = cursor; i < endptr; i++) {
	    PrintChar(input_line[i], i+1);	/* i+1??? brain-dammage */
	}
	for (i = 0; i < distance; i++) {
	    outputchar(' ');
	}

/* 	MoveCursor(endptr + distance, cursor); INCONSISTANT USAGE... */
	for (i = distance; i > 0; i--) {
	    if (BkdCh != 0) {
	        tputs (BkdCh, 0, outputchar);
	    }
	    else {
		outputchar ('\b');
	    }
	}			/* now I'm at the end... */
	MoveCursor(endptr, cursor);
    }
    else {
	for (i = 0; i < distance; i++) {
	    tputs (DeleteCh, 0, outputchar);
	}
    }
}

static
InsChar(c)
char c;
{
    register int i;
    int outputchar();

    if (StartIns == 0) {
	for (i = cursor-1; i < endptr; i++) {
	    PrintChar(input_line[i], i+1);
	}
	MoveCursor(endptr, cursor);
    }
    else {
	if (cursor != endptr) {
	    tputs (StartIns, 0, outputchar);
	}
	
	PrintChar(c, cursor);
	
	if (cursor != endptr) {
	    tputs (EndIns, 0, outputchar);
	}
	
    }

}

static
MoveCursor(where_from, where_to)
int where_from;
int where_to;
{
    int diff;
    int i;
    int Count();
    int outputchar();
    
    diff = Count(where_to) - Count(where_from);

    if (diff > 0) {
	if (FwdCh != 0) {
	    for (i = 0; i < diff; i++) {
	        tputs (FwdCh, 0, outputchar);
	    }
	}
	else {
	    for (i = where_from; i < where_to; i++) {
		PrintChar(input_line[i], i);
	    }
	}
    }
    else if (diff < 0) {
	diff = -diff;
	for (i = 0; i < diff; i++) {
	    if (BkdCh != 0) {
	        tputs (BkdCh, 0, outputchar);
	    }
	    else {
		outputchar ('\b');
	    }
	}
    }
}


/*
    Set terminal to CBREAK and NOECHO.
*/
static
set_term()
{
    if(tty_driver_not_setup == 0) {	/* tty driver is set up */
	ioctl (SHIN, TIOCGETP, &test_term);	/* get the setup */
	ioctl (SHIN, TIOCLGET, &test_modwd);	/* get local mode word */
	if ((old_term.sg_ispeed != test_term.sg_ispeed) ||
	    (old_term.sg_ospeed != test_term.sg_ospeed) ||
	    (old_term.sg_flags != test_term.sg_flags) ||
	    (old_modwd != test_modwd)) {

/* 	    printf ("tty has changed.\n");	 */

	    tty_driver_not_setup = 1;
				/* things have changed, fake it */
	}
    }
    if (tty_driver_not_setup) {
			/* hasn't been setup yet or reseting do to a change */
	get_tty_modes ();	/* get ONLY the mode word and term strucs */
        tty_driver_not_setup = 0;
    }

    ioctl (SHIN, TIOCSETN, &new_term);    /* set the terminal in CBREAK */
    ioctl (SHIN, TIOCSETC, &new_tchars);	/* with these special chars */
    ioctl (SHIN, TIOCSLTC, &new_ltchars);	/* and these local specials */

/* 	printf("in: %d out: %d\n", new_term.sg_ispeed,
		new_term.sg_ospeed); */

}

/* 
	get the special characters from the tty driver.
 */
static
get_tty_special () {

        ioctl (SHIN, TIOCGETP, &old_term);
        ioctl (SHIN, TIOCGETC, &old_tchars);
        ioctl (SHIN, TIOCGLTC, &old_ltchars);

        ioctl (SHIN, TIOCGETP, &new_term);
	ioctl (SHIN, TIOCGETP, &none_term);

        ioctl (SHIN, TIOCGETC, &new_tchars);
        ioctl (SHIN, TIOCGETC, &none_tchars);

        ioctl (SHIN, TIOCGLTC, &new_ltchars);
        ioctl (SHIN, TIOCGLTC, &none_ltchars);

	ioctl (SHIN, TIOCLGET, &old_modwd);	/* get local mode word */
					       /* so that we know old setup */
/* test prints */
/* 	printf("in: %d out: %d\n", old_term.sg_ispeed,
		old_term.sg_ospeed); */


/* the old setup for when inputl exits */

        old_term.sg_flags &= ~(RAW | CBREAK);		/* unset */
        old_term.sg_flags |= (ECHO | CRMOD);		/* set */

	DoBindToKey (DELETE_CHAR_BACKWARD, old_term.sg_erase);
	DoBindToKey (KILL_TO_BEGINNING, old_term.sg_kill);

	DoBindToKey (TTY_SIGINTR, old_tchars.t_intrc);
	DoBindToKey (TTY_SIGQUIT, old_tchars.t_quitc);
	DoBindToKey (TTY_START_OUTPUT, old_tchars.t_startc);
	DoBindToKey (TTY_STOP_OUTPUT, old_tchars.t_stopc);
	DoBindToKey (SEND_EOF, old_tchars.t_eofc);

	old_tchars.t_brkc = -1;		/* none */

	DoBindToKey (TTY_SIGTSUSP, old_ltchars.t_suspc);
	DoBindToKey (TTY_DSUSP, old_ltchars.t_dsuspc);
	DoBindToKey (REDISPLAY, old_ltchars.t_rprntc);
	DoBindToKey (TTY_FLUSH_OUTPUT, old_ltchars.t_flushc);
	DoBindToKey (DELETE_WORD_BACKWARD, old_ltchars.t_werasc);
	DoBindToKey (QUOTE_NEXT, old_ltchars.t_lnextc);

/* now for the current setup (CBREAK with some de-assigned special chars */

        new_term.sg_flags &= ~(ECHO | XTABS);		/* unset */
        new_term.sg_flags |= (CBREAK | CRMOD);		/* set */

				/* the new chars should get worked out in the
				   old chars bind-to-keys */

/*  now the none structures for literal nexting of characters */

        none_term.sg_flags &= ~(ECHO | CRMOD);		/* unset */
        none_term.sg_flags |= (CBREAK);			/* set */

	none_tchars.t_intrc = -1;
	none_tchars.t_quitc = -1;
	none_tchars.t_startc = -1;
	none_tchars.t_stopc = -1;
	none_tchars.t_eofc = -1;
	none_tchars.t_brkc = -1;

	none_ltchars.t_suspc = -1;
	none_ltchars.t_dsuspc = -1;
	none_ltchars.t_rprntc = -1;
	none_ltchars.t_flushc = -1;
	none_ltchars.t_werasc = -1;
	none_ltchars.t_lnextc = -1;

	reset_term ();		/* export the new bound values */
}

/* 
	get only the modes from the tty driver.
 */
static
get_tty_modes () {

        ioctl (SHIN, TIOCGETP, &new_term);	/* get new now, old later */
	ioctl (SHIN, TIOCGETP, &none_term);

	ioctl (SHIN, TIOCLGET, &old_modwd);	/* get local mode word */
					       /* so that we know old setup */
/* now for the current setup (CBREAK with some de-assigned special chars */

        new_term.sg_flags &= ~(ECHO | XTABS);		/* unset */
        new_term.sg_flags |= (CBREAK | CRMOD);		/* set */

	new_term.sg_erase = old_term.sg_erase;	/* do this so that the */
	new_term.sg_kill = old_term.sg_kill;	/* erase and kill chars */
						/* don't get changed to */
        ioctl (SHIN, TIOCGETP, &old_term);	/* something else. */

	old_term.sg_erase = new_term.sg_erase;
	old_term.sg_kill = new_term.sg_kill;

/* the old setup for when inputl exits */

        old_term.sg_flags &= ~(RAW | CBREAK);		/* unset */
        old_term.sg_flags |= (ECHO | CRMOD);		/* set */

/*  now the none structures for literal nexting of characters */

        none_term.sg_flags &= ~(ECHO | CRMOD);		/* unset */
        none_term.sg_flags |= (CBREAK);			/* set */

	reset_term ();		/* export the new bound values */
}

/*
    Reset the terminal to normal mode.
*/
static
reset_term() {

    ioctl (SHIN, TIOCSETN, &old_term);        /* set the terminal in CBREAK */
    ioctl (SHIN, TIOCSETC, &old_tchars);	/* with these special chars */
    ioctl (SHIN, TIOCSLTC, &old_ltchars);	/* and these local specials */

}

/*  unset all special chars */
static
nospecial() {
    ioctl (SHIN, TIOCSETN, &none_term);       /* set the terminal in CBREAK */
    ioctl (SHIN, TIOCSETC, &none_tchars);	/* with these special chars */
    ioctl (SHIN, TIOCSLTC, &none_ltchars);	/* and these local specials */
}

/* ******************************************************* */
#ifdef TEST
main()
{
    char buffer[1024];
    char retnval;
    
    while (1) {

	if ((retnval = inputl("inputl:", buffer, 72)) == '\033') {
	    strcat(buffer, "foo");
	    ilpushback(buffer);
	    continue;
	}
	else
	    printf ("Return value = %o,\nI saw :%s\n", retnval, buffer);
	if (buffer[0] == 'Q') exit(0);
    }
}
#endif
/* ********************************************************* */


static int
outputchar(c)
char c;
{
    (void) write (SHOUT, &c, 1);
}

static
beep()
{
    outputchar(BELL);
}

static
Delete()
{
    register int i;
    
    if (cursor < endptr) {
	for (i = cursor; i < endptr; i++) {
	    input_line[i] = input_line[i+1];
	}
	endptr--;
	DelChar();
    }
    else beep();
}

static
DelPrev()
{
    register int i;
    
    if (cursor != 0) {
	DelPrevChar();
	--cursor;
	if (cursor <0 ) cursor = 0;
	for (i = cursor; i < endptr; i++) {
	    input_line[i] = input_line[i+1];
	}
	endptr--;
	if (endptr <0 ) endptr = 0;
    }
    else beep();
}

static
Redisplay()
{
    register int i;
    
    ClearLine();
    (void) write (SHOUT, prompt, prompt_length);
    for (i = 1; i <= endptr; i++) {
	PrintChar(input_line[i -1], i);
    }
    MoveCursor(endptr, cursor);
}


static
Insert(c)
char c;
{
    register int i;

    if (cursor >= endptr){	/* put it at the end of the line */
	endptr++;
	if (endptr > 1023) endptr = 1023;
	input_line[cursor++] = c;
    }
    else {		/* its in the middle of the line, shift line
				   first, then put in char */
        endptr++;
	if (endptr > 1023) endptr = 1023;
        for (i = endptr; i >= cursor; i--) {
            input_line[i+1] = input_line[i];
        }
        input_line[cursor++] = c;
    }
    InsChar(c);
}

static int
IsInWord (c)
char c;
{
    register char *cp;
    register int is_word_char = 0;
    for (cp = WORD_CHARS; *cp; cp++)
	if (*cp == c)
	    is_word_char = 1;
    return (is_word_char);
}

static int
Count(to_where)			/* count the number of spaces that many */
int to_where;			/* characters take up on the screen */
{
    register int i;
    register int cntr = 0;
    
    for (i = 0; i < to_where; i++) {
	if (input_line[i] == '\t') {
	    cntr += prompt_length;
	    cntr = (((cntr/8)+1)*8);
	    cntr -= prompt_length;
	}
	else if ((input_line[i] < ' ') || (input_line[i] == '\0177')) {
	    cntr += 2;
	}
	else cntr++;
    }
    return (cntr);
}

static
PrintChar(c, pos)
char c;
int pos;			/* where the character is */
{
    register int count;
    register int i;
    
    c &= 0177;
    if (c == '\177') {
	outputchar('^');
	c = '?';
    }
    else if (c == '\t') {
	count = Count(pos) - Count(pos -1);
	for (i = 0; i < count; i++) {
	    outputchar(' ');
	}
	return;
    }
    else if (c < ' ') {
	outputchar('^');
	c |= 0100;
    }
    outputchar(c);
}

/* **************************************************************** */
ilpushback(string)
char string[];
{
    register int i;
    int string_length;
    int matched;
    
    string_length = strlen(string);
    if (string_length > 1023) string_length = 1023;
    matching_length = 0;
    matched = 1;

    for (i = 0; i <= string_length; i++) {
        if ((input_line[i] == string[i]) && (matched)) {
	    matching_length++;
	}
	else {
	    matched = FALSE;
	    input_line[i] = string[i];
	}
    }
    
    endptr = string_length;
    if (endptr > 1023) endptr = 1023;
    cursor = endptr;
    
    pushed_input = TRUE;
/*  do_redisplay = TRUE; */
    
}

ilredisplay()
{
    do_redisplay = TRUE;
}

/* this next pair is done this way so that I don't get an infinate recursion
   bug with ilsetup.  it is important that ilsetup is called first, so that
   the keys actually get bound. */

ilbindtokey (func, key)
int func;
char key;
{
    if (has_been_setup == FALSE) {
        ilsetup();
    }

    DoBindToKey (func, key);
}

/* force a reset_term */

ilreset_tty()
{
    reset_term();
}
    
static
DoBindToKey (func, key)
int func;
char key;
{
    if (key == -1) {		/* trap unsetings */
	return (-1);
    }

    if ((func < 0) || (func > 200)) {
	return(-1);
    }

    keybindings[key] = func;
    switch (func) {
	    case TTY_DSUSP:
	        old_ltchars.t_dsuspc = key;
	        new_ltchars.t_dsuspc = key;
		break;
		
	    case TTY_FLUSH_OUTPUT:
	        old_ltchars.t_flushc = key;	/* ^O */
	        new_ltchars.t_flushc = key;	/* ^O */
		break;

	    case TTY_SIGINTR:
		old_tchars.t_intrc = key;	/* ^C */
		new_tchars.t_intrc = key;	/* ^C */
		break;

	    case TTY_SIGQUIT:
		old_tchars.t_quitc = key;	/* ^\ */
		new_tchars.t_quitc = key;	/* ^\ */
		break;

	    case TTY_SIGTSUSP:
		old_ltchars.t_suspc = key;	/* ^Z */
		new_ltchars.t_suspc = key;	/* ^Z */
		break;

	    case TTY_START_OUTPUT:
		old_tchars.t_startc = key;	/* ^Q */
		new_tchars.t_startc = key;	/* ^Q */
		break;

	    case TTY_STOP_OUTPUT:
		old_tchars.t_stopc = key;	/* ^S */
		new_tchars.t_stopc = key;	/* ^S */
		break;

	    case REDISPLAY:
		old_ltchars.t_rprntc = key;	/* ^R */
		new_ltchars.t_rprntc = -1;	/* ^R */
		break;

	    case SEND_EOF:
		old_tchars.t_eofc = key;	/* ^_ */
		new_tchars.t_eofc = -1;		/* ^_ */
		break;

	    case QUOTE_NEXT:
		old_ltchars.t_lnextc = key;	/* ^^ */
		new_ltchars.t_lnextc = -1;	/* ^^ */
		break;

	    case DELETE_WORD_BACKWARD:
		old_ltchars.t_werasc = key;	/* ^W */
		new_ltchars.t_werasc = -1;	/* ^W */
		break;

	    case DELETE_CHAR_BACKWARD:
		old_term.sg_erase = key;
		new_term.sg_erase = -1;
		break;

	    case KILL_TO_BEGINNING:
		old_term.sg_kill = key;
		new_term.sg_kill = -1;
		break;

	    default:
		return;
    }
}

int
ilgetbinding(key)
char key;
{
    return (keybindings[key]);
}


static int saved_ldisc;		/* line discipline */
static struct sgttyb saved_term;	/* old terminal modes + ERASE, KILL */
static struct tchars  saved_tchars;      /* INT, QUIT, XON, XOFF, EOF, BRK */
static struct ltchars saved_ltchars;
				/* SUSP, DSTOP, RPRNT, FLUSH, WERASE, LNEXT */
static long saved_modwd;
static int tty_has_been_saved = 0;

/* 
 * Save and restore all of the terminal states
 */

ilsavetty () {
    ioctl (SHIN, TIOCGETD, &saved_ldisc);
    ioctl (SHIN, TIOCGETP, &saved_term);
    ioctl (SHIN, TIOCGETC, &saved_tchars);
    ioctl (SHIN, TIOCGLTC, &saved_ltchars);
    ioctl (SHIN, TIOCLGET, &saved_modwd);
    tty_has_been_saved = 1;
}

ilrestoretty () {
    if (tty_has_been_saved == 0) return;
    ioctl (SHIN, TIOCSETD, &saved_ldisc);
    ioctl (SHIN, TIOCSETN, &saved_term);
    ioctl (SHIN, TIOCSETC, &saved_tchars);
    ioctl (SHIN, TIOCSLTC, &saved_ltchars);
    ioctl (SHIN, TIOCLSET, &saved_modwd);
}
!Funky!Stuff!
echo x - inputl.h
cat > inputl.h << '!Funky!Stuff!'
#ifdef MAKE_INPUTL

#define	TRUE	1
#define	FALSE	0
#define	NULL	0
#define	BELL	'\007'

/* this next is for all characters that should be considered a word to the
   shell */

#define WORD_CHARS	"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890*?_-."

#include <sgtty.h>

#endif

#define	AUTO_INSERT		0
#define	BACKWARD_CHAR		1
#define	BACKWARD_WORD		2
#define	BEGINNING_OF_LINE	3
#define	CLEAR_SCREEN		4
#define	DELETE_CHAR_BACKWARD	5
#define	DELETE_CHAR_FORWARD	6
#define	DELETE_WORD_BACKWARD	7
#define	DELETE_WORD_FORWARD	8
#define	END_OF_LINE		9
#define	SEND_EOF		10
#define	FORWARD_CHAR		11
#define	FORWARD_WORD		12
#define	KILL_TO_BEGINNING	13
#define	KILL_TO_END		14
#define	NL_SEND_LINE		15
#define	QUOTE_NEXT		16
#define	REDISPLAY		17
#define	SEND_LINE		18
#define	START_OVER		19
#define	TRANSPOSE_CHARS		20
#define	YANK_KILLBUFFER		21
#define	YANK_LAST_INPUT		22
#define NOP			23
#define ERR			24

#define	TTY_DSUSP		101
#define	TTY_FLUSH_OUTPUT	102
#define	TTY_SIGINTR		103
#define	TTY_SIGQUIT		104
#define	TTY_SIGTSUSP		105
#define	TTY_START_OUTPUT	106
#define	TTY_STOP_OUTPUT		107
!Funky!Stuff!



More information about the Comp.sources.unix mailing list