fatbase

portable OpenBSD tools
git clone git@git.2f30.org/fatbase.git
Log | Files | Refs

bc.y (22533B)


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