1027 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1027 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/* tio.c */
 | 
						|
 | 
						|
/* Author:
 | 
						|
 *	Steve Kirkendall
 | 
						|
 *	14407 SW Teal Blvd. #C
 | 
						|
 *	Beaverton, OR 97005
 | 
						|
 *	kirkenda@cs.pdx.edu
 | 
						|
 */
 | 
						|
 | 
						|
 | 
						|
/* This file contains terminal I/O functions */
 | 
						|
 | 
						|
#include "config.h"
 | 
						|
#include "vi.h"
 | 
						|
#include "ctype.h"
 | 
						|
 | 
						|
 | 
						|
/* This function reads in a line from the terminal. */
 | 
						|
int vgets(prompt, buf, bsize)
 | 
						|
	char	prompt;	/* the prompt character, or '\0' for none */
 | 
						|
	char	*buf;	/* buffer into which the string is read */
 | 
						|
	int	bsize;	/* size of the buffer */
 | 
						|
{
 | 
						|
	int	len;	/* how much we've read so far */
 | 
						|
	int	ch;	/* a character from the user */
 | 
						|
	int	quoted;	/* is the next char quoted? */
 | 
						|
	int	tab;	/* column position of cursor */
 | 
						|
	char	widths[132];	/* widths of characters */
 | 
						|
	int	word;	/* index of first letter of word */
 | 
						|
#ifndef NO_DIGRAPH
 | 
						|
	int	erased;	/* 0, or first char of a digraph */
 | 
						|
#endif
 | 
						|
 | 
						|
	/* show the prompt */
 | 
						|
	move(LINES - 1, 0);
 | 
						|
	tab = 0;
 | 
						|
	if (prompt)
 | 
						|
	{
 | 
						|
		addch(prompt);
 | 
						|
		tab = 1;
 | 
						|
	}
 | 
						|
	clrtoeol();
 | 
						|
	refresh();
 | 
						|
 | 
						|
	/* read in the line */
 | 
						|
#ifndef NO_DIGRAPH
 | 
						|
	erased =
 | 
						|
#endif
 | 
						|
	quoted = len = 0;
 | 
						|
	for (;;)
 | 
						|
	{
 | 
						|
#ifndef NO_ABBR
 | 
						|
		if (quoted || mode == MODE_EX)
 | 
						|
		{
 | 
						|
			ch = getkey(0);
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			/* maybe expand an abbreviation while getting key */
 | 
						|
			for (word = len; --word >= 0 && isalnum(buf[word]); )
 | 
						|
			{
 | 
						|
			}
 | 
						|
			word++;
 | 
						|
			ch = getabkey(WHEN_EX, &buf[word], len - word);
 | 
						|
		}
 | 
						|
#else
 | 
						|
		ch = getkey(0);
 | 
						|
#endif
 | 
						|
#ifndef NO_EXTENSIONS
 | 
						|
		if (ch == ctrl('O'))
 | 
						|
		{
 | 
						|
			ch = getkey(quoted ? 0 : WHEN_EX);
 | 
						|
		}
 | 
						|
#endif
 | 
						|
 | 
						|
		/* some special conversions */
 | 
						|
		if (ch == ctrl('D') && len == 0)
 | 
						|
			ch = ctrl('[');
 | 
						|
#ifndef NO_DIGRAPH
 | 
						|
		if (*o_digraph && erased != 0 && ch != '\b')
 | 
						|
		{
 | 
						|
			ch = digraph(erased, ch);
 | 
						|
			erased = 0;
 | 
						|
		}
 | 
						|
#endif
 | 
						|
 | 
						|
		/* inhibit detection of special chars (except ^J) after a ^V */
 | 
						|
		if (quoted && ch != '\n')
 | 
						|
		{
 | 
						|
			ch |= 256;
 | 
						|
		}
 | 
						|
 | 
						|
		/* process the character */
 | 
						|
		switch(ch)
 | 
						|
		{
 | 
						|
		  case ctrl('V'):
 | 
						|
			qaddch('^');
 | 
						|
			qaddch('\b');
 | 
						|
			quoted = TRUE;
 | 
						|
			break;
 | 
						|
 | 
						|
		  case ctrl('['):
 | 
						|
			return -1;
 | 
						|
 | 
						|
		  case '\n':
 | 
						|
#if OSK
 | 
						|
		  case '\l':
 | 
						|
#else
 | 
						|
		  case '\r':
 | 
						|
#endif
 | 
						|
			clrtoeol();
 | 
						|
			goto BreakBreak;
 | 
						|
 | 
						|
		  case '\b':
 | 
						|
			if (len > 0)
 | 
						|
			{
 | 
						|
				len--;
 | 
						|
#ifndef NO_DIGRAPH
 | 
						|
				erased = buf[len];
 | 
						|
#endif
 | 
						|
				for (ch = widths[len]; ch > 0; ch--)
 | 
						|
					addch('\b');
 | 
						|
				if (mode == MODE_EX)
 | 
						|
				{
 | 
						|
					clrtoeol();
 | 
						|
				}
 | 
						|
				tab -= widths[len];
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				return -1;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
 | 
						|
		  default:
 | 
						|
			/* strip off quotation bit */
 | 
						|
			if (ch & 256)
 | 
						|
			{
 | 
						|
				ch &= ~256;
 | 
						|
				qaddch(' ');
 | 
						|
				qaddch('\b');
 | 
						|
			}
 | 
						|
 | 
						|
			/* add & echo the char */
 | 
						|
			if (len < bsize - 1)
 | 
						|
			{
 | 
						|
				if (ch == '\t' && !quoted)
 | 
						|
				{
 | 
						|
					widths[len] = *o_tabstop - (tab % *o_tabstop);
 | 
						|
					addstr("        " + 8 - widths[len]);
 | 
						|
					tab += widths[len];
 | 
						|
				}
 | 
						|
				else if (ch > 0 && ch < ' ') /* > 0 by GB */
 | 
						|
				{
 | 
						|
					addch('^');
 | 
						|
					addch(ch + '@');
 | 
						|
					widths[len] = 2;
 | 
						|
					tab += 2;
 | 
						|
				}
 | 
						|
				else if (ch == '\177')
 | 
						|
				{
 | 
						|
					addch('^');
 | 
						|
					addch('?');
 | 
						|
					widths[len] = 2;
 | 
						|
					tab += 2;
 | 
						|
				}
 | 
						|
				else
 | 
						|
				{
 | 
						|
					addch(ch);
 | 
						|
					widths[len] = 1;
 | 
						|
					tab++;
 | 
						|
				}
 | 
						|
				buf[len++] = ch;
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				beep();
 | 
						|
			}
 | 
						|
			quoted = FALSE;
 | 
						|
		}
 | 
						|
	}
 | 
						|
BreakBreak:
 | 
						|
	refresh();
 | 
						|
	buf[len] = '\0';
 | 
						|
	return len;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int	manymsgs; /* This variable keeps msgs from overwriting each other */
 | 
						|
static char	pmsg[80]; /* previous message (waiting to be displayed) */
 | 
						|
 | 
						|
 | 
						|
static int showmsg()
 | 
						|
{
 | 
						|
	/* if there is no message to show, then don't */
 | 
						|
	if (!manymsgs)
 | 
						|
		return FALSE;
 | 
						|
 | 
						|
	/* display the message */
 | 
						|
	move(LINES - 1, 0);
 | 
						|
	if (*pmsg)
 | 
						|
	{
 | 
						|
		standout();
 | 
						|
		qaddch(' ');
 | 
						|
		qaddstr(pmsg);
 | 
						|
		qaddch(' ');
 | 
						|
		standend();
 | 
						|
	}
 | 
						|
	clrtoeol();
 | 
						|
 | 
						|
	manymsgs = FALSE;
 | 
						|
	return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void endmsgs()
 | 
						|
{
 | 
						|
	if (manymsgs)
 | 
						|
	{
 | 
						|
		showmsg();
 | 
						|
		addch('\n');
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/* Write a message in an appropriate way.  This should really be a varargs
 | 
						|
 * function, but there is no such thing as vwprintw.  Hack!!!
 | 
						|
 *
 | 
						|
 * In MODE_EX or MODE_COLON, the message is written immediately, with a
 | 
						|
 * newline at the end.
 | 
						|
 *
 | 
						|
 * In MODE_VI, the message is stored in a character buffer.  It is not
 | 
						|
 * displayed until getkey() is called.  msg() will call getkey() itself,
 | 
						|
 * if necessary, to prevent messages from being lost.
 | 
						|
 *
 | 
						|
 * msg("")		- clears the message line
 | 
						|
 * msg("%s %d", ...)	- does a printf onto the message line
 | 
						|
 */
 | 
						|
/*VARARGS1*/
 | 
						|
void msg(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
 | 
						|
	char	*fmt;
 | 
						|
	long	arg1, arg2, arg3, arg4, arg5, arg6, arg7;
 | 
						|
{
 | 
						|
	if (mode != MODE_VI)
 | 
						|
	{
 | 
						|
		sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
 | 
						|
		qaddstr(pmsg);
 | 
						|
		addch('\n');
 | 
						|
		exrefresh();
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		/* wait for keypress between consecutive msgs */
 | 
						|
		if (manymsgs)
 | 
						|
		{
 | 
						|
			getkey(WHEN_MSG);
 | 
						|
		}
 | 
						|
 | 
						|
		/* real message */
 | 
						|
		sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
 | 
						|
		if (*fmt)
 | 
						|
		{
 | 
						|
			manymsgs = TRUE;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* This function calls refresh() if the option exrefresh is set */
 | 
						|
void exrefresh()
 | 
						|
{
 | 
						|
	char	*scan;
 | 
						|
 | 
						|
	/* If this ex command wrote ANYTHING set exwrote so vi's  :  command
 | 
						|
	 * can tell that it must wait for a user keystroke before redrawing.
 | 
						|
	 */
 | 
						|
	for (scan=kbuf; scan<stdscr; scan++)
 | 
						|
		if (*scan == '\n')
 | 
						|
			exwrote = TRUE;
 | 
						|
 | 
						|
	/* now we do the refresh thing */
 | 
						|
	if (*o_exrefresh)
 | 
						|
	{
 | 
						|
		refresh();
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		wqrefresh();
 | 
						|
	}
 | 
						|
	if (mode != MODE_VI)
 | 
						|
	{
 | 
						|
		manymsgs = FALSE;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* This structure is used to store maps and abbreviations.  The distinction
 | 
						|
 * between them is that maps are stored in the list referenced by the "maps"
 | 
						|
 * pointer, while abbreviations are referenced by the "abbrs" pointer.
 | 
						|
 */
 | 
						|
typedef struct _map
 | 
						|
{
 | 
						|
	struct _map	*next;	/* another abbreviation */
 | 
						|
	short		len;	/* length of the "rawin" characters */
 | 
						|
	short		flags;	/* various flags */
 | 
						|
	char		*label;	/* label of the map/abbr, or NULL */
 | 
						|
	char		*rawin;	/* the "rawin" characters */
 | 
						|
	char		*cooked;/* the "cooked" characters */
 | 
						|
} MAP;
 | 
						|
 | 
						|
static char	keybuf[KEYBUFSIZE];
 | 
						|
static int	cend;	/* end of input characters */
 | 
						|
static int	user;	/* from user through end are chars typed by user */
 | 
						|
static int	next;	/* index of the next character to be returned */
 | 
						|
static MAP	*match;	/* the matching map, found by countmatch() */
 | 
						|
static MAP	*maps;	/* the map table */
 | 
						|
#ifndef NO_ABBR
 | 
						|
static MAP	*abbrs;	/* the abbreviation table */
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/* ring the terminal's bell */
 | 
						|
void beep()
 | 
						|
{
 | 
						|
	/* do a visible/audible bell */
 | 
						|
	if (*o_flash)
 | 
						|
	{
 | 
						|
		do_VB();
 | 
						|
		refresh();
 | 
						|
	}
 | 
						|
	else if (*o_errorbells)
 | 
						|
	{
 | 
						|
		ttywrite("\007", 1);
 | 
						|
	}
 | 
						|
 | 
						|
	/* discard any buffered input, and abort macros */
 | 
						|
	next = user = cend;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/* This function replaces a "rawin" character sequence with the "cooked" version,
 | 
						|
 * by modifying the internal type-ahead buffer.
 | 
						|
 */
 | 
						|
void execmap(rawlen, cookedstr, visual)
 | 
						|
	int	rawlen;		/* length of rawin text -- string to delete */
 | 
						|
	char	*cookedstr;	/* the cooked text -- string to insert */
 | 
						|
	int	visual;		/* boolean -- chars to be executed in visual mode? */
 | 
						|
{
 | 
						|
	int	cookedlen;
 | 
						|
	char	*src, *dst;
 | 
						|
	int	i;
 | 
						|
 | 
						|
	/* find the length of the cooked string */
 | 
						|
	cookedlen = strlen(cookedstr);
 | 
						|
#ifndef NO_EXTENSIONS
 | 
						|
	if (visual)
 | 
						|
	{
 | 
						|
		cookedlen *= 2;
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	/* if too big to fit in type-ahead buffer, then don't do it */
 | 
						|
	if (cookedlen + (cend - next) - rawlen > KEYBUFSIZE)
 | 
						|
	{
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* shift to make room for cookedstr at the front of keybuf */
 | 
						|
	src = &keybuf[next + rawlen];
 | 
						|
	dst = &keybuf[cookedlen];
 | 
						|
	i = cend - (next + rawlen);
 | 
						|
	if (src >= dst)
 | 
						|
	{
 | 
						|
		while (i-- > 0)
 | 
						|
		{
 | 
						|
			*dst++ = *src++;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		src += i;
 | 
						|
		dst += i;
 | 
						|
		while (i-- > 0)
 | 
						|
		{
 | 
						|
			*--dst = *--src;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* insert cookedstr, and adjust offsets */
 | 
						|
	cend += cookedlen - rawlen - next;
 | 
						|
	user += cookedlen - rawlen - next;
 | 
						|
	next = 0;
 | 
						|
	for (dst = keybuf, src = cookedstr; *src; )
 | 
						|
	{
 | 
						|
#ifndef NO_EXTENSIONS
 | 
						|
		if (visual)
 | 
						|
		{
 | 
						|
			*dst++ = ctrl('O');
 | 
						|
			cookedlen--;
 | 
						|
		}
 | 
						|
#endif
 | 
						|
		*dst++ = *src++;
 | 
						|
	}
 | 
						|
 | 
						|
#ifdef DEBUG2
 | 
						|
	{
 | 
						|
#include <stdio.h>
 | 
						|
		FILE	*debout;
 | 
						|
		int		i;
 | 
						|
 | 
						|
		debout = fopen("debug.out", "a");
 | 
						|
		fprintf(debout, "After execmap(%d, \"%s\", %d)...\n", rawlen, cookedstr, visual);
 | 
						|
		for (i = 0; i < cend; i++)
 | 
						|
		{
 | 
						|
			if (i == next) fprintf(debout, "(next)");
 | 
						|
			if (i == user) fprintf(debout, "(user)");
 | 
						|
			if (UCHAR(keybuf[i]) < ' ')
 | 
						|
				fprintf(debout, "^%c", keybuf[i] ^ '@');
 | 
						|
			else
 | 
						|
				fprintf(debout, "%c", keybuf[i]);
 | 
						|
		}
 | 
						|
		fprintf(debout, "(end)\n");
 | 
						|
		fclose(debout);
 | 
						|
	}
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
/* This function calls ttyread().  If necessary, it will also redraw the screen,
 | 
						|
 * change the cursor shape, display the mode, and update the ruler.  If the
 | 
						|
 * number of characters read is 0, and we didn't time-out, then it exits because
 | 
						|
 * we've apparently reached the end of an EX script.
 | 
						|
 */
 | 
						|
static int fillkeybuf(when, timeout)
 | 
						|
	int	when;	/* mixture of WHEN_XXX flags */
 | 
						|
	int	timeout;/* timeout in 1/10 second increments, or 0 */
 | 
						|
{
 | 
						|
	int	nkeys;
 | 
						|
#ifndef NO_SHOWMODE
 | 
						|
	static int	oldwhen;	/* "when" from last time */
 | 
						|
	static int	oldleft;
 | 
						|
	static long	oldtop;
 | 
						|
	static long	oldnlines;
 | 
						|
	char		*str;
 | 
						|
#endif
 | 
						|
#ifndef NO_CURSORSHAPE
 | 
						|
	static int	oldcurs;
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef DEBUG
 | 
						|
	watch();
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
#ifndef NO_CURSORSHAPE
 | 
						|
	/* make sure the cursor is the right shape */
 | 
						|
	if (has_CQ)
 | 
						|
	{
 | 
						|
		if (when != oldcurs)
 | 
						|
		{
 | 
						|
			switch (when)
 | 
						|
			{
 | 
						|
			  case WHEN_EX:		do_CX();	break;
 | 
						|
			  case WHEN_VICMD:	do_CV();	break;
 | 
						|
			  case WHEN_VIINP:	do_CI();	break;
 | 
						|
			  case WHEN_VIREP:	do_CR();	break;
 | 
						|
			}
 | 
						|
			oldcurs = when;
 | 
						|
		}
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
#ifndef NO_SHOWMODE
 | 
						|
	/* if "showmode" then say which mode we're in */
 | 
						|
	if (*o_smd && (when & WHENMASK))
 | 
						|
	{
 | 
						|
		/* redraw the screen before we check to see whether the
 | 
						|
		 * "showmode" message needs to be redrawn.
 | 
						|
		 */
 | 
						|
		redraw(cursor, !(when & WHEN_VICMD));
 | 
						|
 | 
						|
		/* now the "topline" test should be valid */
 | 
						|
		if (when != oldwhen || topline != oldtop || leftcol != oldleft || nlines != oldnlines)
 | 
						|
		{
 | 
						|
			oldwhen = when;
 | 
						|
			oldtop = topline;
 | 
						|
			oldleft = leftcol;
 | 
						|
			oldnlines = nlines;
 | 
						|
 | 
						|
			if (when & WHEN_VICMD)	    str = "Command";
 | 
						|
			else if (when & WHEN_VIINP) str = " Input ";
 | 
						|
			else if (when & WHEN_VIREP) str = "Replace";
 | 
						|
			else if (when & WHEN_REP1)  str = " Rep 1 ";
 | 
						|
			else if (when & WHEN_CUT)   str = "BufName";
 | 
						|
			else if (when & WHEN_MARK)  str = "Mark AZ";
 | 
						|
			else if (when & WHEN_CHAR)  str = "Dest Ch";
 | 
						|
			else			    str = (char *)0;
 | 
						|
 | 
						|
			if (str)
 | 
						|
			{
 | 
						|
				move(LINES - 1, COLS - 10);
 | 
						|
				standout();
 | 
						|
				qaddstr(str);
 | 
						|
				standend();
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
#ifndef NO_EXTENSIONS
 | 
						|
	/* maybe display the ruler */
 | 
						|
	if (*o_ruler && (when & (WHEN_VICMD|WHEN_VIINP|WHEN_VIREP)))
 | 
						|
	{
 | 
						|
		char	buf[20];
 | 
						|
 | 
						|
		redraw(cursor, !(when & WHEN_VICMD));
 | 
						|
		pfetch(markline(cursor));
 | 
						|
		sprintf(buf, "%7ld,%-4d", markline(cursor), 1 + idx2col(cursor, ptext, when & (WHEN_VIINP|WHEN_VIREP)));
 | 
						|
		move(LINES - 1, COLS - 22);
 | 
						|
		addstr(buf);
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	/* redraw, so the cursor is in the right place */
 | 
						|
	if (when & WHENMASK)
 | 
						|
	{
 | 
						|
		redraw(cursor, !(when & (WHENMASK & ~(WHEN_VIREP|WHEN_VIINP))));
 | 
						|
	}
 | 
						|
 | 
						|
	/* Okay, now we can finally read the rawin keystrokes */
 | 
						|
	refresh();
 | 
						|
	nkeys = ttyread(keybuf + cend, sizeof keybuf - cend, timeout);
 | 
						|
 | 
						|
	/* if nkeys == 0 then we've reached EOF of an ex script. */
 | 
						|
	if (nkeys == 0 && timeout == 0)
 | 
						|
	{
 | 
						|
		tmpabort(TRUE);
 | 
						|
		move(LINES - 1, 0);
 | 
						|
		clrtoeol();
 | 
						|
		refresh();
 | 
						|
		endwin();
 | 
						|
		exit(1);
 | 
						|
	}
 | 
						|
 | 
						|
	cend += nkeys;
 | 
						|
	user += nkeys;
 | 
						|
	return nkeys;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* This function counts the number of maps that could match the characters
 | 
						|
 * between &keybuf[next] and &keybuf[cend], including incomplete matches.
 | 
						|
 * The longest comlete match is remembered via the "match" variable.
 | 
						|
 */
 | 
						|
static int countmatch(when)
 | 
						|
	int	when;	/* mixture of WHEN_XXX flags */
 | 
						|
{
 | 
						|
	MAP	*map;
 | 
						|
	int	count;
 | 
						|
 | 
						|
	/* clear the "match" variable */
 | 
						|
	match = (MAP *)0;
 | 
						|
 | 
						|
	/* check every map */
 | 
						|
	for (count = 0, map = maps; map; map = map->next)
 | 
						|
	{
 | 
						|
		/* can't match if wrong mode */
 | 
						|
		if ((map->flags & when) == 0)
 | 
						|
		{
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		/* would this be a complete match? */
 | 
						|
		if (map->len <= cend - next)
 | 
						|
		{
 | 
						|
			/* Yes, it would be.  Now does it really match? */
 | 
						|
			if (!strncmp(map->rawin, &keybuf[next], map->len))
 | 
						|
			{
 | 
						|
				count++;
 | 
						|
 | 
						|
				/* if this is the longest complete match,
 | 
						|
				 * then remember it.
 | 
						|
				 */
 | 
						|
				if (!match || match->len < map->len)
 | 
						|
				{
 | 
						|
					match = map;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			/* No, it wouldn't.  But check for partial match */
 | 
						|
			if (!strncmp(map->rawin, &keybuf[next], cend - next))
 | 
						|
			{
 | 
						|
				count++;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return count;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#ifndef NO_ABBR
 | 
						|
/* This function checks to see whether a word is an abbreviation.  If it is,
 | 
						|
 * then an appropriate number of backspoace characters is inserted into the
 | 
						|
 * type-ahead buffer, followed by the expanded form of the abbreviation.
 | 
						|
 */
 | 
						|
static void expandabbr(word, wlen)
 | 
						|
	char	*word;
 | 
						|
	int	wlen;
 | 
						|
{
 | 
						|
	MAP	*abbr;
 | 
						|
 | 
						|
	/* if the next character wouldn't end the word, then don't expand */
 | 
						|
	if (isalnum(keybuf[next]) || keybuf[next] == ctrl('V'))
 | 
						|
	{
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* find the abbreviation, if any */
 | 
						|
	for (abbr = abbrs;
 | 
						|
	     abbr && (abbr->len != wlen || strncmp(abbr->rawin, word, wlen));
 | 
						|
	     abbr = abbr->next)
 | 
						|
	{
 | 
						|
	}
 | 
						|
 | 
						|
	/* If an abbreviation was found, then expand it by inserting the long
 | 
						|
	 * version into the type-ahead buffer, and then inserting (in front of
 | 
						|
	 * the long version) enough backspaces to erase to the short version.
 | 
						|
	 */
 | 
						|
	if (abbr)
 | 
						|
	{
 | 
						|
		execmap(0, abbr->cooked, FALSE);
 | 
						|
		while (wlen > 15)
 | 
						|
		{
 | 
						|
			execmap(0, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", FALSE);
 | 
						|
			wlen -= 15;
 | 
						|
		}
 | 
						|
		if (wlen > 0)
 | 
						|
		{
 | 
						|
			execmap(0, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" + 15 - wlen, FALSE);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
/* This function calls getabkey() without attempting to expand abbreviations */
 | 
						|
int getkey(when)
 | 
						|
	int	when;	/* mixture of WHEN_XXX flags */
 | 
						|
{
 | 
						|
	return getabkey(when, "", 0);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* This is it.  This function returns keystrokes one-at-a-time, after mapping
 | 
						|
 * and abbreviations have been taken into account.
 | 
						|
 */
 | 
						|
int getabkey(when, word, wlen)
 | 
						|
	int	when;	/* mixture of WHEN_XXX flags */
 | 
						|
	char	*word;	/* a word that may need to be expanded as an abbr */
 | 
						|
	int	wlen;	/* length of "word" -- since "word" might not have \0 */
 | 
						|
{
 | 
						|
	int	matches;
 | 
						|
 | 
						|
	/* if this key is needed for delay between multiple error messages,
 | 
						|
	 * then reset the manymsgs flag and abort any mapped key sequence.
 | 
						|
	 */
 | 
						|
	if (showmsg())
 | 
						|
	{
 | 
						|
		if (when == WHEN_MSG)
 | 
						|
		{
 | 
						|
#ifndef CRUNCH
 | 
						|
			if (!*o_more)
 | 
						|
			{
 | 
						|
				refresh();
 | 
						|
				return ' ';
 | 
						|
			}
 | 
						|
#endif
 | 
						|
			qaddstr("[More...]");
 | 
						|
			refresh();
 | 
						|
			execmap(user, "", FALSE);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
#ifdef DEBUG
 | 
						|
	/* periodically check for screwed up internal tables */
 | 
						|
	watch();
 | 
						|
#endif
 | 
						|
 | 
						|
	/* if buffer empty, read some characters without timeout */
 | 
						|
	if (next >= cend)
 | 
						|
	{
 | 
						|
		next = user = cend = 0;
 | 
						|
		fillkeybuf(when, 0);
 | 
						|
	}
 | 
						|
 | 
						|
	/* try to map the key, unless already mapped and not ":set noremap" */
 | 
						|
	if (next >= user || *o_remap)
 | 
						|
	{
 | 
						|
		do
 | 
						|
		{
 | 
						|
			do
 | 
						|
			{
 | 
						|
				matches = countmatch(when);
 | 
						|
			} while (matches > 1 && fillkeybuf(when, *o_keytime) > 0);
 | 
						|
			if (matches == 1)
 | 
						|
			{
 | 
						|
				execmap(match->len, match->cooked,
 | 
						|
					(match->flags & WHEN_INMV) != 0 
 | 
						|
					 && (when & (WHEN_VIINP|WHEN_VIREP)) != 0);
 | 
						|
			}
 | 
						|
		} while (*o_remap && matches == 1);
 | 
						|
	}
 | 
						|
 | 
						|
#ifndef NO_ABBR
 | 
						|
	/* try to expand an abbreviation, except in visual command mode */
 | 
						|
	if (wlen > 0 && (mode & (WHEN_EX|WHEN_VIINP|WHEN_VIREP)) != 0)
 | 
						|
	{
 | 
						|
		expandabbr(word, wlen);
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	/* ERASEKEY should always be mapped to '\b'. */
 | 
						|
	if (keybuf[next] == ERASEKEY)
 | 
						|
	{
 | 
						|
		keybuf[next] = '\b';
 | 
						|
	}
 | 
						|
 | 
						|
	/* return the next key */
 | 
						|
	return keybuf[next++];
 | 
						|
}
 | 
						|
 | 
						|
/* This function maps or unmaps a key */
 | 
						|
void mapkey(rawin, cooked, when, name)
 | 
						|
	char	*rawin;	/* the input key sequence, before mapping */
 | 
						|
	char	*cooked;/* after mapping -- or NULL to remove map */
 | 
						|
	short	when;	/* bitmap of when mapping should happen */
 | 
						|
	char	*name;	/* name of the key, NULL for no name, "abbr" for abbr */
 | 
						|
{
 | 
						|
	MAP	**head;	/* head of list of maps or abbreviations */
 | 
						|
	MAP	*scan;	/* used for scanning through the list */
 | 
						|
	MAP	*prev;	/* used during deletions */
 | 
						|
 | 
						|
	/* Is this a map or an abbreviation?  Choose the right list. */
 | 
						|
#ifndef NO_ABBR
 | 
						|
	head = ((!name || strcmp(name, "abbr")) ? &maps : &abbrs);
 | 
						|
#else
 | 
						|
	head = &maps;
 | 
						|
#endif
 | 
						|
 | 
						|
	/* try to find the map in the list */
 | 
						|
	for (scan = *head, prev = (MAP *)0;
 | 
						|
	     scan && (strcmp(rawin, scan->rawin) ||
 | 
						|
		!(scan->flags & when & (WHEN_EX|WHEN_VICMD|WHEN_VIINP|WHEN_VIREP)));
 | 
						|
	     prev = scan, scan = scan->next)
 | 
						|
	{
 | 
						|
	}
 | 
						|
 | 
						|
	/* trying to map? (not unmap) */
 | 
						|
	if (cooked && *cooked)
 | 
						|
	{
 | 
						|
		/* if map starts with "visual ", then mark it as a visual map */
 | 
						|
		if (head == &maps && !strncmp(cooked, "visual ", 7))
 | 
						|
		{
 | 
						|
			cooked += 7;
 | 
						|
			when |= WHEN_INMV;
 | 
						|
		}
 | 
						|
 | 
						|
		/* "visual" maps always work in input mode */
 | 
						|
		if (when & WHEN_INMV)
 | 
						|
		{
 | 
						|
			when |= WHEN_VIINP|WHEN_VIREP|WHEN_POPUP;
 | 
						|
		}
 | 
						|
 | 
						|
		/* if not already in the list, then allocate a new structure */
 | 
						|
		if (!scan)
 | 
						|
		{
 | 
						|
			scan = (MAP *)malloc(sizeof(MAP));
 | 
						|
			scan->len = strlen(rawin);
 | 
						|
			scan->rawin = malloc(scan->len + 1);
 | 
						|
			strcpy(scan->rawin, rawin);
 | 
						|
			scan->flags = when;
 | 
						|
			scan->label = name;
 | 
						|
			if (*head)
 | 
						|
			{
 | 
						|
				prev->next = scan;
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				*head = scan;
 | 
						|
			}
 | 
						|
			scan->next = (MAP *)0;
 | 
						|
		}
 | 
						|
		else /* recycle old structure */
 | 
						|
		{
 | 
						|
			free(scan->cooked);
 | 
						|
		}
 | 
						|
		scan->cooked = malloc(strlen(cooked) + 1);
 | 
						|
		strcpy(scan->cooked, cooked);
 | 
						|
	}
 | 
						|
	else /* unmapping */
 | 
						|
	{
 | 
						|
		/* if nothing to unmap, then exit silently */
 | 
						|
		if (!scan)
 | 
						|
		{
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		/* unlink the structure from the list */
 | 
						|
		if (prev)
 | 
						|
		{
 | 
						|
			prev->next = scan->next;
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			*head = scan->next;
 | 
						|
		}
 | 
						|
 | 
						|
		/* free it, and the strings that it refers to */
 | 
						|
		free(scan->rawin);
 | 
						|
		free(scan->cooked);
 | 
						|
		free(scan);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* This function returns a printable version of a string.  It uses tmpblk.c */
 | 
						|
char *printable(str)
 | 
						|
	char	*str;	/* the string to convert */
 | 
						|
{
 | 
						|
	char	*build;	/* used for building the string */
 | 
						|
 | 
						|
	for (build = tmpblk.c; *str; str++)
 | 
						|
	{
 | 
						|
#if AMIGA
 | 
						|
		if (*str == '\233')
 | 
						|
		{
 | 
						|
			*build++ = '<';
 | 
						|
			*build++ = 'C';
 | 
						|
			*build++ = 'S';
 | 
						|
			*build++ = 'I';
 | 
						|
			*build++ = '>';
 | 
						|
		} else 
 | 
						|
#endif
 | 
						|
		if (UCHAR(*str) < ' ' || *str == '\177')
 | 
						|
		{
 | 
						|
			*build++ = '^';
 | 
						|
			*build++ = *str ^ '@';
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			*build++ = *str;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	*build = '\0';
 | 
						|
	return tmpblk.c;
 | 
						|
}
 | 
						|
 | 
						|
/* This function displays the contents of either the map table or the
 | 
						|
 * abbreviation table.  User commands call this function as follows:
 | 
						|
 *	:map	dumpkey(WHEN_VICMD, FALSE);
 | 
						|
 *	:map!	dumpkey(WHEN_VIREP|WHEN_VIINP, FALSE);
 | 
						|
 *	:abbr	dumpkey(WHEN_VIINP|WHEN_VIREP, TRUE);
 | 
						|
 *	:abbr!	dumpkey(WHEN_EX|WHEN_VIINP|WHEN_VIREP, TRUE);
 | 
						|
 */
 | 
						|
void dumpkey(when, abbr)
 | 
						|
	int	when;	/* WHEN_XXXX of mappings to be dumped */
 | 
						|
	int	abbr;	/* boolean: dump abbreviations instead of maps? */
 | 
						|
{
 | 
						|
	MAP	*scan;
 | 
						|
	char	*str;
 | 
						|
	int	len;
 | 
						|
 | 
						|
#ifndef NO_ABBR
 | 
						|
	for (scan = (abbr ? abbrs : maps); scan; scan = scan->next)
 | 
						|
#else
 | 
						|
	for (scan = maps; scan; scan = scan->next)
 | 
						|
#endif
 | 
						|
	{
 | 
						|
		/* skip entries that don't match "when" */
 | 
						|
		if ((scan->flags & when) == 0)
 | 
						|
		{
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		/* dump the key label, if any */
 | 
						|
		if (!abbr)
 | 
						|
		{
 | 
						|
			len = 8;
 | 
						|
			if (scan->label)
 | 
						|
			{
 | 
						|
				qaddstr(scan->label);
 | 
						|
				len -= strlen(scan->label);
 | 
						|
			}
 | 
						|
			do
 | 
						|
			{
 | 
						|
				qaddch(' ');
 | 
						|
			} while (len-- > 0);
 | 
						|
		}
 | 
						|
 | 
						|
		/* dump the rawin version */
 | 
						|
		str = printable(scan->rawin);
 | 
						|
		qaddstr(str);
 | 
						|
		len = strlen(str);
 | 
						|
		do
 | 
						|
		{
 | 
						|
			qaddch(' ');
 | 
						|
		} while (len++ < 8);
 | 
						|
			
 | 
						|
		/* dump the mapped version */
 | 
						|
#ifndef NO_EXTENSIONS
 | 
						|
		if ((scan->flags & WHEN_INMV) && (when & (WHEN_VIINP|WHEN_VIREP)))
 | 
						|
		{
 | 
						|
			qaddstr("visual ");
 | 
						|
		}
 | 
						|
#endif
 | 
						|
		str = printable(scan->cooked);
 | 
						|
		qaddstr(str);
 | 
						|
		addch('\n');
 | 
						|
		exrefresh();
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
#ifndef NO_MKEXRC
 | 
						|
 | 
						|
static safequote(str)
 | 
						|
	char	*str;
 | 
						|
{
 | 
						|
	char	*build;
 | 
						|
 | 
						|
	build = tmpblk.c + strlen(tmpblk.c);
 | 
						|
	while (*str)
 | 
						|
	{
 | 
						|
		if (*str <= ' ' && *str >= 1 || *str == '|')
 | 
						|
		{
 | 
						|
			*build++ = ctrl('V');
 | 
						|
		}
 | 
						|
		*build++ = *str++;
 | 
						|
	}
 | 
						|
	*build = '\0';
 | 
						|
}
 | 
						|
 | 
						|
/* This function saves the contents of either the map table or the
 | 
						|
 * abbreviation table into a file.  Both the "bang" and "no bang" versions
 | 
						|
 * are saved.
 | 
						|
 *	:map	dumpkey(WHEN_VICMD, FALSE);
 | 
						|
 *	:map!	dumpkey(WHEN_VIREP|WHEN_VIINP, FALSE);
 | 
						|
 *	:abbr	dumpkey(WHEN_VIINP|WHEN_VIREP, TRUE);
 | 
						|
 *	:abbr!	dumpkey(WHEN_EX|WHEN_VIINP|WHEN_VIREP, TRUE);
 | 
						|
 */
 | 
						|
savemaps(fd, abbr)
 | 
						|
	int	fd;	/* file descriptor of an open file to write to */
 | 
						|
	int	abbr;	/* boolean: do abbr table? (else do map table) */
 | 
						|
{
 | 
						|
	MAP	*scan;
 | 
						|
	char	*str;
 | 
						|
	int	bang;
 | 
						|
	int	when;
 | 
						|
	int	len;
 | 
						|
 | 
						|
# ifndef NO_ABBR
 | 
						|
	for (scan = (abbr ? abbrs : maps); scan; scan = scan->next)
 | 
						|
# else
 | 
						|
	for (scan = maps; scan; scan = scan->next)
 | 
						|
# endif
 | 
						|
	{
 | 
						|
		/* skip maps that have labels, except for function keys */
 | 
						|
		if (scan->label && *scan->label != '#')
 | 
						|
		{
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		for (bang = 0; bang < 2; bang++)
 | 
						|
		{
 | 
						|
			/* decide which "when" flags we want */
 | 
						|
# ifndef NO_ABBR
 | 
						|
			if (abbr)
 | 
						|
				when = (bang ? WHEN_EX|WHEN_VIINP|WHEN_VIREP : WHEN_VIINP|WHEN_VIREP);
 | 
						|
			else
 | 
						|
# endif
 | 
						|
				when = (bang ? WHEN_VIREP|WHEN_VIINP : WHEN_VICMD);
 | 
						|
 | 
						|
			/* skip entries that don't match "when" */
 | 
						|
			if ((scan->flags & when) == 0)
 | 
						|
			{
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
 | 
						|
			/* write a "map" or "abbr" command name */
 | 
						|
# ifndef NO_ABBR
 | 
						|
			if (abbr)
 | 
						|
				strcpy(tmpblk.c, "abbr");
 | 
						|
			else
 | 
						|
# endif
 | 
						|
				strcpy(tmpblk.c, "map");
 | 
						|
 | 
						|
			/* maybe write a bang.  Definitely write a space */
 | 
						|
			if (bang)
 | 
						|
				strcat(tmpblk.c, "! ");
 | 
						|
			else
 | 
						|
				strcat(tmpblk.c, " ");
 | 
						|
 | 
						|
			/* write the rawin version */
 | 
						|
# ifndef NO_FKEY
 | 
						|
			if (scan->label)
 | 
						|
				strcat(tmpblk.c, scan->label);
 | 
						|
			else
 | 
						|
# endif
 | 
						|
				safequote(scan->rawin);
 | 
						|
			strcat(tmpblk.c, " ");
 | 
						|
				
 | 
						|
			/* dump the mapped version */
 | 
						|
# ifndef NO_EXTENSIONS
 | 
						|
			if ((scan->flags & WHEN_INMV) && (when & (WHEN_VIINP|WHEN_VIREP)))
 | 
						|
			{
 | 
						|
				strcat(tmpblk.c, "visual ");
 | 
						|
			}
 | 
						|
# endif
 | 
						|
			safequote(scan->cooked);
 | 
						|
			strcat(tmpblk.c, "\n");
 | 
						|
			twrite(fd, tmpblk.c, strlen(tmpblk.c));
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
#endif
 |