GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libedit/keymacro.c Lines: 57 217 26.3 %
Date: 2017-11-13 Branches: 29 163 17.8 %

Line Branch Exec Source
1
/*	$OpenBSD: keymacro.c,v 1.16 2016/05/25 09:23:49 schwarze Exp $	*/
2
/*	$NetBSD: keymacro.c,v 1.23 2016/05/24 15:00:45 christos Exp $	*/
3
4
/*-
5
 * Copyright (c) 1992, 1993
6
 *	The Regents of the University of California.  All rights reserved.
7
 *
8
 * This code is derived from software contributed to Berkeley by
9
 * Christos Zoulas of Cornell University.
10
 *
11
 * Redistribution and use in source and binary forms, with or without
12
 * modification, are permitted provided that the following conditions
13
 * are met:
14
 * 1. Redistributions of source code must retain the above copyright
15
 *    notice, this list of conditions and the following disclaimer.
16
 * 2. Redistributions in binary form must reproduce the above copyright
17
 *    notice, this list of conditions and the following disclaimer in the
18
 *    documentation and/or other materials provided with the distribution.
19
 * 3. Neither the name of the University nor the names of its contributors
20
 *    may be used to endorse or promote products derived from this software
21
 *    without specific prior written permission.
22
 *
23
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33
 * SUCH DAMAGE.
34
 */
35
36
#include "config.h"
37
38
/*
39
 * keymacro.c: This module contains the procedures for maintaining
40
 *	       the extended-key map.
41
 *
42
 *      An extended-key (key) is a sequence of keystrokes introduced
43
 *	with a sequence introducer and consisting of an arbitrary
44
 *	number of characters.  This module maintains a map (the
45
 *	el->el_keymacro.map)
46
 *	to convert these extended-key sequences into input strs
47
 *	(XK_STR) or editor functions (XK_CMD).
48
 *
49
 *      Warning:
50
 *	  If key is a substr of some other keys, then the longer
51
 *	  keys are lost!!  That is, if the keys "abcd" and "abcef"
52
 *	  are in el->el_keymacro.map, adding the key "abc" will cause
53
 *	  the first two definitions to be lost.
54
 *
55
 *      Restrictions:
56
 *      -------------
57
 *      1) It is not possible to have one key that is a
58
 *	   substr of another.
59
 */
60
#include <stdlib.h>
61
#include <string.h>
62
63
#include "el.h"
64
#include "fcns.h"
65
66
/*
67
 * The Nodes of the el->el_keymacro.map.  The el->el_keymacro.map is a
68
 * linked list of these node elements
69
 */
70
struct keymacro_node_t {
71
	wchar_t		 ch;		/* single character of key	 */
72
	int		 type;		/* node type			 */
73
	keymacro_value_t val;		/* command code or pointer to str,  */
74
					/* if this is a leaf		 */
75
	struct keymacro_node_t *next;	/* ptr to next char of this key  */
76
	struct keymacro_node_t *sibling;/* ptr to another key with same prefix*/
77
};
78
79
static int		 node_trav(EditLine *, keymacro_node_t *, wchar_t *,
80
    keymacro_value_t *);
81
static int		 node__try(EditLine *, keymacro_node_t *,
82
    const wchar_t *, keymacro_value_t *, int);
83
static keymacro_node_t	*node__get(wint_t);
84
static void		 node__free(keymacro_node_t *);
85
static void		 node__put(EditLine *, keymacro_node_t *);
86
static int		 node__delete(EditLine *, keymacro_node_t **,
87
    const wchar_t *);
88
static int		 node_lookup(EditLine *, const wchar_t *,
89
    keymacro_node_t *, size_t);
90
static int		 node_enum(EditLine *, keymacro_node_t *, size_t);
91
92
#define	KEY_BUFSIZ	EL_BUFSIZ
93
94
95
/* keymacro_init():
96
 *	Initialize the key maps
97
 */
98
protected int
99
keymacro_init(EditLine *el)
100
{
101
102
6
	el->el_keymacro.buf = reallocarray(NULL, KEY_BUFSIZ,
103
	    sizeof(*el->el_keymacro.buf));
104
3
	if (el->el_keymacro.buf == NULL)
105
		return -1;
106
3
	el->el_keymacro.map = NULL;
107
3
	keymacro_reset(el);
108
3
	return 0;
109
3
}
110
111
/* keymacro_end():
112
 *	Free the key maps
113
 */
