xref: /freebsd/usr.sbin/jail/jaillex.l (revision dd41de95a84d979615a2ef11df6850622bf6184e)
1 %{
2 /*-
3  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4  *
5  * Copyright (c) 2011 James Gritton
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <err.h>
34 #include <stddef.h>
35 #include <stdlib.h>
36 #include <string.h>
37 
38 #include "jailp.h"
39 #include "y.tab.h"
40 
41 extern int yynerrs;
42 
43 static ssize_t text2lval(size_t triml, size_t trimr, int tovar);
44 
45 static int instr;
46 static int lineno = 1;
47 
48 #define YY_DECL int yylex(void)
49 %}
50 
51 %option noyywrap
52 %option noinput
53 %option nounput
54 
55 %start _ DQ
56 
57 %%
58 
59 			/* Whitespace or equivalent */
60 <_>[ \t]+		instr = 0;
61 <_>#.*			;
62 <_>\/\/.*		;
63 <_>\/\*([^*]|(\*+([^*\/])))*\*+\/ {
64 				const char *s;
65 
66 				for (s = yytext; s < yytext + yyleng; s++)
67 					if (*s == '\n')
68 						lineno++;
69 				instr = 0;
70 			}
71 <_>\n			{
72 				lineno++;
73 				instr = 0;
74 			}
75 
76 			/* Reserved tokens */
77 <_>\+=			{
78 				instr = 0;
79 				return PLEQ;
80 			}
81 <_>[,;={}]		{
82 				instr = 0;
83 				return yytext[0];
84 			}
85 
86 			/* Atomic (unquoted) strings */
87 <_,DQ>[A-Za-z0-9_!%&()\-.:<>?@\[\]^`|~]+ |
88 <_,DQ>\\(.|\n|[0-7]{1,3}|x[0-9A-Fa-f]{1,2}) |
89 <_,DQ>[$*+/\\]		{
90 				(void)text2lval(0, 0, 0);
91 				return instr ? STR1 : (instr = 1, STR);
92 			}
93 
94 			/* Single and double quoted strings */
95 <_>'([^\'\\]|\\(.|\n))*' {
96 				(void)text2lval(1, 1, 0);
97 				return instr ? STR1 : (instr = 1, STR);
98 			}
99 <_>\"([^"\\]|\\(.|\n))*\" |
100 <DQ>[^\"$\\]([^"\\]|\\(.|\n))*\" {
101 				size_t skip;
102 				ssize_t atvar;
103 
104 				skip = yytext[0] == '"' ? 1 : 0;
105 				atvar = text2lval(skip, 1, 1);
106 				if (atvar < 0)
107 					BEGIN _;
108 				else {
109 					/*
110 					 * The string has a variable inside it.
111 					 * Go into DQ mode to get the variable
112 					 * and then the rest of the string.
113 					 */
114 					BEGIN DQ;
115 					yyless(atvar);
116 				}
117 				return instr ? STR1 : (instr = 1, STR);
118 			}
119 <DQ>\"			BEGIN _;
120 
121 			/* Variables, single-word or bracketed */
122 <_,DQ>$[A-Za-z_][A-Za-z_0-9]* {
123 				(void)text2lval(1, 0, 0);
124 				return instr ? VAR1 : (instr = 1, VAR);
125 			}
126 <_>$\{([^\n{}]|\\(.|\n))*\} |
127 <DQ>$\{([^\n\"{}]|\\(.|\n))*\} {
128 				(void)text2lval(2, 1, 0);
129 				return instr ? VAR1 : (instr = 1, VAR);
130 			}
131 
132 			/* Partially formed bits worth complaining about */
133 <_>\/\*([^*]|(\*+([^*\/])))*\** {
134 				warnx("%s line %d: unterminated comment",
135 				    cfname, lineno);
136 				yynerrs++;
137 			}
138 <_>'([^\n'\\]|\\.)*	|
139 <_>\"([^\n\"\\]|\\.)*	{
140 				warnx("%s line %d: unterminated string",
141 				    cfname, lineno);
142 				yynerrs++;
143 			}
144 <_>$\{([^\n{}]|\\.)*	|
145 <DQ>$\{([^\n\"{}]|\\.)*	{
146 				warnx("%s line %d: unterminated variable",
147 				    cfname, lineno);
148 				yynerrs++;
149 			}
150 
151 			/* A hack because "<0>" rules aren't allowed */
152 <_>.			return yytext[0];
153 .|\n			{
154 				BEGIN _;
155 				yyless(0);
156 			}
157 
158 %%
159 
160 void
161 yyerror(const char *s)
162 {
163 	if (!yytext)
164 		warnx("%s line %d: %s", cfname, lineno, s);
165 	else if (!yytext[0])
166 		warnx("%s: unexpected EOF", cfname);
167 	else
168 		warnx("%s line %d: %s: %s", cfname, lineno, yytext, s);
169 }
170 
171 /*
172  * Copy string from yytext to yylval, handling backslash escapes,
173  * and optionally stopping at the beginning of a variable.
174  */
175 static ssize_t
176 text2lval(size_t triml, size_t trimr, int tovar)
177 {
178 	char *d;
179 	const char *s, *se;
180 
181 	yylval.cs = d = emalloc(yyleng - trimr - triml + 1);
182 	se = yytext + (yyleng - trimr);
183 	for (s = yytext + triml; s < se; s++, d++) {
184 		if (*s != '\\') {
185 			if (tovar && *s == '$') {
186 				*d = '\0';
187 				return s - yytext;
188 			}
189 			if (*s == '\n')
190 				lineno++;
191 			*d = *s;
192 			continue;
193 		}
194 		s++;
195 		if (*s >= '0' && *s <= '7') {
196 			*d = *s - '0';
197 			if (s + 1 < se && s[1] >= '0' && s[1] <= '7') {
198 				*d = 010 * *d + (*++s - '0');
199 				if (s + 1 < se && s[1] >= '0' && s[1] <= '7')
200 					*d = 010 * *d + (*++s - '0');
201 			}
202 			continue;
203 		}
204 		switch (*s) {
205 		case 'a':	*d = '\a';	break;
206 		case 'b':	*d = '\b';	break;
207 		case 'f':	*d = '\f';	break;
208 		case 'n':	*d = '\n';	break;
209 		case 'r':	*d = '\r';	break;
210 		case 't':	*d = '\t';	break;
211 		case 'v':	*d = '\v';	break;
212 		case '\n':	d--; lineno++;	break;
213 		default:	*d = *s;	break;
214 		case 'x':
215 			*d = 0;
216 			if (s + 1 >= se)
217 				break;
218 			if (s[1] >= '0' && s[1] <= '9')
219 				*d = *++s - '0';
220 			else if (s[1] >= 'A' && s[1] <= 'F')
221 				*d = *++s + (0xA - 'A');
222 			else if (s[1] >= 'a' && s[1] <= 'f')
223 				*d = *++s + (0xa - 'a');
224 			else
225 				break;
226 			if (s + 1 >= se)
227 				break;
228 			if (s[1] >= '0' && s[1] <= '9')
229 				*d = *d * 0x10 + (*++s - '0');
230 			else if (s[1] >= 'A' && s[1] <= 'F')
231 				*d = *d * 0x10 + (*++s + (0xA - 'A'));
232 			else if (s[1] >= 'a' && s[1] <= 'f')
233 				*d = *d * 0x10 + (*++s + (0xa - 'a'));
234 		}
235 	}
236 	*d = '\0';
237 	return -1;
238 }
239