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