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