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
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#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
160void
161yyerror(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 */
175static ssize_t
176text2lval(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