xref: /openbsd/lib/libedit/parse.c (revision 09467b48)
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 
57 static 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  */
75 protected int
76 parse_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  */
93 int
94 el_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  */
135 protected int
136 parse__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  */
235 protected wchar_t *
236 parse__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  */
273 protected int
274 parse_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