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