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