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