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