Changeset View
Changeset View
Standalone View
Standalone View
contrib/mg/extend.c
- This file was added.
/* $OpenBSD: extend.c,v 1.75 2021/05/06 14:16:12 lum 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). | |||||
*/ | |||||
/* ARGSUSED */ | |||||
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. | |||||
*/ | |||||
/* 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("%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)); | |||||
} | |||||
/* 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)); | |||||
} | |||||
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. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
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. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
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. | |||||
*/ | |||||
/* 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, ret; | |||||
int nbytes = 0; | |||||
char excbuf[BUFSIZE], fncpy[NFILEN]; | |||||
FILE *ffp; | |||||
if ((fname = adjustname(fname, TRUE)) == NULL) | |||||
/* just to be careful */ | |||||
return (FALSE); | |||||
ret = ffropen(&ffp, fname, NULL); | |||||
if (ret != FIOSUC) { | |||||
if (ret == FIODIR) | |||||
(void)ffclose(ffp, NULL); | |||||
return (FALSE); | |||||
} | |||||
/* keep a note of fname incase of errors in loaded file. */ | |||||
(void)strlcpy(fncpy, fname, sizeof(fncpy)); | |||||
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", fncpy, line); | |||||
break; | |||||
} | |||||
} | |||||
(void)ffclose(ffp, NULL); | |||||
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 = NULL; | |||||
#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); | |||||
} |