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