GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mg/extend.c Lines: 0 471 0.0 %
Date: 2017-11-07 Branches: 0 357 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: extend.c,v 1.64 2016/09/01 21:06:09 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
9
#include <sys/queue.h>
10
#include <sys/types.h>
11
#include <ctype.h>
12
#include <limits.h>
13
#include <signal.h>
14
#include <stdio.h>
15
#include <stdlib.h>
16
#include <string.h>
17
18
#include "chrdef.h"
19
#include "def.h"
20
#include "funmap.h"
21
#include "kbd.h"
22
#include "key.h"
23
#include "macro.h"
24
25
static int	 remap(KEYMAP *, int, PF, KEYMAP *);
26
static KEYMAP	*reallocmap(KEYMAP *);
27
static void	 fixmap(KEYMAP *, KEYMAP *, KEYMAP *);
28
static int	 dobind(KEYMAP *, const char *, int);
29
static char	*skipwhite(char *);
30
static char	*parsetoken(char *);
31
static int	 bindkey(KEYMAP **, const char *, KCHAR *, int);
32
33
/*
34
 * Insert a string, mainly for use from macros (created by selfinsert).
35
 */
36
/* ARGSUSED */
37
int
38
insert(int f, int n)
39
{
40
	char	 buf[128], *bufp, *cp;
41
	int	 count, c;
42
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)
48
					return (FALSE);
49
			}
50
		}
51
		maclcur = maclcur->l_fp;
52
		return (TRUE);
53
	}
54
	if (n == 1)
55
		/* CFINS means selfinsert can tack on the end */
56
		thisflag |= CFINS;
57
58
	if ((bufp = eread("Insert: ", buf, sizeof(buf), EFNEW)) == NULL)
59
		return (ABORT);
60
	else if (bufp[0] == '\0')
61
		return (FALSE);
62
	while (--n >= 0) {
63
		cp = buf;
64
		while (*cp) {
65
			if (((*cp == '\n') ? lnewline() : linsert(1, *cp))
66
			    != TRUE)
67
				return (FALSE);
68
			cp++;
69
		}
70
	}
71
	return (TRUE);
72
}
73
74
/*
75
 * Bind a key to a function.  Cases range from the trivial (replacing an
76
 * existing binding) to the extremely complex (creating a new prefix in a
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
81
 * malloced versus static storage in both keymaps and map_elements.
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.
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
90
 */
