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