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 35 int lineno; 36 bool interactive; 37 38 HistEvent he; 39 EditLine *el; 40 History *hist; 41 42 static char *strbuf = NULL; 43 static size_t strbuf_sz = 1; 44 static bool dot_seen; 45 static int use_el; 46 static volatile sig_atomic_t skipchars; 47 48 static void init_strbuf(void); 49 static void add_str(const char *); 50 51 static 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 63 DIGIT [0-9A-F] 64 ALPHA [a-z_] 65 ALPHANUM [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 209 static void 210 init_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 220 static void 221 add_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 */ 244 void 245 abort_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 */ 267 unsigned char 268 bc_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 279 int 280 yywrap(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 327 static int 328 bc_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