jaillex.l revision 214117
1%{
2/*-
3 * Copyright (c) 2010 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: projects/jailconf/usr.sbin/jail/jaillex.l 214117 2010-10-20 20:42:33Z 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_UNPUT
40
41extern int yynerrs;
42
43static ssize_t text2lval(size_t triml, size_t trimr, int tovar);
44
45static int instr;
46static int lineno = 1;
47%}
48
49%start _ DQ
50
51%%
52
53			/* Whitespace or equivalent */
54<_>[ \t]+		instr = 0;
55<_>#.*			;
56<_>\/\/.*		;
57<_>\/\*([^*]|(\*+([^*\/])))*\*+\/ {
58				const char *s;
59
60				for (s = yytext; s < yytext + yyleng; s++)
61					if (*s == '\n')
62						lineno++;
63				instr = 0;
64			}
65<_>\n			{
66				lineno++;
67				instr = 0;
68			}
69
70			/* Reserved tokens */
71<_>\+=			{
72				instr = 0;
73				return PLEQ;
74			}
75<_>[,;={}]		{
76				instr = 0;
77				return yytext[0];
78			}
79
80			/* Atomic (unquoted) strings */
81<_,DQ>[A-Za-z0-9_!%&()\-.:<>?@\[\]^`|~]+ |
82<_,DQ>\\(.|\n|[0-7]{1,3}|x[0-9A-Fa-f]{1,2}) |
83<_,DQ>[$*+/\\]		{
84				(void)text2lval(0, 0, 0);
85				return instr ? STR1 : (instr = 1, STR);
86			}
87
88			/* Single and double quoted strings */
89<_>'([^\'\\]|\\(.|\n))*' {
90				(void)text2lval(1, 1, 0);
91				return instr ? STR1 : (instr = 1, STR);
92			}
93<_>\"([^"\\]|\\(.|\n))*\" |
94<DQ>[^\"$\\]([^"\\]|\\(.|\n))*\" {
95				size_t skip;
96				ssize_t atvar;
97
98				skip = yytext[0] == '"' ? 1 : 0;
99				atvar = text2lval(skip, 1, 1);
100				if (atvar < 0)
101					BEGIN _;
102				else {
103					/*
104					 * The string has a variable inside it.
105					 * Go into DQ mode to get the variable
106					 * and then the rest of the string.
107					 */
108					BEGIN DQ;
109					yyless(atvar);
110				}
111				return instr ? STR1 : (instr = 1, STR);
112			}
113<DQ>\"			BEGIN _;
114
115			/* Variables, single-word or bracketed */
116<_,DQ>$[A-Za-z_][A-Za-z_0-9]* {
117				(void)text2lval(1, 0, 0);
118				return instr ? VAR1 : (instr = 1, VAR);
119			}
120<_>$\{([^\n{}]|\\(.|\n))*\} |
121<DQ>$\{([^\n\"{}]|\\(.|\n))*\} {
122				(void)text2lval(2, 1, 0);
123				return instr ? VAR1 : (instr = 1, VAR);
124			}
125
126			/* Partially formed bits worth complaining about */
127<_>\/\*([^*]|(\*+([^*\/])))*\** {
128				warnx("%s line %d: unterminated comment",
129				    cfname, lineno);
130				yynerrs++;
131			}
132<_>'([^\n'\\]|\\.)*	|
133<_>\"([^\n\"\\]|\\.)*	{
134				warnx("%s line %d: unterminated string",
135				    cfname, lineno);
136				yynerrs++;
137			}
138<_>$\{([^\n{}]|\\.)*	|
139<DQ>$\{([^\n\"{}]|\\.)*	{
140				warnx("%s line %d: unterminated variable",
141				    cfname, lineno);
142				yynerrs++;
143			}
144
145			/* A hack because "<0>" rules aren't allowed */
146<_>.			return yytext[0];
147.|\n			{
148				BEGIN _;
149				yyless(0);
150			}
151
152%%
153
154void
155yyerror(const char *s)
156{
157	if (!yytext)
158		warnx("%s line %d: %s", cfname, lineno, s);
159	else if (!yytext[0])
160		warnx("%s: unexpected EOF", cfname);
161	else
162		warnx("%s line %d: %s: %s", cfname, lineno, yytext, s);
163}
164
165/*
166 * Copy string from yytext to yylval, handling backslash escapes,
167 * and optionally stopping at the beginning of a variable.
168 */
169static ssize_t
170text2lval(size_t triml, size_t trimr, int tovar)
171{
172	char *d;
173	const char *s, *se;
174
175	yylval.cs = d = emalloc(yyleng - trimr - triml + 1);
176	se = yytext + (yyleng - trimr);
177	for (s = yytext + triml; s < se; s++, d++) {
178		if (*s != '\\') {
179			if (tovar && *s == '$') {
180				*d = '\0';
181				return s - yytext;
182			}
183			if (*s == '\n')
184				lineno++;
185			*d = *s;
186			continue;
187		}
188		s++;
189		if (*s >= '0' && *s <= '7') {
190			*d = *s - '0';
191			if (s + 1 < se && s[1] >= '0' && s[1] <= '7') {
192				*d = 010 * *d + (*++s - '0');
193				if (s + 1 < se && s[1] >= '0' && s[1] <= '7')
194					*d = 010 * *d + (*++s - '0');
195			}
196			continue;
197		}
198		switch (*s) {
199		case 'a':	*d = '\a';	break;
200		case 'b':	*d = '\b';	break;
201		case 'f':	*d = '\f';	break;
202		case 'n':	*d = '\n';	break;
203		case 'r':	*d = '\r';	break;
204		case 't':	*d = '\t';	break;
205		case 'v':	*d = '\v';	break;
206		case '\n':	d--; lineno++;	break;
207		default:	*d = *s;	break;
208		case 'x':
209			*d = 0;
210			if (s + 1 >= se)
211				break;
212			if (s[1] >= '0' && s[1] <= '9')
213				*d = *++s - '0';
214			else if (s[1] >= 'A' && s[1] <= 'F')
215				*d = *++s + (0xA - 'A');
216			else if (s[1] >= 'a' && s[1] <= 'a')
217				*d = *++s + (0xa - 'a');
218			else
219				break;
220			if (s + 1 >= se)
221				break;
222			if (s[1] >= '0' && s[1] <= '9')
223				*d = *d * 0x10 + (*++s - '0');
224			else if (s[1] >= 'A' && s[1] <= 'F')
225				*d = *d * 0x10 + (*++s + (0xA - 'A'));
226			else if (s[1] >= 'a' && s[1] <= 'a')
227				*d = *d * 0x10 + (*++s + (0xa - 'a'));
228		}
229	}
230	*d = '\0';
231	return -1;
232}
233