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