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