91
static int
92
remap(KEYMAP *curmap, int c, PF funct, KEYMAP *pref_map)
93
{
94
	int		 i, n1, n2, nold;
95
	KEYMAP		*mp, *newmap;
96
	PF		*pfp;
97
	struct map_element	*mep;
98
99
	if (ele >= &curmap->map_element[curmap->map_num] || c < ele->k_base) {
100
		if (ele > &curmap->map_element[0] && (funct != NULL ||
101
		    (ele - 1)->k_prefmap == NULL))
102
			n1 = c - (ele - 1)->k_num;
103
		else
104
			n1 = HUGE;
105
		if (ele < &curmap->map_element[curmap->map_num] &&
106
		    (funct != NULL || ele->k_prefmap == NULL))
107
			n2 = ele->k_base - c;
108
		else
109
			n2 = HUGE;
110
		if (n1 <= MAPELEDEF && n1 <= n2) {
111
			ele--;
112
			if ((pfp = calloc(c - ele->k_base + 1,
113
			    sizeof(PF))) == NULL) {
114
				dobeep();
115
				ewprintf("Out of memory");
116
				return (FALSE);
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) {
127
			if ((pfp = calloc(ele->k_num - c + 1,
128
			    sizeof(PF))) == NULL) {
129
				dobeep();
130
				ewprintf("Out of memory");
131
				return (FALSE);
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;
141
		} else {
142
			if (curmap->map_num >= curmap->map_max) {
143
				if ((newmap = reallocmap(curmap)) == NULL)
144
					return (FALSE);
145
				curmap = newmap;
146
			}
147
			if ((pfp = malloc(sizeof(PF))) == NULL) {
148
				dobeep();
149
				ewprintf("Out of memory");
150
				return (FALSE);
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
		}
166
		if (funct == NULL) {
167
			if (pref_map != NULL)
168
				ele->k_prefmap = pref_map;
169
			else {
170
				if ((mp = malloc(sizeof(KEYMAP) +
171
				    (MAPINIT - 1) * sizeof(struct map_element))) == NULL) {
172
					dobeep();
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
						dobeep();
203
						ewprintf("Out of memory");
204
						ele->k_funcp[c - ele->k_base] =
205
						    curmap->map_default;
206
						return (FALSE);
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
			/*
216
			 * This case is the splits.
217
			 * Determine which side of the break c goes on
218
			 * 0 = after break; 1 = before break
219
			 */
220
			n2 = 1;
221
			for (i = 0; n2 && i < n1; i++)
222
				n2 &= ele->k_funcp[i] != NULL;
223
			if (curmap->map_num >= curmap->map_max) {
224
				if ((newmap = reallocmap(curmap)) == NULL)
225
					return (FALSE);
226
				curmap = newmap;
227
			}
228
			if ((pfp = calloc(ele->k_num - c + !n2,
229
			    sizeof(PF))) == NULL) {
230
				dobeep();
231
				ewprintf("Out of memory");
232
				return (FALSE);
233
			}
234
			ele->k_funcp[n1] = NULL;
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)
252
				    * sizeof(struct map_element))) == NULL) {
253
					dobeep();
254
					ewprintf("Out of memory");
255
					ele->k_funcp[c - ele->k_base] =
256
					    curmap->map_default;
257
					return (FALSE);
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
		}
266
	}
267
	return (TRUE);
268
}
269
270
/*
271
 * Reallocate a keymap. Returns NULL (without trashing the current map)
272
 * on failure.
273
 */
274
static KEYMAP *
275
reallocmap(KEYMAP *curmap)
276
{
277
	struct maps_s	*mps;
278
	KEYMAP	*mp;
279
	int	 i;
280
281
	if (curmap->map_max > SHRT_MAX - MAPGROW) {
282
		dobeep();
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) {
288
		dobeep();
289
		ewprintf("Out of memory");
290
		return (NULL);
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
	}
301
	for (mps = maps; mps != NULL; mps = mps->p_next) {
302
		if (mps->p_map == curmap)
303
			mps->p_map = mp;
304
		else
305
			fixmap(curmap, mp, mps->p_map);
306
	}
307
	ele = &mp->map_element[ele - &curmap->map_element[0]];
308
	return (mp);
309
}
310
311
/*
312
 * Fix references to a reallocated keymap (recursive).
313
 */
314
static void
315
fixmap(KEYMAP *curmap, KEYMAP *mp, KEYMAP *mt)
316
{
317
	int	 i;
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
		}
326
	}
327
}
328
329
/*
330
 * Do the input for local-set-key, global-set-key  and define-key
331
 * then call remap to do the work.
332
 */
333
static int
334
dobind(KEYMAP *curmap, const char *p, int unbind)
335
{
336
	KEYMAP	*pref_map = NULL;
337
	PF	 funct;
338
	char	 bprompt[80], *bufp, *pep;
339
	int	 c, s, n;
340
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
		 */
346
		dobeep();
347
		ewprintf("Can't rebind key in macro");
348
		return (FALSE);
349
	}
350
	if (inmacro) {
351
		for (s = 0; s < maclcur->l_used - 1; s++) {
352
			if (doscan(curmap, c = CHARMASK(maclcur->l_text[s]), &curmap)
353
			    != NULL) {
354
				if (remap(curmap, c, NULL, NULL)
355
				    != TRUE)
356
					return (FALSE);
357
			}
358
		}
359
		(void)doscan(curmap, c = maclcur->l_text[s], NULL);
360
		maclcur = maclcur->l_fp;
361
	} else {
362
		n = strlcpy(bprompt, p, sizeof(bprompt));
363
		if (n >= sizeof(bprompt))
364
			n = sizeof(bprompt) - 1;
365
		pep = bprompt + n;
366
		for (;;) {
367
			ewprintf("%s", bprompt);
368
			pep[-1] = ' ';
369
			pep = getkeyname(pep, sizeof(bprompt) -
370
			    (pep - bprompt), c = getkey(FALSE));
371
			if (doscan(curmap, c, &curmap) != NULL)
372
				break;
373
			*pep++ = '-';
374
			*pep = '\0';
375
		}
376
	}
377
	if (unbind)
378
		funct = rescan;
