bc.y revision 202845
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: head/usr.bin/bc/bc.y 202845 2010-01-22 23:35:06Z delphij $");
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 <limits.h>
44#include <search.h>
45#include <signal.h>
46#include <stdarg.h>
47#include <stdbool.h>
48#include <string.h>
49#include <unistd.h>
50
51#include "extern.h"
52#include "pathnames.h"
53
54#define BC_VER		"1.0-FreeBSD"
55#define END_NODE	((ssize_t) -1)
56#define CONST_STRING	((ssize_t) -2)
57#define ALLOC_STRING	((ssize_t) -3)
58
59extern char	*yytext;
60extern FILE	*yyin;
61
62struct tree {
63	ssize_t			index;
64	union {
65		char		*astr;
66		const char	*cstr;
67	} u;
68};
69
70int			 yyparse(void);
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	ssize_t		 node;
147	struct lvalue	 lvalue;
148	const char	*str;
149	char		*astr;
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	grow();
788	instructions[current].index = CONST_STRING;
789	instructions[current].u.cstr = str;
790	return (current++);
791}
792
793static ssize_t
794as(const char *str)
795{
796	grow();
797	instructions[current].index = ALLOC_STRING;
798	instructions[current].u.astr = strdup(str);
799	if (instructions[current].u.astr == NULL)
800		err(1, NULL);
801	return (current++);
802}
803
804static ssize_t
805node(ssize_t arg, ...)
806{
807	va_list		 ap;
808	ssize_t		 ret;
809
810	va_start(ap, arg);
811
812	ret = current;
813	grow();
814	instructions[current++].index = arg;
815
816	do {
817		arg = va_arg(ap, ssize_t);
818		grow();
819		instructions[current++].index = arg;
820	} while (arg != END_NODE);
821
822	va_end(ap);
823	return (ret);
824}
825
826static void
827emit(ssize_t i)
828{
829	if (instructions[i].index >= 0)
830		while (instructions[i].index != END_NODE)
831			emit(instructions[i++].index);
832	else
833		fputs(instructions[i].u.cstr, stdout);
834}
835
836static void
837emit_macro(int nodeidx, ssize_t code)
838{
839	putchar('[');
840	emit(code);
841	printf("]s%s\n", instructions[nodeidx].u.cstr);
842	nesting--;
843}
844
845static void
846free_tree(void)
847{
848	ssize_t	 i;
849
850	for (i = 0; i < current; i++)
851		if (instructions[i].index == ALLOC_STRING)
852			free(instructions[i].u.astr);
853	current = 0;
854}
855
856static ssize_t
857numnode(int num)
858{
859	const char	*p;
860
861	if (num < 10)
862		p = str_table['0' + num];
863	else if (num < 16)
864		p = str_table['A' - 10 + num];
865	else
866		errx(1, "internal error: break num > 15");
867	return (node(cs(" "), cs(p), END_NODE));
868}
869
870
871static ssize_t
872lookup(char * str, size_t len, char type)
873{
874	ENTRY		 entry, *found;
875	u_short		 num;
876	u_char		*p;
877
878	/* The scanner allocated an extra byte already */
879	if (str[len-1] != type) {
880		str[len] = type;
881		str[len+1] = '\0';
882	}
883	entry.key = str;
884	found = hsearch(entry, FIND);
885	if (found == NULL) {
886		if (var_count == MAX_VARIABLES)
887			errx(1, "too many variables");
888		p = malloc(4);
889		if (p == NULL)
890			err(1, NULL);
891		num = var_count++;
892		p[0] = 255;
893		p[1] = ENCODE(num / VAR_BASE + 1);
894		p[2] = ENCODE(num % VAR_BASE + 1);
895		p[3] = '\0';
896
897		entry.data = (char *)p;
898		entry.key = strdup(str);
899		if (entry.key == NULL)
900			err(1, NULL);
901		found = hsearch(entry, ENTER);
902		if (found == NULL)
903			err(1, NULL);
904	}
905	return (cs(found->data));
906}
907
908static ssize_t
909letter_node(char *str)
910{
911	size_t	 len;
912
913	len = strlen(str);
914	if (len == 1 && str[0] != '_')
915		return (cs(str_table[(int)str[0]]));
916	else
917		return (lookup(str, len, 'L'));
918}
919
920static ssize_t
921array_node(char *str)
922{
923	size_t	 len;
924
925	len = strlen(str);
926	if (len == 1 && str[0] != '_')
927		return (cs(str_table[(int)str[0] - 'a' + ARRAY_CHAR]));
928	else
929		return (lookup(str, len, 'A'));
930}
931
932static ssize_t
933function_node(char *str)
934{
935	size_t	 len;
936
937	len = strlen(str);
938	if (len == 1 && str[0] != '_')
939		return (cs(str_table[(int)str[0] - 'a' + FUNC_CHAR]));
940	else
941		return (lookup(str, len, 'F'));
942}
943
944static void
945add_par(ssize_t n)
946{
947	prologue = node(cs("S"), n, prologue, END_NODE);
948	epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE);
949}
950
951static void
952add_local(ssize_t n)
953{
954	prologue = node(cs("0S"), n, prologue, END_NODE);
955	epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE);
956}
957
958void
959yyerror(const char *s)
960{
961	char	*str, *p;
962	int	 n;
963
964	if (yyin != NULL && feof(yyin))
965		n = asprintf(&str, "%s: %s:%d: %s: unexpected EOF",
966		    __progname, filename, lineno, s);
967	else if (isspace(yytext[0]) || !isprint(yytext[0]))
968		n = asprintf(&str,
969		    "%s: %s:%d: %s: ascii char 0x%02x unexpected",
970		    __progname, filename, lineno, s, yytext[0]);
971	else
972		n = asprintf(&str, "%s: %s:%d: %s: %s unexpected",
973		    __progname, filename, lineno, s, yytext);
974	if (n == -1)
975		err(1, NULL);
976
977	fputs("c[", stdout);
978	for (p = str; *p != '\0'; p++) {
979		if (*p == '[' || *p == ']' || *p =='\\')
980			putchar('\\');
981		putchar(*p);
982	}
983	fputs("]pc\n", stdout);
984	free(str);
985}
986
987void
988fatal(const char *s)
989{
990	errx(1, "%s:%d: %s", filename, lineno, s);
991}
992
993static void
994warning(const char *s)
995{
996	warnx("%s:%d: %s", filename, lineno, s);
997}
998
999static void
1000init(void)
1001{
1002	unsigned int	 i;
1003
1004	for (i = 0; i < UCHAR_MAX; i++) {
1005		str_table[i][0] = i;
1006		str_table[i][1] = '\0';
1007	}
1008	if (hcreate(1 << 16) == 0)
1009		err(1, NULL);
1010}
1011
1012
1013static void
1014usage(void)
1015{
1016	fprintf(stderr, "usage: %s [-chlqv] [-e expression] [file ...]\n",
1017	    __progname);
1018	exit(1);
1019}
1020
1021static char *
1022escape(const char *str)
1023{
1024	char	*ret, *p;
1025
1026	ret = malloc(strlen(str) + 1);
1027	if (ret == NULL)
1028		err(1, NULL);
1029
1030	p = ret;
1031	while (*str != '\0') {
1032		/*
1033		 * We get _escaped_ strings here. Single backslashes are
1034		 * already converted to double backslashes
1035		 */
1036		if (*str == '\\') {
1037			if (*++str == '\\') {
1038				switch (*++str) {
1039				case 'a':
1040					*p++ = '\a';
1041					break;
1042				case 'b':
1043					*p++ = '\b';
1044					break;
1045				case 'f':
1046					*p++ = '\f';
1047					break;
1048				case 'n':
1049					*p++ = '\n';
1050					break;
1051				case 'q':
1052					*p++ = '"';
1053					break;
1054				case 'r':
1055					*p++ = '\r';
1056					break;
1057				case 't':
1058					*p++ = '\t';
1059					break;
1060				case '\\':
1061					*p++ = '\\';
1062					break;
1063				}
1064				str++;
1065			} else {
1066				*p++ = '\\';
1067				*p++ = *str++;
1068			}
1069		} else
1070			*p++ = *str++;
1071	}
1072	*p = '\0';
1073	return (ret);
1074}
1075
1076/* ARGSUSED */
1077void
1078sigchld(int signo)
1079{
1080	pid_t	 pid;
1081	int	 status;
1082
1083	switch (signo) {
1084	default:
1085		for (;;) {
1086			pid = waitpid(dc, &status, WCONTINUED);
1087			if (pid == -1) {
1088				if (errno == EINTR)
1089					continue;
1090				_exit(0);
1091			}
1092			if (WIFEXITED(status) || WIFSIGNALED(status))
1093				_exit(0);
1094			else
1095				break;
1096		}
1097	}
1098}
1099
1100int
1101main(int argc, char *argv[])
1102{
1103	int	 i, ch;
1104	int	 p[2];
1105	char	*q;
1106
1107	init();
1108	setlinebuf(stdout);
1109
1110	sargv = malloc(argc * sizeof(char *));
1111	if (sargv == NULL)
1112		err(1, NULL);
1113
1114	if ((cmdexpr = strdup("")) == NULL)
1115		err(1, NULL);
1116	/* The d debug option is 4.4 BSD bc(1) compatible */
1117	while ((ch = getopt_long(argc, argv, "cde:hlqv",
1118	   long_options, NULL)) != -1) {
1119		switch (ch) {
1120		case 'c':
1121		case 'd':
1122			do_fork = false;
1123			break;
1124		case 'e':
1125			q = cmdexpr;
1126			if (asprintf(&cmdexpr, "%s%s\n", cmdexpr, optarg) == -1)
1127				err(1, NULL);
1128			free(q);
1129			break;
1130		case 'h':
1131			usage();
1132			break;
1133		case 'l':
1134			sargv[sargc++] = _PATH_LIBB;
1135			break;
1136		case 'q':
1137			/* compatibility option */
1138			break;
1139		case 'v':
1140			fprintf(stderr, "%s (BSD bc) %s\n", __progname, BC_VER);
1141			exit(0);
1142			break;
1143		default:
1144			usage();
1145		}
1146	}
1147
1148	argc -= optind;
1149	argv += optind;
1150
1151	interactive = isatty(STDIN_FILENO);
1152	for (i = 0; i < argc; i++)
1153		sargv[sargc++] = argv[i];
1154
1155	if (do_fork) {
1156		if (pipe(p) == -1)
1157			err(1, "cannot create pipe");
1158		dc = fork();
1159		if (dc == -1)
1160			err(1, "cannot fork");
1161		else if (dc != 0) {
1162			signal(SIGCHLD, sigchld);
1163			close(STDOUT_FILENO);
1164			dup(p[1]);
1165			close(p[0]);
1166			close(p[1]);
1167		} else {
1168			close(STDIN_FILENO);
1169			dup(p[0]);
1170			close(p[0]);
1171			close(p[1]);
1172			execl(_PATH_DC, "dc", "-x", (char *)NULL);
1173			err(1, "cannot find dc");
1174		}
1175	}
1176	yywrap();
1177	return (yyparse());
1178}
1179