xref: /openbsd/usr.bin/bc/scan.l (revision 73471bf0)
1 %{
2 /*      $OpenBSD: scan.l,v 1.31 2021/08/09 19:13:56 otto Exp $	*/
3 
4 /*
5  * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <err.h>
21 #include <histedit.h>
22 #include <signal.h>
23 #include <string.h>
24 #include <unistd.h>
25 
26 #include "extern.h"
27 #include "pathnames.h"
28 #include "bc.h"
29 
30 int		lineno;
31 bool		interactive;
32 
33 HistEvent	 he;
34 EditLine	*el;
35 History		*hist;
36 
37 static char	*strbuf = NULL;
38 static size_t	strbuf_sz = 1;
39 static bool	dot_seen;
40 static int	use_el;
41 static volatile sig_atomic_t skipchars;
42 
43 static void	init_strbuf(void);
44 static void	add_str(const char *);
45 
46 static int	 bc_yyinput(char *, int);
47 
48 #undef YY_INPUT
49 #define YY_INPUT(buf,retval,max) \
50 	(retval = bc_yyinput(buf, max))
51 
52 %}
53 
54 %option always-interactive
55 
56 DIGIT		[0-9A-F]
57 ALPHA		[a-z_]
58 ALPHANUM	[a-z_0-9]
59 
60 %x		comment string number
61 
62 %%
63 
64 "/*"		BEGIN(comment);
65 <comment>{
66 	"*/"	BEGIN(INITIAL);
67 	\n	lineno++;
68 	\*	;
69 	[^*\n]+	;
70 	<<EOF>>	fatal("end of file in comment");
71 }
72 
73 \"		BEGIN(string); init_strbuf();
74 <string>{
75 	[^"\n\\\[\]]+	add_str(yytext);
76 	\[	add_str("\\[");
77 	\]	add_str("\\]");
78 	\\	add_str("\\\\");
79 	\n	add_str("\n"); lineno++;
80 	\"	BEGIN(INITIAL); yylval.str = strbuf; return STRING;
81 	<<EOF>>	fatal("end of file in string");
82 }
83 
84 {DIGIT}+	{
85 			BEGIN(number);
86 			dot_seen = false;
87 			init_strbuf();
88 			add_str(yytext);
89 		}
90 \.		{
91 			BEGIN(number);
92 			dot_seen = true;
93 			init_strbuf();
94 			add_str(".");
95 		}
96 <number>{
97 	{DIGIT}+	add_str(yytext);
98 	\.	{
99 			if (dot_seen) {
100 				BEGIN(INITIAL);
101 				yylval.str = strbuf;
102 				unput('.');
103 				return NUMBER;
104 			} else {
105 				dot_seen = true;
106 				add_str(".");
107 			}
108 		}
109 	\\\n[ \t]*	lineno++;
110 	[^0-9A-F\.]	{
111 			BEGIN(INITIAL);
112 			unput(yytext[0]);
113 			if (strcmp(strbuf, ".") == 0)
114 				return DOT;
115 			else {
116 				yylval.str = strbuf;
117 				return NUMBER;
118 			}
119 		}
120 }
121 
122 "auto"		return AUTO;
123 "break"		return BREAK;
124 "continue"	return CONTINUE;
125 "define"	return DEFINE;
126 "else"		return ELSE;
127 "ibase"		return IBASE;
128 "if"		return IF;
129 "last"		return DOT;
130 "for"		return FOR;
131 "length"	return LENGTH;
132 "obase"		return OBASE;
133 "print"		return PRINT;
134 "quit"		return QUIT;
135 "return"	return RETURN;
136 "scale"		return SCALE;
137 "sqrt"		return SQRT;
138 "while"		return WHILE;
139 
140 "^"		return EXPONENT;
141 "*"		return MULTIPLY;
142 "/"		return DIVIDE;
143 "%"		return REMAINDER;
144 
145 "!"		return BOOL_NOT;
146 "&&"		return BOOL_AND;
147 "||"		return BOOL_OR;
148 
149 "+"		return PLUS;
150 "-"		return MINUS;
151 
152 "++"		return INCR;
153 "--"		return DECR;
154 
155 "="		yylval.str = ""; return ASSIGN_OP;
156 "+="		yylval.str = "+"; return ASSIGN_OP;
157 "-="		yylval.str = "-"; return ASSIGN_OP;
158 "*="		yylval.str = "*"; return ASSIGN_OP;
159 "/="		yylval.str = "/"; return ASSIGN_OP;
160 "%="		yylval.str = "%"; return ASSIGN_OP;
161 "^="		yylval.str = "^"; return ASSIGN_OP;
162 
163 "=="		return EQUALS;
164 "<="		return LESS_EQ;
165 ">="		return GREATER_EQ;
166 "!="		return UNEQUALS;
167 "<"		return LESS;
168 ">"		return GREATER;
169 
170 ","		return COMMA;
171 ";"		return SEMICOLON;
172 
173 "("		return LPAR;
174 ")"		return RPAR;
175 
176 "["		return LBRACKET;
177 "]"		return RBRACKET;
178 
179 "{"		return LBRACE;
180 "}"		return RBRACE;
181 
182 {ALPHA}{ALPHANUM}* {
183 			/* alloc an extra byte for the type marker */
184 			char *p = malloc(yyleng + 2);
185 			if (p == NULL)
186 				err(1, NULL);
187 			strlcpy(p, yytext, yyleng + 1);
188 			yylval.astr = p;
189 			return LETTER;
190 		}
191 
192 \\\n		lineno++;
193 \n		lineno++; return NEWLINE;
194 
195 #[^\n]*		;
196 [ \t]		;
197 <<EOF>>		return QUIT;
198 .		yyerror("illegal character");
199 
200 %%
201 
202 static void
203 init_strbuf(void)
204 {
205 	if (strbuf == NULL) {
206 		strbuf = malloc(strbuf_sz);
207 		if (strbuf == NULL)
208 			err(1, NULL);
209 	}
210 	strbuf[0] = '\0';
211 }
212 
213 static void
214 add_str(const char *str)
215 {
216 	size_t arglen;
217 
218 	arglen = strlen(str);
219 
220 	if (strlen(strbuf) + arglen + 1 > strbuf_sz) {
221 		size_t newsize;
222 		char *p;
223 
224 		newsize = strbuf_sz + arglen + 1;
225 		p = realloc(strbuf, newsize);
226 		if (p == NULL) {
227 			free(strbuf);
228 			err(1, NULL);
229 		}
230 		strbuf_sz = newsize;
231 		strbuf = p;
232 	}
233 	strlcat(strbuf, str, strbuf_sz);
234 }
235 
236 /* ARGSUSED */
237 void
238 abort_line(int sig)
239 {
240 	static const char str1[] = "[\n]P\n";
241 	static const char str2[] = "[^C\n]P\n";
242 	int save_errno;
243 	const LineInfo *info;
244 
245 	save_errno = errno;
246 	if (use_el) {
247 		write(STDOUT_FILENO, str2, sizeof(str2) - 1);
248 		/* XXX signal race */
249 		info = el_line(el);
250 		skipchars = info->lastchar - info->buffer;
251 	} else
252 		write(STDOUT_FILENO, str1, sizeof(str1) - 1);
253 	errno = save_errno;
254 }
255 
256 /*
257  * Avoid the echo of ^D by the default code of editline and take
258  * into account skipchars to make ^D work when the cursor is at start of
259  * line after a ^C.
260  */
261 unsigned char
262 bc_eof(EditLine *e, int ch)
263 {
264 	const struct lineinfo *info = el_line(e);
265 
266 	if (info->buffer + skipchars == info->cursor &&
267 	    info->cursor == info->lastchar)
268 		return (CC_EOF);
269 	else
270 		return (CC_ERROR);
271 }
272 
273 int
274 yywrap(void)
275 {
276 	static int state;
277 	static YY_BUFFER_STATE buf;
278 
279 	if (fileindex == 0 && sargc > 0 && strcmp(sargv[0], _PATH_LIBB) == 0) {
280 		filename = sargv[fileindex++];
281 		yyin = fopen(filename, "r");
282 		lineno = 1;
283 		if (yyin == NULL)
284 			err(1, "cannot open %s", filename);
285 		return (0);
286 	}
287 	if (state == 0 && cmdexpr[0] != '\0') {
288 		buf = yy_scan_string(cmdexpr);
289 		state++;
290 		lineno = 1;
291 		filename = "command line";
292 		return (0);
293 	} else if (state == 1) {
294 		yy_delete_buffer(buf);
295 		free(cmdexpr);
296 		state++;
297 	}
298 	if (yyin != NULL && yyin != stdin)
299 		fclose(yyin);
300 	if (fileindex < sargc) {
301 		filename = sargv[fileindex++];
302 		yyin = fopen(filename, "r");
303 		lineno = 1;
304 		if (yyin == NULL)
305 			err(1, "cannot open %s", filename);
306 		return (0);
307 	} else if (fileindex == sargc) {
308 		fileindex++;
309 		yyin = stdin;
310 		if (interactive) {
311 			signal(SIGINT, abort_line);
312 			signal(SIGTSTP, tstpcont);
313 		}
314 		lineno = 1;
315 		filename = "stdin";
316 		return (0);
317 	}
318 	return (1);
319 }
320 
321 static int
322 bc_yyinput(char *buf, int maxlen)
323 {
324 	int num;
325 
326 	if (el != NULL)
327 		el_get(el, EL_EDITMODE, &use_el);
328 
329 	if (yyin == stdin && interactive && use_el) {
330 		const char *bp;
331 		sigset_t oset, nset;
332 
333 		if ((bp = el_gets(el, &num)) == NULL || num == 0)
334 			return (0);
335 		sigemptyset(&nset);
336 		sigaddset(&nset, SIGINT);
337 		sigprocmask(SIG_BLOCK, &nset, &oset);
338 		if (skipchars < num) {
339 			bp += skipchars;
340 			num -= skipchars;
341 		}
342 		skipchars = 0;
343 		sigprocmask(SIG_SETMASK, &oset, NULL);
344 		if (num > maxlen) {
345 			el_push(el, (char *)(void *)bp + maxlen);
346 			num = maxlen;
347 		}
348 		memcpy(buf, bp, num);
349 		history(hist, &he, H_ENTER, bp);
350 		el_get(el, EL_EDITMODE, &use_el);
351 	} else {
352 		int c = '*';
353 		for (num = 0; num < maxlen &&
354 		    (c = getc(yyin)) != EOF && c != '\n'; ++num)
355 			buf[num] = (char) c;
356 		if (c == '\n')
357 			buf[num++] = (char) c;
358 		if (c == EOF && ferror(yyin))
359 			YY_FATAL_ERROR( "input in flex scanner failed" );
360 	}
361 	return (num);
362 }
363 
364 
365