1 %{ 2 /* 3 * $OpenBSD: bc.y,v 1.32 2006/05/18 05:49:53 otto Exp $ 4 */ 5 6 /* 7 * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net> 8 * 9 * Permission to use, copy, modify, and distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22 /* 23 * This implementation of bc(1) uses concepts from the original 4.4 24 * BSD bc(1). The code itself is a complete rewrite, based on the 25 * Posix defined bc(1) grammar. Other differences include type safe 26 * usage of pointers to build the tree of emitted code, typed yacc 27 * rule values, dynamic allocation of all data structures and a 28 * completely rewritten lexical analyzer using lex(1). 29 * 30 * Some effort has been made to make sure that the generated code is 31 * the same as the code generated by the older version, to provide 32 * easy regression testing. 33 */ 34 35 #include <sys/types.h> 36 #include <sys/wait.h> 37 38 #include <ctype.h> 39 #include <err.h> 40 #include <errno.h> 41 #include <histedit.h> 42 #include <limits.h> 43 #include <search.h> 44 #include <signal.h> 45 #include <stdarg.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 50 #include "extern.h" 51 #include "pathnames.h" 52 53 #define END_NODE ((ssize_t) -1) 54 #define CONST_STRING ((ssize_t) -2) 55 #define ALLOC_STRING ((ssize_t) -3) 56 57 struct tree { 58 ssize_t index; 59 union { 60 char *astr; 61 const char *cstr; 62 } u; 63 }; 64 65 int yyparse(void); 66 int yywrap(void); 67 68 int fileindex; 69 int sargc; 70 char **sargv; 71 char *filename; 72 char *cmdexpr; 73 74 static void grow(void); 75 static ssize_t cs(const char *); 76 static ssize_t as(const char *); 77 static ssize_t node(ssize_t, ...); 78 static void emit(ssize_t); 79 static void emit_macro(int, ssize_t); 80 static void free_tree(void); 81 static ssize_t numnode(int); 82 static ssize_t lookup(char *, size_t, char); 83 static ssize_t letter_node(char *); 84 static ssize_t array_node(char *); 85 static ssize_t function_node(char *); 86 87 static void add_par(ssize_t); 88 static void add_local(ssize_t); 89 static void warning(const char *); 90 static void init(void); 91 static __dead2 void usage(void); 92 static char *escape(const char *); 93 94 static ssize_t instr_sz = 0; 95 static struct tree *instructions = NULL; 96 static ssize_t current = 0; 97 static int macro_char = '0'; 98 static int reset_macro_char = '0'; 99 static int nesting = 0; 100 static int breakstack[16]; 101 static int breaksp = 0; 102 static ssize_t prologue; 103 static ssize_t epilogue; 104 static bool st_has_continue; 105 static char str_table[UCHAR_MAX][2]; 106 static bool do_fork = true; 107 static u_short var_count; 108 static pid_t dc; 109 110 extern char *__progname; 111 112 #define BREAKSTACK_SZ (sizeof(breakstack)/sizeof(breakstack[0])) 113 114 /* These values are 4.4BSD bc compatible */ 115 #define FUNC_CHAR 0x01 116 #define ARRAY_CHAR 0xa1 117 118 /* Skip '\0', [, \ and ] */ 119 #define ENCODE(c) ((c) < '[' ? (c) : (c) + 3); 120 #define VAR_BASE (256-4) 121 #define MAX_VARIABLES (VAR_BASE * VAR_BASE) 122 123 %} 124 125 %start program 126 127 %union { 128 ssize_t node; 129 struct lvalue lvalue; 130 const char *str; 131 char *astr; 132 } 133 134 %token COMMA SEMICOLON LPAR RPAR LBRACE RBRACE LBRACKET RBRACKET DOT 135 %token NEWLINE 136 %token <astr> LETTER 137 %token <str> NUMBER STRING 138 %token DEFINE BREAK QUIT LENGTH 139 %token RETURN FOR IF WHILE SQRT 140 %token SCALE IBASE OBASE AUTO 141 %token CONTINUE ELSE PRINT 142 143 %left BOOL_OR 144 %left BOOL_AND 145 %nonassoc BOOL_NOT 146 %nonassoc EQUALS LESS_EQ GREATER_EQ UNEQUALS LESS GREATER 147 %right <str> ASSIGN_OP 148 %left PLUS MINUS 149 %left MULTIPLY DIVIDE REMAINDER 150 %right EXPONENT 151 %nonassoc UMINUS 152 %nonassoc INCR DECR 153 154 %type <lvalue> named_expression 155 %type <node> argument_list 156 %type <node> alloc_macro 157 %type <node> expression 158 %type <node> function 159 %type <node> function_header 160 %type <node> input_item 161 %type <node> opt_argument_list 162 %type <node> opt_expression 163 %type <node> opt_relational_expression 164 %type <node> opt_statement 165 %type <node> print_expression 166 %type <node> print_expression_list 167 %type <node> relational_expression 168 %type <node> return_expression 169 %type <node> semicolon_list 170 %type <node> statement 171 %type <node> statement_list 172 173 %% 174 175 program : /* empty */ 176 | program input_item 177 ; 178 179 input_item : semicolon_list NEWLINE 180 { 181 emit($1); 182 macro_char = reset_macro_char; 183 putchar('\n'); 184 free_tree(); 185 st_has_continue = false; 186 } 187 | function 188 { 189 putchar('\n'); 190 free_tree(); 191 st_has_continue = false; 192 } 193 | error NEWLINE 194 { 195 yyerrok; 196 } 197 | error QUIT 198 { 199 yyerrok; 200 } 201 ; 202 203 semicolon_list : /* empty */ 204 { 205 $$ = cs(""); 206 } 207 | statement 208 | semicolon_list SEMICOLON statement 209 { 210 $$ = node($1, $3, END_NODE); 211 } 212 | semicolon_list SEMICOLON 213 ; 214 215 statement_list : /* empty */ 216 { 217 $$ = cs(""); 218 } 219 | statement 220 | statement_list NEWLINE 221 | statement_list NEWLINE statement 222 { 223 $$ = node($1, $3, END_NODE); 224 } 225 | statement_list SEMICOLON 226 | statement_list SEMICOLON statement 227 { 228 $$ = node($1, $3, END_NODE); 229 } 230 ; 231 232 233 opt_statement : /* empty */ 234 { 235 $$ = cs(""); 236 } 237 | statement 238 ; 239 240 statement : expression 241 { 242 $$ = node($1, cs("ps."), END_NODE); 243 } 244 | named_expression ASSIGN_OP expression 245 { 246 if ($2[0] == '\0') 247 $$ = node($3, cs($2), $1.store, 248 END_NODE); 249 else 250 $$ = node($1.load, $3, cs($2), $1.store, 251 END_NODE); 252 } 253 | STRING 254 { 255 $$ = node(cs("["), as($1), 256 cs("]P"), END_NODE); 257 } 258 | BREAK 259 { 260 if (breaksp == 0) { 261 warning("break not in for or while"); 262 YYERROR; 263 } else { 264 $$ = node( 265 numnode(nesting - 266 breakstack[breaksp-1]), 267 cs("Q"), END_NODE); 268 } 269 } 270 | CONTINUE 271 { 272 if (breaksp == 0) { 273 warning("continue not in for or while"); 274 YYERROR; 275 } else { 276 st_has_continue = true; 277 $$ = node(numnode(nesting - 278 breakstack[breaksp-1] - 1), 279 cs("J"), END_NODE); 280 } 281 } 282 | QUIT 283 { 284 sigset_t mask; 285 286 putchar('q'); 287 fflush(stdout); 288 if (dc) { 289 sigprocmask(SIG_BLOCK, NULL, &mask); 290 sigsuspend(&mask); 291 } else 292 exit(0); 293 } 294 | RETURN return_expression 295 { 296 if (nesting == 0) { 297 warning("return must be in a function"); 298 YYERROR; 299 } 300 $$ = $2; 301 } 302 | FOR LPAR alloc_macro opt_expression SEMICOLON 303 opt_relational_expression SEMICOLON 304 opt_expression RPAR opt_statement pop_nesting 305 { 306 ssize_t n; 307 308 if (st_has_continue) 309 n = node($10, cs("M"), $8, cs("s."), 310 $6, $3, END_NODE); 311 else 312 n = node($10, $8, cs("s."), $6, $3, 313 END_NODE); 314 315 emit_macro($3, n); 316 $$ = node($4, cs("s."), $6, $3, cs(" "), 317 END_NODE); 318 } 319 | IF LPAR alloc_macro pop_nesting relational_expression RPAR 320 opt_statement 321 { 322 emit_macro($3, $7); 323 $$ = node($5, $3, cs(" "), END_NODE); 324 } 325 | IF LPAR alloc_macro pop_nesting relational_expression RPAR 326 opt_statement ELSE alloc_macro pop_nesting opt_statement 327 { 328 emit_macro($3, $7); 329 emit_macro($9, $11); 330 $$ = node($5, $3, cs("e"), $9, cs(" "), 331 END_NODE); 332 } 333 | WHILE LPAR alloc_macro relational_expression RPAR 334 opt_statement pop_nesting 335 { 336 ssize_t n; 337 338 if (st_has_continue) 339 n = node($6, cs("M"), $4, $3, END_NODE); 340 else 341 n = node($6, $4, $3, END_NODE); 342 emit_macro($3, n); 343 $$ = node($4, $3, cs(" "), END_NODE); 344 } 345 | LBRACE statement_list RBRACE 346 { 347 $$ = $2; 348 } 349 | PRINT print_expression_list 350 { 351 $$ = $2; 352 } 353 ; 354 355 alloc_macro : /* empty */ 356 { 357 $$ = cs(str_table[macro_char]); 358 macro_char++; 359 /* Do not use [, \ and ] */ 360 if (macro_char == '[') 361 macro_char += 3; 362 /* skip letters */ 363 else if (macro_char == 'a') 364 macro_char = '{'; 365 else if (macro_char == ARRAY_CHAR) 366 macro_char += 26; 367 else if (macro_char == 255) 368 fatal("program too big"); 369 if (breaksp == BREAKSTACK_SZ) 370 fatal("nesting too deep"); 371 breakstack[breaksp++] = nesting++; 372 } 373 ; 374 375 pop_nesting : /* empty */ 376 { 377 breaksp--; 378 } 379 ; 380 381 function : function_header opt_parameter_list RPAR opt_newline 382 LBRACE NEWLINE opt_auto_define_list 383 statement_list RBRACE 384 { 385 int n = node(prologue, $8, epilogue, 386 cs("0"), numnode(nesting), 387 cs("Q"), END_NODE); 388 emit_macro($1, n); 389 reset_macro_char = macro_char; 390 nesting = 0; 391 breaksp = 0; 392 } 393 ; 394 395 function_header : DEFINE LETTER LPAR 396 { 397 $$ = function_node($2); 398 free($2); 399 prologue = cs(""); 400 epilogue = cs(""); 401 nesting = 1; 402 breaksp = 0; 403 breakstack[breaksp] = 0; 404 } 405 ; 406 407 opt_newline : /* empty */ 408 | NEWLINE 409 ; 410 411 opt_parameter_list 412 : /* empty */ 413 | parameter_list 414 ; 415 416 417 parameter_list : LETTER 418 { 419 add_par(letter_node($1)); 420 free($1); 421 } 422 | LETTER LBRACKET RBRACKET 423 { 424 add_par(array_node($1)); 425 free($1); 426 } 427 | parameter_list COMMA LETTER 428 { 429 add_par(letter_node($3)); 430 free($3); 431 } 432 | parameter_list COMMA LETTER LBRACKET RBRACKET 433 { 434 add_par(array_node($3)); 435 free($3); 436 } 437 ; 438 439 440 441 opt_auto_define_list 442 : /* empty */ 443 | AUTO define_list NEWLINE 444 | AUTO define_list SEMICOLON 445 ; 446 447 448 define_list : LETTER 449 { 450 add_local(letter_node($1)); 451 free($1); 452 } 453 | LETTER LBRACKET RBRACKET 454 { 455 add_local(array_node($1)); 456 free($1); 457 } 458 | define_list COMMA LETTER 459 { 460 add_local(letter_node($3)); 461 free($3); 462 } 463 | define_list COMMA LETTER LBRACKET RBRACKET 464 { 465 add_local(array_node($3)); 466 free($3); 467 } 468 ; 469 470 471 opt_argument_list 472 : /* empty */ 473 { 474 $$ = cs(""); 475 } 476 | argument_list 477 ; 478 479 480 argument_list : expression 481 | argument_list COMMA expression 482 { 483 $$ = node($1, $3, END_NODE); 484 } 485 | argument_list COMMA LETTER LBRACKET RBRACKET 486 { 487 $$ = node($1, cs("l"), array_node($3), 488 END_NODE); 489 free($3); 490 } 491 ; 492 493 opt_relational_expression 494 : /* empty */ 495 { 496 $$ = cs(" 0 0="); 497 } 498 | relational_expression 499 ; 500 501 relational_expression 502 : expression EQUALS expression 503 { 504 $$ = node($1, $3, cs("="), END_NODE); 505 } 506 | expression UNEQUALS expression 507 { 508 $$ = node($1, $3, cs("!="), END_NODE); 509 } 510 | expression LESS expression 511 { 512 $$ = node($1, $3, cs(">"), END_NODE); 513 } 514 | expression LESS_EQ expression 515 { 516 $$ = node($1, $3, cs("!<"), END_NODE); 517 } 518 | expression GREATER expression 519 { 520 $$ = node($1, $3, cs("<"), END_NODE); 521 } 522 | expression GREATER_EQ expression 523 { 524 $$ = node($1, $3, cs("!>"), END_NODE); 525 } 526 | expression 527 { 528 $$ = node($1, cs(" 0!="), END_NODE); 529 } 530 ; 531 532 533 return_expression 534 : /* empty */ 535 { 536 $$ = node(cs("0"), epilogue, 537 numnode(nesting), cs("Q"), END_NODE); 538 } 539 | expression 540 { 541 $$ = node($1, epilogue, 542 numnode(nesting), cs("Q"), END_NODE); 543 } 544 | LPAR RPAR 545 { 546 $$ = node(cs("0"), epilogue, 547 numnode(nesting), cs("Q"), END_NODE); 548 } 549 ; 550 551 552 opt_expression : /* empty */ 553 { 554 $$ = cs(" 0"); 555 } 556 | expression 557 ; 558 559 expression : named_expression 560 { 561 $$ = node($1.load, END_NODE); 562 } 563 | DOT { 564 $$ = node(cs("l."), END_NODE); 565 } 566 | NUMBER 567 { 568 $$ = node(cs(" "), as($1), END_NODE); 569 } 570 | LPAR expression RPAR 571 { 572 $$ = $2; 573 } 574 | LETTER LPAR opt_argument_list RPAR 575 { 576 $$ = node($3, cs("l"), 577 function_node($1), cs("x"), 578 END_NODE); 579 free($1); 580 } 581 | MINUS expression %prec UMINUS 582 { 583 $$ = node(cs(" 0"), $2, cs("-"), 584 END_NODE); 585 } 586 | expression PLUS expression 587 { 588 $$ = node($1, $3, cs("+"), END_NODE); 589 } 590 | expression MINUS expression 591 { 592 $$ = node($1, $3, cs("-"), END_NODE); 593 } 594 | expression MULTIPLY expression 595 { 596 $$ = node($1, $3, cs("*"), END_NODE); 597 } 598 | expression DIVIDE expression 599 { 600 $$ = node($1, $3, cs("/"), END_NODE); 601 } 602 | expression REMAINDER expression 603 { 604 $$ = node($1, $3, cs("%"), END_NODE); 605 } 606 | expression EXPONENT expression 607 { 608 $$ = node($1, $3, cs("^"), END_NODE); 609 } 610 | INCR named_expression 611 { 612 $$ = node($2.load, cs("1+d"), $2.store, 613 END_NODE); 614 } 615 | DECR named_expression 616 { 617 $$ = node($2.load, cs("1-d"), 618 $2.store, END_NODE); 619 } 620 | named_expression INCR 621 { 622 $$ = node($1.load, cs("d1+"), 623 $1.store, END_NODE); 624 } 625 | named_expression DECR 626 { 627 $$ = node($1.load, cs("d1-"), 628 $1.store, END_NODE); 629 } 630 | named_expression ASSIGN_OP expression 631 { 632 if ($2[0] == '\0') 633 $$ = node($3, cs($2), cs("d"), $1.store, 634 END_NODE); 635 else 636 $$ = node($1.load, $3, cs($2), cs("d"), 637 $1.store, END_NODE); 638 } 639 | LENGTH LPAR expression RPAR 640 { 641 $$ = node($3, cs("Z"), END_NODE); 642 } 643 | SQRT LPAR expression RPAR 644 { 645 $$ = node($3, cs("v"), END_NODE); 646 } 647 | SCALE LPAR expression RPAR 648 { 649 $$ = node($3, cs("X"), END_NODE); 650 } 651 | BOOL_NOT expression 652 { 653 $$ = node($2, cs("N"), END_NODE); 654 } 655 | expression BOOL_AND alloc_macro pop_nesting expression 656 { 657 ssize_t n = node(cs("R"), $5, END_NODE); 658 emit_macro($3, n); 659 $$ = node($1, cs("d0!="), $3, END_NODE); 660 } 661 | expression BOOL_OR alloc_macro pop_nesting expression 662 { 663 ssize_t n = node(cs("R"), $5, END_NODE); 664 emit_macro($3, n); 665 $$ = node($1, cs("d0="), $3, END_NODE); 666 } 667 | expression EQUALS expression 668 { 669 $$ = node($1, $3, cs("G"), END_NODE); 670 } 671 | expression UNEQUALS expression 672 { 673 $$ = node($1, $3, cs("GN"), END_NODE); 674 } 675 | expression LESS expression 676 { 677 $$ = node($3, $1, cs("("), END_NODE); 678 } 679 | expression LESS_EQ expression 680 { 681 $$ = node($3, $1, cs("{"), END_NODE); 682 } 683 | expression GREATER expression 684 { 685 $$ = node($1, $3, cs("("), END_NODE); 686 } 687 | expression GREATER_EQ expression 688 { 689 $$ = node($1, $3, cs("{"), END_NODE); 690 } 691 ; 692 693 named_expression 694 : LETTER 695 { 696 $$.load = node(cs("l"), letter_node($1), 697 END_NODE); 698 $$.store = node(cs("s"), letter_node($1), 699 END_NODE); 700 free($1); 701 } 702 | LETTER LBRACKET expression RBRACKET 703 { 704 $$.load = node($3, cs(";"), 705 array_node($1), END_NODE); 706 $$.store = node($3, cs(":"), 707 array_node($1), END_NODE); 708 free($1); 709 } 710 | SCALE 711 { 712 $$.load = cs("K"); 713 $$.store = cs("k"); 714 } 715 | IBASE 716 { 717 $$.load = cs("I"); 718 $$.store = cs("i"); 719 } 720 | OBASE 721 { 722 $$.load = cs("O"); 723 $$.store = cs("o"); 724 } 725 ; 726 727 print_expression_list 728 : print_expression 729 | print_expression_list COMMA print_expression 730 { 731 $$ = node($1, $3, END_NODE); 732 } 733 734 print_expression 735 : expression 736 { 737 $$ = node($1, cs("ds.n"), END_NODE); 738 } 739 | STRING 740 { 741 char *p = escape($1); 742 $$ = node(cs("["), as(p), cs("]n"), END_NODE); 743 free(p); 744 } 745 %% 746 747 748 static void 749 grow(void) 750 { 751 struct tree *p; 752 size_t newsize; 753 754 if (current == instr_sz) { 755 newsize = instr_sz * 2 + 1; 756 p = realloc(instructions, newsize * sizeof(*p)); 757 if (p == NULL) { 758 free(instructions); 759 err(1, NULL); 760 } 761 instructions = p; 762 instr_sz = newsize; 763 } 764 } 765 766 static ssize_t 767 cs(const char *str) 768 { 769 grow(); 770 instructions[current].index = CONST_STRING; 771 instructions[current].u.cstr = str; 772 return current++; 773 } 774 775 static ssize_t 776 as(const char *str) 777 { 778 grow(); 779 instructions[current].index = ALLOC_STRING; 780 instructions[current].u.astr = strdup(str); 781 if (instructions[current].u.astr == NULL) 782 err(1, NULL); 783 return current++; 784 } 785 786 static ssize_t 787 node(ssize_t arg, ...) 788 { 789 va_list ap; 790 ssize_t ret; 791 792 va_start(ap, arg); 793 794 ret = current; 795 grow(); 796 instructions[current++].index = arg; 797 798 do { 799 arg = va_arg(ap, ssize_t); 800 grow(); 801 instructions[current++].index = arg; 802 } while (arg != END_NODE); 803 804 va_end(ap); 805 return ret; 806 } 807 808 static void 809 emit(ssize_t i) 810 { 811 if (instructions[i].index >= 0) 812 while (instructions[i].index != END_NODE) 813 emit(instructions[i++].index); 814 else 815 fputs(instructions[i].u.cstr, stdout); 816 } 817 818 static void 819 emit_macro(int node, ssize_t code) 820 { 821 putchar('['); 822 emit(code); 823 printf("]s%s\n", instructions[node].u.cstr); 824 nesting--; 825 } 826 827 static void 828 free_tree(void) 829 { 830 ssize_t i; 831 832 for (i = 0; i < current; i++) 833 if (instructions[i].index == ALLOC_STRING) 834 free(instructions[i].u.astr); 835 current = 0; 836 } 837 838 static ssize_t 839 numnode(int num) 840 { 841 const char *p; 842 843 if (num < 10) 844 p = str_table['0' + num]; 845 else if (num < 16) 846 p = str_table['A' - 10 + num]; 847 else 848 errx(1, "internal error: break num > 15"); 849 return node(cs(" "), cs(p), END_NODE); 850 } 851 852 853 static ssize_t 854 lookup(char * str, size_t len, char type) 855 { 856 ENTRY entry, *found; 857 u_short num; 858 u_char *p; 859 860 /* The scanner allocated an extra byte already */ 861 if (str[len-1] != type) { 862 str[len] = type; 863 str[len+1] = '\0'; 864 } 865 entry.key = str; 866 found = hsearch(entry, FIND); 867 if (found == NULL) { 868 if (var_count == MAX_VARIABLES) 869 errx(1, "too many variables"); 870 p = malloc(4); 871 if (p == NULL) 872 err(1, NULL); 873 num = var_count++; 874 p[0] = 255; 875 p[1] = ENCODE(num / VAR_BASE + 1); 876 p[2] = ENCODE(num % VAR_BASE + 1); 877 p[3] = '\0'; 878 879 entry.data = (char *)p; 880 entry.key = strdup(str); 881 if (entry.key == NULL) 882 err(1, NULL); 883 found = hsearch(entry, ENTER); 884 if (found == NULL) 885 err(1, NULL); 886 } 887 return cs(found->data); 888 } 889 890 static ssize_t 891 letter_node(char *str) 892 { 893 size_t len; 894 895 len = strlen(str); 896 if (len == 1 && str[0] != '_') 897 return cs(str_table[(int)str[0]]); 898 else 899 return lookup(str, len, 'L'); 900 } 901 902 static ssize_t 903 array_node(char *str) 904 { 905 size_t len; 906 907 len = strlen(str); 908 if (len == 1 && str[0] != '_') 909 return cs(str_table[(int)str[0] - 'a' + ARRAY_CHAR]); 910 else 911 return lookup(str, len, 'A'); 912 } 913 914 static ssize_t 915 function_node(char *str) 916 { 917 size_t len; 918 919 len = strlen(str); 920 if (len == 1 && str[0] != '_') 921 return cs(str_table[(int)str[0] - 'a' + FUNC_CHAR]); 922 else 923 return lookup(str, len, 'F'); 924 } 925 926 static void 927 add_par(ssize_t n) 928 { 929 prologue = node(cs("S"), n, prologue, END_NODE); 930 epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE); 931 } 932 933 static void 934 add_local(ssize_t n) 935 { 936 prologue = node(cs("0S"), n, prologue, END_NODE); 937 epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE); 938 } 939 940 void 941 yyerror(char *s) 942 { 943 char *str, *p; 944 int n; 945 946 if (yyin != NULL && feof(yyin)) 947 n = asprintf(&str, "%s: %s:%d: %s: unexpected EOF", 948 __progname, filename, lineno, s); 949 else if (yytext[0] == '\n') 950 n = asprintf(&str, 951 "%s: %s:%d: %s: newline unexpected", 952 __progname, filename, lineno, s); 953 else if (isspace(yytext[0]) || !isprint(yytext[0])) 954 n = asprintf(&str, 955 "%s: %s:%d: %s: ascii char 0x%02x unexpected", 956 __progname, filename, lineno, s, yytext[0]); 957 else 958 n = asprintf(&str, "%s: %s:%d: %s: %s unexpected", 959 __progname, filename, lineno, s, yytext); 960 if (n == -1) 961 err(1, NULL); 962 963 fputs("c[", stdout); 964 for (p = str; *p != '\0'; p++) { 965 if (*p == '[' || *p == ']' || *p =='\\') 966 putchar('\\'); 967 putchar(*p); 968 } 969 fputs("]pc\n", stdout); 970 free(str); 971 } 972 973 void 974 fatal(const char *s) 975 { 976 errx(1, "%s:%d: %s", filename, lineno, s); 977 } 978 979 static void 980 warning(const char *s) 981 { 982 warnx("%s:%d: %s", filename, lineno, s); 983 } 984 985 static void 986 init(void) 987 { 988 int i; 989 990 for (i = 0; i < UCHAR_MAX; i++) { 991 str_table[i][0] = i; 992 str_table[i][1] = '\0'; 993 } 994 if (hcreate(1 << 16) == 0) 995 err(1, NULL); 996 } 997 998 999 static __dead2 void 1000 usage(void) 1001 { 1002 fprintf(stderr, "usage: %s [-cl] [-e expression] [file ...]\n", 1003 __progname); 1004 exit(1); 1005 } 1006 1007 static char * 1008 escape(const char *str) 1009 { 1010 char *ret, *p; 1011 1012 ret = malloc(strlen(str) + 1); 1013 if (ret == NULL) 1014 err(1, NULL); 1015 1016 p = ret; 1017 while (*str != '\0') { 1018 /* 1019 * We get _escaped_ strings here. Single backslashes are 1020 * already converted to double backslashes 1021 */ 1022 if (*str == '\\') { 1023 if (*++str == '\\') { 1024 switch (*++str) { 1025 case 'a': 1026 *p++ = '\a'; 1027 break; 1028 case 'b': 1029 *p++ = '\b'; 1030 break; 1031 case 'f': 1032 *p++ = '\f'; 1033 break; 1034 case 'n': 1035 *p++ = '\n'; 1036 break; 1037 case 'q': 1038 *p++ = '"'; 1039 break; 1040 case 'r': 1041 *p++ = '\r'; 1042 break; 1043 case 't': 1044 *p++ = '\t'; 1045 break; 1046 case '\\': 1047 *p++ = '\\'; 1048 break; 1049 } 1050 str++; 1051 } else { 1052 *p++ = '\\'; 1053 *p++ = *str++; 1054 } 1055 } else 1056 *p++ = *str++; 1057 } 1058 *p = '\0'; 1059 return ret; 1060 } 1061 1062 /* ARGSUSED */ 1063 static void 1064 sigchld(int signo) 1065 { 1066 pid_t pid; 1067 int status, save_errno = errno; 1068 1069 for (;;) { 1070 pid = waitpid(dc, &status, WCONTINUED | WNOHANG); 1071 if (pid == -1) { 1072 if (errno == EINTR) 1073 continue; 1074 _exit(0); 1075 } else if (pid == 0) 1076 break; 1077 if (WIFEXITED(status) || WIFSIGNALED(status)) 1078 _exit(0); 1079 else 1080 break; 1081 } 1082 errno = save_errno; 1083 } 1084 1085 static const char * 1086 dummy_prompt(void) 1087 { 1088 return (""); 1089 } 1090 1091 int 1092 main(int argc, char *argv[]) 1093 { 1094 int i, ch; 1095 int p[2]; 1096 char *q; 1097 1098 init(); 1099 setlinebuf(stdout); 1100 1101 sargv = malloc(argc * sizeof(char *)); 1102 if (sargv == NULL) 1103 err(1, NULL); 1104 1105 if ((cmdexpr = strdup("")) == NULL) 1106 err(1, NULL); 1107 /* The d debug option is 4.4 BSD bc(1) compatible */ 1108 while ((ch = getopt(argc, argv, "cde:l")) != -1) { 1109 switch (ch) { 1110 case 'c': 1111 case 'd': 1112 do_fork = false; 1113 break; 1114 case 'e': 1115 q = cmdexpr; 1116 if (asprintf(&cmdexpr, "%s%s\n", cmdexpr, optarg) == -1) 1117 err(1, NULL); 1118 free(q); 1119 break; 1120 case 'l': 1121 sargv[sargc++] = _PATH_LIBB; 1122 break; 1123 default: 1124 usage(); 1125 } 1126 } 1127 1128 argc -= optind; 1129 argv += optind; 1130 1131 interactive = isatty(STDIN_FILENO); 1132 for (i = 0; i < argc; i++) 1133 sargv[sargc++] = argv[i]; 1134 1135 if (do_fork) { 1136 if (pipe(p) == -1) 1137 err(1, "cannot create pipe"); 1138 dc = fork(); 1139 if (dc == -1) 1140 err(1, "cannot fork"); 1141 else if (dc != 0) { 1142 signal(SIGCHLD, sigchld); 1143 close(STDOUT_FILENO); 1144 dup(p[1]); 1145 close(p[0]); 1146 close(p[1]); 1147 } else { 1148 close(STDIN_FILENO); 1149 dup(p[0]); 1150 close(p[0]); 1151 close(p[1]); 1152 execl(_PATH_DC, "dc", "-x", NULL); 1153 err(1, "cannot find dc"); 1154 } 1155 } 1156 if (interactive) { 1157 gettty(&ttysaved); 1158 el = el_init("bc", stdin, stderr, stderr); 1159 hist = history_init(); 1160 history(hist, &he, H_SETSIZE, 100); 1161 el_set(el, EL_HIST, history, hist); 1162 el_set(el, EL_EDITOR, "emacs"); 1163 el_set(el, EL_SIGNAL, 1); 1164 el_set(el, EL_SIGNAL, 0); 1165 el_set(el, EL_PROMPT, dummy_prompt); 1166 el_set(el, EL_ADDFN, "bc_eof", "", bc_eof); 1167 el_set(el, EL_BIND, "^D", "bc_eof", NULL); 1168 el_source(el, NULL); 1169 } 1170 yywrap(); 1171 return yyparse(); 1172 } 1173