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