1%{
2/*      $OpenBSD: scan.l,v 1.32 2023/03/08 04:43:10 guenther 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
30int		lineno;
31bool		interactive;
32
33HistEvent	 he;
34EditLine	*el;
35History		*hist;
36
37static char	*strbuf = NULL;
38static size_t	strbuf_sz = 1;
39static bool	dot_seen;
40static int	use_el;
41static volatile sig_atomic_t skipchars;
42
43static void	init_strbuf(void);
44static void	add_str(const char *);
45
46static 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
56DIGIT		[0-9A-F]
57ALPHA		[a-z_]
58ALPHANUM	[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
202static void
203init_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
213static void
214add_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
236void
237abort_line(int sig)
238{
239	static const char str1[] = "[\n]P\n";
240	static const char str2[] = "[^C\n]P\n";
241	int save_errno;
242	const LineInfo *info;
243
244	save_errno = errno;
245	if (use_el) {
246		write(STDOUT_FILENO, str2, sizeof(str2) - 1);
247		/* XXX signal race */
248		info = el_line(el);
249		skipchars = info->lastchar - info->buffer;
250	} else
251		write(STDOUT_FILENO, str1, sizeof(str1) - 1);
252	errno = save_errno;
253}
254
255/*
256 * Avoid the echo of ^D by the default code of editline and take
257 * into account skipchars to make ^D work when the cursor is at start of
258 * line after a ^C.
259 */
260unsigned char
261bc_eof(EditLine *e, int ch)
262{
263	const struct lineinfo *info = el_line(e);
264
265	if (info->buffer + skipchars == info->cursor &&
266	    info->cursor == info->lastchar)
267		return (CC_EOF);
268	else
269		return (CC_ERROR);
270}
271
272int
273yywrap(void)
274{
275	static int state;
276	static YY_BUFFER_STATE buf;
277
278	if (fileindex == 0 && sargc > 0 && strcmp(sargv[0], _PATH_LIBB) == 0) {
279		filename = sargv[fileindex++];
280		yyin = fopen(filename, "r");
281		lineno = 1;
282		if (yyin == NULL)
283			err(1, "cannot open %s", filename);
284		return (0);
285	}
286	if (state == 0 && cmdexpr[0] != '\0') {
287		buf = yy_scan_string(cmdexpr);
288		state++;
289		lineno = 1;
290		filename = "command line";
291		return (0);
292	} else if (state == 1) {
293		yy_delete_buffer(buf);
294		free(cmdexpr);
295		state++;
296	}
297	if (yyin != NULL && yyin != stdin)
298		fclose(yyin);
299	if (fileindex < sargc) {
300		filename = sargv[fileindex++];
301		yyin = fopen(filename, "r");
302		lineno = 1;
303		if (yyin == NULL)
304			err(1, "cannot open %s", filename);
305		return (0);
306	} else if (fileindex == sargc) {
307		fileindex++;
308		yyin = stdin;
309		if (interactive) {
310			signal(SIGINT, abort_line);
311			signal(SIGTSTP, tstpcont);
312		}
313		lineno = 1;
314		filename = "stdin";
315		return (0);
316	}
317	return (1);
318}
319
320static int
321bc_yyinput(char *buf, int maxlen)
322{
323	int num;
324
325	if (el != NULL)
326		el_get(el, EL_EDITMODE, &use_el);
327
328	if (yyin == stdin && interactive && use_el) {
329		const char *bp;
330		sigset_t oset, nset;
331
332		if ((bp = el_gets(el, &num)) == NULL || num == 0)
333			return (0);
334		sigemptyset(&nset);
335		sigaddset(&nset, SIGINT);
336		sigprocmask(SIG_BLOCK, &nset, &oset);
337		if (skipchars < num) {
338			bp += skipchars;
339			num -= skipchars;
340		}
341		skipchars = 0;
342		sigprocmask(SIG_SETMASK, &oset, NULL);
343		if (num > maxlen) {
344			el_push(el, (char *)(void *)bp + maxlen);
345			num = maxlen;
346		}
347		memcpy(buf, bp, num);
348		history(hist, &he, H_ENTER, bp);
349		el_get(el, EL_EDITMODE, &use_el);
350	} else {
351		int c = '*';
352		for (num = 0; num < maxlen &&
353		    (c = getc(yyin)) != EOF && c != '\n'; ++num)
354			buf[num] = (char) c;
355		if (c == '\n')
356			buf[num++] = (char) c;
357		if (c == EOF && ferror(yyin))
358			YY_FATAL_ERROR( "input in flex scanner failed" );
359	}
360	return (num);
361}
362
363
364