379
	else {
380
		if ((bufp = eread("%s to command: ", bprompt, sizeof(bprompt),
381
		    EFFUNC | EFNEW, bprompt)) == NULL)
382
			return (ABORT);
383
		else if (bufp[0] == '\0')
384
			return (FALSE);
385
		if (((funct = name_function(bprompt)) == NULL) ?
386
		    (pref_map = name_map(bprompt)) == NULL : funct == NULL) {
387
			dobeep();
388
			ewprintf("[No match]");
389
			return (FALSE);
390
		}
391
	}
392
	return (remap(curmap, c, funct, pref_map));
393
}
394
395
/*
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
399
 * KEYMAP **mapp.
400
 */
401
static int
402
bindkey(KEYMAP **mapp, const char *fname, KCHAR *keys, int kcount)
403
{
404
	KEYMAP	*curmap = *mapp;
405
	KEYMAP	*pref_map = NULL;
406
	PF	 funct;
407
	int	 c;
408
409
	if (fname == NULL)
410
		funct = rescan;
411
	else if (((funct = name_function(fname)) == NULL) ?
412
	    (pref_map = name_map(fname)) == NULL : funct == NULL) {
413
		dobeep();
414
		ewprintf("[No match: %s]", fname);
415
		return (FALSE);
416
	}
417
	while (--kcount) {
418
		if (doscan(curmap, c = *keys++, &curmap) != NULL) {
419
			if (remap(curmap, c, NULL, NULL) != TRUE)
420
				return (FALSE);
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;
426
		}
427
	}
428
	(void)doscan(curmap, c = *keys, NULL);
429
	return (remap(curmap, c, funct, pref_map));
430
}
431
432
/*
433
 * Wrapper for bindkey() that converts escapes.
434
 */
435
int
436
dobindkey(KEYMAP *map, const char *func, const char *str)
437
{
438
	int	 i;
439
440
	for (i = 0; *str && i < MAXKEY; i++) {
441
		/* XXX - convert numbers w/ strol()? */
442
		if (*str == '^' && *(str + 1) !=  '\0') {
443
			key.k_chars[i] = CCHR(toupper((unsigned char)*++str));
444
		} else if (*str == '\\' && *(str + 1) != '\0') {
445
			switch (*++str) {
446
			case '^':
447
				key.k_chars[i] = '^';
448
				break;
449
			case 't':
450
			case 'T':
451
				key.k_chars[i] = '\t';
452
				break;
453
			case 'n':
454
			case 'N':
455
				key.k_chars[i] = '\n';
456
				break;
457
			case 'r':
458
			case 'R':
459
				key.k_chars[i] = '\r';
460
				break;
461
			case 'e':
462
			case 'E':
463
				key.k_chars[i] = CCHR('[');
464
				break;
465
			case '\\':
466
				key.k_chars[i] = '\\';
467
				break;
468
			}
469
		} else
470
			key.k_chars[i] = *str;
471
		str++;
472
	}
473
	key.k_count = i;
474
	return (bindkey(&map, func, key.k_chars, key.k_count));
475
}
476
477
/*
478
 * This function modifies the fundamental keyboard map.
479
 */
480
/* ARGSUSED */
481
int
482
bindtokey(int f, int n)
483
{
484
	return (dobind(fundamental_map, "Global set key: ", FALSE));
485
}
486
487
/*
488
 * This function modifies the current mode's keyboard map.
489
 */
490
/* ARGSUSED */
491
int
492
localbind(int f, int n)
493
{
494
	return (dobind(curbp->b_modes[curbp->b_nmodes]->p_map,
495
	    "Local set key: ", FALSE));
496
}
497
498
/*
499
 * This function redefines a key in any keymap.
500
 */
