Edit

IABSD.fr/src/usr.bin/mg/extend.c

Branch :

  • Show log

    Commit

  • Author : op
    Date : 2023-04-17 10:11:30
    Hash : d7784223
    Message : fix a few dobeep_msgs() calls: a space is already added between the arguments ok tb@

  • usr.bin/mg/extend.c
  • /*	$OpenBSD: extend.c,v 1.80 2023/04/17 10:11:30 op Exp $	*/
    /* This file is in the public domain. */
    
    /*
     *	Extended (M-x) commands, rebinding, and	startup file processing.
     */
    
    #include <sys/queue.h>
    #include <sys/types.h>
    #include <regex.h>
    #include <ctype.h>
    #include <limits.h>
    #include <signal.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include "chrdef.h"
    #include "def.h"
    #include "funmap.h"
    #include "kbd.h"
    #include "key.h"
    #include "macro.h"
    
    static int	 remap(KEYMAP *, int, PF, KEYMAP *);
    static KEYMAP	*reallocmap(KEYMAP *);
    static void	 fixmap(KEYMAP *, KEYMAP *, KEYMAP *);
    static int	 dobind(KEYMAP *, const char *, int);
    static char	*parsetoken(char *);
    static int	 bindkey(KEYMAP **, const char *, KCHAR *, int);
    
    /*
     * Insert a string, mainly for use from macros (created by selfinsert).
     */
    int
    insert(int f, int n)
    {
    	char	 buf[BUFSIZE], *bufp, *cp;
    	int	 count, c;
    
    	if (inmacro) {
    		while (--n >= 0) {
    			for (count = 0; count < maclcur->l_used; count++) {
    				if ((((c = maclcur->l_text[count]) ==
    				    *curbp->b_nlchr)
    				    ? lnewline() : linsert(1, c)) != TRUE)
    					return (FALSE);
    			}
    		}
    		maclcur = maclcur->l_fp;
    		return (TRUE);
    	}
    	if (n == 1)
    		/* CFINS means selfinsert can tack on the end */
    		thisflag |= CFINS;
    
    	if ((bufp = eread("Insert: ", buf, sizeof(buf), EFNEW)) == NULL)
    		return (ABORT);
    	else if (bufp[0] == '\0')
    		return (FALSE);
    	while (--n >= 0) {
    		cp = buf;
    		while (*cp) {
    			if (((*cp == *curbp->b_nlchr) ?
    			    lnewline() : linsert(1, *cp))
    			    != TRUE)
    				return (FALSE);
    			cp++;
    		}
    	}
    	return (TRUE);
    }
    
    /*
     * Bind a key to a function.  Cases range from the trivial (replacing an
     * existing binding) to the extremely complex (creating a new prefix in a
     * map_element that already has one, so the map_element must be split,
     * but the keymap doesn't have enough room for another map_element, so
     * the keymap is reallocated).	No attempt is made to reclaim space no
     * longer used, if this is a problem flags must be added to indicate
     * malloced versus static storage in both keymaps and map_elements.
     * Structure assignments would come in real handy, but K&R based compilers
     * don't have them.  Care is taken so running out of memory will leave
     * the keymap in a usable state.
     * Parameters are:
     * curmap:  	pointer to the map being changed
     * c:		character being changed
     * funct: 	function being changed to
     * pref_map: 	if funct==NULL, map to bind to or NULL for new
     */
    static int
    remap(KEYMAP *curmap, int c, PF funct, KEYMAP *pref_map)
    {
    	int		 i, n1, n2, nold;
    	KEYMAP		*mp, *newmap;
    	PF		*pfp;
    	struct map_element	*mep;
    
    	if (ele >= &curmap->map_element[curmap->map_num] || c < ele->k_base) {
    		if (ele > &curmap->map_element[0] && (funct != NULL ||
    		    (ele - 1)->k_prefmap == NULL))
    			n1 = c - (ele - 1)->k_num;
    		else
    			n1 = HUGE;
    		if (ele < &curmap->map_element[curmap->map_num] &&
    		    (funct != NULL || ele->k_prefmap == NULL))
    			n2 = ele->k_base - c;
    		else
    			n2 = HUGE;
    		if (n1 <= MAPELEDEF && n1 <= n2) {
    			ele--;
    			if ((pfp = calloc(c - ele->k_base + 1,
    			    sizeof(PF))) == NULL)
    				return (dobeep_msg("Out of memory"));
    
    			nold = ele->k_num - ele->k_base + 1;
    			for (i = 0; i < nold; i++)
    				pfp[i] = ele->k_funcp[i];
    			while (--n1)
    				pfp[i++] = curmap->map_default;
    			pfp[i] = funct;
    			ele->k_num = c;
    			ele->k_funcp = pfp;
    		} else if (n2 <= MAPELEDEF) {
    			if ((pfp = calloc(ele->k_num - c + 1,
    			    sizeof(PF))) == NULL)
    				return (dobeep_msg("Out of memory"));
    
    			nold = ele->k_num - ele->k_base + 1;
    			for (i = 0; i < nold; i++)
    				pfp[i + n2] = ele->k_funcp[i];
    			while (--n2)
    				pfp[n2] = curmap->map_default;
    			pfp[0] = funct;
    			ele->k_base = c;
    			ele->k_funcp = pfp;
    		} else {
    			if (curmap->map_num >= curmap->map_max) {
    				if ((newmap = reallocmap(curmap)) == NULL)
    					return (FALSE);
    				curmap = newmap;
    			}
    			if ((pfp = malloc(sizeof(PF))) == NULL)
    				return (dobeep_msg("Out of memory"));
    
    			pfp[0] = funct;
    			for (mep = &curmap->map_element[curmap->map_num];
    			    mep > ele; mep--) {
    				mep->k_base = (mep - 1)->k_base;
    				mep->k_num = (mep - 1)->k_num;
    				mep->k_funcp = (mep - 1)->k_funcp;
    				mep->k_prefmap = (mep - 1)->k_prefmap;
    			}
    			ele->k_base = c;
    			ele->k_num = c;
    			ele->k_funcp = pfp;
    			ele->k_prefmap = NULL;
    			curmap->map_num++;
    		}
    		if (funct == NULL) {
    			if (pref_map != NULL)
    				ele->k_prefmap = pref_map;
    			else {
    				if ((mp = malloc(sizeof(KEYMAP) +
    				    (MAPINIT - 1) * sizeof(struct map_element))) == NULL) {
    					(void)dobeep_msg("Out of memory");
    					ele->k_funcp[c - ele->k_base] =
    					    curmap->map_default;
    					return (FALSE);
    				}
    				mp->map_num = 0;
    				mp->map_max = MAPINIT;
    				mp->map_default = rescan;
    				ele->k_prefmap = mp;
    			}
    		}
    	} else {
    		n1 = c - ele->k_base;
    		if (ele->k_funcp[n1] == funct && (funct != NULL ||
    		    pref_map == NULL || pref_map == ele->k_prefmap))
    			/* no change */
    			return (TRUE);
    		if (funct != NULL || ele->k_prefmap == NULL) {
    			if (ele->k_funcp[n1] == NULL)
    				ele->k_prefmap = NULL;
    			/* easy case */
    			ele->k_funcp[n1] = funct;
    			if (funct == NULL) {
    				if (pref_map != NULL)
    					ele->k_prefmap = pref_map;
    				else {
    					if ((mp = malloc(sizeof(KEYMAP) +
    					    (MAPINIT - 1) *
    					    sizeof(struct map_element))) == NULL) {
    						(void)dobeep_msg("Out of memory");
    						ele->k_funcp[c - ele->k_base] =
    						    curmap->map_default;
    						return (FALSE);
    					}
    					mp->map_num = 0;
    					mp->map_max = MAPINIT;
    					mp->map_default = rescan;
    					ele->k_prefmap = mp;
    				}
    			}
    		} else {
    			/*
    			 * This case is the splits.
    			 * Determine which side of the break c goes on
    			 * 0 = after break; 1 = before break
    			 */
    			n2 = 1;
    			for (i = 0; n2 && i < n1; i++)
    				n2 &= ele->k_funcp[i] != NULL;
    			if (curmap->map_num >= curmap->map_max) {
    				if ((newmap = reallocmap(curmap)) == NULL)
    					return (FALSE);
    				curmap = newmap;
    			}
    			if ((pfp = calloc(ele->k_num - c + !n2,
    			    sizeof(PF))) == NULL)
    				return (dobeep_msg("Out of memory"));
    
    			ele->k_funcp[n1] = NULL;
    			for (i = n1 + n2; i <= ele->k_num - ele->k_base; i++)
    				pfp[i - n1 - n2] = ele->k_funcp[i];
    			for (mep = &curmap->map_element[curmap->map_num];
    			    mep > ele; mep--) {
    				mep->k_base = (mep - 1)->k_base;
    				mep->k_num = (mep - 1)->k_num;
    				mep->k_funcp = (mep - 1)->k_funcp;
    				mep->k_prefmap = (mep - 1)->k_prefmap;
    			}
    			ele->k_num = c - !n2;
    			(ele + 1)->k_base = c + n2;
    			(ele + 1)->k_funcp = pfp;
    			ele += !n2;
    			ele->k_prefmap = NULL;
    			curmap->map_num++;
    			if (pref_map == NULL) {
    				if ((mp = malloc(sizeof(KEYMAP) + (MAPINIT - 1)
    				    * sizeof(struct map_element))) == NULL) {
    					(void)dobeep_msg("Out of memory");
    					ele->k_funcp[c - ele->k_base] =
    					    curmap->map_default;
    					return (FALSE);
    				}
    				mp->map_num = 0;
    				mp->map_max = MAPINIT;
    				mp->map_default = rescan;
    				ele->k_prefmap = mp;
    			} else
    				ele->k_prefmap = pref_map;
    		}
    	}
    	return (TRUE);
    }
    
    /*
     * Reallocate a keymap. Returns NULL (without trashing the current map)
     * on failure.
     */
    static KEYMAP *
    reallocmap(KEYMAP *curmap)
    {
    	struct maps_s	*mps;
    	KEYMAP	*mp;
    	int	 i;
    
    	if (curmap->map_max > SHRT_MAX - MAPGROW) {
    		(void)dobeep_msg("keymap too large");
    		return (NULL);
    	}
    	if ((mp = malloc(sizeof(KEYMAP) + (curmap->map_max + (MAPGROW - 1)) *
    	    sizeof(struct map_element))) == NULL) {
    		(void)dobeep_msg("Out of memory");
    		return (NULL);
    	}
    	mp->map_num = curmap->map_num;
    	mp->map_max = curmap->map_max + MAPGROW;
    	mp->map_default = curmap->map_default;
    	for (i = curmap->map_num; i--;) {
    		mp->map_element[i].k_base = curmap->map_element[i].k_base;
    		mp->map_element[i].k_num = curmap->map_element[i].k_num;
    		mp->map_element[i].k_funcp = curmap->map_element[i].k_funcp;
    		mp->map_element[i].k_prefmap = curmap->map_element[i].k_prefmap;
    	}
    	for (mps = maps; mps != NULL; mps = mps->p_next) {
    		if (mps->p_map == curmap)
    			mps->p_map = mp;
    		else
    			fixmap(curmap, mp, mps->p_map);
    	}
    	ele = &mp->map_element[ele - &curmap->map_element[0]];
    	return (mp);
    }
    
    /*
     * Fix references to a reallocated keymap (recursive).
     */
    static void
    fixmap(KEYMAP *curmap, KEYMAP *mp, KEYMAP *mt)
    {
    	int	 i;
    
    	for (i = mt->map_num; i--;) {
    		if (mt->map_element[i].k_prefmap != NULL) {
    			if (mt->map_element[i].k_prefmap == curmap)
    				mt->map_element[i].k_prefmap = mp;
    			else
    				fixmap(curmap, mp, mt->map_element[i].k_prefmap);
    		}
    	}
    }
    
    /*
     * Do the input for local-set-key, global-set-key  and define-key
     * then call remap to do the work.
     */
    static int
    dobind(KEYMAP *curmap, const char *p, int unbind)
    {
    	KEYMAP	*pref_map = NULL;
    	PF	 funct;
    	char	 bprompt[80], *bufp, *pep;
    	int	 c, s, n;
    
    	if (macrodef) {
    		/*
    		 * Keystrokes aren't collected. Not hard, but pretty useless.
    		 * Would not work for function keys in any case.
    		 */
    		return (dobeep_msg("Can't rebind key in macro"));
    	}
    	if (inmacro) {
    		for (s = 0; s < maclcur->l_used - 1; s++) {
    			if (doscan(curmap, c = CHARMASK(maclcur->l_text[s]), &curmap)
    			    != NULL) {
    				if (remap(curmap, c, NULL, NULL)
    				    != TRUE)
    					return (FALSE);
    			}
    		}
    		(void)doscan(curmap, c = maclcur->l_text[s], NULL);
    		maclcur = maclcur->l_fp;
    	} else {
    		n = strlcpy(bprompt, p, sizeof(bprompt));
    		if (n >= sizeof(bprompt))
    			n = sizeof(bprompt) - 1;
    		pep = bprompt + n;
    		for (;;) {
    			ewprintf("%s", bprompt);
    			pep[-1] = ' ';
    			pep = getkeyname(pep, sizeof(bprompt) -
    			    (pep - bprompt), c = getkey(FALSE));
    			if (doscan(curmap, c, &curmap) != NULL)
    				break;
    			*pep++ = '-';
    			*pep = '\0';
    		}
    	}
    	if (unbind)
    		funct = rescan;
    	else {
    		if ((bufp = eread("%s to command: ", bprompt, sizeof(bprompt),
    		    EFFUNC | EFNEW, bprompt)) == NULL)
    			return (ABORT);
    		else if (bufp[0] == '\0')
    			return (FALSE);
    		if (((funct = name_function(bprompt)) == NULL) ?
    		    (pref_map = name_map(bprompt)) == NULL : funct == NULL)
    			return (dobeep_msg("[No match]"));
    
    	}
    	return (remap(curmap, c, funct, pref_map));
    }
    
    /*
     * bindkey: bind key sequence to a function in the specified map.  Used by
     * excline so it can bind function keys.  To close to release to change
     * calling sequence, should just pass KEYMAP *curmap rather than
     * KEYMAP **mapp.
     */
    static int
    bindkey(KEYMAP **mapp, const char *fname, KCHAR *keys, int kcount)
    {
    	KEYMAP	*curmap = *mapp;
    	KEYMAP	*pref_map = NULL;
    	PF	 funct;
    	int	 c;
    
    	if (fname == NULL)
    		funct = rescan;
    	else if (((funct = name_function(fname)) == NULL) ?
    	    (pref_map = name_map(fname)) == NULL : funct == NULL) {
    		dobeep();
    		ewprintf("[No match: %s]", fname);
    		return (FALSE);
    	}
    	while (--kcount) {
    		if (doscan(curmap, c = *keys++, &curmap) != NULL) {
    			if (remap(curmap, c, NULL, NULL) != TRUE)
    				return (FALSE);
    			/*
    			 * XXX - Bizzarreness. remap creates an empty KEYMAP
    			 *       that the last key is supposed to point to.
    			 */
    			curmap = ele->k_prefmap;
    		}
    	}
    	(void)doscan(curmap, c = *keys, NULL);
    	return (remap(curmap, c, funct, pref_map));
    }
    
    /*
     * Wrapper for bindkey() that converts escapes.
     */
    int
    dobindkey(KEYMAP *map, const char *func, const char *str)
    {
    	int	 i;
    
    	for (i = 0; *str && i < MAXKEY; i++) {
    		/* XXX - convert numbers w/ strol()? */
    		if (*str == '^' && *(str + 1) !=  '\0') {
    			key.k_chars[i] = CCHR(toupper((unsigned char)*++str));
    		} else if (*str == '\\' && *(str + 1) != '\0') {
    			switch (*++str) {
    			case '^':
    				key.k_chars[i] = '^';
    				break;
    			case 't':
    			case 'T':
    				key.k_chars[i] = '\t';
    				break;
    			case 'n':
    			case 'N':
    				key.k_chars[i] = *curbp->b_nlchr;
    				break;
    			case 'r':
    			case 'R':
    				key.k_chars[i] = '\r';
    				break;
    			case 'e':
    			case 'E':
    				key.k_chars[i] = CCHR('[');
    				break;
    			case '\\':
    				key.k_chars[i] = '\\';
    				break;
    			}
    		} else
    			key.k_chars[i] = *str;
    		str++;
    	}
    	key.k_count = i;
    	return (bindkey(&map, func, key.k_chars, key.k_count));
    }
    
    /*
     * This function modifies the fundamental keyboard map.
     */
    int
    bindtokey(int f, int n)
    {
    	return (dobind(fundamental_map, "Global set key: ", FALSE));
    }
    
    /*
     * This function modifies the current mode's keyboard map.
     */
    int
    localbind(int f, int n)
    {
    	return (dobind(curbp->b_modes[curbp->b_nmodes]->p_map,
    	    "Local set key: ", FALSE));
    }
    
    /*
     * This function redefines a key in any keymap.
     */
    int
    redefine_key(int f, int n)
    {
    	static char	 buf[48];
    	char		 tmp[32], *bufp;
    	KEYMAP		*mp;
    
    	(void)strlcpy(buf, "Define key map: ", sizeof(buf));
    	if ((bufp = eread("%s", tmp, sizeof(tmp), EFNEW, buf)) == NULL)
    		return (ABORT);
    	else if (bufp[0] == '\0')
    		return (FALSE);
    	(void)strlcat(buf, tmp, sizeof(buf));
    	if ((mp = name_map(tmp)) == NULL)
    		return (dobeep_msgs("Unknown map", tmp));
    
    	if (strlcat(buf, "key: ", sizeof(buf)) >= sizeof(buf))
    		return (FALSE);
    
    	return (dobind(mp, buf, FALSE));
    }
    
    int
    unbindtokey(int f, int n)
    {
    	return (dobind(fundamental_map, "Global unset key: ", TRUE));
    }
    
    int
    localunbind(int f, int n)
    {
    	return (dobind(curbp->b_modes[curbp->b_nmodes]->p_map,
    	    "Local unset key: ", TRUE));
    }
    
    /*
     * Extended command. Call the message line routine to read in the command
     * name and apply autocompletion to it. When it comes back, look the name
     * up in the symbol table and run the command if it is found.  Print an
     * error if there is anything wrong.
     */
    int
    extend(int f, int n)
    {
    	PF	 funct;
    	char	 xname[NXNAME], *bufp;
    
    	if (!(f & FFARG))
    		bufp = eread("M-x ", xname, NXNAME, EFNEW | EFFUNC);
    	else
    		bufp = eread("%d M-x ", xname, NXNAME, EFNEW | EFFUNC, n);
    	if (bufp == NULL)
    		return (ABORT);
    	else if (bufp[0] == '\0')
    		return (FALSE);
    	if ((funct = name_function(bufp)) != NULL) {
    		if (macrodef) {
    			struct line	*lp = maclcur;
    			macro[macrocount - 1].m_funct = funct;
    			maclcur = lp->l_bp;
    			maclcur->l_fp = lp->l_fp;
    			free(lp);
    		}
    		return ((*funct)(f, n));
    	}
    	return (dobeep_msg("[No match]"));
    }
    
    /*
     * Define the commands needed to do startup-file processing.
     * This code is mostly a kludge just so we can get startup-file processing.
     *
     * If you're serious about having this code, you should rewrite it.
     * To wit:
     *	It has lots of funny things in it to make the startup-file look
     *	like a GNU startup file; mostly dealing with parens and semicolons.
     *	This should all vanish.
     *
     * We define eval-expression because it's easy.	 It can make
     * *-set-key or define-key set an arbitrary key sequence, so it isn't
     * useless.
     */
    
    /*
     * evalexpr - get one line from the user, and run it.
     * Use strlen for length of line, assume user is not typing in a '\0' in the
     * modeline. llen only used for foundparen() so old-school will be ok.
     */
    int
    evalexpr(int f, int n)
    {
    	char	 exbuf[BUFSIZE], *bufp;
    	int	 llen;
    
    	if ((bufp = eread("Eval: ", exbuf, sizeof(exbuf),
    	    EFNEW | EFCR)) == NULL)
    		return (ABORT);
    	else if (bufp[0] == '\0')
    		return (FALSE);
    	llen = strlen(bufp);
    
    	return (excline(exbuf, llen, 1));
    }
    
    /*
     * evalbuffer - evaluate the current buffer as line commands. Useful for
     * testing startup files.
     */
    int
    evalbuffer(int f, int n)
    {
    	struct line		*lp;
    	struct buffer		*bp = curbp;
    	int		 s, llen, lnum = 0;
    	static char	 excbuf[BUFSIZE];
    
    	for (lp = bfirstlp(bp); lp != bp->b_headp; lp = lforw(lp)) {
    		lnum++;
    		llen = llength(lp);
    		if (llen >= BUFSIZE)
    			return (FALSE);
    		(void)strncpy(excbuf, ltext(lp), llen);
    
    		/* make sure the line is terminated */
    		excbuf[llen] = '\0';
    		if ((s = excline(excbuf, llen, lnum)) != TRUE) {
    			cleanup();
    			return (s);
    		}
    	}
    	cleanup();
    	return (TRUE);
    }
    
    /*
     * evalfile - go get a file and evaluate it as line commands. You can
     *	go get your own startup file if need be.
     */
    int
    evalfile(int f, int n)
    {
    	FILE	*ffp;
    	char	 fname[NFILEN], *bufp;
    	int	 ret;
    
    	if ((bufp = eread("Load file: ", fname, NFILEN,
    	    EFNEW | EFCR)) == NULL)
    		return (ABORT);
    	if (bufp[0] == '\0')
    		return (FALSE);
    	if ((bufp = adjustname(fname, TRUE)) == NULL)
    		return (FALSE);
    	ret = ffropen(&ffp, bufp, NULL);
    	if (ret == FIODIR)
    		(void)ffclose(ffp, NULL);
    	if (ret != FIOSUC)
    		return (FALSE);
    	ret = load(ffp, bufp);
    	(void)ffclose(ffp, NULL);
    	return (ret);
    }
    
    /*
     * load - go load the file name we got passed.
     */
    int
    load(FILE *ffp, const char *fname)
    {
    	int	 s = TRUE, line;
    	int	 nbytes = 0;
    	char	 excbuf[BUFSIZE];
    
    	line = 0;
    	while ((s = ffgetline(ffp, excbuf, sizeof(excbuf) - 1, &nbytes))
    	    == FIOSUC) {
    		line++;
    		excbuf[nbytes] = '\0';
    		if (excline(excbuf, nbytes, line) != TRUE) {
    			s = FIOERR;
    			dobeep();
    			ewprintf("Error loading file %s at line %d", fname, line);
    			break;
    		}
    	}
    	excbuf[nbytes] = '\0';
    	if (s != FIOEOF || (nbytes && excline(excbuf, nbytes, ++line) != TRUE))
    		return (FALSE);
    	return (TRUE);
    }
    
    /*
     * excline - run a line from a load file or eval-expression.
     */
    int
    excline(char *line, int llen, int lnum)
    {
    	PF	 fp;
    	struct line	*lp, *np;
    	int	 status, c, f, n;
    	char	*funcp, *tmp;
    	char	*argp = NULL;
    	long	 nl;
    	int	 bind;
    	KEYMAP	*curmap;
    #define BINDARG		0  /* this arg is key to bind (local/global set key) */
    #define	BINDNO		1  /* not binding or non-quoted BINDARG */
    #define BINDNEXT	2  /* next arg " (define-key) */
    #define BINDDO		3  /* already found key to bind */
    #define BINDEXT		1  /* space for trailing \0 */
    
    	lp = NULL;
    
    	if (macrodef || inmacro)
    		return (dobeep_msg("Not now!"));
    
    	f = 0;
    	n = 1;
    	funcp = skipwhite(line);
    	if (*funcp == '\0')
    		return (TRUE);	/* No error on blank lines */
    	if (*funcp == '(')
    		return (foundparen(funcp, llen, lnum));
    	line = parsetoken(funcp);
    	if (*line != '\0') {
    		*line++ = '\0';
    		line = skipwhite(line);
    		if (ISDIGIT(*line) || *line == '-') {
    			argp = line;
    			line = parsetoken(line);
    		}
    	}
    	if (argp != NULL) {
    		f = FFARG;
    		nl = strtol(argp, &tmp, 10);
    		if (*tmp != '\0')
    			return (FALSE);
    		if (nl >= INT_MAX || nl <= INT_MIN)
    			return (FALSE);
    		n = (int)nl;
    	}
    	if ((fp = name_function(funcp)) == NULL)
    		return (dobeep_msgs("Unknown function:", funcp));
    
    	if (fp == bindtokey || fp == unbindtokey) {
    		bind = BINDARG;
    		curmap = fundamental_map;
    	} else if (fp == localbind || fp == localunbind) {
    		bind = BINDARG;
    		curmap = curbp->b_modes[curbp->b_nmodes]->p_map;
    	} else if (fp == redefine_key)
    		bind = BINDNEXT;
    	else
    		bind = BINDNO;
    	/* Pack away all the args now... */
    	if ((np = lalloc(0)) == FALSE)
    		return (FALSE);
    	np->l_fp = np->l_bp = maclcur = np;
    	while (*line != '\0') {
    		argp = skipwhite(line);
    		if (*argp == '\0')
    			break;
    		line = parsetoken(argp);
    		if (*argp != '"') {
    			if (*argp == '\'')
    				++argp;
    			if ((lp = lalloc((int) (line - argp) + BINDEXT)) ==
    			    NULL) {
    				status = FALSE;
    				goto cleanup;
    			}
    			bcopy(argp, ltext(lp), (int)(line - argp));
    			/* don't count BINDEXT */
    			lp->l_used--;
    			if (bind == BINDARG)
    				bind = BINDNO;
    		} else {
    			/* quoted strings are special */
    			++argp;
    			if (bind != BINDARG) {
    				lp = lalloc((int)(line - argp) + BINDEXT);
    				if (lp == NULL) {
    					status = FALSE;
    					goto cleanup;
    				}
    				lp->l_used = 0;
    			} else
    				key.k_count = 0;
    			while (*argp != '"' && *argp != '\0') {
    				if (*argp != '\\')
    					c = *argp++;
    				else {
    					switch (*++argp) {
    					case 't':
    					case 'T':
    						c = CCHR('I');
    						break;
    					case 'n':
    					case 'N':
    						c = CCHR('J');
    						break;
    					case 'r':
    					case 'R':
    						c = CCHR('M');
    						break;
    					case 'e':
    					case 'E':
    						c = CCHR('[');
    						break;
    					case '^':
    						/*
    						 * split into two statements
    						 * due to bug in OSK cpp
    						 */
    						c = CHARMASK(*++argp);
    						c = ISLOWER(c) ?
    						    CCHR(TOUPPER(c)) : CCHR(c);
    						break;
    					case '0':
    					case '1':
    					case '2':
    					case '3':
    					case '4':
    					case '5':
    					case '6':
    					case '7':
    						c = *argp - '0';
    						if (argp[1] <= '7' &&
    						    argp[1] >= '0') {
    							c <<= 3;
    							c += *++argp - '0';
    							if (argp[1] <= '7' &&
    							    argp[1] >= '0') {
    								c <<= 3;
    								c += *++argp
    								    - '0';
    							}
    						}
    						break;
    					case 'f':
    					case 'F':
    						c = *++argp - '0';
    						if (ISDIGIT(argp[1])) {
    							c *= 10;
    							c += *++argp - '0';
    						}
    						c += KFIRST;
    						break;
    					default:
    						c = CHARMASK(*argp);
    						break;
    					}
    					argp++;
    				}
    				if (bind == BINDARG)
    					key.k_chars[key.k_count++] = c;
    				else
    					lp->l_text[lp->l_used++] = c;
    			}
    			if (*line)
    				line++;
    		}
    		switch (bind) {
    		case BINDARG:
    			bind = BINDDO;
    			break;
    		case BINDNEXT:
    			lp->l_text[lp->l_used] = '\0';
    			if ((curmap = name_map(lp->l_text)) == NULL) {
    				(void)dobeep_msgs("No such mode:", lp->l_text);
    				status = FALSE;
    				free(lp);
    				goto cleanup;
    			}
    			free(lp);
    			bind = BINDARG;
    			break;
    		default:
    			lp->l_fp = np->l_fp;
    			lp->l_bp = np;
    			np->l_fp = lp;
    			np = lp;
    		}
    	}
    	switch (bind) {
    	default:
    		(void)dobeep_msg("Bad args to set key");
    		status = FALSE;
    		break;
    	case BINDDO:
    		if (fp != unbindtokey && fp != localunbind) {
    			lp->l_text[lp->l_used] = '\0';
    			status = bindkey(&curmap, lp->l_text, key.k_chars,
    			    key.k_count);
    		} else
    			status = bindkey(&curmap, NULL, key.k_chars,
    			    key.k_count);
    		break;
    	case BINDNO:
    		inmacro = TRUE;
    		maclcur = maclcur->l_fp;
    		status = (*fp)(f, n);
    		inmacro = FALSE;
    	}
    cleanup:
    	lp = maclcur->l_fp;
    	while (lp != maclcur) {
    		np = lp->l_fp;
    		free(lp);
    		lp = np;
    	}
    	free(lp);
    	maclhead = NULL;
    	macrodef = FALSE;
    	return (status);
    }
    
    /*
     * a pair of utility functions for the above
     */
    char *
    skipwhite(char *s)
    {
    	while (*s == ' ' || *s == '\t')
    		s++;
    	if ((*s == ';') || (*s == '#'))
    		*s = '\0';
    	return (s);
    }
    
    static char *
    parsetoken(char *s)
    {
    	if (*s != '"') {
    		while (*s && *s != ' ' && *s != '\t' && *s != ')' && *s != '(')
    			s++;
    		if (*s == ';')
    			*s = '\0';
    	} else
    		do {
    			/*
    			 * Strings get special treatment.
    			 * Beware: You can \ out the end of the string!
    			 */
    			if (*s == '\\')
    				++s;
    		} while (*++s != '"' && *s != '\0');
    	return (s);
    }