Annotation of src/usr.bin/mg/extend.c, Revision 1.76
1.76 ! jmc 1: /* $OpenBSD: extend.c,v 1.75 2021/05/06 14:16:12 lum Exp $ */
1.34 kjell 2: /* This file is in the public domain. */
1.6 niklas 3:
1.1 deraadt 4: /*
1.65 lum 5: * Extended (M-x) commands, rebinding, and startup file processing.
1.1 deraadt 6: */
1.51 kjell 7:
1.60 bcallah 8: #include <sys/queue.h>
1.51 kjell 9: #include <sys/types.h>
1.67 lum 10: #include <regex.h>
1.51 kjell 11: #include <ctype.h>
1.57 guenther 12: #include <limits.h>
1.60 bcallah 13: #include <signal.h>
14: #include <stdio.h>
15: #include <stdlib.h>
16: #include <string.h>
1.39 kjell 17:
1.60 bcallah 18: #include "chrdef.h"
19: #include "def.h"
20: #include "funmap.h"
21: #include "kbd.h"
22: #include "key.h"
1.5 millert 23: #include "macro.h"
1.1 deraadt 24:
1.21 millert 25: static int remap(KEYMAP *, int, PF, KEYMAP *);
1.29 vincent 26: static KEYMAP *reallocmap(KEYMAP *);
1.21 millert 27: static void fixmap(KEYMAP *, KEYMAP *, KEYMAP *);
1.23 vincent 28: static int dobind(KEYMAP *, const char *, int);
1.21 millert 29: static char *parsetoken(char *);
1.23 vincent 30: static int bindkey(KEYMAP **, const char *, KCHAR *, int);
1.1 deraadt 31:
1.15 mickey 32: /*
1.33 db 33: * Insert a string, mainly for use from macros (created by selfinsert).
1.5 millert 34: */
1.4 millert 35: /* ARGSUSED */
36: int
1.23 vincent 37: insert(int f, int n)
1.1 deraadt 38: {
1.69 lum 39: char buf[BUFSIZE], *bufp, *cp;
1.5 millert 40: int count, c;
1.1 deraadt 41:
1.4 millert 42: if (inmacro) {
43: while (--n >= 0) {
44: for (count = 0; count < maclcur->l_used; count++) {
1.72 lum 45: if ((((c = maclcur->l_text[count]) ==
46: *curbp->b_nlchr)
1.4 millert 47: ? lnewline() : linsert(1, c)) != TRUE)
1.33 db 48: return (FALSE);
1.4 millert 49: }
50: }
51: maclcur = maclcur->l_fp;
1.33 db 52: return (TRUE);
1.1 deraadt 53: }
1.4 millert 54: if (n == 1)
1.5 millert 55: /* CFINS means selfinsert can tack on the end */
56: thisflag |= CFINS;
1.52 lum 57:
1.31 vincent 58: if ((bufp = eread("Insert: ", buf, sizeof(buf), EFNEW)) == NULL)
1.33 db 59: return (ABORT);
1.31 vincent 60: else if (bufp[0] == '\0')
1.33 db 61: return (FALSE);
1.4 millert 62: while (--n >= 0) {
63: cp = buf;
64: while (*cp) {
1.72 lum 65: if (((*cp == *curbp->b_nlchr) ?
66: lnewline() : linsert(1, *cp))
1.4 millert 67: != TRUE)
1.33 db 68: return (FALSE);
1.4 millert 69: cp++;
70: }
1.1 deraadt 71: }
1.33 db 72: return (TRUE);
1.1 deraadt 73: }
74:
75: /*
76: * Bind a key to a function. Cases range from the trivial (replacing an
1.33 db 77: * existing binding) to the extremely complex (creating a new prefix in a
1.1 deraadt 78: * map_element that already has one, so the map_element must be split,
79: * but the keymap doesn't have enough room for another map_element, so
80: * the keymap is reallocated). No attempt is made to reclaim space no
81: * longer used, if this is a problem flags must be added to indicate
1.50 martin 82: * malloced versus static storage in both keymaps and map_elements.
1.1 deraadt 83: * Structure assignments would come in real handy, but K&R based compilers
84: * don't have them. Care is taken so running out of memory will leave
85: * the keymap in a usable state.
1.49 kjell 86: * Parameters are:
87: * curmap: pointer to the map being changed
88: * c: character being changed
89: * funct: function being changed to
90: * pref_map: if funct==NULL, map to bind to or NULL for new
1.1 deraadt 91: */
1.4 millert 92: static int
1.49 kjell 93: remap(KEYMAP *curmap, int c, PF funct, KEYMAP *pref_map)
1.1 deraadt 94: {
1.5 millert 95: int i, n1, n2, nold;
1.30 vincent 96: KEYMAP *mp, *newmap;
1.5 millert 97: PF *pfp;
1.40 deraadt 98: struct map_element *mep;
1.4 millert 99:
100: if (ele >= &curmap->map_element[curmap->map_num] || c < ele->k_base) {
1.7 art 101: if (ele > &curmap->map_element[0] && (funct != NULL ||
1.5 millert 102: (ele - 1)->k_prefmap == NULL))
1.4 millert 103: n1 = c - (ele - 1)->k_num;
1.5 millert 104: else
1.4 millert 105: n1 = HUGE;
106: if (ele < &curmap->map_element[curmap->map_num] &&
1.7 art 107: (funct != NULL || ele->k_prefmap == NULL))
1.4 millert 108: n2 = ele->k_base - c;
1.5 millert 109: else
1.4 millert 110: n2 = HUGE;
111: if (n1 <= MAPELEDEF && n1 <= n2) {
112: ele--;
1.45 kjell 113: if ((pfp = calloc(c - ele->k_base + 1,
1.68 lum 114: sizeof(PF))) == NULL)
115: return (dobeep_msg("Out of memory"));
116:
1.4 millert 117: nold = ele->k_num - ele->k_base + 1;
118: for (i = 0; i < nold; i++)
119: pfp[i] = ele->k_funcp[i];
120: while (--n1)
121: pfp[i++] = curmap->map_default;
122: pfp[i] = funct;
123: ele->k_num = c;
124: ele->k_funcp = pfp;
125: } else if (n2 <= MAPELEDEF) {
1.45 kjell 126: if ((pfp = calloc(ele->k_num - c + 1,
1.68 lum 127: sizeof(PF))) == NULL)
128: return (dobeep_msg("Out of memory"));
129:
1.4 millert 130: nold = ele->k_num - ele->k_base + 1;
131: for (i = 0; i < nold; i++)
132: pfp[i + n2] = ele->k_funcp[i];
133: while (--n2)
134: pfp[n2] = curmap->map_default;
135: pfp[0] = funct;
136: ele->k_base = c;
137: ele->k_funcp = pfp;
1.1 deraadt 138: } else {
1.30 vincent 139: if (curmap->map_num >= curmap->map_max) {
140: if ((newmap = reallocmap(curmap)) == NULL)
1.33 db 141: return (FALSE);
1.30 vincent 142: curmap = newmap;
143: }
1.68 lum 144: if ((pfp = malloc(sizeof(PF))) == NULL)
145: return (dobeep_msg("Out of memory"));
146:
1.4 millert 147: pfp[0] = funct;
148: for (mep = &curmap->map_element[curmap->map_num];
149: mep > ele; mep--) {
150: mep->k_base = (mep - 1)->k_base;
151: mep->k_num = (mep - 1)->k_num;
152: mep->k_funcp = (mep - 1)->k_funcp;
153: mep->k_prefmap = (mep - 1)->k_prefmap;
154: }
155: ele->k_base = c;
156: ele->k_num = c;
157: ele->k_funcp = pfp;
158: ele->k_prefmap = NULL;
159: curmap->map_num++;
160: }
1.7 art 161: if (funct == NULL) {
1.29 vincent 162: if (pref_map != NULL)
1.4 millert 163: ele->k_prefmap = pref_map;
1.29 vincent 164: else {
1.45 kjell 165: if ((mp = malloc(sizeof(KEYMAP) +
1.40 deraadt 166: (MAPINIT - 1) * sizeof(struct map_element))) == NULL) {
1.68 lum 167: (void)dobeep_msg("Out of memory");
1.4 millert 168: ele->k_funcp[c - ele->k_base] =
169: curmap->map_default;
1.33 db 170: return (FALSE);
1.4 millert 171: }
172: mp->map_num = 0;
173: mp->map_max = MAPINIT;
174: mp->map_default = rescan;
175: ele->k_prefmap = mp;
176: }
1.1 deraadt 177: }
178: } else {
1.4 millert 179: n1 = c - ele->k_base;
1.7 art 180: if (ele->k_funcp[n1] == funct && (funct != NULL ||
1.4 millert 181: pref_map == NULL || pref_map == ele->k_prefmap))
1.5 millert 182: /* no change */
1.33 db 183: return (TRUE);
1.7 art 184: if (funct != NULL || ele->k_prefmap == NULL) {
185: if (ele->k_funcp[n1] == NULL)
1.13 art 186: ele->k_prefmap = NULL;
1.5 millert 187: /* easy case */
188: ele->k_funcp[n1] = funct;
1.7 art 189: if (funct == NULL) {
1.4 millert 190: if (pref_map != NULL)
191: ele->k_prefmap = pref_map;
192: else {
1.28 vincent 193: if ((mp = malloc(sizeof(KEYMAP) +
1.15 mickey 194: (MAPINIT - 1) *
1.40 deraadt 195: sizeof(struct map_element))) == NULL) {
1.68 lum 196: (void)dobeep_msg("Out of memory");
1.4 millert 197: ele->k_funcp[c - ele->k_base] =
198: curmap->map_default;
1.33 db 199: return (FALSE);
1.4 millert 200: }
201: mp->map_num = 0;
202: mp->map_max = MAPINIT;
203: mp->map_default = rescan;
204: ele->k_prefmap = mp;
205: }
206: }
207: } else {
208: /*
1.5 millert 209: * This case is the splits.
210: * Determine which side of the break c goes on
1.4 millert 211: * 0 = after break; 1 = before break
212: */
213: n2 = 1;
214: for (i = 0; n2 && i < n1; i++)
1.7 art 215: n2 &= ele->k_funcp[i] != NULL;
1.49 kjell 216: if (curmap->map_num >= curmap->map_max) {
217: if ((newmap = reallocmap(curmap)) == NULL)
218: return (FALSE);
219: curmap = newmap;
220: }
1.45 kjell 221: if ((pfp = calloc(ele->k_num - c + !n2,
1.68 lum 222: sizeof(PF))) == NULL)
223: return (dobeep_msg("Out of memory"));
224:
1.7 art 225: ele->k_funcp[n1] = NULL;
1.4 millert 226: for (i = n1 + n2; i <= ele->k_num - ele->k_base; i++)
227: pfp[i - n1 - n2] = ele->k_funcp[i];
228: for (mep = &curmap->map_element[curmap->map_num];
229: mep > ele; mep--) {
230: mep->k_base = (mep - 1)->k_base;
231: mep->k_num = (mep - 1)->k_num;
232: mep->k_funcp = (mep - 1)->k_funcp;
233: mep->k_prefmap = (mep - 1)->k_prefmap;
234: }
235: ele->k_num = c - !n2;
236: (ele + 1)->k_base = c + n2;
237: (ele + 1)->k_funcp = pfp;
238: ele += !n2;
239: ele->k_prefmap = NULL;
240: curmap->map_num++;
241: if (pref_map == NULL) {
242: if ((mp = malloc(sizeof(KEYMAP) + (MAPINIT - 1)
1.40 deraadt 243: * sizeof(struct map_element))) == NULL) {
1.68 lum 244: (void)dobeep_msg("Out of memory");
1.4 millert 245: ele->k_funcp[c - ele->k_base] =
246: curmap->map_default;
1.33 db 247: return (FALSE);
1.4 millert 248: }
249: mp->map_num = 0;
250: mp->map_max = MAPINIT;
251: mp->map_default = rescan;
252: ele->k_prefmap = mp;
253: } else
254: ele->k_prefmap = pref_map;
255: }
1.1 deraadt 256: }
1.33 db 257: return (TRUE);
1.1 deraadt 258: }
259:
1.4 millert 260: /*
1.49 kjell 261: * Reallocate a keymap. Returns NULL (without trashing the current map)
262: * on failure.
1.4 millert 263: */
264: static KEYMAP *
1.29 vincent 265: reallocmap(KEYMAP *curmap)
1.4 millert 266: {
1.40 deraadt 267: struct maps_s *mps;
1.5 millert 268: KEYMAP *mp;
269: int i;
1.4 millert 270:
1.45 kjell 271: if (curmap->map_max > SHRT_MAX - MAPGROW) {
1.68 lum 272: (void)dobeep_msg("keymap too large");
1.45 kjell 273: return (NULL);
274: }
275: if ((mp = malloc(sizeof(KEYMAP) + (curmap->map_max + (MAPGROW - 1)) *
276: sizeof(struct map_element))) == NULL) {
1.68 lum 277: (void)dobeep_msg("Out of memory");
1.33 db 278: return (NULL);
1.4 millert 279: }
280: mp->map_num = curmap->map_num;
281: mp->map_max = curmap->map_max + MAPGROW;
282: mp->map_default = curmap->map_default;
283: for (i = curmap->map_num; i--;) {
284: mp->map_element[i].k_base = curmap->map_element[i].k_base;
285: mp->map_element[i].k_num = curmap->map_element[i].k_num;
286: mp->map_element[i].k_funcp = curmap->map_element[i].k_funcp;
287: mp->map_element[i].k_prefmap = curmap->map_element[i].k_prefmap;
288: }
1.17 art 289: for (mps = maps; mps != NULL; mps = mps->p_next) {
290: if (mps->p_map == curmap)
291: mps->p_map = mp;
1.4 millert 292: else
1.17 art 293: fixmap(curmap, mp, mps->p_map);
1.4 millert 294: }
295: ele = &mp->map_element[ele - &curmap->map_element[0]];
1.33 db 296: return (mp);
1.4 millert 297: }
298:
299: /*
300: * Fix references to a reallocated keymap (recursive).
301: */
1.12 art 302: static void
1.23 vincent 303: fixmap(KEYMAP *curmap, KEYMAP *mp, KEYMAP *mt)
1.4 millert 304: {
1.5 millert 305: int i;
1.4 millert 306:
307: for (i = mt->map_num; i--;) {
308: if (mt->map_element[i].k_prefmap != NULL) {
309: if (mt->map_element[i].k_prefmap == curmap)
310: mt->map_element[i].k_prefmap = mp;
311: else
312: fixmap(curmap, mp, mt->map_element[i].k_prefmap);
313: }
1.1 deraadt 314: }
315: }
316:
317: /*
1.33 db 318: * Do the input for local-set-key, global-set-key and define-key
1.1 deraadt 319: * then call remap to do the work.
320: */
1.4 millert 321: static int
1.23 vincent 322: dobind(KEYMAP *curmap, const char *p, int unbind)
1.4 millert 323: {
1.5 millert 324: KEYMAP *pref_map = NULL;
325: PF funct;
1.41 kjell 326: char bprompt[80], *bufp, *pep;
1.26 vincent 327: int c, s, n;
1.1 deraadt 328:
1.4 millert 329: if (macrodef) {
330: /*
331: * Keystrokes aren't collected. Not hard, but pretty useless.
332: * Would not work for function keys in any case.
333: */
1.68 lum 334: return (dobeep_msg("Can't rebind key in macro"));
1.1 deraadt 335: }
1.4 millert 336: if (inmacro) {
337: for (s = 0; s < maclcur->l_used - 1; s++) {
1.8 art 338: if (doscan(curmap, c = CHARMASK(maclcur->l_text[s]), &curmap)
1.7 art 339: != NULL) {
1.13 art 340: if (remap(curmap, c, NULL, NULL)
1.4 millert 341: != TRUE)
1.33 db 342: return (FALSE);
1.4 millert 343: }
1.1 deraadt 344: }
1.12 art 345: (void)doscan(curmap, c = maclcur->l_text[s], NULL);
1.4 millert 346: maclcur = maclcur->l_fp;
1.1 deraadt 347: } else {
1.41 kjell 348: n = strlcpy(bprompt, p, sizeof(bprompt));
349: if (n >= sizeof(bprompt))
350: n = sizeof(bprompt) - 1;
351: pep = bprompt + n;
1.4 millert 352: for (;;) {
1.41 kjell 353: ewprintf("%s", bprompt);
1.4 millert 354: pep[-1] = ' ';
1.42 kjell 355: pep = getkeyname(pep, sizeof(bprompt) -
1.41 kjell 356: (pep - bprompt), c = getkey(FALSE));
1.8 art 357: if (doscan(curmap, c, &curmap) != NULL)
1.4 millert 358: break;
359: *pep++ = '-';
360: *pep = '\0';
361: }
1.1 deraadt 362: }
1.4 millert 363: if (unbind)
364: funct = rescan;
1.1 deraadt 365: else {
1.41 kjell 366: if ((bufp = eread("%s to command: ", bprompt, sizeof(bprompt),
367: EFFUNC | EFNEW, bprompt)) == NULL)
1.33 db 368: return (ABORT);
1.31 vincent 369: else if (bufp[0] == '\0')
1.33 db 370: return (FALSE);
1.41 kjell 371: if (((funct = name_function(bprompt)) == NULL) ?
1.68 lum 372: (pref_map = name_map(bprompt)) == NULL : funct == NULL)
373: return (dobeep_msg("[No match]"));
374:
1.1 deraadt 375: }
1.33 db 376: return (remap(curmap, c, funct, pref_map));
1.1 deraadt 377: }
378:
379: /*
1.15 mickey 380: * bindkey: bind key sequence to a function in the specified map. Used by
381: * excline so it can bind function keys. To close to release to change
382: * calling sequence, should just pass KEYMAP *curmap rather than
1.5 millert 383: * KEYMAP **mapp.
384: */
385: static int
1.23 vincent 386: bindkey(KEYMAP **mapp, const char *fname, KCHAR *keys, int kcount)
1.5 millert 387: {
388: KEYMAP *curmap = *mapp;
389: KEYMAP *pref_map = NULL;
390: PF funct;
391: int c;
1.1 deraadt 392:
1.4 millert 393: if (fname == NULL)
394: funct = rescan;
1.7 art 395: else if (((funct = name_function(fname)) == NULL) ?
1.5 millert 396: (pref_map = name_map(fname)) == NULL : funct == NULL) {
1.54 lum 397: dobeep();
1.4 millert 398: ewprintf("[No match: %s]", fname);
1.33 db 399: return (FALSE);
1.4 millert 400: }
401: while (--kcount) {
1.8 art 402: if (doscan(curmap, c = *keys++, &curmap) != NULL) {
1.13 art 403: if (remap(curmap, c, NULL, NULL) != TRUE)
1.33 db 404: return (FALSE);
1.9 art 405: /*
406: * XXX - Bizzarreness. remap creates an empty KEYMAP
407: * that the last key is supposed to point to.
408: */
409: curmap = ele->k_prefmap;
1.4 millert 410: }
1.1 deraadt 411: }
1.12 art 412: (void)doscan(curmap, c = *keys, NULL);
1.33 db 413: return (remap(curmap, c, funct, pref_map));
1.1 deraadt 414: }
1.3 millert 415:
416: /*
417: * Wrapper for bindkey() that converts escapes.
418: */
419: int
1.23 vincent 420: dobindkey(KEYMAP *map, const char *func, const char *str)
1.3 millert 421: {
1.5 millert 422: int i;
1.3 millert 423:
424: for (i = 0; *str && i < MAXKEY; i++) {
425: /* XXX - convert numbers w/ strol()? */
1.38 kjell 426: if (*str == '^' && *(str + 1) != '\0') {
1.56 guenther 427: key.k_chars[i] = CCHR(toupper((unsigned char)*++str));
1.38 kjell 428: } else if (*str == '\\' && *(str + 1) != '\0') {
1.4 millert 429: switch (*++str) {
1.38 kjell 430: case '^':
431: key.k_chars[i] = '^';
432: break;
1.4 millert 433: case 't':
434: case 'T':
1.3 millert 435: key.k_chars[i] = '\t';
436: break;
1.4 millert 437: case 'n':
438: case 'N':
1.72 lum 439: key.k_chars[i] = *curbp->b_nlchr;
1.3 millert 440: break;
1.4 millert 441: case 'r':
442: case 'R':
1.3 millert 443: key.k_chars[i] = '\r';
444: break;
1.4 millert 445: case 'e':
446: case 'E':
1.3 millert 447: key.k_chars[i] = CCHR('[');
448: break;
1.38 kjell 449: case '\\':
450: key.k_chars[i] = '\\';
451: break;
1.3 millert 452: }
1.38 kjell 453: } else
454: key.k_chars[i] = *str;
1.3 millert 455: str++;
456: }
457: key.k_count = i;
1.4 millert 458: return (bindkey(&map, func, key.k_chars, key.k_count));
1.3 millert 459: }
1.1 deraadt 460:
461: /*
462: * This function modifies the fundamental keyboard map.
463: */
1.4 millert 464: /* ARGSUSED */
465: int
1.23 vincent 466: bindtokey(int f, int n)
1.1 deraadt 467: {
1.33 db 468: return (dobind(fundamental_map, "Global set key: ", FALSE));
1.1 deraadt 469: }
470:
471: /*
472: * This function modifies the current mode's keyboard map.
473: */
1.4 millert 474: /* ARGSUSED */
475: int
1.23 vincent 476: localbind(int f, int n)
1.1 deraadt 477: {
1.33 db 478: return (dobind(curbp->b_modes[curbp->b_nmodes]->p_map,
479: "Local set key: ", FALSE));
1.1 deraadt 480: }
481:
482: /*
483: * This function redefines a key in any keymap.
484: */
1.4 millert 485: /* ARGSUSED */
486: int
1.42 kjell 487: redefine_key(int f, int n)
1.1 deraadt 488: {
1.33 db 489: static char buf[48];
490: char tmp[32], *bufp;
491: KEYMAP *mp;
492:
1.43 kjell 493: (void)strlcpy(buf, "Define key map: ", sizeof(buf));
1.63 guenther 494: if ((bufp = eread("%s", tmp, sizeof(tmp), EFNEW, buf)) == NULL)
1.33 db 495: return (ABORT);
1.31 vincent 496: else if (bufp[0] == '\0')
1.33 db 497: return (FALSE);
1.35 kjell 498: (void)strlcat(buf, tmp, sizeof(buf));
1.68 lum 499: if ((mp = name_map(tmp)) == NULL)
500: return (dobeep_msgs("Unknown map ", tmp));
501:
1.43 kjell 502: if (strlcat(buf, "key: ", sizeof(buf)) >= sizeof(buf))
503: return (FALSE);
1.19 vincent 504:
1.33 db 505: return (dobind(mp, buf, FALSE));
1.1 deraadt 506: }
507:
1.37 kjell 508: /* ARGSUSED */
1.4 millert 509: int
1.23 vincent 510: unbindtokey(int f, int n)
1.1 deraadt 511: {
1.33 db 512: return (dobind(fundamental_map, "Global unset key: ", TRUE));
1.1 deraadt 513: }
514:
1.37 kjell 515: /* ARGSUSED */
1.4 millert 516: int
1.27 vincent 517: localunbind(int f, int n)
1.1 deraadt 518: {
1.33 db 519: return (dobind(curbp->b_modes[curbp->b_nmodes]->p_map,
520: "Local unset key: ", TRUE));
1.1 deraadt 521: }
522:
523: /*
1.15 mickey 524: * Extended command. Call the message line routine to read in the command
525: * name and apply autocompletion to it. When it comes back, look the name
526: * up in the symbol table and run the command if it is found. Print an
1.5 millert 527: * error if there is anything wrong.
1.1 deraadt 528: */
1.4 millert 529: int
1.23 vincent 530: extend(int f, int n)
1.1 deraadt 531: {
1.5 millert 532: PF funct;
1.31 vincent 533: char xname[NXNAME], *bufp;
1.4 millert 534:
535: if (!(f & FFARG))
1.31 vincent 536: bufp = eread("M-x ", xname, NXNAME, EFNEW | EFFUNC);
1.4 millert 537: else
1.31 vincent 538: bufp = eread("%d M-x ", xname, NXNAME, EFNEW | EFFUNC, n);
539: if (bufp == NULL)
1.33 db 540: return (ABORT);
1.31 vincent 541: else if (bufp[0] == '\0')
1.33 db 542: return (FALSE);
1.31 vincent 543: if ((funct = name_function(bufp)) != NULL) {
1.4 millert 544: if (macrodef) {
1.40 deraadt 545: struct line *lp = maclcur;
1.4 millert 546: macro[macrocount - 1].m_funct = funct;
547: maclcur = lp->l_bp;
548: maclcur->l_fp = lp->l_fp;
1.44 kjell 549: free(lp);
1.4 millert 550: }
1.33 db 551: return ((*funct)(f, n));
1.1 deraadt 552: }
1.68 lum 553: return (dobeep_msg("[No match]"));
1.1 deraadt 554: }
555:
556: /*
557: * Define the commands needed to do startup-file processing.
558: * This code is mostly a kludge just so we can get startup-file processing.
559: *
560: * If you're serious about having this code, you should rewrite it.
561: * To wit:
562: * It has lots of funny things in it to make the startup-file look
563: * like a GNU startup file; mostly dealing with parens and semicolons.
564: * This should all vanish.
565: *
566: * We define eval-expression because it's easy. It can make
567: * *-set-key or define-key set an arbitrary key sequence, so it isn't
568: * useless.
569: */
570:
571: /*
572: * evalexpr - get one line from the user, and run it.
1.74 lum 573: * Use strlen for length of line, assume user is not typing in a '\0' in the
574: * modeline. llen only used for foundparen() so old-school will be ok.
1.1 deraadt 575: */
1.4 millert 576: /* ARGSUSED */
577: int
1.23 vincent 578: evalexpr(int f, int n)
1.1 deraadt 579: {
1.69 lum 580: char exbuf[BUFSIZE], *bufp;
1.74 lum 581: int llen;
1.1 deraadt 582:
1.35 kjell 583: if ((bufp = eread("Eval: ", exbuf, sizeof(exbuf),
584: EFNEW | EFCR)) == NULL)
1.33 db 585: return (ABORT);
1.31 vincent 586: else if (bufp[0] == '\0')
1.33 db 587: return (FALSE);
1.74 lum 588: llen = strlen(bufp);
589:
1.75 lum 590: return (excline(exbuf, llen, 1));
1.1 deraadt 591: }
1.4 millert 592:
1.1 deraadt 593: /*
1.15 mickey 594: * evalbuffer - evaluate the current buffer as line commands. Useful for
1.5 millert 595: * testing startup files.
1.1 deraadt 596: */
1.4 millert 597: /* ARGSUSED */
598: int
1.23 vincent 599: evalbuffer(int f, int n)
1.1 deraadt 600: {
1.40 deraadt 601: struct line *lp;
602: struct buffer *bp = curbp;
1.75 lum 603: int s, llen, lnum = 0;
1.69 lum 604: static char excbuf[BUFSIZE];
1.1 deraadt 605:
1.48 kjell 606: for (lp = bfirstlp(bp); lp != bp->b_headp; lp = lforw(lp)) {
1.75 lum 607: lnum++;
1.74 lum 608: llen = llength(lp);
609: if (llen >= BUFSIZE)
1.33 db 610: return (FALSE);
1.74 lum 611: (void)strncpy(excbuf, ltext(lp), llen);
1.5 millert 612:
1.74 lum 613: /* make sure the line is terminated */
614: excbuf[llen] = '\0';
1.75 lum 615: if ((s = excline(excbuf, llen, lnum)) != TRUE) {
1.73 lum 616: cleanup();
1.33 db 617: return (s);
1.71 lum 618: }
1.1 deraadt 619: }
1.73 lum 620: cleanup();
1.33 db 621: return (TRUE);
1.1 deraadt 622: }
1.4 millert 623:
1.1 deraadt 624: /*
625: * evalfile - go get a file and evaluate it as line commands. You can
626: * go get your own startup file if need be.
627: */
1.4 millert 628: /* ARGSUSED */
629: int
1.23 vincent 630: evalfile(int f, int n)
1.1 deraadt 631: {
1.31 vincent 632: char fname[NFILEN], *bufp;
1.1 deraadt 633:
1.35 kjell 634: if ((bufp = eread("Load file: ", fname, NFILEN,
635: EFNEW | EFCR)) == NULL)
1.33 db 636: return (ABORT);
1.31 vincent 637: else if (bufp[0] == '\0')
1.33 db 638: return (FALSE);
639: return (load(fname));
1.1 deraadt 640: }
641:
642: /*
643: * load - go load the file name we got passed.
644: */
1.4 millert 645: int
1.23 vincent 646: load(const char *fname)
1.4 millert 647: {
1.64 lum 648: int s = TRUE, line, ret;
1.18 matthieu 649: int nbytes = 0;
1.69 lum 650: char excbuf[BUFSIZE], fncpy[NFILEN];
1.53 lum 651: FILE *ffp;
1.1 deraadt 652:
1.46 jason 653: if ((fname = adjustname(fname, TRUE)) == NULL)
1.5 millert 654: /* just to be careful */
1.33 db 655: return (FALSE);
1.1 deraadt 656:
1.64 lum 657: ret = ffropen(&ffp, fname, NULL);
658: if (ret != FIOSUC) {
659: if (ret == FIODIR)
660: (void)ffclose(ffp, NULL);
1.33 db 661: return (FALSE);
1.64 lum 662: }
1.5 millert 663:
1.76 ! jmc 664: /* keep a note of fname in case of errors in loaded file. */
1.66 lum 665: (void)strlcpy(fncpy, fname, sizeof(fncpy));
1.25 vincent 666: line = 0;
1.53 lum 667: while ((s = ffgetline(ffp, excbuf, sizeof(excbuf) - 1, &nbytes))
668: == FIOSUC) {
1.25 vincent 669: line++;
1.1 deraadt 670: excbuf[nbytes] = '\0';
1.75 lum 671: if (excline(excbuf, nbytes, line) != TRUE) {
1.1 deraadt 672: s = FIOERR;
1.54 lum 673: dobeep();
1.66 lum 674: ewprintf("Error loading file %s at line %d", fncpy, line);
1.1 deraadt 675: break;
676: }
677: }
1.53 lum 678: (void)ffclose(ffp, NULL);
1.1 deraadt 679: excbuf[nbytes] = '\0';
1.75 lum 680: if (s != FIOEOF || (nbytes && excline(excbuf, nbytes, ++line) != TRUE))
1.33 db 681: return (FALSE);
682: return (TRUE);
1.1 deraadt 683: }
684:
685: /*
1.59 bcallah 686: * excline - run a line from a load file or eval-expression.
1.1 deraadt 687: */
1.4 millert 688: int
1.75 lum 689: excline(char *line, int llen, int lnum)
1.1 deraadt 690: {
1.5 millert 691: PF fp;
1.40 deraadt 692: struct line *lp, *np;
1.5 millert 693: int status, c, f, n;
1.22 ho 694: char *funcp, *tmp;
1.5 millert 695: char *argp = NULL;
1.22 ho 696: long nl;
1.5 millert 697: int bind;
698: KEYMAP *curmap;
699: #define BINDARG 0 /* this arg is key to bind (local/global set key) */
700: #define BINDNO 1 /* not binding or non-quoted BINDARG */
701: #define BINDNEXT 2 /* next arg " (define-key) */
702: #define BINDDO 3 /* already found key to bind */
703: #define BINDEXT 1 /* space for trailing \0 */
704:
705: lp = NULL;
1.1 deraadt 706:
1.68 lum 707: if (macrodef || inmacro)
708: return (dobeep_msg("Not now!"));
709:
1.1 deraadt 710: f = 0;
711: n = 1;
712: funcp = skipwhite(line);
1.4 millert 713: if (*funcp == '\0')
1.33 db 714: return (TRUE); /* No error on blank lines */
1.67 lum 715: if (*funcp == '(')
1.75 lum 716: return (foundparen(funcp, llen, lnum));
1.1 deraadt 717: line = parsetoken(funcp);
718: if (*line != '\0') {
719: *line++ = '\0';
720: line = skipwhite(line);
1.20 vincent 721: if (ISDIGIT(*line) || *line == '-') {
1.1 deraadt 722: argp = line;
723: line = parsetoken(line);
724: }
725: }
726: if (argp != NULL) {
727: f = FFARG;
1.22 ho 728: nl = strtol(argp, &tmp, 10);
1.20 vincent 729: if (*tmp != '\0')
1.33 db 730: return (FALSE);
1.22 ho 731: if (nl >= INT_MAX || nl <= INT_MIN)
1.33 db 732: return (FALSE);
1.22 ho 733: n = (int)nl;
1.1 deraadt 734: }
1.68 lum 735: if ((fp = name_function(funcp)) == NULL)
736: return (dobeep_msgs("Unknown function: ", funcp));
737:
1.4 millert 738: if (fp == bindtokey || fp == unbindtokey) {
1.1 deraadt 739: bind = BINDARG;
1.11 art 740: curmap = fundamental_map;
1.4 millert 741: } else if (fp == localbind || fp == localunbind) {
1.1 deraadt 742: bind = BINDARG;
743: curmap = curbp->b_modes[curbp->b_nmodes]->p_map;
1.42 kjell 744: } else if (fp == redefine_key)
1.4 millert 745: bind = BINDNEXT;
746: else
747: bind = BINDNO;
1.5 millert 748: /* Pack away all the args now... */
1.4 millert 749: if ((np = lalloc(0)) == FALSE)
1.33 db 750: return (FALSE);
1.1 deraadt 751: np->l_fp = np->l_bp = maclcur = np;
752: while (*line != '\0') {
753: argp = skipwhite(line);
1.4 millert 754: if (*argp == '\0')
755: break;
1.1 deraadt 756: line = parsetoken(argp);
757: if (*argp != '"') {
1.4 millert 758: if (*argp == '\'')
759: ++argp;
1.20 vincent 760: if ((lp = lalloc((int) (line - argp) + BINDEXT)) ==
761: NULL) {
1.4 millert 762: status = FALSE;
763: goto cleanup;
764: }
1.5 millert 765: bcopy(argp, ltext(lp), (int)(line - argp));
766: /* don't count BINDEXT */
767: lp->l_used--;
1.4 millert 768: if (bind == BINDARG)
769: bind = BINDNO;
1.5 millert 770: } else {
771: /* quoted strings are special */
1.4 millert 772: ++argp;
773: if (bind != BINDARG) {
1.5 millert 774: lp = lalloc((int)(line - argp) + BINDEXT);
1.4 millert 775: if (lp == NULL) {
776: status = FALSE;
777: goto cleanup;
778: }
779: lp->l_used = 0;
1.33 db 780: } else
1.4 millert 781: key.k_count = 0;
782: while (*argp != '"' && *argp != '\0') {
783: if (*argp != '\\')
784: c = *argp++;
785: else {
786: switch (*++argp) {
787: case 't':
788: case 'T':
789: c = CCHR('I');
790: break;
791: case 'n':
792: case 'N':
793: c = CCHR('J');
794: break;
795: case 'r':
796: case 'R':
797: c = CCHR('M');
798: break;
799: case 'e':
800: case 'E':
801: c = CCHR('[');
802: break;
803: case '^':
804: /*
805: * split into two statements
806: * due to bug in OSK cpp
807: */
808: c = CHARMASK(*++argp);
809: c = ISLOWER(c) ?
810: CCHR(TOUPPER(c)) : CCHR(c);
811: break;
812: case '0':
813: case '1':
814: case '2':
815: case '3':
816: case '4':
817: case '5':
818: case '6':
819: case '7':
820: c = *argp - '0';
1.15 mickey 821: if (argp[1] <= '7' &&
1.5 millert 822: argp[1] >= '0') {
1.4 millert 823: c <<= 3;
824: c += *++argp - '0';
825: if (argp[1] <= '7' &&
826: argp[1] >= '0') {
827: c <<= 3;
828: c += *++argp
829: - '0';
830: }
831: }
832: break;
833: case 'f':
834: case 'F':
835: c = *++argp - '0';
836: if (ISDIGIT(argp[1])) {
837: c *= 10;
838: c += *++argp - '0';
839: }
840: c += KFIRST;
841: break;
842: default:
843: c = CHARMASK(*argp);
844: break;
845: }
846: argp++;
847: }
848: if (bind == BINDARG)
849: key.k_chars[key.k_count++] = c;
850: else
851: lp->l_text[lp->l_used++] = c;
852: }
853: if (*line)
854: line++;
1.1 deraadt 855: }
1.4 millert 856: switch (bind) {
857: case BINDARG:
1.1 deraadt 858: bind = BINDDO;
859: break;
1.4 millert 860: case BINDNEXT:
1.1 deraadt 861: lp->l_text[lp->l_used] = '\0';
1.10 art 862: if ((curmap = name_map(lp->l_text)) == NULL) {
1.68 lum 863: (void)dobeep_msgs("No such mode: ", lp->l_text);
1.4 millert 864: status = FALSE;
1.44 kjell 865: free(lp);
1.4 millert 866: goto cleanup;
1.1 deraadt 867: }
1.44 kjell 868: free(lp);
1.1 deraadt 869: bind = BINDARG;
870: break;
1.4 millert 871: default:
1.1 deraadt 872: lp->l_fp = np->l_fp;
873: lp->l_bp = np;
874: np->l_fp = lp;
875: np = lp;
876: }
877: }
1.4 millert 878: switch (bind) {
879: default:
1.68 lum 880: (void)dobeep_msg("Bad args to set key");
1.1 deraadt 881: status = FALSE;
882: break;
1.4 millert 883: case BINDDO:
884: if (fp != unbindtokey && fp != localunbind) {
885: lp->l_text[lp->l_used] = '\0';
886: status = bindkey(&curmap, lp->l_text, key.k_chars,
887: key.k_count);
1.33 db 888: } else
1.13 art 889: status = bindkey(&curmap, NULL, key.k_chars,
1.4 millert 890: key.k_count);
1.1 deraadt 891: break;
1.4 millert 892: case BINDNO:
1.1 deraadt 893: inmacro = TRUE;
894: maclcur = maclcur->l_fp;
1.5 millert 895: status = (*fp)(f, n);
1.1 deraadt 896: inmacro = FALSE;
897: }
898: cleanup:
899: lp = maclcur->l_fp;
1.4 millert 900: while (lp != maclcur) {
901: np = lp->l_fp;
1.44 kjell 902: free(lp);
1.4 millert 903: lp = np;
1.1 deraadt 904: }
1.44 kjell 905: free(lp);
1.62 florian 906: maclhead = NULL;
907: macrodef = FALSE;
1.33 db 908: return (status);
1.1 deraadt 909: }
910:
911: /*
912: * a pair of utility functions for the above
913: */
1.70 lum 914: char *
1.23 vincent 915: skipwhite(char *s)
1.1 deraadt 916: {
1.67 lum 917: while (*s == ' ' || *s == '\t')
1.4 millert 918: s++;
1.55 lum 919: if ((*s == ';') || (*s == '#'))
1.4 millert 920: *s = '\0';
1.33 db 921: return (s);
1.1 deraadt 922: }
923:
924: static char *
1.23 vincent 925: parsetoken(char *s)
1.1 deraadt 926: {
927: if (*s != '"') {
1.4 millert 928: while (*s && *s != ' ' && *s != '\t' && *s != ')' && *s != '(')
929: s++;
930: if (*s == ';')
931: *s = '\0';
1.1 deraadt 932: } else
1.5 millert 933: do {
1.15 mickey 934: /*
935: * Strings get special treatment.
936: * Beware: You can \ out the end of the string!
1.5 millert 937: */
1.4 millert 938: if (*s == '\\')
939: ++s;
940: } while (*++s != '"' && *s != '\0');
1.33 db 941: return (s);
1.1 deraadt 942: }