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