[comp.sources.misc] v03i047: key mapping library for curses type applications

mjr@welchsun2.UUCP (Marcus J. Ranum) (06/12/88)

comp.sources.misc: Volume 3, Issue 47
Submitted-By: "Marcus J. Ranum" <mjr@welchsun2.UUCP>
Archive-Name: kiface

Key Interface Library

This library provides some fairly useful tools for mapping keys to
return values that can be checked by a user interface, and acted
upon. The idea is essentially to allow a program to assign a return
value to a key, or a string of keys, so that a single returned value
can be checked after a set of keys is input. IE: suppose we want to be
able to support a 'generic UP key' that could be either '^P' for
you EMACS fans, or '^[[A' for you with arrow keys that happen to return
that.

---chop---slice---dice---shred---chop---slice---dice---shred---chop---slice--
#!/bin/sh
#	This is a shell archive.
#	run the file through sh.
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	README
#	Makefile
#	iface.h
#	iface.3
#	iface.c
#	tester.c
# This archive created: Sat Jun 11 16:02:20 1988
echo shar: extracting README '(3032 characters)'
sed 's/^XX//' << \SHAR_EOF > README
XXKey Interface Library
XX
XXThis library provides some fairly useful tools for mapping keys to
XX"return values" that can be checked by a user interface, and acted
XXupon. The idea is essentially to allow a program to assign a return
XXvalue to a key, or a string of keys, so that a single returned value
XXcan be checked after a set of keys is input. IE: suppose we want to be
XXable to support a 'generic UP key' that could be either '^P' for
XXyou EMACS fans, or '^[[A' for you with arrow keys that happen to return
XXthat.
XX
XXThere are some basic keys that must be supported for some of the
XXroutines to work. The interface must be configured with at least
XXa key that is defined to mean 'delete', as well as 'return', 'abort',
XXand a few others. A function (that could be improved upon) is 
XXprovided to establish this minimal set from termcap. Those of you
XXwith bitmapped displays and windows systems will find this all to
XXbe useless.
XX
XXThe main disadvantage with this package - (and it is a severe one!)
XXis that I was not patient to do forward-looking in the mapping. If
XXboth "foo" and "foobar" are defined, "foobar" will never be reached,
XXsince "foo" will always return first. In practice, this may require 
XXsome juggling. Right now, the code performs no checks against having
XXoverlapped mappings, so you have the option to really screw yourself.
XXI suppose something could be done with a timeout, and stuff like that,
XXbut this is simpler and more likely to be portable. If anyone adds
XXthis, please let me know :-)
XX
XXAn additional routine is provided to support input and line-editing of
XXa buffer. This could be considerably enhanced depending on your display
XXand if you're running curses, or whatever, but the demo program is
XXas simple as possible. The intent of the ifgetstr() function is to
XXallow something like:
XX	
XX	bufs[5][xx];
XX	thisbuf = 0;
XX
XX	while(1) {
XX		/* curses call to move cursor to beginning of bufs[thisbuf] */
XX		/* wherever it lies on the screen */
XX		ret = ifgetstr(&keymap,bufs[thisbuf],xx);
XX		switch(ret) {
XX			case IF_UPWRMOVE:
XX				if(--thisbuf) < 0
XX					thisbuf = xx-1;
XX				break;
XX
XX			case IF_DWNWMOVE:
XX			case IF_CRETURN:
XX				if(++thisbuf) > xx-1
XX					thisbuf = 0;
XX				break;
XX
XX			case IF_ABORTKEY:
XX				return(something);
XX		}
XX	}
XX
XXWhich would allow editing multiple buffers until an escape key was pressed.
XX(note "esc" preceeds a lot of the arrow keys, see defect above)
XXThere are loads of hooks in the code, including a pointer to where we
XXleft off in the buffer, but use of such things should depend on the
XXapplication. The ifgetstr() function is designed to be useful, but also
XXto provide a basic starting point for more elaborate input management
XXroutines.
XX
XXThis code is not public domain, though it is freely modifiable
XXand distributable as long as the copyright notices are retained.  If
XXyou make any nifty improvements, please let me know, (E-mail, I am not
XXon usenet) bugfixes, etc, are welcome.
XX
XX 
XX        Marcus J. Ranum, William Welch Medical Library, 1988
XX        mjr@jhuigf.BITNET || uunet!mimsy!aplcen!osiris!welchvax!mjr
SHAR_EOF
if test 3032 -ne "`wc -c README`"
then
echo shar: error transmitting README '(should have been 3032 characters)'
fi
echo shar: extracting Makefile '(1094 characters)'
sed 's/^XX//' << \SHAR_EOF > Makefile
XX# Makefile for keyboard input library
XX# Copyright(C) 1988, Marcus J. Ranum, William Welch Medical Library
XX# 
XX# $Header: Makefile,v 1.2 88/06/10 22:44:14 mjr rel $: Makefile 
XX# 
XX# $Log:	Makefile,v $
XX# Revision 1.2  88/06/10  22:44:14  mjr
XX# cleaned up some, added install option, other junk.
XX# 
XX# Revision 1.1  88/06/10  17:02:10  mjr
XX# Initial revision
XX# 
XX# 
XX
XXLIBS= -ltermcap
XXCFLAGS=-O
XXLFLAGS=-s
XXLINTFLAGS=-h -x -u
XX
XXLIB=	libiface.a
XX
XX# installation stuff
XXLIBDIR=	/usr/local/lib
XXHDR=	iface.h
XXHDRDIR=	/usr/local/include
XXMAN=	iface.3
XXMANDIR=	/usr/man/manl
XX
XXall:	tester
XX
XXtester:		tester.o $(LIB)
XX	cc $(LFLAGS) -o tester tester.o $(LIB) $(LIBS)
XX
XXclean:
XX	rm -f *.o tester iface.shar $(LIB)
XX
XXlint:
XX	lint $(LINTFLAGS) iface.c 
XX
XXshar:
XX	shar -a README Makefile iface.h iface.3 iface.c tester.c > iface.shar
XX
XXinstall: $(LIB)
XX	cp $(LIB) $(LIBDIR)/$(LIB)
XX	chmod 644 $(LIBDIR)/$(LIB)
XX	ranlib $(LIBDIR)/$(LIB)
XX	cp $(HDR) $(HDRDIR)/$(HDR)
XX	chmod 644 $(HDRDIR)/$(HDR)
XX	cp $(MAN) $(MANDIR)/$(MAN)
XX	chmod 644 $(MANDIR)/$(MAN)
XX
XXlibiface.a:	iface.o
XX		ar rcv libiface.a iface.o
XX		ranlib $@
XX
XXiface.o:	iface.c Makefile
SHAR_EOF
if test 1094 -ne "`wc -c Makefile`"
then
echo shar: error transmitting Makefile '(should have been 1094 characters)'
fi
echo shar: extracting iface.h '(2594 characters)'
sed 's/^XX//' << \SHAR_EOF > iface.h
XX/*
XX * Copyright (C) 1988, Marcus J. Ranum, William Welch Medical Library
XX * $Author: mjr $
XX */
XX
XX/*
XX * $Header: iface.h,v 1.3 88/06/11 15:58:53 mjr rel $: iface.h
XX *
XX * $Log:	iface.h,v $
XX * Revision 1.3  88/06/11  15:58:53  mjr
XX * added possibly useful macros. moved input buffer into map structure
XX * 
XX * Revision 1.2  88/06/10  22:45:43  mjr
XX * improved readability of some comments.
XX * 
XX * Revision 1.1  88/06/10  17:02:00  mjr
XX * Initial revision
XX * 
XX */
XX
XX#ifndef _INCL_IFACE_H
XX
XX/* size to hash key mappings into - a key mapping is stored in lexical */
XX/* order, hashed based on the first character (hell of a hash, eh ?) */
XX/* this really does not have to be a very big number */
XX#define	IF_HSIZE	10
XX
XX/* size of input buffer */
XX#define IF_INBUFSIZ	300
XX
XX/* a key mapping element */
XXstruct	ifkey	{
XX	char	*lhs;
XX	int	rhs;
XX	struct	ifkey	*next;
XX};
XX
XX/* a command/key map */
XXstruct	ifmap	{
XX	struct	ifkey	*htab[IF_HSIZE];	/* key mapping entries */
XX	int	hold;				/* holdover key */
XX						/* usually the key that */
XX						/* caused an error, or 0 */
XX	int	lastret;			/* last recognized key */
XX						/* reset during getkey() */
XX						/* each time called */
XX	int	lastbuf;			/* usable in ifgetstr() */
XX						/* - last position of curs */
XX						/* use at own risk */
XX	char	inbuf[IF_INBUFSIZ];		/* input buffer - whatever */
XX						/* we currently have read */
XX						/* cleared in each call to */
XX						/* ifgetkey - may be long */
XX};
XX
XX/* can be used to flush a pending character */
XX#define	ifclearhold(map)	((map).hold = '\0')
XX#define	iflastch(map)		((map).hold)
XX#define	iflastret(map)		((map).lastret)
XX
XX/* these are expected to be builtins - if you intend to use ifgetstr */
XX/* these must be defined, at a minimum. (more can be). - to set up a */
XX/* more or less "standard" set, you can use ifdfltmap() - which uses */
XX/* some reasonable assumptions from termcap */
XX#define	IF_NOTFOUND	-1	/* this one does not need to be bound ! */
XX				/* since it is what we return when we are */
XX				/* unable to find something interesting */
XX#define	IF_DELCHAR	-2	/* we want to delete a character */
XX#define	IF_BACKMOVE	-3	/* go backwards a char (nondestructive) */
XX#define	IF_FORWMOVE	-4	/* go forward a char (nondestructive) */
XX#define	IF_UPWRMOVE	-5	/* go up a char - sometimes can mean EOL */
XX#define	IF_DWNWMOVE	-6	/* go down a char - sometimes can mean EOL */
XX#define	IF_CRETURN	-7	/* return from input normally */
XX#define	IF_ABORTKEY	-8	/* return from input with cancel */
XX
XX
XXextern	void	ifinitmap();
XXextern	void	ifdfltmap();
XXextern	void	iffreemap();
XXextern	struct	ifkey	*iflookup();
XX
XX#define _INCL_IFACE_H
XX#endif
SHAR_EOF
if test 2594 -ne "`wc -c iface.h`"
then
echo shar: error transmitting iface.h '(should have been 2594 characters)'
fi
echo shar: extracting iface.3 '(8748 characters)'
sed 's/^XX//' << \SHAR_EOF > iface.3
XX.\" iface.3l (C)1988 Marcus J. Ranum, Welch Medical Library
XX.\" $Header: iface.3,v 1.1 88/06/11 15:58:24 mjr rel $
XX.TH IFACE 3l
XX.SH NAME
XXifinstall, ifdelete, ifgetkey, ifgetstr, ifdfltmap, iffreemap, ifinitmap, 
XXiflookup, ifclearhold, iflastch, iflastret
XX.br
XX\- mappable key interface library
XX.SH SYNTAX
XX.B #include <local/iface.h>
XX.PP
XX.B int ifinstall(keymap,string,retval)
XX.br
XX.SM
XX.B struct ifmap *keymap;
XX.br
XX.B char *string;
XX.br
XX.B int retval;
XX.PP
XX.B int ifdelete(keymap,string)
XX.br
XX.SM
XX.B struct ifmap *keymap;
XX.br
XX.B char *string;
XX.PP
XX.B int ifgetkey(keymap)
XX.br
XX.SM
XX.B struct ifmap *keymap;
XX.PP
XX.B int ifgetstr(keymap,buf,max,boffset)
XX.br
XX.SM
XX.B struct ifmap *keymap;
XX.br
XX.B char *buf;
XX.br
XX.B int max, boffset;
XX.PP
XX.B void ifdfltmap(keymap)
XX.br
XX.SM
XX.B struct ifmap *keymap;
XX.PP
XX.B void iffreemap(keymap)
XX.br
XX.SM
XX.B struct ifmap *keymap;
XX.PP
XX.B void ifinitmap(keymap)
XX.br
XX.SM
XX.B struct ifmap *keymap;
XX.PP
XX.B struct ifkey *iflookup(keymap,string)
XX.br
XX.SM
XX.B struct ifmap *keymap;
XX.br
XX.B char *string;
XX.PP
XX.B (macro) ifclearhold(keymap)
XX.br
XX.SM
XX.B struct ifmap *keymap;
XX.PP
XX.B (macro) iflastch(keymap)
XX.br
XX.SM
XX.B struct ifmap *keymap;
XX.PP
XX.B (macro) iflastret(keymap)
XX.br
XX.SM
XX.B struct ifmap *keymap;
XX.PP
XX.SH DESCRIPTION
XX.PP
XXThe mappable key interface library is designed to allow basic keymapping in
XXapplications that rely on arrow keys, escape codes, etc. It is designed to
XXgather keyboard input, looking for specific strings, and to return numerical
XXcodes to indicate matches. Multiple mappings are supported, to allow many
XXstrings to map to one return value. Every effort is made to minimize code
XXsize and to maximize efficiency in string matching. The library includes 
XXtools to allow basic key initialization, as well as deleting mappings,
XXsearching mappings, and simple visual string editing. The primary shortcoming
XXof the library is that a value is returned \fIas soon as a match is made\fP.
XXThere is no effort to scan ahead, or timeout on input to try to figure
XXwhat keys will be pressed next. This means that strings that are unique
XXand short will always be matched before longer strings containing them.
XXFor example, if "foo" is mapped to a value, "foobar" will never be matched,
XXsince input will return at "foo". This becomes a problem when mapping
XXsomething like the \fIesc\fP key, since the \fIesc\fP key preceeds many
XXdesirable sequences such as arrow keys. This means that juggling on the
XXpart of the user is necessary to prevent mapping useful keys to where 
XXthey are inaccessible.
XX.PP
XXThe key mappings are all stored in a hashed linked list, in alphabetic
XXorder. Dynamic memory allocation is used to maintain the list. Initially
XXa mapping table has no keys mapped at all, and must be "prepped" with a
XXcall to 
XX.B ifinitmap
XXto make sure everything is initialized properly. The library requires
XXthat certain basic tokens be mapped (see \fIiface.h\fP) and provides a
XXminimal function to set up some reasonable defaults from termcap values.
XXUsers who do not like these values can assemble their own quite easily.
XX.B Ifgetkey
XXis the workhorse routine that performs matching of the mappings, and
XXreturns values accordingly. Several additional values in the 
XX.B ifmap
XXstructure are used to hold information, including the last keypress
XXin the event of a failure to match. This information can be accessed
XXby the user to handle errors, or to perform default actions (see the
XXcode in \fIiface.c\fP line \fI420\fP for an example). There are 
XXmacros in \fIiface.h\fP that allow manipulation of these values,
XXthough values and effects are not documented except in the source code.
XX.PP
XXThe
XX.B ifinstall
XXfunction installs
XX.B string
XXas a string in
XX.B keymap
XXwith a return code of
XX.B retval.
XXThere can be more than one string installed that returns
XX.B retval.
XX.B Ifinstall
XXreturns 0 on success; -1 in the event of error. If the string already
XXexists, the return value is modified to be
XX.B retval.
XX.PP
XXThe
XX.B ifdelete
XXfunction removes the mapping string
XX.B string
XXfrom
XX.B keymap,
XXreturning 0 if it found and deleted something, -1 if it failed to
XXfind the string.
XX.PP
XXThe 
XX.B ifgetkey
XXroutine is the main entry point to the library, performing the actual
XXgathering of user input, and returning mapped values. 
XX.B Ifgetkey
XXassumes that input is being done in cbreak mode, presumably with
XXnoecho set. Setting the terminal modes is the user's problem, and
XXshould be handled elsewhere.
XX.B  Ifgetkey
XXreads characters, trying to match them against the mapped strings
XXin
XX.B keymap,
XXreturning the installed integer value of found strings. If there is no
XXmatch found, 
XX.B ifgetkey
XXreturns the default value
XX.B IF_NOTFOUND
XXwhich is defined in \fIiface.h\fP to be -1. Additional information
XXabout what was found by
XX.B ifgetkey
XXcan be extracted from
XX.B keymap.
XXThere is an integer value
XX.B keymap.hold
XXwhich contains the last character read, and a character buffer
XX.B keymap.inbuf
XXwhich contains the current input string, in the even of a match that
XXfailed after several characters. (IE - if we were trying to match "frobnitz"
XXand only got "frobb", 
XX.B keymap.ibuf
XXwould have "frobb" stored in it, marking the point at which our input
XXceased to match.)
XXThe 
XX.B ifclearhold
XXand
XX.B iflastch
XXmacros allow access to these structures.
XX.PP
XXThe
XX.B ifgetstr
XXfunction is a building-block for a buffer editor. It uses the default
XXkey mappings for upkey, downkey, etc, to handle editing of a character
XXarray. Input is allowed until a return, abort, or up or down arrow
XXis entered. The input is assembled into
XX.B buf
XXwith input being prohibited past
XX.B max
XXcharacters.
XXThe argument
XX.B boffset
XXcan be given as a point within the buffer at which to begin the edit.
XXIt is the \fIuser's\fP responsibility to initially "paint" the contents
XXof 
XX.B buf
XXon the terminal in a desired location, before calling
XX.B ifgetstr.
XX.B Ifgetstr
XXwill maintain its location on the screen by using backspaces, etc, to the
XXbest of its ability, but if the user's application requires moving the
XXcursor around, the results will be less than pretty. The intent of
XXproviding
XX.B ifgetstr
XXis to demonstrate how these routines can be linked into something that
XXis actually useful, rather than to provide a solution to string input 
XXproblems.
XX.PP
XXThe
XX.B ifdfltmap
XXfunction builds a minimal "default" key mapping set into 
XX.B keymap,
XXusing termcap/termlib and whatever seems reasonable. This is also an
XXexample routine, and far from definitive, but provides a minimal set of
XXoperations. EMACS fans would probably prefer to map up, down, etc, to
XXdifferent keys. Before calling
XX.B ifdfltmap,
XXthe keymap should be initialized using
XX.B ifinitmap.
XX.PP
XXThe
XXiffreemap
XXfunction frees all (even the minimal) mappings in
XX.B keymap.
XXThis actually frees the dynamically allocated memory, and is completely
XXdifferent in purpose from
XX.B ifinitmap
XXwhich only initializes the mapping hash table.
XX.PP
XXThe
XX.B ifinitmap
XXfunction makes sure that the hash table, etc, pointers in
XX.B keymap
XXare all properly blanked out. It should be called before the keymap
XXis used, unless you are sure that everything is already set up properly.
XXCalling
XX.B ifinitmap
XXon a keymap with entries added will not free any memory that has been
XXallocated.
XX.PP
XXThe
XX.B iflookup
XXfunction returns a pointer to a structure of type
XX.B ifmap,
XXor to NULL, depending on whether there is a match for
XX.B string
XXin
XX.B keymap
XXor not. This routine is provided for completeness, not because I can
XXthink of much use for it. (otherwise, it can be inlined into 
XX.B ifinstall
XXif you like)
XX.PP
XXThe
XX.B ifclearhold,
XX.B iflastch,
XXand
XX.B iflastret
XXmacros allow easy access to the various bits of information retained
XXin 
XX.B keymap.
XX.B Ifclearhold
XXsets the held character to 0, which prevents 
XX.B ifgetkey
XXfrom trying to use it to complete the next match. For example, if the
XXprogram is trying to match "foo" and gets "fg", the 'g' is retained
XXin 
XX.B keymap.hold
XXto use as the first input character for the next call to
XX.B ifgetkey.
XXThis is sometimes not desirable. The 
XX.B iflastch
XXmacro allows access to this last character, if desired, but does not
XXclear it. The
XX.B iflastret
XXmacro allows access to whatever 
XX.B ifgetkey
XXlast \fIreturned\fP. This is included for logical completeness. It is
XXreset during every call to
XX.B ifgetkey.
XX.PP
XX.SH RESTRICTIONS
XX.PP
XXIf two mappings completely overlap, the longer will never be returned
XXsince the shorter one will always match first. Thus, "foo" and "foobar"
XXwill cause "foo" to always match first, even if "foobar" is typed. Mappings
XXsuch as "foop" and "foobar" will not conflict.
XX.PP
XXThis library is useless to anyone with a higher-level window management
XXsystem.
XX.SH DIAGNOSTICS
XX.PP
XX.SH AUTHOR
XX.PP
XXMarcus J. Ranum, Welch Medical Library.
XX.br
XX.ti 1i
XXuunet!aplcen!osiris!welchvax!mjr
XX.br
XX.ti 1i
XXmjr@jhuigf.BITNET
XX.SH SEE ALSO
SHAR_EOF
if test 8748 -ne "`wc -c iface.3`"
then
echo shar: error transmitting iface.3 '(should have been 8748 characters)'
fi
echo shar: extracting iface.c '(8279 characters)'
sed 's/^XX//' << \SHAR_EOF > iface.c
XX/*
XX * Copyright (C) 1988, Marcus J. Ranum, William Welch Medical Library
XX * $Author: mjr $
XX */
XX
XX#ifndef lint
XXstatic char *RCSid="$Header: iface.c,v 1.3 88/06/11 15:58:32 mjr rel $: iface.c";
XX#endif
XX
XX/*
XX * $Log:	iface.c,v $
XX * Revision 1.3  88/06/11  15:58:32  mjr
XX * added better initializations to ifinitmap
XX * 
XX * Revision 1.2  88/06/10  22:45:14  mjr
XX * added option to edit a buffer from an arbitrary point in the buffer.
XX * 
XX * Revision 1.1  88/06/10  17:01:40  mjr
XX * Initial revision
XX * 
XX */
XX
XX#include	<stdio.h>
XX#include	"iface.h"
XX
XX
XX
XX/* free a key map table - this zaps all mappings totally */
XXvoid
XXiffreemap(kmap)
XXstruct	ifmap	*kmap;
XX{
XX	int	x;
XX	struct	ifkey	*kp;
XX	struct	ifkey	*xp;
XX	
XX	for(x = 0; x < IF_HSIZE; x++) {
XX		kp = kmap->htab[x]; 
XX		while(kp != NULL) {
XX			xp = kp->next;
XX			(void)free(kp->lhs);
XX			(void)free((char *)kp);
XX			kp = xp;
XX		}
XX		kmap->htab[x] = NULL;
XX	}
XX}
XX
XX
XX
XX/* clear a key map table - this prepares the table for adding mappings */
XXvoid
XXifinitmap(kmap)
XXstruct	ifmap	*kmap;
XX{
XX	int	x;
XX	
XX	/* all we do here is set the hash entries to NULL */
XX	/* THIS IS NOT THE SAME AS FREEING ALL THE MAP ENTRIES */
XX	/* call this when a map table is first created, call iffreemap() */
XX	/* if you want to deallocate a mapping table */
XX	for(x = 0; x < IF_HSIZE; x++)
XX		kmap->htab[x] = NULL;
XX
XX	/* set these, too */
XX	kmap->hold = '\0';
XX	kmap->lastret = -1;
XX	kmap->lastbuf = 0;
XX	kmap->inbuf[0] = '\0';
XX}
XX
XX
XX/* install a default key map table - or most of one */
XXvoid
XXifdfltmap(kmap)
XXstruct	ifmap	*kmap;
XX{
XX	char	tbuf[1024];
XX	char	fill[1024];
XX	char	*fptr = fill;
XX	char	*forch;
XX	char	*uprch;
XX	char	*bwrch;
XX	char	*dnrch;
XX	char	*crch;
XX	static	char	hbuf[2];
XX	static	char	*dforch = "\33[C";
XX	static	char	*duprch = "\33[A";
XX	static	char	*dbwrch = "\33[D";
XX	static	char	*ddnrch = "\33[B";
XX	static	char	*dcrch = "\n";
XX	extern	char	*getenv();
XX	extern	char	*tgetstr();
XX
XX	if(((forch = getenv("TERM")) != NULL) && (tgetent(tbuf,forch) == 1)) {
XX		if((forch = tgetstr("kr",&fptr)) == 0)
XX			forch = dforch;
XX		if((uprch = tgetstr("ku",&fptr)) == 0)
XX			uprch = duprch;
XX		if((bwrch = tgetstr("kl",&fptr)) == 0)
XX			bwrch = dbwrch;
XX		if((dnrch = tgetstr("kd",&fptr)) == 0)
XX			dnrch = ddnrch;
XX		if((crch = tgetstr("cr",&fptr)) == 0)
XX			crch = dcrch;
XX	} else {
XX		/* could get terminal entry - use whatever */
XX		forch = dforch;
XX		uprch = duprch;
XX		bwrch = dbwrch;
XX		dnrch = ddnrch;
XX		crch = dcrch;
XX	}
XX
XX	/* install the foogers */
XX	hbuf[0] = 127;
XX	hbuf[1] = '\0';
XX	(void)ifinstall(kmap,hbuf,IF_DELCHAR);
XX	(void)ifinstall(kmap,"\b",IF_DELCHAR);
XX	(void)ifinstall(kmap,bwrch,IF_BACKMOVE);
XX	(void)ifinstall(kmap,forch,IF_FORWMOVE);
XX	(void)ifinstall(kmap,uprch,IF_UPWRMOVE);
XX	(void)ifinstall(kmap,dnrch,IF_DWNWMOVE);
XX	(void)ifinstall(kmap,crch,IF_CRETURN);
XX	return;
XX}
XX
XX
XX
XX/* look the bad boy up */
XXstruct	ifkey	*
XXiflookup(kmap,s)
XXstruct	ifmap	*kmap;
XXchar	*s;
XX{
XX	struct	ifkey	*kp;
XX	for(kp = kmap->htab[*s % IF_HSIZE]; kp != NULL; kp = kp->next)
XX		if(strcmp(s,kp->lhs) == 0)
XX			return(kp);
XX	return(NULL);
XX}
XX	
XX
XX
XX/* install a key mapping, hashed, and in lexical order */
XXifinstall(kmap,lh,rh)
XXstruct	ifmap	*kmap;
XXchar	*lh;
XXint	rh;
XX{
XX	struct	ifkey	*kp;
XX	struct	ifkey	*after;
XX	extern	char	*malloc();
XX	extern	char	*strcpy();
XX	
XX	/* not found - install anew */
XX	if((kp = iflookup(kmap,lh)) == NULL) {
XX
XX		/* figure out where to put it */
XX		kp = kmap->htab[*lh % IF_HSIZE];
XX		after = NULL;
XX		while(kp != NULL) {
XX			if(strcmp(lh,kp->lhs) < 0)
XX				break;
XX			after = kp;
XX			kp = kp->next;
XX		}
XX
XX/* avoid the possible pointer alignment problem message */
XX#ifndef lint
XX		/* allocate it */
XX		kp = (struct ifkey *)malloc(sizeof(struct ifkey));
XX#else
XX		kp = NULL;
XX#endif
XX		if(kp == NULL)
XX			return(-1);
XX		kp->lhs = malloc((unsigned)strlen(lh));
XX		if(kp->lhs == NULL)
XX			return(-1);
XX		kp->rhs = rh;
XX		(void)strcpy(kp->lhs,lh);
XX
XX		/* fixup sibling pointers */
XX		if(after != NULL) {
XX				kp->next = after->next;
XX				after->next = kp;
XX		} else {
XX			if(kmap->htab[*lh % IF_HSIZE] != NULL) {
XX				kp->next = kmap->htab[*lh % IF_HSIZE];
XX				kmap->htab[*lh % IF_HSIZE] = kp;
XX			} else {
XX				kmap->htab[*lh % IF_HSIZE] = kp;
XX				kp->next = NULL;
XX			}
XX		}
XX	} else {
XX		/* just change the return value */
XX		kp->rhs = rh;
XX	}
XX
XX	return(0);
XX}
XX	
XX
XX
XX/* de-install a key mapping (by key string) */
XXifdelete(kmap,s)
XXstruct	ifmap	*kmap;
XXchar	*s;
XX{
XX	struct	ifkey	*kp;
XX	struct	ifkey	*lptr = NULL;
XX	extern	char	*strcpy();
XX	
XX	for(kp = kmap->htab[*s % IF_HSIZE]; kp != NULL; kp = kp->next) {
XX		if(strcmp(s,kp->lhs) == 0) {
XX			if(lptr != NULL) {
XX				/* last node next points to the next one */
XX				lptr->next = kp->next;
XX			} else {
XX				/* root node next points to the next one */
XX				kmap->htab[*s % IF_HSIZE] = kp->next;
XX			}
XX			(void)free(kp->lhs);
XX			(void)free((char *)kp);
XX			return(0);
XX		}
XX		lptr = kp;
XX	}
XX	return(-1);
XX}
XX
XX
XX
XXifgetkey(kmap)
XXstruct	ifmap	*kmap;
XX{
XX	int	c =0;
XX	int	nfound;	
XX	int	nclose;	
XX	char	*i;
XX	char	*o;
XX	int	cnt =0;
XX	int	xcnt;
XX	struct	ifkey	*kp;		/* current key pointer */
XX	struct	ifkey	*bp = NULL;	/* bottom closest match pointer */
XX	struct	ifkey	*ret = NULL;	/* our match */
XX
XX
XX	while(1) {
XX		if((c = getchar()) == EOF) {
XX			kmap->lastret = IF_NOTFOUND;
XX			kmap->hold = c;
XX			return(IF_NOTFOUND);
XX		}
XX
XX		/* should happen once and only once per call */
XX		/* we set the bottom pointer to point to just before */
XX		/* the first char of our input stream */
XX		if(bp == NULL) {
XX			bp = kmap->htab[c % IF_HSIZE]; 
XX
XX			/* nothing in this hash chain to find */
XX			if(bp == NULL) {
XX				kmap->lastret = IF_NOTFOUND;
XX				kmap->hold = c;
XX				return(IF_NOTFOUND);
XX			}
XX			
XX			/* otherwise skate or die */
XX			while(bp->next != NULL && c > *bp->lhs)
XX				bp = bp->next;
XX		}
XX
XX
XX		nfound = 0;
XX		nclose = 0;
XX		kp = bp;
XX
XX		if(cnt + 1 > IF_INBUFSIZ)
XX			return(-1);
XX
XX		kmap->inbuf[cnt++] = c;
XX		kmap->inbuf[cnt] = '\0';
XX
XX		while(kp != NULL) {
XX
XX			xcnt = 0;
XX			o = kp->lhs;
XX			i = kmap->inbuf;
XX
XX			if(*o != *i)
XX				break;
XX
XX			/* a mix of strcmp and strncmp */
XX			for(; *o == *i; o++, i++, xcnt++) {
XX				if(*o == '\0') {
XX					nfound++;
XX					ret = kp;
XX					break;
XX				}
XX			}
XX			if(xcnt == cnt)
XX				nclose++;
XX			kp = kp->next;
XX		}
XX
XX		/* didn't find any !! - unmapped key */
XX		if(nfound == 0 && nclose == 0) {
XX			kmap->hold = c;
XX			kmap->lastret = IF_NOTFOUND;
XX			return(IF_NOTFOUND);
XX		}
XX
XX		/* only found one - great! */
XX		if(nfound) {
XX			kmap->hold = '\0';
XX			kmap->lastret = ret->rhs;
XX			return(ret->rhs);
XX		}
XX	}
XX}
XX
XX
XX
XXifgetstr(kmap,buf,max,bpos)
XXstruct	ifmap	*kmap;
XXchar	buf[];
XXint	max;
XXint	bpos;
XX{
XX	int	c =0;		/* input returned char */
XX	int	high =0;	/* line high water mark */
XX	int	cnt =0;		/* current position/count */
XX
XX	high = strlen(buf);
XX	if(bpos > high)
XX		cnt = high;
XX	else
XX		cnt = bpos;
XX
XX	while(1) {
XX
XX		/* get a key from the map */
XX		switch(c = ifgetkey(kmap)) {
XX
XX				/* delete a character */
XX			case IF_DELCHAR:
XX				/* if at current high water mark */
XX
XX				/* if at beginning of line */
XX				if(cnt - 1 < 0) {
XX					(void)fputc(07,stderr);
XX				} else {
XX					if(cnt == high) {
XX						high = --cnt;
XX						buf[cnt] = 0;
XX					} else {
XX						buf[--cnt] = ' ';
XX					}
XX					(void)fprintf(stderr,"\b \b");
XX				}
XX				break;
XX
XX
XX				/* NON-DESTRUCTIVE backwards move */
XX			case IF_BACKMOVE:
XX				if(cnt - 1 < 0) {
XX					(void)fputc(07,stderr);
XX				} else {
XX					cnt--;
XX					(void)fprintf(stderr,"\b");
XX				}
XX				break;
XX
XX
XX				/* NON-DESTRUCTIVE forward move */
XX			case IF_FORWMOVE:
XX				if(cnt + 1 > max) {
XX					(void)fputc(07,stderr);
XX				} else {
XX					if(high == cnt) {
XX						buf[cnt] = ' ';
XX						high = ++cnt;
XX					} else {
XX						++cnt;
XX					}
XX					(void)fputc(buf[cnt - 1],stderr);
XX				}
XX				break;
XX
XX
XX				/* FINISHED */
XX			case IF_CRETURN:
XX				buf[high] = '\0';
XX				kmap->lastbuf = cnt;
XX				return(IF_CRETURN);
XX
XX
XX				/* DOWN */
XX			case IF_DWNWMOVE:
XX				buf[high] = '\0';
XX				kmap->lastbuf = cnt;
XX				return(IF_DWNWMOVE);
XX
XX
XX				/* UP */
XX			case IF_UPWRMOVE:
XX				buf[high] = '\0';
XX				kmap->lastbuf = cnt;
XX				return(IF_UPWRMOVE);
XX
XX
XX				/* abort */
XX			case IF_ABORTKEY:
XX				buf[0] = '\0';
XX				kmap->lastbuf = cnt;
XX				return(IF_ABORTKEY);
XX
XX
XX				/* must be a character insert */
XX			case IF_NOTFOUND:
XX				c = kmap->hold;
XX				kmap->hold = '\0';
XX				if(cnt + 1 > max) {
XX					(void)fputc(07,stderr);
XX					buf[cnt] = '\0';
XX				} else {
XX					buf[cnt] = c;
XX					(void)fputc(c,stderr);
XX					if(high == cnt)
XX						high = ++cnt;
XX					else
XX						++cnt;
XX				}
XX				break;
XX		}
XX	}
XX}
SHAR_EOF
if test 8279 -ne "`wc -c iface.c`"
then
echo shar: error transmitting iface.c '(should have been 8279 characters)'
fi
echo shar: extracting tester.c '(4851 characters)'
sed 's/^XX//' << \SHAR_EOF > tester.c
XX/*
XX * Copyright (C) 1988, Marcus J. Ranum, William Welch Medical Library
XX * $Author: mjr $
XX */
XX
XX#ifndef lint
XXstatic char *RCSid="$Header: tester.c,v 1.2 88/06/10 22:46:12 mjr rel $: tester.c";
XX#endif
XX
XX/*
XX * $Log:	tester.c,v $
XX * Revision 1.2  88/06/10  22:46:12  mjr
XX * added example of function calling, buffer edit.
XX * 
XX * Revision 1.1  88/06/10  17:01:51  mjr
XX * Initial revision
XX * 
XX */
XX
XX#include <stdio.h>
XX#include <sys/ioctl.h>
XX#include "iface.h"
XX
XX#define EDBUFSIZ	100
XX
XX/* for the sake of example - a decent way to use this library */
XX/* obviously, you really should build a structure with pointers to */
XX/* functions and everything - then call the iface. exercise for the */
XX/* reader */
XX#define	TEST_DOTHIS	1
XXextern	void	dothis();
XX#define	TEST_DOTHAT	2
XXextern	void	dothat();
XX#define	TEST_DONADA	3
XXextern	void	donada();
XX
XXmain()
XX{
XX	char	instr[BUFSIZ];
XX	int	c;
XX	char	buf[BUFSIZ];
XX	char	edbuf[EDBUFSIZ];
XX	int	ret;
XX	struct	ifmap	keymap;
XX	struct	ifkey	*keyp;
XX	struct	sgttyb	ttybuf;
XX	extern	void	igets();
XX	extern	char	*strcpy();
XX
XX	ifinitmap(&keymap);
XX	ifdfltmap(&keymap);
XX
XX	/* for edit demo */
XX	(void)strcpy(edbuf,"Edit This !:");
XX
XX
XX	/* install dothis, dothat, donada - to "1", "2" and "3" */
XX	if(ifinstall(&keymap,"1",TEST_DOTHIS) != 0)
XX		(void)printf("error!\n");
XX	if(ifinstall(&keymap,"2",TEST_DOTHAT) != 0)
XX		(void)printf("error!\n");
XX	if(ifinstall(&keymap,"3",TEST_DONADA) != 0)
XX		(void)printf("error!\n");
XX
XX	/* you have to handle putting the terminal is noecho cbreak */
XX	if(ioctl(0,TIOCGETP,(char *)&ttybuf) < 0)
XX		perror("ioctl - GETP");
XX	ttybuf.sg_flags |= CBREAK;
XX	ttybuf.sg_flags &= ~ECHO;
XX	if(ioctl(0,TIOCSETP,(char *)&ttybuf) <0)
XX		perror("ioctl - SETP");
XX
XX
XX	/* main demo loop */
XX	while (1) {
XX		(void)printf("\nEdit  Run  Install  Search  Delete  GetKey   Line   Quit:");
XX		if((c = getchar()) == NULL) {
XX			(void)printf("\n");
XX			exit(0);
XX		}
XX
XX		switch (c) {
XX
XX		/* quit */
XX		case 'q':
XX		case 'Q':
XX			ttybuf.sg_flags &= ~CBREAK;
XX			ttybuf.sg_flags |= ECHO;
XX			if(ioctl(0,TIOCSETP,(char *)&ttybuf) <0)
XX				perror("ioctl - SETP");
XX			(void)printf("\n");
XX			exit(0);
XX			
XX
XX		/* edit a string */
XX		case 'e':
XX		case 'E':
XX			(void)printf("\n%s",edbuf);
XX			ret = ifgetstr(&keymap,edbuf,EDBUFSIZ,EDBUFSIZ);
XX			switch(ret) {
XX				case IF_UPWRMOVE:
XX					(void)printf("\n(up)got:\"%s\"\n",edbuf);
XX					break;
XX
XX				case IF_DWNWMOVE:
XX					(void)printf("\n(down)got:\"%s\"\n",edbuf);
XX					break;
XX
XX				case IF_CRETURN:
XX					(void)printf("\n(return)got:\"%s\"\n",edbuf);
XX					break;
XX
XX				case IF_ABORTKEY:
XX					(void)printf("\n(abort)got:\"%s\"\n",edbuf);
XX					break;
XX
XX				default:
XX					(void)printf("\n(err)got:\"%s\"\n",edbuf);
XX					break;
XX			}
XX			break;
XX			
XX
XX		case 'r':
XX		case 'R':
XX			(void)printf("\ncommand ? (press 1, 2, or 3):");
XX			ret = ifgetkey(&keymap);
XX			switch(ret) {
XX				case TEST_DOTHIS:
XX						dothis();
XX						break;
XX				case TEST_DOTHAT:
XX						dothat();
XX						break;
XX				case TEST_DONADA:
XX						donada();
XX						break;
XX				case IF_NOTFOUND:
XX						(void)printf("\nI said, 1, 2, or 3\n");
XX						break;
XX				default:
XX						(void)printf("\nwhassat ?\n");
XX			}
XX			break;
XX
XX
XX		/* search for a mapping entry */
XX		case 's':
XX		case 'S':
XX			(void)printf("\nSearch for:");
XX			(void)igets(buf,&ttybuf);
XX			keyp = iflookup(&keymap,buf);
XX			if(keyp == NULL)
XX				(void)printf("\nnot found\n");
XX			else
XX				(void)printf("\nKval:%d\n",keyp->rhs);
XX			break;
XX
XX
XX		/* install a mapping entry */
XX		case 'i':
XX		case 'I':
XX			(void)printf("\nNumber:");
XX			(void)igets(buf,&ttybuf);
XX			ret = atoi(buf);
XX			(void)printf("Install string:");
XX			(void)igets(buf,&ttybuf);
XX			if(ifinstall(&keymap,buf,ret) != 0)
XX				(void)printf("error!\n");
XX			break;
XX			
XX
XX		/* enter a line of junk */
XX		case 'L':
XX		case 'l':
XX			(void)printf("\nEnter Stuff:");
XX			ret = ifgetstr(&keymap,instr,BUFSIZ,0);
XX			if(ret != 0)
XX				(void)printf("\nabort..\n");
XX			else
XX				(void)printf("\ngot:\"%s\"\n",instr);
XX			break;
XX			
XX		/* get a key in "raw" mode */
XX		case 'g':
XX		case 'G':
XX			ifclearhold(keymap);
XX			(void)printf("\ngo:\n",ret);
XX			while((ret = ifgetkey(&keymap)) != IF_NOTFOUND)		
XX				(void)printf("\n%d",ret);
XX			(void)printf("\n(done)\n",ret);
XX			break;
XX
XX
XX		/* delete a map entry */
XX		case 'd':
XX		case 'D':
XX			(void)printf("\nDelete string:");
XX			(void)igets(buf,&ttybuf);
XX			if(ifdelete(&keymap,buf) != 0)
XX				(void)printf("error!\n");
XX			break;
XX
XX		default:
XX			(void)printf("\nhuh?\n");
XX		}
XX	}
XX}
XX
XXvoid
XXigets(buf,ttybuf)
XXchar	buf[];
XXstruct	sgttyb	*ttybuf;
XX{
XX	ttybuf->sg_flags &= ~CBREAK;
XX	ttybuf->sg_flags |= ECHO;
XX	if(ioctl(0,TIOCSETP,(char *)ttybuf) <0)
XX		perror("ioctl - SETP");
XX	(void)gets(buf);
XX	ttybuf->sg_flags |= CBREAK;
XX	ttybuf->sg_flags &= ~ECHO;
XX	if(ioctl(0,TIOCSETP,(char *)ttybuf) <0)
XX		perror("ioctl - SETP");
XX}
XX
XXvoid
XXdothis()
XX{
XX	(void)printf("\ndothis called");
XX}
XX
XXvoid
XXdothat()
XX{
XX	(void)printf("\ndothat called");
XX}
XX
XXvoid
XXdonada()
XX{
XX	(void)printf("\ndonada called");
XX}
SHAR_EOF
if test 4851 -ne "`wc -c tester.c`"
then
echo shar: error transmitting tester.c '(should have been 4851 characters)'
fi
#	End of shell archive
exit 0