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