]> pd.if.org Git - pd_readline/blobdiff - mg/extend.c
Added mg from an OpenBSD mirror site. Many thanks to the OpenBSD team and the mg...
[pd_readline] / mg / extend.c
diff --git a/mg/extend.c b/mg/extend.c
new file mode 100644 (file)
index 0000000..2f47de6
--- /dev/null
@@ -0,0 +1,963 @@
+/*     $OpenBSD: extend.c,v 1.53 2012/05/25 04:56:58 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ *     Extended (M-X) commands, rebinding, and startup file processing.
+ */
+#include "chrdef.h"
+#include "def.h"
+#include "kbd.h"
+#include "funmap.h"
+
+#include <sys/types.h>
+#include <ctype.h>
+
+#include "macro.h"
+
+#ifdef FKEYS
+#include "key.h"
+#ifndef        BINDKEY
+#define        BINDKEY                 /* bindkey is used by FKEYS startup code */
+#endif /* !BINDKEY */
+#endif /* FKEYS */
+
+#include <ctype.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    *skipwhite(char *);
+static char    *parsetoken(char *);
+#ifdef BINDKEY
+static int      bindkey(KEYMAP **, const char *, KCHAR *, int);
+#endif /* BINDKEY */
+
+/*
+ * Insert a string, mainly for use from macros (created by selfinsert).
+ */
+/* ARGSUSED */
+int
+insert(int f, int n)
+{
+       char     buf[128], *bufp, *cp;
+       int      count, c;
+
+       if (inmacro) {
+               while (--n >= 0) {
+                       for (count = 0; count < maclcur->l_used; count++) {
+                               if ((((c = maclcur->l_text[count]) == '\n')
+                                   ? 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 == '\n') ? 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) {
+                               ewprintf("Out of memory");
+                               return (FALSE);
+                       }
+                       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) {
+                               ewprintf("Out of memory");
+                               return (FALSE);
+                       }
+                       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) {
+                               ewprintf("Out of memory");
+                               return (FALSE);
+                       }
+                       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) {
+                                       ewprintf("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) {
+                                               ewprintf("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) {
+                               ewprintf("Out of memory");
+                               return (FALSE);
+                       }
+                       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) {
+                                       ewprintf("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) {
+               ewprintf("keymap too large");
+               return (NULL);
+       }
+       if ((mp = malloc(sizeof(KEYMAP) + (curmap->map_max + (MAPGROW - 1)) *
+           sizeof(struct map_element))) == NULL) {
+               ewprintf("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.
+                */
+               ewprintf("Can't rebind key in macro");
+               return (FALSE);
+       }
+       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) {
+                       ewprintf("[No match]");
+                       return (FALSE);
+               }
+       }
+       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.
+ */
+#ifdef BINDKEY
+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) {
+               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));
+}
+
+#ifdef FKEYS
+/*
+ * 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(*++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] = '\n';
+                               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));
+}
+#endif /* FKEYS */
+#endif /* BINDKEY */
+
+/*
+ * This function modifies the fundamental keyboard map.
+ */
+/* ARGSUSED */
+int
+bindtokey(int f, int n)
+{
+       return (dobind(fundamental_map, "Global set key: ", FALSE));
+}
+
+/*
+ * This function modifies the current mode's keyboard map.
+ */
+/* ARGSUSED */
+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.
+ */
+/* ARGSUSED */
+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(buf, tmp, sizeof(tmp), EFNEW)) == NULL)
+               return (ABORT);
+       else if (bufp[0] == '\0')
+               return (FALSE);
+       (void)strlcat(buf, tmp, sizeof(buf));
+       if ((mp = name_map(tmp)) == NULL) {
+               ewprintf("Unknown map %s", tmp);
+               return (FALSE);
+       }
+       if (strlcat(buf, "key: ", sizeof(buf)) >= sizeof(buf))
+               return (FALSE);
+
+       return (dobind(mp, buf, FALSE));
+}
+
+/* ARGSUSED */
+int
+unbindtokey(int f, int n)
+{
+       return (dobind(fundamental_map, "Global unset key: ", TRUE));
+}
+
+/* ARGSUSED */
+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));
+       }
+       ewprintf("[No match]");
+       return (FALSE);
+}
+
+/*
+ * 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.
+ */
+/* ARGSUSED */
+int
+evalexpr(int f, int n)
+{
+       char     exbuf[128], *bufp;
+
+       if ((bufp = eread("Eval: ", exbuf, sizeof(exbuf),
+           EFNEW | EFCR)) == NULL)
+               return (ABORT);
+       else if (bufp[0] == '\0')
+               return (FALSE);
+       return (excline(exbuf));
+}
+
+/*
+ * evalbuffer - evaluate the current buffer as line commands. Useful for
+ * testing startup files.
+ */
+/* ARGSUSED */
+int
+evalbuffer(int f, int n)
+{
+       struct line             *lp;
+       struct buffer           *bp = curbp;
+       int              s;
+       static char      excbuf[128];
+
+       for (lp = bfirstlp(bp); lp != bp->b_headp; lp = lforw(lp)) {
+               if (llength(lp) >= 128)
+                       return (FALSE);
+               (void)strncpy(excbuf, ltext(lp), llength(lp));
+
+               /* make sure it's terminated */
+               excbuf[llength(lp)] = '\0';
+               if ((s = excline(excbuf)) != TRUE)
+                       return (s);
+       }
+       return (TRUE);
+}
+
+/*
+ * evalfile - go get a file and evaluate it as line commands. You can
+ *     go get your own startup file if need be.
+ */
+/* ARGSUSED */
+int
+evalfile(int f, int n)
+{
+       char     fname[NFILEN], *bufp;
+
+       if ((bufp = eread("Load file: ", fname, NFILEN,
+           EFNEW | EFCR)) == NULL)
+               return (ABORT);
+       else if (bufp[0] == '\0')
+               return (FALSE);
+       return (load(fname));
+}
+
+/*
+ * load - go load the file name we got passed.
+ */
+int
+load(const char *fname)
+{
+       int      s = TRUE, line;
+       int      nbytes = 0;
+       char     excbuf[128];
+       FILE    *ffp;
+
+       if ((fname = adjustname(fname, TRUE)) == NULL)
+               /* just to be careful */
+               return (FALSE);
+
+       if (ffropen(&ffp, fname, NULL) != FIOSUC)
+               return (FALSE);
+
+       line = 0;
+       while ((s = ffgetline(ffp, excbuf, sizeof(excbuf) - 1, &nbytes))
+           == FIOSUC) {
+               line++;
+               excbuf[nbytes] = '\0';
+               if (excline(excbuf) != TRUE) {
+                       s = FIOERR;
+                       ewprintf("Error loading file %s at line %d", fname, line);
+                       break;
+               }
+       }
+       (void)ffclose(ffp, NULL);
+       excbuf[nbytes] = '\0';
+       if (s != FIOEOF || (nbytes && excline(excbuf) != TRUE))
+               return (FALSE);
+       return (TRUE);
+}
+
+/*
+ * excline - run a line from a load file or eval-expression.  If FKEYS is
+ * defined, duplicate functionality of dobind so function key values don't
+ * have to fit in type char.
+ */
+int
+excline(char *line)
+{
+       PF       fp;
+       struct line     *lp, *np;
+       int      status, c, f, n;
+       char    *funcp, *tmp;
+       char    *argp = NULL;
+       long     nl;
+#ifdef FKEYS
+       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 */
+#else /* FKEYS */
+#define BINDEXT                0
+#endif /* FKEYS */
+
+       lp = NULL;
+
+       if (macrodef || inmacro) {
+               ewprintf("Not now!");
+               return (FALSE);
+       }
+       f = 0;
+       n = 1;
+       funcp = skipwhite(line);
+       if (*funcp == '\0')
+               return (TRUE);  /* No error on blank lines */
+       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) {
+               ewprintf("Unknown function: %s", funcp);
+               return (FALSE);
+       }
+#ifdef FKEYS
+       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;
+#endif /* FKEYS */
+       /* 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));
+#ifdef FKEYS
+                       /* don't count BINDEXT */
+                       lp->l_used--;
+                       if (bind == BINDARG)
+                               bind = BINDNO;
+#endif /* FKEYS */
+               } else {
+                       /* quoted strings are special */
+                       ++argp;
+#ifdef FKEYS
+                       if (bind != BINDARG) {
+#endif /* FKEYS */
+                               lp = lalloc((int)(line - argp) + BINDEXT);
+                               if (lp == NULL) {
+                                       status = FALSE;
+                                       goto cleanup;
+                               }
+                               lp->l_used = 0;
+#ifdef FKEYS
+                       } else
+                               key.k_count = 0;
+#endif /* FKEYS */
+                       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;
+#ifdef FKEYS
+                                       case 'f':
+                                       case 'F':
+                                               c = *++argp - '0';
+                                               if (ISDIGIT(argp[1])) {
+                                                       c *= 10;
+                                                       c += *++argp - '0';
+                                               }
+                                               c += KFIRST;
+                                               break;
+#endif /* FKEYS */
+                                       default:
+                                               c = CHARMASK(*argp);
+                                               break;
+                                       }
+                                       argp++;
+                               }
+#ifdef FKEYS
+                               if (bind == BINDARG)
+                                       key.k_chars[key.k_count++] = c;
+                               else
+#endif /* FKEYS */
+                                       lp->l_text[lp->l_used++] = c;
+                       }
+                       if (*line)
+                               line++;
+               }
+#ifdef FKEYS
+               switch (bind) {
+               case BINDARG:
+                       bind = BINDDO;
+                       break;
+               case BINDNEXT:
+                       lp->l_text[lp->l_used] = '\0';
+                       if ((curmap = name_map(lp->l_text)) == NULL) {
+                               ewprintf("No such mode: %s", lp->l_text);
+                               status = FALSE;
+                               free(lp);
+                               goto cleanup;
+                       }
+                       free(lp);
+                       bind = BINDARG;
+                       break;
+               default:
+#endif /* FKEYS */
+                       lp->l_fp = np->l_fp;
+                       lp->l_bp = np;
+                       np->l_fp = lp;
+                       np = lp;
+#ifdef FKEYS
+               }
+#endif /* FKEYS */
+       }
+#ifdef FKEYS
+       switch (bind) {
+       default:
+               ewprintf("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:
+#endif /* FKEYS */
+               inmacro = TRUE;
+               maclcur = maclcur->l_fp;
+               status = (*fp)(f, n);
+               inmacro = FALSE;
+#ifdef FKEYS
+       }
+#endif /* FKEYS */
+cleanup:
+       lp = maclcur->l_fp;
+       while (lp != maclcur) {
+               np = lp->l_fp;
+               free(lp);
+               lp = np;
+       }
+       free(lp);
+       return (status);
+}
+
+/*
+ * a pair of utility functions for the above
+ */
+static char *
+skipwhite(char *s)
+{
+       while (*s == ' ' || *s == '\t' || *s == ')' || *s == '(')
+               s++;
+       if (*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);
+}