1/*	$NetBSD: parse.c,v 1.42 2019/07/23 10:18:52 christos Exp $	*/
2
3/*-
4 * Copyright (c) 1992, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Christos Zoulas of Cornell University.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include "config.h"
36#if !defined(lint) && !defined(SCCSID)
37#if 0
38static char sccsid[] = "@(#)parse.c	8.1 (Berkeley) 6/4/93";
39#else
40__RCSID("$NetBSD: parse.c,v 1.42 2019/07/23 10:18:52 christos Exp $");
41#endif
42#endif /* not lint && not SCCSID */
43
44/*
45 * parse.c: parse an editline extended command
46 *
47 * commands are:
48 *
49 *	bind
50 *	echotc
51 *	edit
52 *	gettc
53 *	history
54 *	settc
55 *	setty
56 */
57#include <stdlib.h>
58#include <string.h>
59
60#include "el.h"
61#include "parse.h"
62
63static const struct {
64	const wchar_t *name;
65	int (*func)(EditLine *, int, const wchar_t **);
66} cmds[] = {
67	{ L"bind",		map_bind	},
68	{ L"echotc",		terminal_echotc	},
69	{ L"edit",		el_editmode	},
70	{ L"history",		hist_command	},
71	{ L"telltc",		terminal_telltc	},
72	{ L"settc",		terminal_settc	},
73	{ L"setty",		tty_stty	},
74	{ NULL,			NULL		}
75};
76
77
78/* parse_line():
79 *	Parse a line and dispatch it
80 */
81libedit_private int
82parse_line(EditLine *el, const wchar_t *line)
83{
84	const wchar_t **argv;
85	int argc;
86	TokenizerW *tok;
87
88	tok = tok_winit(NULL);
89	tok_wstr(tok, line, &argc, &argv);
90	argc = el_wparse(el, argc, argv);
91	tok_wend(tok);
92	return argc;
93}
94
95
96/* el_parse():
97 *	Command dispatcher
98 */
99int
100el_wparse(EditLine *el, int argc, const wchar_t *argv[])
101{
102	const wchar_t *ptr;
103	int i;
104
105	if (argc < 1)
106		return -1;
107	ptr = wcschr(argv[0], L':');
108	if (ptr != NULL) {
109		wchar_t *tprog;
110		size_t l;
111
112		if (ptr == argv[0])
113			return 0;
114		l = (size_t)(ptr - argv[0]);
115		tprog = el_calloc(l + 1, sizeof(*tprog));
116		if (tprog == NULL)
117			return 0;
118		(void) wcsncpy(tprog, argv[0], l);
119		tprog[l] = '\0';
120		ptr++;
121		l = (size_t)el_match(el->el_prog, tprog);
122		el_free(tprog);
123		if (!l)
124			return 0;
125	} else
126		ptr = argv[0];
127
128	for (i = 0; cmds[i].name != NULL; i++)
129		if (wcscmp(cmds[i].name, ptr) == 0) {
130			i = (*cmds[i].func) (el, argc, argv);
131			return -i;
132		}
133	return -1;
134}
135
136
137/* parse__escape():
138 *	Parse a string of the form ^<char> \<odigit> \<char> \U+xxxx and return
139 *	the appropriate character or -1 if the escape is not valid
140 */
141libedit_private int
142parse__escape(const wchar_t **ptr)
143{
144	const wchar_t *p;
145	wint_t c;
146
147	p = *ptr;
148
149	if (p[1] == 0)
150		return -1;
151
152	if (*p == '\\') {
153		p++;
154		switch (*p) {
155		case 'a':
156			c = '\007';	/* Bell */
157			break;
158		case 'b':
159			c = '\010';	/* Backspace */
160			break;
161		case 't':
162			c = '\011';	/* Horizontal Tab */
163			break;
164		case 'n':
165			c = '\012';	/* New Line */
166			break;
167		case 'v':
168			c = '\013';	/* Vertical Tab */
169			break;
170		case 'f':
171			c = '\014';	/* Form Feed */
172			break;
173		case 'r':
174			c = '\015';	/* Carriage Return */
175			break;
176		case 'e':
177			c = '\033';	/* Escape */
178			break;
179		case 'U':		/* Unicode \U+xxxx or \U+xxxxx format */
180		{
181			int i;
182			const wchar_t hex[] = L"0123456789ABCDEF";
183			const wchar_t *h;
184			++p;
185			if (*p++ != '+')
186				return -1;
187			c = 0;
188			for (i = 0; i < 5; ++i) {
189				h = wcschr(hex, *p++);
190				if (!h && i < 4)
191					return -1;
192				else if (h)
193					c = (c << 4) | ((int)(h - hex));
194				else
195					--p;
196			}
197			if (c > 0x10FFFF) /* outside valid character range */
198				return -1;
199			break;
200		}
201		case '0':
202		case '1':
203		case '2':
204		case '3':
205		case '4':
206		case '5':
207		case '6':
208		case '7':
209		{
210			int cnt, ch;
211
212			for (cnt = 0, c = 0; cnt < 3; cnt++) {
213				ch = *p++;
214				if (ch < '0' || ch > '7') {
215					p--;
216					break;
217				}
218				c = (c << 3) | (ch - '0');
219			}
220			if ((c & (wint_t)0xffffff00) != (wint_t)0)
221				return -1;
222			--p;
223			break;
224		}
225		default:
226			c = *p;
227			break;
228		}
229	} else if (*p == '^') {
230		p++;
231		c = (*p == '?') ? '\177' : (*p & 0237);
232	} else
233		c = *p;
234	*ptr = ++p;
235	return c;
236}
237
238/* parse__string():
239 *	Parse the escapes from in and put the raw string out
240 */
241libedit_private wchar_t *
242parse__string(wchar_t *out, const wchar_t *in)
243{
244	wchar_t *rv = out;
245	int n;
246
247	for (;;)
248		switch (*in) {
249		case '\0':
250			*out = '\0';
251			return rv;
252
253		case '\\':
254		case '^':
255			if ((n = parse__escape(&in)) == -1)
256				return NULL;
257			*out++ = (wchar_t)n;
258			break;
259
260		case 'M':
261			if (in[1] == '-' && in[2] != '\0') {
262				*out++ = '\033';
263				in += 2;
264				break;
265			}
266			/*FALLTHROUGH*/
267
268		default:
269			*out++ = *in++;
270			break;
271		}
272}
273
274
275/* parse_cmd():
276 *	Return the command number for the command string given
277 *	or -1 if one is not found
278 */
279libedit_private int
280parse_cmd(EditLine *el, const wchar_t *cmd)
281{
282	el_bindings_t *b = el->el_map.help;
283	size_t i;
284
285	for (i = 0; i < el->el_map.nfunc; i++)
286		if (wcscmp(b[i].name, cmd) == 0)
287			return b[i].func;
288	return -1;
289}
290