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