501
/* ARGSUSED */
502
int
503
redefine_key(int f, int n)
504
{
505
	static char	 buf[48];
506
	char		 tmp[32], *bufp;
507
	KEYMAP		*mp;
508
509
	(void)strlcpy(buf, "Define key map: ", sizeof(buf));
510
	if ((bufp = eread("%s", tmp, sizeof(tmp), EFNEW, buf)) == NULL)
511
		return (ABORT);
512
	else if (bufp[0] == '\0')
513
		return (FALSE);
514
	(void)strlcat(buf, tmp, sizeof(buf));
515
	if ((mp = name_map(tmp)) == NULL) {
516
		dobeep();
517
		ewprintf("Unknown map %s", tmp);
518
		return (FALSE);
519
	}
520
	if (strlcat(buf, "key: ", sizeof(buf)) >= sizeof(buf))
521
		return (FALSE);
522
523
	return (dobind(mp, buf, FALSE));
524
}
525
526
/* ARGSUSED */
527
int
528
unbindtokey(int f, int n)
529
{
530
	return (dobind(fundamental_map, "Global unset key: ", TRUE));
531
}
532
533
/* ARGSUSED */
534
int
535
localunbind(int f, int n)
536
{
537
	return (dobind(curbp->b_modes[curbp->b_nmodes]->p_map,
538
	    "Local unset key: ", TRUE));
539
}
540
541
/*
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
545
 * error if there is anything wrong.
546
 */
