--- /dev/null
+/* $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);
+}