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