114
protected void
115
keymacro_end(EditLine *el)
116
{
117
118
	free(el->el_keymacro.buf);
119
	el->el_keymacro.buf = NULL;
120
	node__free(el->el_keymacro.map);
121
}
122
123
124
/* keymacro_map_cmd():
125
 *	Associate cmd with a key value
126
 */
127
protected keymacro_value_t *
128
keymacro_map_cmd(EditLine *el, int cmd)
129
{
130
131
210
	el->el_keymacro.val.cmd = (el_action_t) cmd;
132
105
	return &el->el_keymacro.val;
133
}
134
135
136
/* keymacro_map_str():
137
 *	Associate str with a key value
138
 */
139
protected keymacro_value_t *
140
keymacro_map_str(EditLine *el, wchar_t *str)
141
{
142
143
	el->el_keymacro.val.str = str;
144
	return &el->el_keymacro.val;
145
}
146
147
148
/* keymacro_reset():
149
 *	Takes all nodes on el->el_keymacro.map and puts them on free list.
150
 *	Then initializes el->el_keymacro.map with arrow keys
151
 *	[Always bind the ansi arrow keys?]
152
 */
153
protected void
154
keymacro_reset(EditLine *el)
155
{
156
157
18
	node__put(el, el->el_keymacro.map);
158
9
	el->el_keymacro.map = NULL;
159
9
	return;
160
}
161
162
163
/* keymacro_get():
164
 *	Calls the recursive function with entry point el->el_keymacro.map
165
 *      Looks up *ch in map and then reads characters until a
166
 *      complete match is found or a mismatch occurs. Returns the
167
 *      type of the match found (XK_STR or XK_CMD).
168
 *      Returns NULL in val.str and XK_STR for no match.
169
 *      Returns XK_NOD for end of file or read error.
170
 *      The last character read is returned in *ch.
171
 */
172
protected int
173
keymacro_get(EditLine *el, wchar_t *ch, keymacro_value_t *val)
174
{
175
176
	return node_trav(el, el->el_keymacro.map, ch, val);
177
}
178
179
180
/* keymacro_add():
181
 *      Adds key to the el->el_keymacro.map and associates the value in
182
 *	val with it. If key is already is in el->el_keymacro.map, the new
183
 *	code is applied to the existing key. Ntype specifies if code is a
184
 *	command, an out str or a unix command.
185
 */
186
protected void
187
keymacro_add(EditLine *el, const wchar_t *key, keymacro_value_t *val,
188
    int ntype)
189
{
190
191
498
	if (key[0] == '\0') {
192
		(void) fprintf(el->el_errfile,
193
		    "keymacro_add: Null extended-key not allowed.\n");
194
		return;
195
	}
196

498
	if (ntype == XK_CMD && val->cmd == ED_SEQUENCE_LEAD_IN) {
197
		(void) fprintf(el->el_errfile,
198
		    "keymacro_add: sequence-lead-in command not allowed\n");
199
		return;
200
	}
201
249
	if (el->el_keymacro.map == NULL)
202
		/* tree is initially empty.  Set up new node to match key[0] */
203
6
		el->el_keymacro.map = node__get(key[0]);
204
			/* it is properly initialized */
205
206
	/* Now recurse through el->el_keymacro.map */
207
249
	(void) node__try(el, el->el_keymacro.map, key, val, ntype);
208
249
	return;
209
249
}
210
211
212
/* keymacro_clear():
213
 *
214
 */
