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