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