215
protected void
216
keymacro_clear(EditLine *el, el_action_t *map, const wchar_t *in)
217
{
218
324
        if (*in > N_KEYS) /* can't be in the map */
219
                return;
220

162
	if ((map[(unsigned char)*in] == ED_SEQUENCE_LEAD_IN) &&
221
	    ((map == el->el_map.key &&
222
	    el->el_map.alt[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN) ||
223
	    (map == el->el_map.alt &&
224
	    el->el_map.key[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN)))
225
		(void) keymacro_delete(el, in);
226
162
}
227
228
229
/* keymacro_delete():
230
 *      Delete the key and all longer keys staring with key, if
231
 *      they exists.
232
 */
233
protected int
234
keymacro_delete(EditLine *el, const wchar_t *key)
235
{
236
237
	if (key[0] == '\0') {
238
		(void) fprintf(el->el_errfile,
239
		    "keymacro_delete: Null extended-key not allowed.\n");
240
		return -1;
241
	}
242
	if (el->el_keymacro.map == NULL)
243
		return 0;
244
245
	(void) node__delete(el, &el->el_keymacro.map, key);
246
	return 0;
247
}
248
249
250
/* keymacro_print():
251
 *	Print the binding associated with key key.
252
 *	Print entire el->el_keymacro.map if null
253
 */
254
protected void
255
keymacro_print(EditLine *el, const wchar_t *key)
256
{
257
258
	/* do nothing if el->el_keymacro.map is empty and null key specified */
259
	if (el->el_keymacro.map == NULL && *key == 0)
260
		return;
261
262
	el->el_keymacro.buf[0] = '"';
263
	if (node_lookup(el, key, el->el_keymacro.map, 1) <= -1)
264
		/* key is not bound */
265
		(void) fprintf(el->el_errfile, "Unbound extended key \"%ls"
266
		    "\"\n", key);
267
	return;
268
}
269
270
271
/* node_trav():
272
 *	recursively traverses node in tree until match or mismatch is
273
 *	found.  May read in more characters.
274
 */
275
static int
276
node_trav(EditLine *el, keymacro_node_t *ptr, wchar_t *ch,
277
    keymacro_value_t *val)
278
{
279
280
	if (ptr->ch == *ch) {
281
		/* match found */
282
		if (ptr->next) {
283
			/* key not complete so get next char */
284
			if (el_wgetc(el, ch) != 1)
285
				return XK_NOD;
286
			return node_trav(el, ptr->next, ch, val);
287
		} else {
288
			*val = ptr->val;
289
			if (ptr->type != XK_CMD)
290
				*ch = '\0';
291
			return ptr->type;
292
		}
293
	} else {
294
		/* no match found here */
295
		if (ptr->sibling) {
296
			/* try next sibling */
297
			return node_trav(el, ptr->sibling, ch, val);
298
		} else {
299
			/* no next sibling -- mismatch */
300
			val->str = NULL;
301
			return XK_STR;
302
		}
303
	}
304
}
305
306
307
/* node__try():
308
 *	Find a node that matches *str or allocate a new one
309
 */
310
static int
311
node__try(EditLine *el, keymacro_node_t *ptr, const wchar_t *str,
312
    keymacro_value_t *val, int ntype)
313
{
314
315
1236
	if (ptr->ch != *str) {
316
		keymacro_node_t *xm;
317
318
8040
		for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
319
3801
			if (xm->sibling->ch == *str)
320
				break;
321
342
		if (xm->sibling == NULL)
322
219
			xm->sibling = node__get(*str);	/* setup new node */
323
342
		ptr = xm->sibling;
324
342
	}
325
618
	if (*++str == '\0') {
326
		/* we're there */
327
249
		if (ptr->next != NULL) {
328
			node__put(el, ptr->next);
329
				/* lose longer keys with this prefix */
330
			ptr->next = NULL;
331
		}
332

249
		switch (ptr->type) {
333
		case XK_CMD:
334
		case XK_NOD:
335
			break;
336
		case XK_STR:
337
			if (ptr->val.str)
338
				free(ptr->val.str);
339
			break;
340
		default:
341
			EL_ABORT((el->el_errfile, "Bad XK_ type %d\n",
342
			    ptr->type));
343
			break;
344
		}
345
346
249
		switch (ptr->type = ntype) {
347
		case XK_CMD:
348
249
			ptr->val = *val;
349
249
			break;
350
		case XK_STR:
351
			if ((ptr->val.str = wcsdup(val->str)) == NULL)
352
				return -1;
353
			break;
354
		default:
355
			EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype));
356
			break;
357
		}
358
	} else {
359
		/* still more chars to go */
360
369
		if (ptr->next == NULL)
361
39
			ptr->next = node__get(*str);	/* setup new node */
362
369
		(void) node__try(el, ptr->next, str, val, ntype);
363
	}
364
618
	return 0;
365
618
}
366
367
368
/* node__delete():
369
 *	Delete node that matches str
370
 */
371
static int
372
node__delete(EditLine *el, keymacro_node_t **inptr, const wchar_t *str)
373
{
374
	keymacro_node_t *ptr;
375
	keymacro_node_t *prev_ptr = NULL;
376
377
	ptr = *inptr;
378
379
	if (ptr->ch != *str) {
380
		keymacro_node_t *xm;
381
382
		for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
383
			if (xm->sibling->ch == *str)
384
				break;
385
		if (xm->sibling == NULL)
386
			return 0;
387
		prev_ptr = xm;
388
		ptr = xm->sibling;
389
	}
390
	if (*++str == '\0') {
391
		/* we're there */
392
		if (prev_ptr == NULL)
393
			*inptr = ptr->sibling;
394
		else
395
			prev_ptr->sibling = ptr->sibling;
396
		ptr->sibling = NULL;
397
		node__put(el, ptr);
398
		return 1;
399
	} else if (ptr->next != NULL &&
400
	    node__delete(el, &ptr->next, str) == 1) {
401
		if (ptr->next != NULL)
402
			return 0;
403
		if (prev_ptr == NULL)
404
			*inptr = ptr->sibling;
405
		else
406
			prev_ptr->sibling = ptr->sibling;
407
		ptr->sibling = NULL;
408
		node__put(el, ptr);
409
		return 1;
410
	} else {
411
		return 0;
412
	}
413
}
414
415
416
/* node__put():
417
 *	Puts a tree of nodes onto free list using free(3).
418
 */
