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