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