419
static void
420
node__put(EditLine *el, keymacro_node_t *ptr)
421
{
422
258
	if (ptr == NULL)
423
		return;
424
425
99
	if (ptr->next != NULL) {
426
21
		node__put(el, ptr->next);
427
21
		ptr->next = NULL;
428
21
	}
429
99
	node__put(el, ptr->sibling);
430
431

99
	switch (ptr->type) {
432
	case XK_CMD:
433
	case XK_NOD:
434
		break;
435
	case XK_STR:
436
		if (ptr->val.str != NULL)
437
			free(ptr->val.str);
438
		break;
439
	default:
440
		EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ptr->type));
441
		break;
442
	}
443
99
	free(ptr);
444
228
}
445
446
447
/* node__get():
448
 *	Returns pointer to a keymacro_node_t for ch.
449
 */
450
static keymacro_node_t *
451
node__get(wint_t ch)
452
{
453
	keymacro_node_t *ptr;
454
455
528
	ptr = malloc(sizeof(*ptr));
456
264
	if (ptr == NULL)
457
		return NULL;
458
264
	ptr->ch = ch;
459
264
	ptr->type = XK_NOD;
460
264
	ptr->val.str = NULL;
461
264
	ptr->next = NULL;
462
264
	ptr->sibling = NULL;
463
264
	return ptr;
464
264
}
465
466
static void
467
node__free(keymacro_node_t *k)
468
{
469
	if (k == NULL)
470
		return;
471
	node__free(k->sibling);
472
	node__free(k->next);
473
	free(k);
474
}
475
476
/* node_lookup():
477
 *	look for the str starting at node ptr.
478
 *	Print if last node
479
 */
480
static int
481
node_lookup(EditLine *el, const wchar_t *str, keymacro_node_t *ptr,
482
    size_t cnt)
483
{
484
	ssize_t used;
485
486
	if (ptr == NULL)
487
		return -1;	/* cannot have null ptr */
488
489
	if (!str || *str == 0) {
490
		/* no more chars in str.  node_enum from here. */
491
		(void) node_enum(el, ptr, cnt);
492
		return 0;
493
	} else {
494
		/* If match put this char into el->el_keymacro.buf.  Recurse */
495
		if (ptr->ch == *str) {
496
			/* match found */
497
			used = ct_visual_char(el->el_keymacro.buf + cnt,
498
			    KEY_BUFSIZ - cnt, ptr->ch);
499
			if (used == -1)
500
				return -1; /* ran out of buffer space */
501
			if (ptr->next != NULL)
502
				/* not yet at leaf */
503
				return (node_lookup(el, str + 1, ptr->next,
504
				    used + cnt));
505
			else {
506
			    /* next node is null so key should be complete */
507
				if (str[1] == 0) {
508
					el->el_keymacro.buf[cnt+used  ] = '"';
509
					el->el_keymacro.buf[cnt+used+1] = '\0';
510
					keymacro_kprint(el, el->el_keymacro.buf,
511
					    &ptr->val, ptr->type);
512
					return 0;
513
				} else
514
					return -1;
515
					/* mismatch -- str still has chars */
516
			}
517
		} else {
518
			/* no match found try sibling */
519
			if (ptr->sibling)
520
				return (node_lookup(el, str, ptr->sibling,
521
				    cnt));
522
			else
523
				return -1;
524
		}
525
	}
526
}
527
528
529
/* node_enum():
530
 *	Traverse the node printing the characters it is bound in buffer
531
 */
