1/*	$OpenBSD: parse.c,v 1.20 2016/04/11 21:17:29 schwarze Exp $	*/
2/*	$NetBSD: parse.c,v 1.38 2016/04/11 18:56:31 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 * parse.c: parse an editline extended command
40 *
41 * commands are:
42 *
43 *	bind
44 *	echotc
45 *	edit
46 *	gettc
47 *	history
48 *	settc
49 *	setty
50 */
51#include <stdlib.h>
52#include <string.h>
53
54#include "el.h"
55#include "parse.h"
56
57static const struct {
58	const wchar_t *name;
59	int (*func)(EditLine *, int, const wchar_t **);
60} cmds[] = {
61	{ L"bind",		map_bind	},
62	{ L"echotc",		terminal_echotc	},
63	{ L"edit",		el_editmode	},
64	{ L"history",		hist_command	},
65	{ L"telltc",		terminal_telltc	},
66	{ L"settc",		terminal_settc	},
67	{ L"setty",		tty_stty	},
68	{ NULL,			NULL		}
69};
70
71
72/* parse_line():
73 *	Parse a line and dispatch it
74 */
75protected int
76parse_line(EditLine *el, const wchar_t *line)
77{
78	const wchar_t **argv;
79	int argc;
80	TokenizerW *tok;
81
82	tok = tok_winit(NULL);
83	tok_wstr(tok, line, &argc, &argv);
84	argc = el_wparse(el, argc, argv);
85	tok_wend(tok);
86	return argc;
87}
88
89
90/* el_parse():
91 *	Command dispatcher
92 */
93int
94el_wparse(EditLine *el, int argc, const wchar_t *argv[])
95{
96	const wchar_t *ptr;
97	int i;
98
99	if (argc < 1)
100		return -1;
101	ptr = wcschr(argv[0], L':');
102	if (ptr != NULL) {
103		wchar_t *tprog;
104		size_t l;
105
106		if (ptr == argv[0])
107			return 0;
108		l = ptr - argv[0] - 1;
109		tprog = reallocarray(NULL, l + 1, sizeof(*tprog));
110		if (tprog == NULL)
111			return 0;
112		(void) wcsncpy(tprog, argv[0], l);
113		tprog[l] = '\0';
114		ptr++;
115		l = el_match(el->el_prog, tprog);
116		free(tprog);
117		if (!l)
118			return 0;
119	} else
120		ptr = argv[0];
121
122	for (i = 0; cmds[i].name != NULL; i++)
123		if (wcscmp(cmds[i].name, ptr) == 0) {
124			i = (*cmds[i].func) (el, argc, argv);
125			return -i;
126		}
127	return -1;
128}
129
130
131/* parse__escape():
132 *	Parse a string of the form ^<char> \<odigit> \<char> \U+xxxx and return
133 *	the appropriate character or -1 if the escape is not valid
134 */
135protected int
136parse__escape(const wchar_t **ptr)
137{
138	const wchar_t *p;
139	wint_t c;
140
141	p = *ptr;
142
143	if (p[1] == 0)
144		return -1;
145
146	if (*p == '\\') {
147		p++;
148		switch (*p) {
149		case 'a':
150			c = '\007';	/* Bell */
151			break;
152		case 'b':
153			c = '\010';	/* Backspace */
154			break;
155		case 't':
156			c = '\011';	/* Horizontal Tab */
157			break;
158		case 'n':
159			c = '\012';	/* New Line */
160			break;
161		case 'v':
162			c = '\013';	/* Vertical Tab */
163			break;
164		case 'f':
165			c = '\014';	/* Form Feed */
166			break;
167		case 'r':
168			c = '\015';	/* Carriage Return */
169			break;
170		case 'e':
171			c = '\033';	/* Escape */
172			break;
173		case 'U':		/* Unicode \U+xxxx or \U+xxxxx format */
174		{
175			int i;
176			const wchar_t hex[] = L"0123456789ABCDEF";
177			const wchar_t *h;
178			++p;
179			if (*p++ != '+')
180				return -1;
181			c = 0;
182			for (i = 0; i < 5; ++i) {
183				h = wcschr(hex, *p++);
184				if (!h && i < 4)
185					return -1;
186				else if (h)
187					c = (c << 4) | ((int)(h - hex));
188				else
189					--p;
190			}
191			if (c > 0x10FFFF) /* outside valid character range */
192				return -1;
193			break;
194		}
195		case '0':
196		case '1':
197		case '2':
198		case '3':
199		case '4':
200		case '5':
201		case '6':
202		case '7':
203		{
204			int cnt, ch;
205
206			for (cnt = 0, c = 0; cnt < 3; cnt++) {
207				ch = *p++;
208				if (ch < '0' || ch > '7') {
209					p--;
210					break;
211				}
212				c = (c << 3) | (ch - '0');
213			}
214			if ((c & 0xffffff00) != 0)
215				return -1;
216			--p;
217			break;
218		}
219		default:
220			c = *p;
221			break;
222		}
223	} else if (*p == '^') {
224		p++;
225		c = (*p == '?') ? '\177' : (*p & 0237);
226	} else
227		c = *p;
228	*ptr = ++p;
229	return c;
230}
231
232/* parse__string():
233 *	Parse the escapes from in and put the raw string out
234 */
235protected wchar_t *
236parse__string(wchar_t *out, const wchar_t *in)
237{
238	wchar_t *rv = out;
239	int n;
240
241	for (;;)
242		switch (*in) {
243		case '\0':
244			*out = '\0';
245			return rv;
246
247		case '\\':
248		case '^':
249			if ((n = parse__escape(&in)) == -1)
250				return NULL;
251			*out++ = (wchar_t)n;
252			break;
253
254		case 'M':
255			if (in[1] == '-' && in[2] != '\0') {
256				*out++ = '\033';
257				in += 2;
258				break;
259			}
260			/*FALLTHROUGH*/
261
262		default:
263			*out++ = *in++;
264			break;
265		}
266}
267
268
269/* parse_cmd():
270 *	Return the command number for the command string given
271 *	or -1 if one is not found
272 */
273protected int
274parse_cmd(EditLine *el, const wchar_t *cmd)
275{
276	el_bindings_t *b;
277	int i;
278
279	for (b = el->el_map.help, i = 0; i < el->el_map.nfunc; i++)
280		if (wcscmp(b[i].name, cmd) == 0)
281			return b[i].func;
282	return -1;
283}
284