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 33 int lineno; 34 bool interactive; 35 36 HistEvent he; 37 EditLine *el; 38 History *hist; 39 40 static char *strbuf = NULL; 41 static size_t strbuf_sz = 1; 42 static bool dot_seen; 43 static int use_el; 44 static volatile sig_atomic_t skipchars; 45 46 static void init_strbuf(void); 47 static void add_str(const char *); 48 49 static 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 61 DIGIT [0-9A-F] 62 ALPHA [a-z_] 63 ALPHANUM [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 207 static void 208 init_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 218 static void 219 add_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 */ 242 void 243 abort_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 */ 265 unsigned char 266 bc_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 277 int 278 yywrap(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 325 static int 326 bc_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