1202719Sgabor%{
2202719Sgabor/*	$OpenBSD: bc.y,v 1.33 2009/10/27 23:59:36 deraadt Exp $	*/
3202719Sgabor
4202719Sgabor/*
5202719Sgabor * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net>
6202719Sgabor *
7202719Sgabor * Permission to use, copy, modify, and distribute this software for any
8202719Sgabor * purpose with or without fee is hereby granted, provided that the above
9202719Sgabor * copyright notice and this permission notice appear in all copies.
10202719Sgabor *
11202719Sgabor * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12202719Sgabor * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13202719Sgabor * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14202719Sgabor * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15202719Sgabor * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16202719Sgabor * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17202719Sgabor * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18202719Sgabor */
19202719Sgabor
20202719Sgabor/*
21202719Sgabor * This implementation of bc(1) uses concepts from the original 4.4
22202719Sgabor * BSD bc(1). The code itself is a complete rewrite, based on the
23202719Sgabor * Posix defined bc(1) grammar. Other differences include type safe
24202719Sgabor * usage of pointers to build the tree of emitted code, typed yacc
25202719Sgabor * rule values, dynamic allocation of all data structures and a
26202719Sgabor * completely rewritten lexical analyzer using lex(1).
27202719Sgabor *
28202719Sgabor * Some effort has been made to make sure that the generated code is
29202719Sgabor * the same as the code generated by the older version, to provide
30202719Sgabor * easy regression testing.
31202719Sgabor */
32202719Sgabor
33202719Sgabor#include <sys/cdefs.h>
34202719Sgabor__FBSDID("$FreeBSD$");
35202719Sgabor
36202719Sgabor#include <sys/types.h>
37202719Sgabor#include <sys/wait.h>
38202719Sgabor
39202719Sgabor#include <ctype.h>
40202719Sgabor#include <err.h>
41202719Sgabor#include <errno.h>
42202719Sgabor#include <getopt.h>
43203498Sdelphij#include <histedit.h>
44202719Sgabor#include <limits.h>
45202719Sgabor#include <search.h>
46202719Sgabor#include <signal.h>
47202719Sgabor#include <stdarg.h>
48202719Sgabor#include <stdbool.h>
49202719Sgabor#include <string.h>
50202719Sgabor#include <unistd.h>
51233119Skevlo#include <stdlib.h>
52202719Sgabor
53202719Sgabor#include "extern.h"
54202719Sgabor#include "pathnames.h"
55202719Sgabor
56202719Sgabor#define BC_VER		"1.0-FreeBSD"
57202719Sgabor#define END_NODE	((ssize_t) -1)
58202719Sgabor#define CONST_STRING	((ssize_t) -2)
59202719Sgabor#define ALLOC_STRING	((ssize_t) -3)
60202719Sgabor
61202719Sgaborextern char	*yytext;
62202719Sgaborextern FILE	*yyin;
63202719Sgabor
64202719Sgaborstruct tree {
65202719Sgabor	union {
66202719Sgabor		char		*astr;
67202719Sgabor		const char	*cstr;
68202719Sgabor	} u;
69203443Sgabor	ssize_t			index;
70202719Sgabor};
71202719Sgabor
72202719Sgaborint			 yyparse(void);
73202719Sgaborint			 yywrap(void);
74202719Sgabor
75202719Sgaborint			 fileindex;
76202719Sgaborint			 sargc;
77202719Sgaborconst char		**sargv;
78202719Sgaborconst char		*filename;
79202719Sgaborchar			*cmdexpr;
80202719Sgabor
81202719Sgaborstatic void		 grow(void);
82202719Sgaborstatic ssize_t		 cs(const char *);
83202719Sgaborstatic ssize_t		 as(const char *);
84202719Sgaborstatic ssize_t		 node(ssize_t, ...);
85202719Sgaborstatic void		 emit(ssize_t);
86202719Sgaborstatic void		 emit_macro(int, ssize_t);
87202719Sgaborstatic void		 free_tree(void);
88202719Sgaborstatic ssize_t		 numnode(int);
89202719Sgaborstatic ssize_t		 lookup(char *, size_t, char);
90202719Sgaborstatic ssize_t		 letter_node(char *);
91202719Sgaborstatic ssize_t		 array_node(char *);
92202719Sgaborstatic ssize_t		 function_node(char *);
93202719Sgabor
94202719Sgaborstatic void		 add_par(ssize_t);
95202719Sgaborstatic void		 add_local(ssize_t);
96202719Sgaborstatic void		 warning(const char *);
97202719Sgaborstatic void		 init(void);
98202719Sgaborstatic void		 usage(void);
99202719Sgaborstatic char		*escape(const char *);
100202719Sgabor
101202719Sgaborstatic ssize_t		 instr_sz = 0;
102202719Sgaborstatic struct tree	*instructions = NULL;
103202719Sgaborstatic ssize_t		 current = 0;
104202719Sgaborstatic int		 macro_char = '0';
105202719Sgaborstatic int		 reset_macro_char = '0';
106202719Sgaborstatic int		 nesting = 0;
107202719Sgaborstatic int		 breakstack[16];
108202719Sgaborstatic int		 breaksp = 0;
109202719Sgaborstatic ssize_t		 prologue;
110202719Sgaborstatic ssize_t		 epilogue;
111202719Sgaborstatic bool		 st_has_continue;
112202719Sgaborstatic char		 str_table[UCHAR_MAX][2];
113202719Sgaborstatic bool		 do_fork = true;
114202719Sgaborstatic u_short		 var_count;
115202719Sgaborstatic pid_t		 dc;
116202719Sgabor
117202719Sgaborstatic void		 sigchld(int);
118202719Sgabor
119202719Sgaborextern char		*__progname;
120202719Sgabor
121202719Sgabor#define BREAKSTACK_SZ	(sizeof(breakstack)/sizeof(breakstack[0]))
122202719Sgabor
123202719Sgabor/* These values are 4.4BSD bc compatible */
124202719Sgabor#define FUNC_CHAR	0x01
125202719Sgabor#define ARRAY_CHAR	0xa1
126202719Sgabor
127202719Sgabor/* Skip '\0', [, \ and ] */
128202719Sgabor#define ENCODE(c)	((c) < '[' ? (c) : (c) + 3);
129202719Sgabor#define VAR_BASE	(256-4)
130202719Sgabor#define MAX_VARIABLES	(VAR_BASE * VAR_BASE)
131202719Sgabor
132202719Sgaborconst struct option long_options[] =
133202719Sgabor{
134202719Sgabor	{"expression",	required_argument,	NULL,	'e'},
135202719Sgabor	{"help",	no_argument,		NULL,	'h'},
136202719Sgabor	{"mathlib",	no_argument,		NULL,	'l'},
137202719Sgabor	/* compatibility option */
138202719Sgabor	{"quiet",	no_argument,		NULL,	'q'},
139202719Sgabor	{"version",	no_argument,		NULL,	'v'},
140202719Sgabor	{NULL,		no_argument,		NULL,	0}
141202719Sgabor};
142202719Sgabor
143202719Sgabor%}
144202719Sgabor
145202719Sgabor%start program
146202719Sgabor
147202719Sgabor%union {
148202719Sgabor	struct lvalue	 lvalue;
149202719Sgabor	const char	*str;
150202719Sgabor	char		*astr;
151203443Sgabor	ssize_t		 node;
152202719Sgabor}
153202719Sgabor
154202719Sgabor%token COMMA SEMICOLON LPAR RPAR LBRACE RBRACE LBRACKET RBRACKET DOT
155202719Sgabor%token NEWLINE
156202719Sgabor%token <astr> LETTER
157202719Sgabor%token <str> NUMBER STRING
158202719Sgabor%token DEFINE BREAK QUIT LENGTH
159202719Sgabor%token RETURN FOR IF WHILE SQRT
160202719Sgabor%token SCALE IBASE OBASE AUTO
161202719Sgabor%token CONTINUE ELSE PRINT
162202719Sgabor
163202719Sgabor%left BOOL_OR
164202719Sgabor%left BOOL_AND
165202719Sgabor%nonassoc BOOL_NOT
166202719Sgabor%nonassoc EQUALS LESS_EQ GREATER_EQ UNEQUALS LESS GREATER
167202719Sgabor%right <str> ASSIGN_OP
168202719Sgabor%left PLUS MINUS
169202719Sgabor%left MULTIPLY DIVIDE REMAINDER
170202719Sgabor%right EXPONENT
171202719Sgabor%nonassoc UMINUS
172202719Sgabor%nonassoc INCR DECR
173202719Sgabor
174202719Sgabor%type <lvalue>	named_expression
175202719Sgabor%type <node>	argument_list
176202719Sgabor%type <node>	alloc_macro
177202719Sgabor%type <node>	expression
178202719Sgabor%type <node>	function
179202719Sgabor%type <node>	function_header
180202719Sgabor%type <node>	input_item
181202719Sgabor%type <node>	opt_argument_list
182202719Sgabor%type <node>	opt_expression
183202719Sgabor%type <node>	opt_relational_expression
184202719Sgabor%type <node>	opt_statement
185202719Sgabor%type <node>	print_expression
186202719Sgabor%type <node>	print_expression_list
187202719Sgabor%type <node>	relational_expression
188202719Sgabor%type <node>	return_expression
189202719Sgabor%type <node>	semicolon_list
190202719Sgabor%type <node>	statement
191202719Sgabor%type <node>	statement_list
192202719Sgabor
193202719Sgabor%%
194202719Sgabor
195202719Sgaborprogram		: /* empty */
196202719Sgabor		| program input_item
197202719Sgabor		;
198202719Sgabor
199202719Sgaborinput_item	: semicolon_list NEWLINE
200202719Sgabor			{
201202719Sgabor				emit($1);
202202719Sgabor				macro_char = reset_macro_char;
203202719Sgabor				putchar('\n');
204202719Sgabor				free_tree();
205202719Sgabor				st_has_continue = false;
206202719Sgabor			}
207202719Sgabor		| function
208202719Sgabor			{
209202719Sgabor				putchar('\n');
210202719Sgabor				free_tree();
211202719Sgabor				st_has_continue = false;
212202719Sgabor			}
213202719Sgabor		| error NEWLINE
214202719Sgabor			{
215202719Sgabor				yyerrok;
216202719Sgabor			}
217202719Sgabor		| error QUIT
218202719Sgabor			{
219202719Sgabor				yyerrok;
220202719Sgabor			}
221202719Sgabor		;
222202719Sgabor
223202719Sgaborsemicolon_list	: /* empty */
224202719Sgabor			{
225202719Sgabor				$$ = cs("");
226202719Sgabor			}
227202719Sgabor		| statement
228202719Sgabor		| semicolon_list SEMICOLON statement
229202719Sgabor			{
230202719Sgabor				$$ = node($1, $3, END_NODE);
231202719Sgabor			}
232202719Sgabor		| semicolon_list SEMICOLON
233202719Sgabor		;
234202719Sgabor
235202719Sgaborstatement_list	: /* empty */
236202719Sgabor			{
237202719Sgabor				$$ = cs("");
238202719Sgabor			}
239202719Sgabor		| statement
240202719Sgabor		| statement_list NEWLINE
241202719Sgabor		| statement_list NEWLINE statement
242202719Sgabor			{
243202719Sgabor				$$ = node($1, $3, END_NODE);
244202719Sgabor			}
245202719Sgabor		| statement_list SEMICOLON
246202719Sgabor		| statement_list SEMICOLON statement
247202719Sgabor			{
248202719Sgabor				$$ = node($1, $3, END_NODE);
249202719Sgabor			}
250202719Sgabor		;
251202719Sgabor
252202719Sgabor
253202719Sgaboropt_statement	: /* empty */
254202719Sgabor			{
255202719Sgabor				$$ = cs("");
256202719Sgabor			}
257202719Sgabor		| statement
258202719Sgabor		;
259202719Sgabor
260202719Sgaborstatement	: expression
261202719Sgabor			{
262202719Sgabor				$$ = node($1, cs("ps."), END_NODE);
263202719Sgabor			}
264202719Sgabor		| named_expression ASSIGN_OP expression
265202719Sgabor			{
266202719Sgabor				if ($2[0] == '\0')
267202719Sgabor					$$ = node($3, cs($2), $1.store,
268202719Sgabor					    END_NODE);
269202719Sgabor				else
270202719Sgabor					$$ = node($1.load, $3, cs($2), $1.store,
271202719Sgabor					    END_NODE);
272202719Sgabor			}
273202719Sgabor		| STRING
274202719Sgabor			{
275202719Sgabor				$$ = node(cs("["), as($1),
276202719Sgabor				    cs("]P"), END_NODE);
277202719Sgabor			}
278202719Sgabor		| BREAK
279202719Sgabor			{
280202719Sgabor				if (breaksp == 0) {
281202719Sgabor					warning("break not in for or while");
282202719Sgabor					YYERROR;
283202719Sgabor				} else {
284202719Sgabor					$$ = node(
285202719Sgabor					    numnode(nesting -
286202719Sgabor						breakstack[breaksp-1]),
287202719Sgabor					    cs("Q"), END_NODE);
288202719Sgabor				}
289202719Sgabor			}
290202719Sgabor		| CONTINUE
291202719Sgabor			{
292202719Sgabor				if (breaksp == 0) {
293202719Sgabor					warning("continue not in for or while");
294202719Sgabor					YYERROR;
295202719Sgabor				} else {
296202719Sgabor					st_has_continue = true;
297202719Sgabor					$$ = node(numnode(nesting -
298202719Sgabor					    breakstack[breaksp-1] - 1),
299202719Sgabor					    cs("J"), END_NODE);
300202719Sgabor				}
301202719Sgabor			}
302202719Sgabor		| QUIT
303202719Sgabor			{
304203443Sgabor				sigset_t mask;
305202719Sgabor
306202719Sgabor				putchar('q');
307202719Sgabor				fflush(stdout);
308202719Sgabor				if (dc) {
309202719Sgabor					sigprocmask(SIG_BLOCK, NULL, &mask);
310202719Sgabor					sigsuspend(&mask);
311202719Sgabor				} else
312202719Sgabor					exit(0);
313202719Sgabor			}
314202719Sgabor		| RETURN return_expression
315202719Sgabor			{
316202719Sgabor				if (nesting == 0) {
317202719Sgabor					warning("return must be in a function");
318202719Sgabor					YYERROR;
319202719Sgabor				}
320202719Sgabor				$$ = $2;
321202719Sgabor			}
322202719Sgabor		| FOR LPAR alloc_macro opt_expression SEMICOLON
323202719Sgabor		     opt_relational_expression SEMICOLON
324202719Sgabor		     opt_expression RPAR opt_statement pop_nesting
325202719Sgabor			{
326203443Sgabor				ssize_t n;
327202719Sgabor
328202719Sgabor				if (st_has_continue)
329202719Sgabor					n = node($10, cs("M"), $8, cs("s."),
330202719Sgabor					    $6, $3, END_NODE);
331202719Sgabor				else
332202719Sgabor					n = node($10, $8, cs("s."), $6, $3,
333202719Sgabor					    END_NODE);
334202719Sgabor
335202719Sgabor				emit_macro($3, n);
336202719Sgabor				$$ = node($4, cs("s."), $6, $3, cs(" "),
337202719Sgabor				    END_NODE);
338202719Sgabor			}
339202719Sgabor		| IF LPAR alloc_macro pop_nesting relational_expression RPAR
340202719Sgabor		      opt_statement
341202719Sgabor			{
342202719Sgabor				emit_macro($3, $7);
343202719Sgabor				$$ = node($5, $3, cs(" "), END_NODE);
344202719Sgabor			}
345202719Sgabor		| IF LPAR alloc_macro pop_nesting relational_expression RPAR
346202719Sgabor		      opt_statement ELSE alloc_macro pop_nesting opt_statement
347202719Sgabor			{
348202719Sgabor				emit_macro($3, $7);
349202719Sgabor				emit_macro($9, $11);
350202719Sgabor				$$ = node($5, $3, cs("e"), $9, cs(" "),
351202719Sgabor				    END_NODE);
352202719Sgabor			}
353202719Sgabor		| WHILE LPAR alloc_macro relational_expression RPAR
354202719Sgabor		      opt_statement pop_nesting
355202719Sgabor			{
356203443Sgabor				ssize_t n;
357202719Sgabor
358202719Sgabor				if (st_has_continue)
359202719Sgabor					n = node($6, cs("M"), $4, $3, END_NODE);
360202719Sgabor				else
361202719Sgabor					n = node($6, $4, $3, END_NODE);
362202719Sgabor				emit_macro($3, n);
363202719Sgabor				$$ = node($4, $3, cs(" "), END_NODE);
364202719Sgabor			}
365202719Sgabor		| LBRACE statement_list RBRACE
366202719Sgabor			{
367202719Sgabor				$$ = $2;
368202719Sgabor			}
369202719Sgabor		| PRINT print_expression_list
370202719Sgabor			{
371202719Sgabor				$$ = $2;
372202719Sgabor			}
373202719Sgabor		;
374202719Sgabor
375202719Sgaboralloc_macro	: /* empty */
376202719Sgabor			{
377202719Sgabor				$$ = cs(str_table[macro_char]);
378202719Sgabor				macro_char++;
379202719Sgabor				/* Do not use [, \ and ] */
380202719Sgabor				if (macro_char == '[')
381202719Sgabor					macro_char += 3;
382202719Sgabor				/* skip letters */
383202719Sgabor				else if (macro_char == 'a')
384202719Sgabor					macro_char = '{';
385202719Sgabor				else if (macro_char == ARRAY_CHAR)
386202719Sgabor					macro_char += 26;
387202719Sgabor				else if (macro_char == 255)
388202719Sgabor					fatal("program too big");
389202719Sgabor				if (breaksp == BREAKSTACK_SZ)
390202719Sgabor					fatal("nesting too deep");
391202719Sgabor				breakstack[breaksp++] = nesting++;
392202719Sgabor			}
393202719Sgabor		;
394202719Sgabor
395202719Sgaborpop_nesting	: /* empty */
396202719Sgabor			{
397202719Sgabor				breaksp--;
398202719Sgabor			}
399202719Sgabor		;
400202719Sgabor
401202719Sgaborfunction	: function_header opt_parameter_list RPAR opt_newline
402202719Sgabor		  LBRACE NEWLINE opt_auto_define_list
403202719Sgabor		  statement_list RBRACE
404202719Sgabor			{
405202719Sgabor				int n = node(prologue, $8, epilogue,
406202719Sgabor				    cs("0"), numnode(nesting),
407202719Sgabor				    cs("Q"), END_NODE);
408202719Sgabor				emit_macro($1, n);
409202719Sgabor				reset_macro_char = macro_char;
410202719Sgabor				nesting = 0;
411202719Sgabor				breaksp = 0;
412202719Sgabor			}
413202719Sgabor		;
414202719Sgabor
415202719Sgaborfunction_header : DEFINE LETTER LPAR
416202719Sgabor			{
417202719Sgabor				$$ = function_node($2);
418202719Sgabor				free($2);
419202719Sgabor				prologue = cs("");
420202719Sgabor				epilogue = cs("");
421202719Sgabor				nesting = 1;
422202719Sgabor				breaksp = 0;
423202719Sgabor				breakstack[breaksp] = 0;
424202719Sgabor			}
425202719Sgabor		;
426202719Sgabor
427202719Sgaboropt_newline	: /* empty */
428202719Sgabor		| NEWLINE
429202719Sgabor		;
430202719Sgabor
431202719Sgaboropt_parameter_list
432202719Sgabor		: /* empty */
433202719Sgabor		| parameter_list
434202719Sgabor		;
435202719Sgabor
436202719Sgabor
437202719Sgaborparameter_list	: LETTER
438202719Sgabor			{
439202719Sgabor				add_par(letter_node($1));
440202719Sgabor				free($1);
441202719Sgabor			}
442202719Sgabor		| LETTER LBRACKET RBRACKET
443202719Sgabor			{
444202719Sgabor				add_par(array_node($1));
445202719Sgabor				free($1);
446202719Sgabor			}
447202719Sgabor		| parameter_list COMMA LETTER
448202719Sgabor			{
449202719Sgabor				add_par(letter_node($3));
450202719Sgabor				free($3);
451202719Sgabor			}
452202719Sgabor		| parameter_list COMMA LETTER LBRACKET RBRACKET
453202719Sgabor			{
454202719Sgabor				add_par(array_node($3));
455202719Sgabor				free($3);
456202719Sgabor			}
457202719Sgabor		;
458202719Sgabor
459202719Sgabor
460202719Sgabor
461202719Sgaboropt_auto_define_list
462202719Sgabor		: /* empty */
463202719Sgabor		| AUTO define_list NEWLINE
464202719Sgabor		| AUTO define_list SEMICOLON
465202719Sgabor		;
466202719Sgabor
467202719Sgabor
468202719Sgabordefine_list	: LETTER
469202719Sgabor			{
470202719Sgabor				add_local(letter_node($1));
471202719Sgabor				free($1);
472202719Sgabor			}
473202719Sgabor		| LETTER LBRACKET RBRACKET
474202719Sgabor			{
475202719Sgabor				add_local(array_node($1));
476202719Sgabor				free($1);
477202719Sgabor			}
478202719Sgabor		| define_list COMMA LETTER
479202719Sgabor			{
480202719Sgabor				add_local(letter_node($3));
481202719Sgabor				free($3);
482202719Sgabor			}
483202719Sgabor		| define_list COMMA LETTER LBRACKET RBRACKET
484202719Sgabor			{
485202719Sgabor				add_local(array_node($3));
486202719Sgabor				free($3);
487202719Sgabor			}
488202719Sgabor		;
489202719Sgabor
490202719Sgabor
491202719Sgaboropt_argument_list
492202719Sgabor		: /* empty */
493202719Sgabor			{
494202719Sgabor				$$ = cs("");
495202719Sgabor			}
496202719Sgabor		| argument_list
497202719Sgabor		;
498202719Sgabor
499202719Sgabor
500202719Sgaborargument_list	: expression
501202719Sgabor		| argument_list COMMA expression
502202719Sgabor			{
503202719Sgabor				$$ = node($1, $3, END_NODE);
504202719Sgabor			}
505202719Sgabor		| argument_list COMMA LETTER LBRACKET RBRACKET
506202719Sgabor			{
507202719Sgabor				$$ = node($1, cs("l"), array_node($3),
508202719Sgabor				    END_NODE);
509202719Sgabor				free($3);
510202719Sgabor			}
511202719Sgabor		;
512202719Sgabor
513202719Sgaboropt_relational_expression
514202719Sgabor		: /* empty */
515202719Sgabor			{
516202719Sgabor				$$ = cs(" 0 0=");
517202719Sgabor			}
518202719Sgabor		| relational_expression
519202719Sgabor		;
520202719Sgabor
521202719Sgaborrelational_expression
522202719Sgabor		: expression EQUALS expression
523202719Sgabor			{
524202719Sgabor				$$ = node($1, $3, cs("="), END_NODE);
525202719Sgabor			}
526202719Sgabor		| expression UNEQUALS expression
527202719Sgabor			{
528202719Sgabor				$$ = node($1, $3, cs("!="), END_NODE);
529202719Sgabor			}
530202719Sgabor		| expression LESS expression
531202719Sgabor			{
532202719Sgabor				$$ = node($1, $3, cs(">"), END_NODE);
533202719Sgabor			}
534202719Sgabor		| expression LESS_EQ expression
535202719Sgabor			{
536202719Sgabor				$$ = node($1, $3, cs("!<"), END_NODE);
537202719Sgabor			}
538202719Sgabor		| expression GREATER expression
539202719Sgabor			{
540202719Sgabor				$$ = node($1, $3, cs("<"), END_NODE);
541202719Sgabor			}
542202719Sgabor		| expression GREATER_EQ expression
543202719Sgabor			{
544202719Sgabor				$$ = node($1, $3, cs("!>"), END_NODE);
545202719Sgabor			}
546202719Sgabor		| expression
547202719Sgabor			{
548202719Sgabor				$$ = node($1, cs(" 0!="), END_NODE);
549202719Sgabor			}
550202719Sgabor		;
551202719Sgabor
552202719Sgabor
553202719Sgaborreturn_expression
554202719Sgabor		: /* empty */
555202719Sgabor			{
556202719Sgabor				$$ = node(cs("0"), epilogue,
557202719Sgabor				    numnode(nesting), cs("Q"), END_NODE);
558202719Sgabor			}
559202719Sgabor		| expression
560202719Sgabor			{
561202719Sgabor				$$ = node($1, epilogue,
562202719Sgabor				    numnode(nesting), cs("Q"), END_NODE);
563202719Sgabor			}
564202719Sgabor		| LPAR RPAR
565202719Sgabor			{
566202719Sgabor				$$ = node(cs("0"), epilogue,
567202719Sgabor				    numnode(nesting), cs("Q"), END_NODE);
568202719Sgabor			}
569202719Sgabor		;
570202719Sgabor
571202719Sgabor
572202719Sgaboropt_expression : /* empty */
573202719Sgabor			{
574202719Sgabor				$$ = cs(" 0");
575202719Sgabor			}
576202719Sgabor		| expression
577202719Sgabor		;
578202719Sgabor
579202719Sgaborexpression	: named_expression
580202719Sgabor			{
581202719Sgabor				$$ = node($1.load, END_NODE);
582202719Sgabor			}
583202719Sgabor		| DOT	{
584202719Sgabor				$$ = node(cs("l."), END_NODE);
585202719Sgabor			}
586202719Sgabor		| NUMBER
587202719Sgabor			{
588202719Sgabor				$$ = node(cs(" "), as($1), END_NODE);
589202719Sgabor			}
590202719Sgabor		| LPAR expression RPAR
591202719Sgabor			{
592202719Sgabor				$$ = $2;
593202719Sgabor			}
594202719Sgabor		| LETTER LPAR opt_argument_list RPAR
595202719Sgabor			{
596202719Sgabor				$$ = node($3, cs("l"),
597202719Sgabor				    function_node($1), cs("x"),
598202719Sgabor				    END_NODE);
599202719Sgabor				free($1);
600202719Sgabor			}
601202719Sgabor		| MINUS expression %prec UMINUS
602202719Sgabor			{
603202719Sgabor				$$ = node(cs(" 0"), $2, cs("-"),
604202719Sgabor				    END_NODE);
605202719Sgabor			}
606202719Sgabor		| expression PLUS expression
607202719Sgabor			{
608202719Sgabor				$$ = node($1, $3, cs("+"), END_NODE);
609202719Sgabor			}
610202719Sgabor		| expression MINUS expression
611202719Sgabor			{
612202719Sgabor				$$ = node($1, $3, cs("-"), END_NODE);
613202719Sgabor			}
614202719Sgabor		| expression MULTIPLY expression
615202719Sgabor			{
616202719Sgabor				$$ = node($1, $3, cs("*"), END_NODE);
617202719Sgabor			}
618202719Sgabor		| expression DIVIDE expression
619202719Sgabor			{
620202719Sgabor				$$ = node($1, $3, cs("/"), END_NODE);
621202719Sgabor			}
622202719Sgabor		| expression REMAINDER expression
623202719Sgabor			{
624202719Sgabor				$$ = node($1, $3, cs("%"), END_NODE);
625202719Sgabor			}
626202719Sgabor		| expression EXPONENT expression
627202719Sgabor			{
628202719Sgabor				$$ = node($1, $3, cs("^"), END_NODE);
629202719Sgabor			}
630202719Sgabor		| INCR named_expression
631202719Sgabor			{
632202719Sgabor				$$ = node($2.load, cs("1+d"), $2.store,
633202719Sgabor				    END_NODE);
634202719Sgabor			}
635202719Sgabor		| DECR named_expression
636202719Sgabor			{
637202719Sgabor				$$ = node($2.load, cs("1-d"),
638202719Sgabor				    $2.store, END_NODE);
639202719Sgabor			}
640202719Sgabor		| named_expression INCR
641202719Sgabor			{
642202719Sgabor				$$ = node($1.load, cs("d1+"),
643202719Sgabor				    $1.store, END_NODE);
644202719Sgabor			}
645202719Sgabor		| named_expression DECR
646202719Sgabor			{
647202719Sgabor				$$ = node($1.load, cs("d1-"),
648202719Sgabor				    $1.store, END_NODE);
649202719Sgabor			}
650202719Sgabor		| named_expression ASSIGN_OP expression
651202719Sgabor			{
652202719Sgabor				if ($2[0] == '\0')
653202719Sgabor					$$ = node($3, cs($2), cs("d"), $1.store,
654202719Sgabor					    END_NODE);
655202719Sgabor				else
656202719Sgabor					$$ = node($1.load, $3, cs($2), cs("d"),
657202719Sgabor					    $1.store, END_NODE);
658202719Sgabor			}
659202719Sgabor		| LENGTH LPAR expression RPAR
660202719Sgabor			{
661202719Sgabor				$$ = node($3, cs("Z"), END_NODE);
662202719Sgabor			}
663202719Sgabor		| SQRT LPAR expression RPAR
664202719Sgabor			{
665202719Sgabor				$$ = node($3, cs("v"), END_NODE);
666202719Sgabor			}
667202719Sgabor		| SCALE LPAR expression RPAR
668202719Sgabor			{
669202719Sgabor				$$ = node($3, cs("X"), END_NODE);
670202719Sgabor			}
671202719Sgabor		| BOOL_NOT expression
672202719Sgabor			{
673202719Sgabor				$$ = node($2, cs("N"), END_NODE);
674202719Sgabor			}
675202719Sgabor		| expression BOOL_AND alloc_macro pop_nesting expression
676202719Sgabor			{
677202719Sgabor				ssize_t n = node(cs("R"), $5, END_NODE);
678202719Sgabor				emit_macro($3, n);
679202719Sgabor				$$ = node($1, cs("d0!="), $3, END_NODE);
680202719Sgabor			}
681202719Sgabor		| expression BOOL_OR alloc_macro pop_nesting expression
682202719Sgabor			{
683202719Sgabor				ssize_t n = node(cs("R"), $5, END_NODE);
684202719Sgabor				emit_macro($3, n);
685202719Sgabor				$$ = node($1, cs("d0="), $3, END_NODE);
686202719Sgabor			}
687202719Sgabor		| expression EQUALS expression
688202719Sgabor			{
689202719Sgabor				$$ = node($1, $3, cs("G"), END_NODE);
690202719Sgabor			}
691202719Sgabor		| expression UNEQUALS expression
692202719Sgabor			{
693202719Sgabor				$$ = node($1, $3, cs("GN"), END_NODE);
694202719Sgabor			}
695202719Sgabor		| expression LESS expression
696202719Sgabor			{
697202719Sgabor				$$ = node($3, $1, cs("("), END_NODE);
698202719Sgabor			}
699202719Sgabor		| expression LESS_EQ expression
700202719Sgabor			{
701202719Sgabor				$$ = node($3, $1, cs("{"), END_NODE);
702202719Sgabor			}
703202719Sgabor		| expression GREATER expression
704202719Sgabor			{
705202719Sgabor				$$ = node($1, $3, cs("("), END_NODE);
706202719Sgabor			}
707202719Sgabor		| expression GREATER_EQ expression
708202719Sgabor			{
709202719Sgabor				$$ = node($1, $3, cs("{"), END_NODE);
710202719Sgabor			}
711202719Sgabor		;
712202719Sgabor
713202719Sgabornamed_expression
714202719Sgabor		: LETTER
715202719Sgabor			{
716202719Sgabor				$$.load = node(cs("l"), letter_node($1),
717202719Sgabor				    END_NODE);
718202719Sgabor				$$.store = node(cs("s"), letter_node($1),
719202719Sgabor				    END_NODE);
720202719Sgabor				free($1);
721202719Sgabor			}
722202719Sgabor		| LETTER LBRACKET expression RBRACKET
723202719Sgabor			{
724202719Sgabor				$$.load = node($3, cs(";"),
725202719Sgabor				    array_node($1), END_NODE);
726202719Sgabor				$$.store = node($3, cs(":"),
727202719Sgabor				    array_node($1), END_NODE);
728202719Sgabor				free($1);
729202719Sgabor			}
730202719Sgabor		| SCALE
731202719Sgabor			{
732202719Sgabor				$$.load = cs("K");
733202719Sgabor				$$.store = cs("k");
734202719Sgabor			}
735202719Sgabor		| IBASE
736202719Sgabor			{
737202719Sgabor				$$.load = cs("I");
738202719Sgabor				$$.store = cs("i");
739202719Sgabor			}
740202719Sgabor		| OBASE
741202719Sgabor			{
742202719Sgabor				$$.load = cs("O");
743202719Sgabor				$$.store = cs("o");
744202719Sgabor			}
745202719Sgabor		;
746202719Sgabor
747202719Sgaborprint_expression_list
748202719Sgabor		: print_expression
749202719Sgabor		| print_expression_list COMMA print_expression
750202719Sgabor			{
751202719Sgabor				$$ = node($1, $3, END_NODE);
752202719Sgabor			}
753202719Sgabor
754202719Sgaborprint_expression
755202719Sgabor		: expression
756202719Sgabor			{
757202719Sgabor				$$ = node($1, cs("ds.n"), END_NODE);
758202719Sgabor			}
759202719Sgabor		| STRING
760202719Sgabor			{
761202719Sgabor				char *p = escape($1);
762202719Sgabor				$$ = node(cs("["), as(p), cs("]n"), END_NODE);
763202719Sgabor				free(p);
764202719Sgabor			}
765202719Sgabor%%
766202719Sgabor
767202719Sgabor
768202719Sgaborstatic void
769202719Sgaborgrow(void)
770202719Sgabor{
771203443Sgabor	struct tree *p;
772203443Sgabor	size_t newsize;
773202719Sgabor
774202719Sgabor	if (current == instr_sz) {
775202719Sgabor		newsize = instr_sz * 2 + 1;
776202719Sgabor		p = realloc(instructions, newsize * sizeof(*p));
777202719Sgabor		if (p == NULL) {
778202719Sgabor			free(instructions);
779202719Sgabor			err(1, NULL);
780202719Sgabor		}
781202719Sgabor		instructions = p;
782202719Sgabor		instr_sz = newsize;
783202719Sgabor	}
784202719Sgabor}
785202719Sgabor
786202719Sgaborstatic ssize_t
787202719Sgaborcs(const char *str)
788202719Sgabor{
789203443Sgabor
790202719Sgabor	grow();
791202719Sgabor	instructions[current].index = CONST_STRING;
792202719Sgabor	instructions[current].u.cstr = str;
793202719Sgabor	return (current++);
794202719Sgabor}
795202719Sgabor
796202719Sgaborstatic ssize_t
797202719Sgaboras(const char *str)
798202719Sgabor{
799203443Sgabor
800202719Sgabor	grow();
801202719Sgabor	instructions[current].index = ALLOC_STRING;
802202719Sgabor	instructions[current].u.astr = strdup(str);
803202719Sgabor	if (instructions[current].u.astr == NULL)
804202719Sgabor		err(1, NULL);
805202719Sgabor	return (current++);
806202719Sgabor}
807202719Sgabor
808202719Sgaborstatic ssize_t
809202719Sgabornode(ssize_t arg, ...)
810202719Sgabor{
811203443Sgabor	va_list ap;
812203443Sgabor	ssize_t ret;
813202719Sgabor
814202719Sgabor	va_start(ap, arg);
815202719Sgabor
816202719Sgabor	ret = current;
817202719Sgabor	grow();
818202719Sgabor	instructions[current++].index = arg;
819202719Sgabor
820202719Sgabor	do {
821202719Sgabor		arg = va_arg(ap, ssize_t);
822202719Sgabor		grow();
823202719Sgabor		instructions[current++].index = arg;
824202719Sgabor	} while (arg != END_NODE);
825202719Sgabor
826202719Sgabor	va_end(ap);
827202719Sgabor	return (ret);
828202719Sgabor}
829202719Sgabor
830202719Sgaborstatic void
831202719Sgaboremit(ssize_t i)
832202719Sgabor{
833203443Sgabor
834202719Sgabor	if (instructions[i].index >= 0)
835202719Sgabor		while (instructions[i].index != END_NODE)
836202719Sgabor			emit(instructions[i++].index);
837202719Sgabor	else
838202719Sgabor		fputs(instructions[i].u.cstr, stdout);
839202719Sgabor}
840202719Sgabor
841202719Sgaborstatic void
842202719Sgaboremit_macro(int nodeidx, ssize_t code)
843202719Sgabor{
844203443Sgabor
845202719Sgabor	putchar('[');
846202719Sgabor	emit(code);
847202719Sgabor	printf("]s%s\n", instructions[nodeidx].u.cstr);
848202719Sgabor	nesting--;
849202719Sgabor}
850202719Sgabor
851202719Sgaborstatic void
852202719Sgaborfree_tree(void)
853202719Sgabor{
854203443Sgabor	ssize_t	i;
855202719Sgabor
856202719Sgabor	for (i = 0; i < current; i++)
857202719Sgabor		if (instructions[i].index == ALLOC_STRING)
858202719Sgabor			free(instructions[i].u.astr);
859202719Sgabor	current = 0;
860202719Sgabor}
861202719Sgabor
862202719Sgaborstatic ssize_t
863202719Sgabornumnode(int num)
864202719Sgabor{
865203443Sgabor	const char *p;
866202719Sgabor
867202719Sgabor	if (num < 10)
868202719Sgabor		p = str_table['0' + num];
869202719Sgabor	else if (num < 16)
870202719Sgabor		p = str_table['A' - 10 + num];
871202719Sgabor	else
872202719Sgabor		errx(1, "internal error: break num > 15");
873202719Sgabor	return (node(cs(" "), cs(p), END_NODE));
874202719Sgabor}
875202719Sgabor
876202719Sgabor
877202719Sgaborstatic ssize_t
878202719Sgaborlookup(char * str, size_t len, char type)
879202719Sgabor{
880203443Sgabor	ENTRY entry, *found;
881203443Sgabor	u_char *p;
882203443Sgabor	u_short num;
883202719Sgabor
884202719Sgabor	/* The scanner allocated an extra byte already */
885202719Sgabor	if (str[len-1] != type) {
886202719Sgabor		str[len] = type;
887202719Sgabor		str[len+1] = '\0';
888202719Sgabor	}
889202719Sgabor	entry.key = str;
890202719Sgabor	found = hsearch(entry, FIND);
891202719Sgabor	if (found == NULL) {
892202719Sgabor		if (var_count == MAX_VARIABLES)
893202719Sgabor			errx(1, "too many variables");
894202719Sgabor		p = malloc(4);
895202719Sgabor		if (p == NULL)
896202719Sgabor			err(1, NULL);
897202719Sgabor		num = var_count++;
898202719Sgabor		p[0] = 255;
899202719Sgabor		p[1] = ENCODE(num / VAR_BASE + 1);
900202719Sgabor		p[2] = ENCODE(num % VAR_BASE + 1);
901202719Sgabor		p[3] = '\0';
902202719Sgabor
903202719Sgabor		entry.data = (char *)p;
904202719Sgabor		entry.key = strdup(str);
905202719Sgabor		if (entry.key == NULL)
906202719Sgabor			err(1, NULL);
907202719Sgabor		found = hsearch(entry, ENTER);
908202719Sgabor		if (found == NULL)
909202719Sgabor			err(1, NULL);
910202719Sgabor	}
911202719Sgabor	return (cs(found->data));
912202719Sgabor}
913202719Sgabor
914202719Sgaborstatic ssize_t
915202719Sgaborletter_node(char *str)
916202719Sgabor{
917203443Sgabor	size_t len;
918202719Sgabor
919202719Sgabor	len = strlen(str);
920202719Sgabor	if (len == 1 && str[0] != '_')
921202719Sgabor		return (cs(str_table[(int)str[0]]));
922202719Sgabor	else
923202719Sgabor		return (lookup(str, len, 'L'));
924202719Sgabor}
925202719Sgabor
926202719Sgaborstatic ssize_t
927202719Sgaborarray_node(char *str)
928202719Sgabor{
929203443Sgabor	size_t len;
930202719Sgabor
931202719Sgabor	len = strlen(str);
932202719Sgabor	if (len == 1 && str[0] != '_')
933202719Sgabor		return (cs(str_table[(int)str[0] - 'a' + ARRAY_CHAR]));
934202719Sgabor	else
935202719Sgabor		return (lookup(str, len, 'A'));
936202719Sgabor}
937202719Sgabor
938202719Sgaborstatic ssize_t
939202719Sgaborfunction_node(char *str)
940202719Sgabor{
941203443Sgabor	size_t len;
942202719Sgabor
943202719Sgabor	len = strlen(str);
944202719Sgabor	if (len == 1 && str[0] != '_')
945202719Sgabor		return (cs(str_table[(int)str[0] - 'a' + FUNC_CHAR]));
946202719Sgabor	else
947202719Sgabor		return (lookup(str, len, 'F'));
948202719Sgabor}
949202719Sgabor
950202719Sgaborstatic void
951202719Sgaboradd_par(ssize_t n)
952202719Sgabor{
953203443Sgabor
954202719Sgabor	prologue = node(cs("S"), n, prologue, END_NODE);
955202719Sgabor	epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE);
956202719Sgabor}
957202719Sgabor
958202719Sgaborstatic void
959202719Sgaboradd_local(ssize_t n)
960202719Sgabor{
961203443Sgabor
962202719Sgabor	prologue = node(cs("0S"), n, prologue, END_NODE);
963202719Sgabor	epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE);
964202719Sgabor}
965202719Sgabor
966202719Sgaborvoid
967202719Sgaboryyerror(const char *s)
968202719Sgabor{
969203443Sgabor	char *p, *str;
970203443Sgabor	int n;
971202719Sgabor
972202719Sgabor	if (yyin != NULL && feof(yyin))
973202719Sgabor		n = asprintf(&str, "%s: %s:%d: %s: unexpected EOF",
974202719Sgabor		    __progname, filename, lineno, s);
975202719Sgabor	else if (isspace(yytext[0]) || !isprint(yytext[0]))
976202719Sgabor		n = asprintf(&str,
977202719Sgabor		    "%s: %s:%d: %s: ascii char 0x%02x unexpected",
978202719Sgabor		    __progname, filename, lineno, s, yytext[0]);
979202719Sgabor	else
980202719Sgabor		n = asprintf(&str, "%s: %s:%d: %s: %s unexpected",
981202719Sgabor		    __progname, filename, lineno, s, yytext);
982202719Sgabor	if (n == -1)
983202719Sgabor		err(1, NULL);
984202719Sgabor
985202719Sgabor	fputs("c[", stdout);
986202719Sgabor	for (p = str; *p != '\0'; p++) {
987202719Sgabor		if (*p == '[' || *p == ']' || *p =='\\')
988202719Sgabor			putchar('\\');
989202719Sgabor		putchar(*p);
990202719Sgabor	}
991202719Sgabor	fputs("]pc\n", stdout);
992202719Sgabor	free(str);
993202719Sgabor}
994202719Sgabor
995202719Sgaborvoid
996202719Sgaborfatal(const char *s)
997202719Sgabor{
998203443Sgabor
999202719Sgabor	errx(1, "%s:%d: %s", filename, lineno, s);
1000202719Sgabor}
1001202719Sgabor
1002202719Sgaborstatic void
1003202719Sgaborwarning(const char *s)
1004202719Sgabor{
1005203443Sgabor
1006202719Sgabor	warnx("%s:%d: %s", filename, lineno, s);
1007202719Sgabor}
1008202719Sgabor
1009202719Sgaborstatic void
1010202719Sgaborinit(void)
1011202719Sgabor{
1012203443Sgabor	unsigned int i;
1013202719Sgabor
1014202719Sgabor	for (i = 0; i < UCHAR_MAX; i++) {
1015202719Sgabor		str_table[i][0] = i;
1016202719Sgabor		str_table[i][1] = '\0';
1017202719Sgabor	}
1018202719Sgabor	if (hcreate(1 << 16) == 0)
1019202719Sgabor		err(1, NULL);
1020202719Sgabor}
1021202719Sgabor
1022202719Sgabor
1023202719Sgaborstatic void
1024202719Sgaborusage(void)
1025202719Sgabor{
1026203443Sgabor
1027202845Sdelphij	fprintf(stderr, "usage: %s [-chlqv] [-e expression] [file ...]\n",
1028202719Sgabor	    __progname);
1029202719Sgabor	exit(1);
1030202719Sgabor}
1031202719Sgabor
1032202719Sgaborstatic char *
1033202719Sgaborescape(const char *str)
1034202719Sgabor{
1035203443Sgabor	char *p, *ret;
1036202719Sgabor
1037202719Sgabor	ret = malloc(strlen(str) + 1);
1038202719Sgabor	if (ret == NULL)
1039202719Sgabor		err(1, NULL);
1040202719Sgabor
1041202719Sgabor	p = ret;
1042202719Sgabor	while (*str != '\0') {
1043202719Sgabor		/*
1044202719Sgabor		 * We get _escaped_ strings here. Single backslashes are
1045202719Sgabor		 * already converted to double backslashes
1046202719Sgabor		 */
1047202719Sgabor		if (*str == '\\') {
1048202719Sgabor			if (*++str == '\\') {
1049202719Sgabor				switch (*++str) {
1050202719Sgabor				case 'a':
1051202719Sgabor					*p++ = '\a';
1052202719Sgabor					break;
1053202719Sgabor				case 'b':
1054202719Sgabor					*p++ = '\b';
1055202719Sgabor					break;
1056202719Sgabor				case 'f':
1057202719Sgabor					*p++ = '\f';
1058202719Sgabor					break;
1059202719Sgabor				case 'n':
1060202719Sgabor					*p++ = '\n';
1061202719Sgabor					break;
1062202719Sgabor				case 'q':
1063202719Sgabor					*p++ = '"';
1064202719Sgabor					break;
1065202719Sgabor				case 'r':
1066202719Sgabor					*p++ = '\r';
1067202719Sgabor					break;
1068202719Sgabor				case 't':
1069202719Sgabor					*p++ = '\t';
1070202719Sgabor					break;
1071202719Sgabor				case '\\':
1072202719Sgabor					*p++ = '\\';
1073202719Sgabor					break;
1074202719Sgabor				}
1075202719Sgabor				str++;
1076202719Sgabor			} else {
1077202719Sgabor				*p++ = '\\';
1078202719Sgabor				*p++ = *str++;
1079202719Sgabor			}
1080202719Sgabor		} else
1081202719Sgabor			*p++ = *str++;
1082202719Sgabor	}
1083202719Sgabor	*p = '\0';
1084202719Sgabor	return (ret);
1085202719Sgabor}
1086202719Sgabor
1087202719Sgabor/* ARGSUSED */
1088243211Seadlerstatic void
1089202719Sgaborsigchld(int signo)
1090202719Sgabor{
1091203443Sgabor	pid_t pid;
1092203443Sgabor	int status;
1093202719Sgabor
1094202719Sgabor	switch (signo) {
1095202719Sgabor	default:
1096202719Sgabor		for (;;) {
1097233119Skevlo			pid = waitpid(dc, &status, WUNTRACED);
1098202719Sgabor			if (pid == -1) {
1099202719Sgabor				if (errno == EINTR)
1100202719Sgabor					continue;
1101202719Sgabor				_exit(0);
1102202719Sgabor			}
1103202719Sgabor			if (WIFEXITED(status) || WIFSIGNALED(status))
1104202719Sgabor				_exit(0);
1105202719Sgabor			else
1106202719Sgabor				break;
1107202719Sgabor		}
1108202719Sgabor	}
1109202719Sgabor}
1110202719Sgabor
1111203498Sdelphijstatic const char *
1112203498Sdelphijdummy_prompt(void)
1113203498Sdelphij{
1114203498Sdelphij
1115203498Sdelphij        return ("");
1116203498Sdelphij}
1117203498Sdelphij
1118202719Sgaborint
1119202719Sgabormain(int argc, char *argv[])
1120202719Sgabor{
1121203443Sgabor	char *q;
1122203443Sgabor	int p[2];
1123203443Sgabor	int ch, i;
1124202719Sgabor
1125202719Sgabor	init();
1126202719Sgabor	setlinebuf(stdout);
1127202719Sgabor
1128202719Sgabor	sargv = malloc(argc * sizeof(char *));
1129202719Sgabor	if (sargv == NULL)
1130202719Sgabor		err(1, NULL);
1131202719Sgabor
1132202719Sgabor	if ((cmdexpr = strdup("")) == NULL)
1133202719Sgabor		err(1, NULL);
1134202719Sgabor	/* The d debug option is 4.4 BSD bc(1) compatible */
1135202719Sgabor	while ((ch = getopt_long(argc, argv, "cde:hlqv",
1136202719Sgabor	   long_options, NULL)) != -1) {
1137202719Sgabor		switch (ch) {
1138202719Sgabor		case 'c':
1139202719Sgabor		case 'd':
1140202719Sgabor			do_fork = false;
1141202719Sgabor			break;
1142202719Sgabor		case 'e':
1143202719Sgabor			q = cmdexpr;
1144202719Sgabor			if (asprintf(&cmdexpr, "%s%s\n", cmdexpr, optarg) == -1)
1145202719Sgabor				err(1, NULL);
1146202719Sgabor			free(q);
1147202719Sgabor			break;
1148202719Sgabor		case 'h':
1149202719Sgabor			usage();
1150202719Sgabor			break;
1151202719Sgabor		case 'l':
1152202719Sgabor			sargv[sargc++] = _PATH_LIBB;
1153202719Sgabor			break;
1154202719Sgabor		case 'q':
1155202719Sgabor			/* compatibility option */
1156202719Sgabor			break;
1157202719Sgabor		case 'v':
1158202719Sgabor			fprintf(stderr, "%s (BSD bc) %s\n", __progname, BC_VER);
1159202719Sgabor			exit(0);
1160202719Sgabor			break;
1161202719Sgabor		default:
1162202719Sgabor			usage();
1163202719Sgabor		}
1164202719Sgabor	}
1165202719Sgabor
1166202719Sgabor	argc -= optind;
1167202719Sgabor	argv += optind;
1168202719Sgabor
1169202719Sgabor	interactive = isatty(STDIN_FILENO);
1170202719Sgabor	for (i = 0; i < argc; i++)
1171202719Sgabor		sargv[sargc++] = argv[i];
1172202719Sgabor
1173202719Sgabor	if (do_fork) {
1174202719Sgabor		if (pipe(p) == -1)
1175202719Sgabor			err(1, "cannot create pipe");
1176202719Sgabor		dc = fork();
1177202719Sgabor		if (dc == -1)
1178202719Sgabor			err(1, "cannot fork");
1179202719Sgabor		else if (dc != 0) {
1180202719Sgabor			signal(SIGCHLD, sigchld);
1181202719Sgabor			close(STDOUT_FILENO);
1182202719Sgabor			dup(p[1]);
1183202719Sgabor			close(p[0]);
1184202719Sgabor			close(p[1]);
1185202719Sgabor		} else {
1186202719Sgabor			close(STDIN_FILENO);
1187202719Sgabor			dup(p[0]);
1188202719Sgabor			close(p[0]);
1189202719Sgabor			close(p[1]);
1190202719Sgabor			execl(_PATH_DC, "dc", "-x", (char *)NULL);
1191202719Sgabor			err(1, "cannot find dc");
1192202719Sgabor		}
1193202719Sgabor	}
1194233119Skevlo	if (interactive) {
1195233119Skevlo		el = el_init("bc", stdin, stderr, stderr);
1196233119Skevlo		hist = history_init();
1197233119Skevlo		history(hist, &he, H_SETSIZE, 100);
1198233119Skevlo		el_set(el, EL_HIST, history, hist);
1199233119Skevlo		el_set(el, EL_EDITOR, "emacs");
1200233119Skevlo		el_set(el, EL_SIGNAL, 1);
1201233119Skevlo		el_set(el, EL_PROMPT, dummy_prompt);
1202233119Skevlo		el_source(el, NULL);
1203233119Skevlo	}
1204202719Sgabor	yywrap();
1205202719Sgabor	return (yyparse());
1206202719Sgabor}
1207