532
static int
533
node_enum(EditLine *el, keymacro_node_t *ptr, size_t cnt)
534
{
535
        ssize_t used;
536
537
	if (cnt >= KEY_BUFSIZ - 5) {	/* buffer too small */
538
		el->el_keymacro.buf[++cnt] = '"';
539
		el->el_keymacro.buf[++cnt] = '\0';
540
		(void) fprintf(el->el_errfile,
541
		    "Some extended keys too long for internal print buffer");
542
		(void) fprintf(el->el_errfile, " \"%ls...\"\n",
543
		    el->el_keymacro.buf);
544
		return 0;
545
	}
546
	if (ptr == NULL) {
547
#ifdef DEBUG_EDIT
548
		(void) fprintf(el->el_errfile,
549
		    "node_enum: BUG!! Null ptr passed\n!");
550
#endif
551
		return -1;
552
	}
553
	/* put this char at end of str */
554
        used = ct_visual_char(el->el_keymacro.buf + cnt, KEY_BUFSIZ - cnt,
555
	    ptr->ch);
556
	if (ptr->next == NULL) {
557
		/* print this key and function */
558
		el->el_keymacro.buf[cnt + used   ] = '"';
559
		el->el_keymacro.buf[cnt + used + 1] = '\0';
560
		keymacro_kprint(el, el->el_keymacro.buf, &ptr->val, ptr->type);
561
	} else
562
		(void) node_enum(el, ptr->next, cnt + used);
563
564
	/* go to sibling if there is one */
565
	if (ptr->sibling)
566
		(void) node_enum(el, ptr->sibling, cnt);
567
	return 0;
568
}
569
570
571
/* keymacro_kprint():
572
 *	Print the specified key and its associated
573
 *	function specified by val
574
 */
575
protected void
576
keymacro_kprint(EditLine *el, const wchar_t *key, keymacro_value_t *val,
577
    int ntype)
578
{
579
	el_bindings_t *fp;
580
	char unparsbuf[EL_BUFSIZ];
581
	static const char fmt[] = "%-15s->  %s\n";
582
583
	if (val != NULL)
584
		switch (ntype) {
585
		case XK_STR:
586
			(void) keymacro__decode_str(val->str, unparsbuf,
587
			    sizeof(unparsbuf),
588
			    ntype == XK_STR ? "\"\"" : "[]");
589
			(void) fprintf(el->el_outfile, fmt,
590
			    ct_encode_string(key, &el->el_scratch), unparsbuf);
591
			break;
592
		case XK_CMD:
593
			for (fp = el->el_map.help; fp->name; fp++)
594
				if (val->cmd == fp->func) {
595
                    wcstombs(unparsbuf, fp->name, sizeof(unparsbuf));
596
                    unparsbuf[sizeof(unparsbuf) -1] = '\0';
597
					(void) fprintf(el->el_outfile, fmt,
598
                        ct_encode_string(key, &el->el_scratch), unparsbuf);
599
					break;
600
				}
601
#ifdef DEBUG_KEY
602
			if (fp->name == NULL)
603
				(void) fprintf(el->el_outfile,
604
				    "BUG! Command not found.\n");
605
#endif
606
607
			break;
608
		default:
609
			EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype));
610
			break;
611
		}
612
	else
613
		(void) fprintf(el->el_outfile, fmt, ct_encode_string(key,
614
		    &el->el_scratch), "no input");
615
}
616
617
618
#define ADDC(c) \
619
	if (b < eb) \
620
		*b++ = c; \
621
	else \
622
		b++
623
/* keymacro__decode_str():
624
 *	Make a printable version of the ey
625
 */
626
protected size_t
627
keymacro__decode_str(const wchar_t *str, char *buf, size_t len,
628
    const char *sep)
629
{
630
	char *b = buf, *eb = b + len;
631
	const wchar_t *p;
632
633
	b = buf;
634
	if (sep[0] != '\0') {
635
		ADDC(sep[0]);
636
	}
637
	if (*str == '\0') {
638
		ADDC('^');
639
		ADDC('@');
640
		goto add_endsep;
641
	}
642
	for (p = str; *p != 0; p++) {
643
		wchar_t dbuf[VISUAL_WIDTH_MAX];
644
		wchar_t *p2 = dbuf;
645
		ssize_t l = ct_visual_char(dbuf, VISUAL_WIDTH_MAX, *p);
646
		while (l-- > 0) {
647
			ssize_t n = ct_encode_char(b, (size_t)(eb - b), *p2++);
648
			if (n == -1) /* ran out of space */
649
				goto add_endsep;
650
			else
651
				b += n;
652
		}
653
	}
654
add_endsep:
655
	if (sep[0] != '\0' && sep[1] != '\0') {
656
		ADDC(sep[1]);
657
	}
658
	ADDC('\0');
659
	if ((size_t)(b - buf) >= len)
660
	    buf[len - 1] = '\0';
661
	return (size_t)(b - buf);
662
}