1 %{ 2 /* $OpenBSD: bc.y,v 1.47 2014/11/26 18:34:51 millert 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); 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); 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) 807 { 808 if (instructions[i].index >= 0) 809 while (instructions[i].index != END_NODE) 810 emit(instructions[i++].index); 811 else 812 fputs(instructions[i].u.cstr, stdout); 813 } 814 815 static void 816 emit_macro(int node, ssize_t code) 817 { 818 putchar('['); 819 emit(code); 820 printf("]s%s\n", instructions[node].u.cstr); 821 nesting--; 822 } 823 824 static void 825 free_tree(void) 826 { 827 ssize_t i; 828 829 for (i = 0; i < current; i++) 830 if (instructions[i].index == ALLOC_STRING) 831 free(instructions[i].u.astr); 832 current = 0; 833 } 834 835 static ssize_t 836 numnode(int num) 837 { 838 const char *p; 839 840 if (num < 10) 841 p = str_table['0' + num]; 842 else if (num < 16) 843 p = str_table['A' - 10 + num]; 844 else 845 errx(1, "internal error: break num > 15"); 846 return node(cs(" "), cs(p), END_NODE); 847 } 848 849 850 static ssize_t 851 lookup(char * str, size_t len, char type) 852 { 853 ENTRY entry, *found; 854 u_short num; 855 u_char *p; 856 857 /* The scanner allocated an extra byte already */ 858 if (str[len-1] != type) { 859 str[len] = type; 860 str[len+1] = '\0'; 861 } 862 entry.key = str; 863 found = hsearch(entry, FIND); 864 if (found == NULL) { 865 if (var_count == MAX_VARIABLES) 866 errx(1, "too many variables"); 867 p = malloc(4); 868 if (p == NULL) 869 err(1, NULL); 870 num = var_count++; 871 p[0] = 255; 872 p[1] = ENCODE(num / VAR_BASE + 1); 873 p[2] = ENCODE(num % VAR_BASE + 1); 874 p[3] = '\0'; 875 876 entry.data = (char *)p; 877 entry.key = strdup(str); 878 if (entry.key == NULL) 879 err(1, NULL); 880 found = hsearch(entry, ENTER); 881 if (found == NULL) 882 err(1, NULL); 883 } 884 return cs(found->data); 885 } 886 887 static ssize_t 888 letter_node(char *str) 889 { 890 size_t len; 891 892 len = strlen(str); 893 if (len == 1 && str[0] != '_') 894 return cs(str_table[(int)str[0]]); 895 else 896 return lookup(str, len, 'L'); 897 } 898 899 static ssize_t 900 array_node(char *str) 901 { 902 size_t len; 903 904 len = strlen(str); 905 if (len == 1 && str[0] != '_') 906 return cs(str_table[(int)str[0] - 'a' + ARRAY_CHAR]); 907 else 908 return lookup(str, len, 'A'); 909 } 910 911 static ssize_t 912 function_node(char *str) 913 { 914 size_t len; 915 916 len = strlen(str); 917 if (len == 1 && str[0] != '_') 918 return cs(str_table[(int)str[0] - 'a' + FUNC_CHAR]); 919 else 920 return lookup(str, len, 'F'); 921 } 922 923 static void 924 add_par(ssize_t n) 925 { 926 prologue = node(cs("S"), n, prologue, END_NODE); 927 epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE); 928 } 929 930 static void 931 add_local(ssize_t n) 932 { 933 prologue = node(cs("0S"), n, prologue, END_NODE); 934 epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE); 935 } 936 937 void 938 yyerror(char *s) 939 { 940 char *str, *p; 941 int n; 942 943 if (yyin != NULL && feof(yyin)) 944 n = asprintf(&str, "%s: %s:%d: %s: unexpected EOF", 945 __progname, filename, lineno, s); 946 else if (yytext[0] == '\n') 947 n = asprintf(&str, 948 "%s: %s:%d: %s: newline unexpected", 949 __progname, filename, lineno, s); 950 else if (isspace((unsigned char)yytext[0]) || 951 !isprint((unsigned char)yytext[0])) 952 n = asprintf(&str, 953 "%s: %s:%d: %s: ascii char 0x%02x unexpected", 954 __progname, filename, lineno, s, yytext[0]); 955 else 956 n = asprintf(&str, "%s: %s:%d: %s: %s unexpected", 957 __progname, filename, lineno, s, yytext); 958 if (n == -1) 959 err(1, NULL); 960 961 fputs("c[", stdout); 962 for (p = str; *p != '\0'; p++) { 963 if (*p == '[' || *p == ']' || *p =='\\') 964 putchar('\\'); 965 putchar(*p); 966 } 967 fputs("]pc\n", stdout); 968 free(str); 969 } 970 971 void 972 fatal(const char *s) 973 { 974 errx(1, "%s:%d: %s", filename, lineno, s); 975 } 976 977 static void 978 warning(const char *s) 979 { 980 warnx("%s:%d: %s", filename, lineno, s); 981 } 982 983 static void 984 init(void) 985 { 986 int i; 987 988 for (i = 0; i < UCHAR_MAX; i++) { 989 str_table[i][0] = i; 990 str_table[i][1] = '\0'; 991 } 992 if (hcreate(1 << 16) == 0) 993 err(1, NULL); 994 } 995 996 997 static __dead void 998 usage(void) 999 { 1000 fprintf(stderr, "usage: %s [-cl] [-e expression] [file ...]\n", 1001 __progname); 1002 exit(1); 1003 } 1004 1005 static char * 1006 escape(const char *str) 1007 { 1008 char *ret, *p; 1009 1010 ret = malloc(strlen(str) + 1); 1011 if (ret == NULL) 1012 err(1, NULL); 1013 1014 p = ret; 1015 while (*str != '\0') { 1016 /* 1017 * We get _escaped_ strings here. Single backslashes are 1018 * already converted to double backslashes 1019 */ 1020 if (*str == '\\') { 1021 if (*++str == '\\') { 1022 switch (*++str) { 1023 case 'a': 1024 *p++ = '\a'; 1025 break; 1026 case 'b': 1027 *p++ = '\b'; 1028 break; 1029 case 'f': 1030 *p++ = '\f'; 1031 break; 1032 case 'n': 1033 *p++ = '\n'; 1034 break; 1035 case 'q': 1036 *p++ = '"'; 1037 break; 1038 case 'r': 1039 *p++ = '\r'; 1040 break; 1041 case 't': 1042 *p++ = '\t'; 1043 break; 1044 case '\\': 1045 *p++ = '\\'; 1046 break; 1047 } 1048 str++; 1049 } else { 1050 *p++ = '\\'; 1051 *p++ = *str++; 1052 } 1053 } else 1054 *p++ = *str++; 1055 } 1056 *p = '\0'; 1057 return ret; 1058 } 1059 1060 /* ARGSUSED */ 1061 static void 1062 sigchld(int signo) 1063 { 1064 pid_t pid; 1065 int status, save_errno = errno; 1066 1067 for (;;) { 1068 pid = waitpid(dc, &status, WCONTINUED | WNOHANG); 1069 if (pid == -1) { 1070 if (errno == EINTR) 1071 continue; 1072 _exit(0); 1073 } else if (pid == 0) 1074 break; 1075 if (WIFEXITED(status) || WIFSIGNALED(status)) 1076 _exit(0); 1077 else 1078 break; 1079 } 1080 errno = save_errno; 1081 } 1082 1083 static const char * 1084 dummy_prompt(void) 1085 { 1086 1087 return (""); 1088 } 1089 1090 int 1091 main(int argc, char *argv[]) 1092 { 1093 int i, ch; 1094 int p[2]; 1095 char *q; 1096 1097 init(); 1098 setvbuf(stdout, NULL, _IOLBF, 0); 1099 1100 sargv = reallocarray(NULL, argc, sizeof(char *)); 1101 if (sargv == NULL) 1102 err(1, NULL); 1103 1104 if ((cmdexpr = strdup("")) == NULL) 1105 err(1, NULL); 1106 /* The d debug option is 4.4 BSD bc(1) compatible */ 1107 while ((ch = getopt(argc, argv, "cde:l")) != -1) { 1108 switch (ch) { 1109 case 'c': 1110 case 'd': 1111 do_fork = false; 1112 break; 1113 case 'e': 1114 q = cmdexpr; 1115 if (asprintf(&cmdexpr, "%s%s\n", cmdexpr, optarg) == -1) 1116 err(1, NULL); 1117 free(q); 1118 break; 1119 case 'l': 1120 sargv[sargc++] = _PATH_LIBB; 1121 break; 1122 default: 1123 usage(); 1124 } 1125 } 1126 1127 argc -= optind; 1128 argv += optind; 1129 1130 interactive = isatty(STDIN_FILENO); 1131 for (i = 0; i < argc; i++) 1132 sargv[sargc++] = argv[i]; 1133 1134 if (do_fork) { 1135 if (pipe(p) == -1) 1136 err(1, "cannot create pipe"); 1137 dc = fork(); 1138 if (dc == -1) 1139 err(1, "cannot fork"); 1140 else if (dc != 0) { 1141 signal(SIGCHLD, sigchld); 1142 close(STDOUT_FILENO); 1143 dup(p[1]); 1144 close(p[0]); 1145 close(p[1]); 1146 } else { 1147 close(STDIN_FILENO); 1148 dup(p[0]); 1149 close(p[0]); 1150 close(p[1]); 1151 execl(_PATH_DC, "dc", "-x", (char *)NULL); 1152 err(1, "cannot find dc"); 1153 } 1154 } 1155 if (interactive) { 1156 gettty(&ttysaved); 1157 el = el_init("bc", stdin, stderr, stderr); 1158 hist = history_init(); 1159 history(hist, &he, H_SETSIZE, 100); 1160 el_set(el, EL_HIST, history, hist); 1161 el_set(el, EL_EDITOR, "emacs"); 1162 el_set(el, EL_SIGNAL, 0); 1163 el_set(el, EL_PROMPT, dummy_prompt); 1164 el_set(el, EL_ADDFN, "bc_eof", "", bc_eof); 1165 el_set(el, EL_BIND, "^D", "bc_eof", NULL); 1166 el_source(el, NULL); 1167 } 1168 yywrap(); 1169 return yyparse(); 1170 } 1171