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