547
int
548
extend(int f, int n)
549
{
550
	PF	 funct;
551
	char	 xname[NXNAME], *bufp;
552
553
	if (!(f & FFARG))
554
		bufp = eread("M-x ", xname, NXNAME, EFNEW | EFFUNC);
555
	else
556
		bufp = eread("%d M-x ", xname, NXNAME, EFNEW | EFFUNC, n);
557
	if (bufp == NULL)
558
		return (ABORT);
559
	else if (bufp[0] == '\0')
560
		return (FALSE);
561
	if ((funct = name_function(bufp)) != NULL) {
562
		if (macrodef) {
563
			struct line	*lp = maclcur;
564
			macro[macrocount - 1].m_funct = funct;
565
			maclcur = lp->l_bp;
566
			maclcur->l_fp = lp->l_fp;
567
			free(lp);
568
		}
569
		return ((*funct)(f, n));
570
	}
571
	dobeep();
572
	ewprintf("[No match]");
573
	return (FALSE);
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
 */
594
/* ARGSUSED */
595
int
596
evalexpr(int f, int n)
597
{
598
	char	 exbuf[128], *bufp;
599
600
	if ((bufp = eread("Eval: ", exbuf, sizeof(exbuf),
601
	    EFNEW | EFCR)) == NULL)
602
		return (ABORT);
603
	else if (bufp[0] == '\0')
604
		return (FALSE);
605
	return (excline(exbuf));
606
}
607
608
/*
609
 * evalbuffer - evaluate the current buffer as line commands. Useful for
610
 * testing startup files.
611
 */
612
/* ARGSUSED */
613
int
614
evalbuffer(int f, int n)
615
{
616
	struct line		*lp;
617
	struct buffer		*bp = curbp;
618
	int		 s;
619
	static char	 excbuf[128];
620
621
	for (lp = bfirstlp(bp); lp != bp->b_headp; lp = lforw(lp)) {
622
		if (llength(lp) >= 128)
623
			return (FALSE);
624
		(void)strncpy(excbuf, ltext(lp), llength(lp));
625
626
		/* make sure it's terminated */
627
		excbuf[llength(lp)] = '\0';
628
		if ((s = excline(excbuf)) != TRUE)
629
			return (s);
630
	}
631
	return (TRUE);
632
}
633
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
 */
638
/* ARGSUSED */
639
int
640
evalfile(int f, int n)
641
{
642
	char	 fname[NFILEN], *bufp;
643
644
	if ((bufp = eread("Load file: ", fname, NFILEN,
645
	    EFNEW | EFCR)) == NULL)
646
		return (ABORT);
647
	else if (bufp[0] == '\0')
648
		return (FALSE);
649
	return (load(fname));
650
}
651
652
/*
653
 * load - go load the file name we got passed.
654
 */
655
int
656
load(const char *fname)
657
{
658
	int	 s = TRUE, line, ret;
659
	int	 nbytes = 0;
660
	char	 excbuf[128];
661
	FILE    *ffp;
662
663
	if ((fname = adjustname(fname, TRUE)) == NULL)
664
		/* just to be careful */
665
		return (FALSE);
666
667
	ret = ffropen(&ffp, fname, NULL);
668
	if (ret != FIOSUC) {
669
		if (ret == FIODIR)
670
			(void)ffclose(ffp, NULL);
671
		return (FALSE);
672
	}
673
674
	line = 0;
675
	while ((s = ffgetline(ffp, excbuf, sizeof(excbuf) - 1, &nbytes))
676
	    == FIOSUC) {
677
		line++;
678
		excbuf[nbytes] = '\0';
679
		if (excline(excbuf) != TRUE) {
680
			s = FIOERR;
681
			dobeep();
682
			ewprintf("Error loading file %s at line %d", fname, line);
683
			break;
684
		}
685
	}
686
	(void)ffclose(ffp, NULL);
687
	excbuf[nbytes] = '\0';
688
	if (s != FIOEOF || (nbytes && excline(excbuf) != TRUE))
689
		return (FALSE);
690
	return (TRUE);
691
}
692
693
/*
694
 * excline - run a line from a load file or eval-expression.
695
 */
696
int
697
excline(char *line)
698
{
699
	PF	 fp;
700
	struct line	*lp, *np;
701
	int	 status, c, f, n;
702
	char	*funcp, *tmp;
703
	char	*argp = NULL;
704
	long	 nl;
705
	int	 bind;
706
	KEYMAP	*curmap;
707
#define BINDARG		0  /* this arg is key to bind (local/global set key) */
708
#define	BINDNO		1  /* not binding or non-quoted BINDARG */
709
#define BINDNEXT	2  /* next arg " (define-key) */
710
#define BINDDO		3  /* already found key to bind */
711
#define BINDEXT		1  /* space for trailing \0 */
712
713
	lp = NULL;
714
715
	if (macrodef || inmacro) {
716
		dobeep();
717
		ewprintf("Not now!");
718
		return (FALSE);
719
	}
720
	f = 0;
721
	n = 1;
722
	funcp = skipwhite(line);
723
	if (*funcp == '\0')
724
		return (TRUE);	/* No error on blank lines */
725
	line = parsetoken(funcp);
726
	if (*line != '\0') {
727
		*line++ = '\0';
728
		line = skipwhite(line);
729
		if (ISDIGIT(*line) || *line == '-') {
730
			argp = line;
731
			line = parsetoken(line);
732
		}
733
	}
734
	if (argp != NULL) {
735
		f = FFARG;
736
		nl = strtol(argp, &tmp, 10);
737
		if (*tmp != '\0')
738
			return (FALSE);
739
		if (nl >= INT_MAX || nl <= INT_MIN)
740
			return (FALSE);
741
		n = (int)nl;
742
	}
743
	if ((fp = name_function(funcp)) == NULL) {
744
		dobeep();
745
		ewprintf("Unknown function: %s", funcp);
746
		return (FALSE);
747
	}
748
	if (fp == bindtokey || fp == unbindtokey) {
749
		bind = BINDARG;
750
		curmap = fundamental_map;
751
	} else if (fp == localbind || fp == localunbind) {
752
		bind = BINDARG;
753
		curmap = curbp->b_modes[curbp->b_nmodes]->p_map;
754
	} else if (fp == redefine_key)
755
		bind = BINDNEXT;
756
	else
757
		bind = BINDNO;
758
	/* Pack away all the args now... */
759
	if ((np = lalloc(0)) == FALSE)
760
		return (FALSE);
761
	np->l_fp = np->l_bp = maclcur = np;
762
	while (*line != '\0') {
763
		argp = skipwhite(line);
764
		if (*argp == '\0')
765
			break;
766
		line = parsetoken(argp);
767
		if (*argp != '"') {
768
			if (*argp == '\'')
769
				++argp;
770
			if ((lp = lalloc((int) (line - argp) + BINDEXT)) ==
771
			    NULL) {
772
				status = FALSE;
773
				goto cleanup;
774
			}
775
			bcopy(argp, ltext(lp), (int)(line - argp));
776
			/* don't count BINDEXT */
777
			lp->l_used--;
778
			if (bind == BINDARG)
779
				bind = BINDNO;
780
		} else {
781
			/* quoted strings are special */
782
			++argp;
783
			if (bind != BINDARG) {
784
				lp = lalloc((int)(line - argp) + BINDEXT);
785
				if (lp == NULL) {
786
					status = FALSE;
787
					goto cleanup;
788
				}
789
				lp->l_used = 0;
790
			} else
791
				key.k_count = 0;
792
			while (*argp != '"' && *argp != '\0') {
793
				if (*argp != '\\')
794
					c = *argp++;
795
				else {
796
					switch (*++argp) {
797
					case 't':
798
					case 'T':
799
						c = CCHR('I');
800
						break;
801
					case 'n':
802
					case 'N':
803
						c = CCHR('J');
804
						break;
805
					case 'r':
806
					case 'R':
807
						c = CCHR('M');
808
						break;
809
					case 'e':
810
					case 'E':
811
						c = CCHR('[');
812
						break;
813
					case '^':
814
						/*
815
						 * split into two statements
816
						 * due to bug in OSK cpp
817
						 */
818
						c = CHARMASK(*++argp);
819
						c = ISLOWER(c) ?
820
						    CCHR(TOUPPER(c)) : CCHR(c);
821
						break;
822
					case '0':
823
					case '1':
824
					case '2':
825
					case '3':
826
					case '4':
827
					case '5':
828
					case '6':
829
					case '7':
830
						c = *argp - '0';
831
						if (argp[1] <= '7' &&
832
						    argp[1] >= '0') {
833
							c <<= 3;
834
							c += *++argp - '0';
835
							if (argp[1] <= '7' &&
836
							    argp[1] >= '0') {
837
								c <<= 3;
838
								c += *++argp
839
								    - '0';
840
							}
841
						}
842
						break;
843
					case 'f':
844
					case 'F':
845
						c = *++argp - '0';
846
						if (ISDIGIT(argp[1])) {
847
							c *= 10;
848
							c += *++argp - '0';
849
						}
850
						c += KFIRST;
851
						break;
852
					default:
853
						c = CHARMASK(*argp);
854
						break;
855
					}
856
					argp++;
857
				}
858
				if (bind == BINDARG)
859
					key.k_chars[key.k_count++] = c;
860
				else
861
					lp->l_text[lp->l_used++] = c;
862
			}
863
			if (*line)
864
				line++;
865
		}
866
		switch (bind) {
867
		case BINDARG:
868
			bind = BINDDO;
869
			break;
870
		case BINDNEXT:
871
			lp->l_text[lp->l_used] = '\0';
872
			if ((curmap = name_map(lp->l_text)) == NULL) {
873
				dobeep();
874
				ewprintf("No such mode: %s", lp->l_text);
875
				status = FALSE;
876
				free(lp);
877
				goto cleanup;
878
			}
879
			free(lp);
880
			bind = BINDARG;
881
			break;
882
		default:
883
			lp->l_fp = np->l_fp;
884
			lp->l_bp = np;
885
			np->l_fp = lp;
886
			np = lp;
887
		}
888
	}
889
	switch (bind) {
890
	default:
891
		dobeep();
892
		ewprintf("Bad args to set key");
893
		status = FALSE;
894
		break;
895
	case BINDDO:
896
		if (fp != unbindtokey && fp != localunbind) {
897
			lp->l_text[lp->l_used] = '\0';
898
			status = bindkey(&curmap, lp->l_text, key.k_chars,
899
			    key.k_count);
900
		} else
901
			status = bindkey(&curmap, NULL, key.k_chars,
902
			    key.k_count);
903
		break;
904
	case BINDNO:
905
		inmacro = TRUE;
906
		maclcur = maclcur->l_fp;
907
		status = (*fp)(f, n);
908
		inmacro = FALSE;
909
	}
910
cleanup:
911
	lp = maclcur->l_fp;
912
	while (lp != maclcur) {
913
		np = lp->l_fp;
914
		free(lp);
915
		lp = np;
916
	}
917
	free(lp);
918
	maclhead = NULL;
919
	macrodef = FALSE;
920
	return (status);
921
}
922
923
/*
924
 * a pair of utility functions for the above
925
 */
926
static char *
927
skipwhite(char *s)
928
{
929
	while (*s == ' ' || *s == '\t' || *s == ')' || *s == '(')
930
		s++;
931
	if ((*s == ';') || (*s == '#'))
932
		*s = '\0';
933
	return (s);
934
}
935
936
static char *
937
parsetoken(char *s)
938
{
939
	if (*s != '"') {
940
		while (*s && *s != ' ' && *s != '\t' && *s != ')' && *s != '(')
941
			s++;
942
		if (*s == ';')
943
			*s = '\0';
944
	} else
945
		do {
946
			/*
947
			 * Strings get special treatment.
948
			 * Beware: You can \ out the end of the string!
949
			 */
950
			if (*s == '\\')
951
				++s;
952
		} while (*++s != '"' && *s != '\0');
953
	return (s);
954
}