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