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