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