bc.y revision 243075
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: head/usr.bin/bc/bc.y 243075 2012-11-15 15:06:00Z eadler $");
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>
51232994Skevlo#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			 yywrap(void);
73202719Sgabor
74202719Sgaborint			 fileindex;
75202719Sgaborint			 sargc;
76202719Sgaborconst char		**sargv;
77202719Sgaborconst char		*filename;
78202719Sgaborchar			*cmdexpr;
79202719Sgabor
80202719Sgaborstatic void		 grow(void);
81202719Sgaborstatic ssize_t		 cs(const char *);
82202719Sgaborstatic ssize_t		 as(const char *);
83202719Sgaborstatic ssize_t		 node(ssize_t, ...);
84202719Sgaborstatic void		 emit(ssize_t);
85202719Sgaborstatic void		 emit_macro(int, ssize_t);
86202719Sgaborstatic void		 free_tree(void);
87202719Sgaborstatic ssize_t		 numnode(int);
88202719Sgaborstatic ssize_t		 lookup(char *, size_t, char);
89202719Sgaborstatic ssize_t		 letter_node(char *);
90202719Sgaborstatic ssize_t		 array_node(char *);
91202719Sgaborstatic ssize_t		 function_node(char *);
92202719Sgabor
93202719Sgaborstatic void		 add_par(ssize_t);
94202719Sgaborstatic void		 add_local(ssize_t);
95202719Sgaborstatic void		 warning(const char *);
96202719Sgaborstatic void		 init(void);
97202719Sgaborstatic void		 usage(void);
98202719Sgaborstatic char		*escape(const char *);
99202719Sgabor
100202719Sgaborstatic ssize_t		 instr_sz = 0;
101202719Sgaborstatic struct tree	*instructions = NULL;
102202719Sgaborstatic ssize_t		 current = 0;
103202719Sgaborstatic int		 macro_char = '0';
104202719Sgaborstatic int		 reset_macro_char = '0';
105202719Sgaborstatic int		 nesting = 0;
106202719Sgaborstatic int		 breakstack[16];
107202719Sgaborstatic int		 breaksp = 0;
108202719Sgaborstatic ssize_t		 prologue;
109202719Sgaborstatic ssize_t		 epilogue;
110202719Sgaborstatic bool		 st_has_continue;
111202719Sgaborstatic char		 str_table[UCHAR_MAX][2];
112202719Sgaborstatic bool		 do_fork = true;
113202719Sgaborstatic u_short		 var_count;
114202719Sgaborstatic pid_t		 dc;
115202719Sgabor
116202719Sgaborstatic void		 sigchld(int);
117202719Sgabor
118202719Sgaborextern char		*__progname;
119202719Sgabor
120202719Sgabor#define BREAKSTACK_SZ	(sizeof(breakstack)/sizeof(breakstack[0]))
121202719Sgabor
122202719Sgabor/* These values are 4.4BSD bc compatible */
123202719Sgabor#define FUNC_CHAR	0x01
124202719Sgabor#define ARRAY_CHAR	0xa1
125202719Sgabor
126202719Sgabor/* Skip '\0', [, \ and ] */
127202719Sgabor#define ENCODE(c)	((c) < '[' ? (c) : (c) + 3);
128202719Sgabor#define VAR_BASE	(256-4)
129202719Sgabor#define MAX_VARIABLES	(VAR_BASE * VAR_BASE)
130202719Sgabor
131202719Sgaborconst struct option long_options[] =
132202719Sgabor{
133202719Sgabor	{"expression",	required_argument,	NULL,	'e'},
134202719Sgabor	{"help",	no_argument,		NULL,	'h'},
135202719Sgabor	{"mathlib",	no_argument,		NULL,	'l'},
136202719Sgabor	/* compatibility option */
137202719Sgabor	{"quiet",	no_argument,		NULL,	'q'},
138202719Sgabor	{"version",	no_argument,		NULL,	'v'},
139202719Sgabor	{NULL,		no_argument,		NULL,	0}
140202719Sgabor};
141202719Sgabor
142202719Sgabor%}
143202719Sgabor
144202719Sgabor%start program
145202719Sgabor
146202719Sgabor%union {
147202719Sgabor	struct lvalue	 lvalue;
148202719Sgabor	const char	*str;
149202719Sgabor	char		*astr;
150203443Sgabor	ssize_t		 node;
151202719Sgabor}
152202719Sgabor
153202719Sgabor%token COMMA SEMICOLON LPAR RPAR LBRACE RBRACE LBRACKET RBRACKET DOT
154202719Sgabor%token NEWLINE
155202719Sgabor%token <astr> LETTER
156202719Sgabor%token <str> NUMBER STRING
157202719Sgabor%token DEFINE BREAK QUIT LENGTH
158202719Sgabor%token RETURN FOR IF WHILE SQRT
159202719Sgabor%token SCALE IBASE OBASE AUTO
160202719Sgabor%token CONTINUE ELSE PRINT
161202719Sgabor
162202719Sgabor%left BOOL_OR
163202719Sgabor%left BOOL_AND
164202719Sgabor%nonassoc BOOL_NOT
165202719Sgabor%nonassoc EQUALS LESS_EQ GREATER_EQ UNEQUALS LESS GREATER
166202719Sgabor%right <str> ASSIGN_OP
167202719Sgabor%left PLUS MINUS
168202719Sgabor%left MULTIPLY DIVIDE REMAINDER
169202719Sgabor%right EXPONENT
170202719Sgabor%nonassoc UMINUS
171202719Sgabor%nonassoc INCR DECR
172202719Sgabor
173202719Sgabor%type <lvalue>	named_expression
174202719Sgabor%type <node>	argument_list
175202719Sgabor%type <node>	alloc_macro
176202719Sgabor%type <node>	expression
177202719Sgabor%type <node>	function
178202719Sgabor%type <node>	function_header
179202719Sgabor%type <node>	input_item
180202719Sgabor%type <node>	opt_argument_list
181202719Sgabor%type <node>	opt_expression
182202719Sgabor%type <node>	opt_relational_expression
183202719Sgabor%type <node>	opt_statement
184202719Sgabor%type <node>	print_expression
185202719Sgabor%type <node>	print_expression_list
186202719Sgabor%type <node>	relational_expression
187202719Sgabor%type <node>	return_expression
188202719Sgabor%type <node>	semicolon_list
189202719Sgabor%type <node>	statement
190202719Sgabor%type <node>	statement_list
191202719Sgabor
192202719Sgabor%%
193202719Sgabor
194202719Sgaborprogram		: /* empty */
195202719Sgabor		| program input_item
196202719Sgabor		;
197202719Sgabor
198202719Sgaborinput_item	: semicolon_list NEWLINE
199202719Sgabor			{
200202719Sgabor				emit($1);
201202719Sgabor				macro_char = reset_macro_char;
202202719Sgabor				putchar('\n');
203202719Sgabor				free_tree();
204202719Sgabor				st_has_continue = false;
205202719Sgabor			}
206202719Sgabor		| function
207202719Sgabor			{
208202719Sgabor				putchar('\n');
209202719Sgabor				free_tree();
210202719Sgabor				st_has_continue = false;
211202719Sgabor			}
212202719Sgabor		| error NEWLINE
213202719Sgabor			{
214202719Sgabor				yyerrok;
215202719Sgabor			}
216202719Sgabor		| error QUIT
217202719Sgabor			{
218202719Sgabor				yyerrok;
219202719Sgabor			}
220202719Sgabor		;
221202719Sgabor
222202719Sgaborsemicolon_list	: /* empty */
223202719Sgabor			{
224202719Sgabor				$$ = cs("");
225202719Sgabor			}
226202719Sgabor		| statement
227202719Sgabor		| semicolon_list SEMICOLON statement
228202719Sgabor			{
229202719Sgabor				$$ = node($1, $3, END_NODE);
230202719Sgabor			}
231202719Sgabor		| semicolon_list SEMICOLON
232202719Sgabor		;
233202719Sgabor
234202719Sgaborstatement_list	: /* empty */
235202719Sgabor			{
236202719Sgabor				$$ = cs("");
237202719Sgabor			}
238202719Sgabor		| statement
239202719Sgabor		| statement_list NEWLINE
240202719Sgabor		| statement_list NEWLINE statement
241202719Sgabor			{
242202719Sgabor				$$ = node($1, $3, END_NODE);
243202719Sgabor			}
244202719Sgabor		| statement_list SEMICOLON
245202719Sgabor		| statement_list SEMICOLON statement
246202719Sgabor			{
247202719Sgabor				$$ = node($1, $3, END_NODE);
248202719Sgabor			}
249202719Sgabor		;
250202719Sgabor
251202719Sgabor
252202719Sgaboropt_statement	: /* empty */
253202719Sgabor			{
254202719Sgabor				$$ = cs("");
255202719Sgabor			}
256202719Sgabor		| statement
257202719Sgabor		;
258202719Sgabor
259202719Sgaborstatement	: expression
260202719Sgabor			{
261202719Sgabor				$$ = node($1, cs("ps."), END_NODE);
262202719Sgabor			}
263202719Sgabor		| named_expression ASSIGN_OP expression
264202719Sgabor			{
265202719Sgabor				if ($2[0] == '\0')
266202719Sgabor					$$ = node($3, cs($2), $1.store,
267202719Sgabor					    END_NODE);
268202719Sgabor				else
269202719Sgabor					$$ = node($1.load, $3, cs($2), $1.store,
270202719Sgabor					    END_NODE);
271202719Sgabor			}
272202719Sgabor		| STRING
273202719Sgabor			{
274202719Sgabor				$$ = node(cs("["), as($1),
275202719Sgabor				    cs("]P"), END_NODE);
276202719Sgabor			}
277202719Sgabor		| BREAK
278202719Sgabor			{
279202719Sgabor				if (breaksp == 0) {
280202719Sgabor					warning("break not in for or while");
281202719Sgabor					YYERROR;
282202719Sgabor				} else {
283202719Sgabor					$$ = node(
284202719Sgabor					    numnode(nesting -
285202719Sgabor						breakstack[breaksp-1]),
286202719Sgabor					    cs("Q"), END_NODE);
287202719Sgabor				}
288202719Sgabor			}
289202719Sgabor		| CONTINUE
290202719Sgabor			{
291202719Sgabor				if (breaksp == 0) {
292202719Sgabor					warning("continue not in for or while");
293202719Sgabor					YYERROR;
294202719Sgabor				} else {
295202719Sgabor					st_has_continue = true;
296202719Sgabor					$$ = node(numnode(nesting -
297202719Sgabor					    breakstack[breaksp-1] - 1),
298202719Sgabor					    cs("J"), END_NODE);
299202719Sgabor				}
300202719Sgabor			}
301202719Sgabor		| QUIT
302202719Sgabor			{
303203443Sgabor				sigset_t mask;
304202719Sgabor
305202719Sgabor				putchar('q');
306202719Sgabor				fflush(stdout);
307202719Sgabor				if (dc) {
308202719Sgabor					sigprocmask(SIG_BLOCK, NULL, &mask);
309202719Sgabor					sigsuspend(&mask);
310202719Sgabor				} else
311202719Sgabor					exit(0);
312202719Sgabor			}
313202719Sgabor		| RETURN return_expression
314202719Sgabor			{
315202719Sgabor				if (nesting == 0) {
316202719Sgabor					warning("return must be in a function");
317202719Sgabor					YYERROR;
318202719Sgabor				}
319202719Sgabor				$$ = $2;
320202719Sgabor			}
321202719Sgabor		| FOR LPAR alloc_macro opt_expression SEMICOLON
322202719Sgabor		     opt_relational_expression SEMICOLON
323202719Sgabor		     opt_expression RPAR opt_statement pop_nesting
324202719Sgabor			{
325203443Sgabor				ssize_t n;
326202719Sgabor
327202719Sgabor				if (st_has_continue)
328202719Sgabor					n = node($10, cs("M"), $8, cs("s."),
329202719Sgabor					    $6, $3, END_NODE);
330202719Sgabor				else
331202719Sgabor					n = node($10, $8, cs("s."), $6, $3,
332202719Sgabor					    END_NODE);
333202719Sgabor
334202719Sgabor				emit_macro($3, n);
335202719Sgabor				$$ = node($4, cs("s."), $6, $3, cs(" "),
336202719Sgabor				    END_NODE);
337202719Sgabor			}
338202719Sgabor		| IF LPAR alloc_macro pop_nesting relational_expression RPAR
339202719Sgabor		      opt_statement
340202719Sgabor			{
341202719Sgabor				emit_macro($3, $7);
342202719Sgabor				$$ = node($5, $3, cs(" "), END_NODE);
343202719Sgabor			}
344202719Sgabor		| IF LPAR alloc_macro pop_nesting relational_expression RPAR
345202719Sgabor		      opt_statement ELSE alloc_macro pop_nesting opt_statement
346202719Sgabor			{
347202719Sgabor				emit_macro($3, $7);
348202719Sgabor				emit_macro($9, $11);
349202719Sgabor				$$ = node($5, $3, cs("e"), $9, cs(" "),
350202719Sgabor				    END_NODE);
351202719Sgabor			}
352202719Sgabor		| WHILE LPAR alloc_macro relational_expression RPAR
353202719Sgabor		      opt_statement pop_nesting
354202719Sgabor			{
355203443Sgabor				ssize_t n;
356202719Sgabor
357202719Sgabor				if (st_has_continue)
358202719Sgabor					n = node($6, cs("M"), $4, $3, END_NODE);
359202719Sgabor				else
360202719Sgabor					n = node($6, $4, $3, END_NODE);
361202719Sgabor				emit_macro($3, n);
362202719Sgabor				$$ = node($4, $3, cs(" "), END_NODE);
363202719Sgabor			}
364202719Sgabor		| LBRACE statement_list RBRACE
365202719Sgabor			{
366202719Sgabor				$$ = $2;
367202719Sgabor			}
368202719Sgabor		| PRINT print_expression_list
369202719Sgabor			{
370202719Sgabor				$$ = $2;
371202719Sgabor			}
372202719Sgabor		;
373202719Sgabor
374202719Sgaboralloc_macro	: /* empty */
375202719Sgabor			{
376202719Sgabor				$$ = cs(str_table[macro_char]);
377202719Sgabor				macro_char++;
378202719Sgabor				/* Do not use [, \ and ] */
379202719Sgabor				if (macro_char == '[')
380202719Sgabor					macro_char += 3;
381202719Sgabor				/* skip letters */
382202719Sgabor				else if (macro_char == 'a')
383202719Sgabor					macro_char = '{';
384202719Sgabor				else if (macro_char == ARRAY_CHAR)
385202719Sgabor					macro_char += 26;
386202719Sgabor				else if (macro_char == 255)
387202719Sgabor					fatal("program too big");
388202719Sgabor				if (breaksp == BREAKSTACK_SZ)
389202719Sgabor					fatal("nesting too deep");
390202719Sgabor				breakstack[breaksp++] = nesting++;
391202719Sgabor			}
392202719Sgabor		;
393202719Sgabor
394202719Sgaborpop_nesting	: /* empty */
395202719Sgabor			{
396202719Sgabor				breaksp--;
397202719Sgabor			}
398202719Sgabor		;
399202719Sgabor
400202719Sgaborfunction	: function_header opt_parameter_list RPAR opt_newline
401202719Sgabor		  LBRACE NEWLINE opt_auto_define_list
402202719Sgabor		  statement_list RBRACE
403202719Sgabor			{
404202719Sgabor				int n = node(prologue, $8, epilogue,
405202719Sgabor				    cs("0"), numnode(nesting),
406202719Sgabor				    cs("Q"), END_NODE);
407202719Sgabor				emit_macro($1, n);
408202719Sgabor				reset_macro_char = macro_char;
409202719Sgabor				nesting = 0;
410202719Sgabor				breaksp = 0;
411202719Sgabor			}
412202719Sgabor		;
413202719Sgabor
414202719Sgaborfunction_header : DEFINE LETTER LPAR
415202719Sgabor			{
416202719Sgabor				$$ = function_node($2);
417202719Sgabor				free($2);
418202719Sgabor				prologue = cs("");
419202719Sgabor				epilogue = cs("");
420202719Sgabor				nesting = 1;
421202719Sgabor				breaksp = 0;
422202719Sgabor				breakstack[breaksp] = 0;
423202719Sgabor			}
424202719Sgabor		;
425202719Sgabor
426202719Sgaboropt_newline	: /* empty */
427202719Sgabor		| NEWLINE
428202719Sgabor		;
429202719Sgabor
430202719Sgaboropt_parameter_list
431202719Sgabor		: /* empty */
432202719Sgabor		| parameter_list
433202719Sgabor		;
434202719Sgabor
435202719Sgabor
436202719Sgaborparameter_list	: LETTER
437202719Sgabor			{
438202719Sgabor				add_par(letter_node($1));
439202719Sgabor				free($1);
440202719Sgabor			}
441202719Sgabor		| LETTER LBRACKET RBRACKET
442202719Sgabor			{
443202719Sgabor				add_par(array_node($1));
444202719Sgabor				free($1);
445202719Sgabor			}
446202719Sgabor		| parameter_list COMMA LETTER
447202719Sgabor			{
448202719Sgabor				add_par(letter_node($3));
449202719Sgabor				free($3);
450202719Sgabor			}
451202719Sgabor		| parameter_list COMMA LETTER LBRACKET RBRACKET
452202719Sgabor			{
453202719Sgabor				add_par(array_node($3));
454202719Sgabor				free($3);
455202719Sgabor			}
456202719Sgabor		;
457202719Sgabor
458202719Sgabor
459202719Sgabor
460202719Sgaboropt_auto_define_list
461202719Sgabor		: /* empty */
462202719Sgabor		| AUTO define_list NEWLINE
463202719Sgabor		| AUTO define_list SEMICOLON
464202719Sgabor		;
465202719Sgabor
466202719Sgabor
467202719Sgabordefine_list	: LETTER
468202719Sgabor			{
469202719Sgabor				add_local(letter_node($1));
470202719Sgabor				free($1);
471202719Sgabor			}
472202719Sgabor		| LETTER LBRACKET RBRACKET
473202719Sgabor			{
474202719Sgabor				add_local(array_node($1));
475202719Sgabor				free($1);
476202719Sgabor			}
477202719Sgabor		| define_list COMMA LETTER
478202719Sgabor			{
479202719Sgabor				add_local(letter_node($3));
480202719Sgabor				free($3);
481202719Sgabor			}
482202719Sgabor		| define_list COMMA LETTER LBRACKET RBRACKET
483202719Sgabor			{
484202719Sgabor				add_local(array_node($3));
485202719Sgabor				free($3);
486202719Sgabor			}
487202719Sgabor		;
488202719Sgabor
489202719Sgabor
490202719Sgaboropt_argument_list
491202719Sgabor		: /* empty */
492202719Sgabor			{
493202719Sgabor				$$ = cs("");
494202719Sgabor			}
495202719Sgabor		| argument_list
496202719Sgabor		;
497202719Sgabor
498202719Sgabor
499202719Sgaborargument_list	: expression
500202719Sgabor		| argument_list COMMA expression
501202719Sgabor			{
502202719Sgabor				$$ = node($1, $3, END_NODE);
503202719Sgabor			}
504202719Sgabor		| argument_list COMMA LETTER LBRACKET RBRACKET
505202719Sgabor			{
506202719Sgabor				$$ = node($1, cs("l"), array_node($3),
507202719Sgabor				    END_NODE);
508202719Sgabor				free($3);
509202719Sgabor			}
510202719Sgabor		;
511202719Sgabor
512202719Sgaboropt_relational_expression
513202719Sgabor		: /* empty */
514202719Sgabor			{
515202719Sgabor				$$ = cs(" 0 0=");
516202719Sgabor			}
517202719Sgabor		| relational_expression
518202719Sgabor		;
519202719Sgabor
520202719Sgaborrelational_expression
521202719Sgabor		: expression EQUALS expression
522202719Sgabor			{
523202719Sgabor				$$ = node($1, $3, cs("="), END_NODE);
524202719Sgabor			}
525202719Sgabor		| expression UNEQUALS expression
526202719Sgabor			{
527202719Sgabor				$$ = node($1, $3, cs("!="), END_NODE);
528202719Sgabor			}
529202719Sgabor		| expression LESS expression
530202719Sgabor			{
531202719Sgabor				$$ = node($1, $3, cs(">"), END_NODE);
532202719Sgabor			}
533202719Sgabor		| expression LESS_EQ expression
534202719Sgabor			{
535202719Sgabor				$$ = node($1, $3, cs("!<"), END_NODE);
536202719Sgabor			}
537202719Sgabor		| expression GREATER expression
538202719Sgabor			{
539202719Sgabor				$$ = node($1, $3, cs("<"), END_NODE);
540202719Sgabor			}
541202719Sgabor		| expression GREATER_EQ expression
542202719Sgabor			{
543202719Sgabor				$$ = node($1, $3, cs("!>"), END_NODE);
544202719Sgabor			}
545202719Sgabor		| expression
546202719Sgabor			{
547202719Sgabor				$$ = node($1, cs(" 0!="), END_NODE);
548202719Sgabor			}
549202719Sgabor		;
550202719Sgabor
551202719Sgabor
552202719Sgaborreturn_expression
553202719Sgabor		: /* empty */
554202719Sgabor			{
555202719Sgabor				$$ = node(cs("0"), epilogue,
556202719Sgabor				    numnode(nesting), cs("Q"), END_NODE);
557202719Sgabor			}
558202719Sgabor		| expression
559202719Sgabor			{
560202719Sgabor				$$ = node($1, epilogue,
561202719Sgabor				    numnode(nesting), cs("Q"), END_NODE);
562202719Sgabor			}
563202719Sgabor		| LPAR RPAR
564202719Sgabor			{
565202719Sgabor				$$ = node(cs("0"), epilogue,
566202719Sgabor				    numnode(nesting), cs("Q"), END_NODE);
567202719Sgabor			}
568202719Sgabor		;
569202719Sgabor
570202719Sgabor
571202719Sgaboropt_expression : /* empty */
572202719Sgabor			{
573202719Sgabor				$$ = cs(" 0");
574202719Sgabor			}
575202719Sgabor		| expression
576202719Sgabor		;
577202719Sgabor
578202719Sgaborexpression	: named_expression
579202719Sgabor			{
580202719Sgabor				$$ = node($1.load, END_NODE);
581202719Sgabor			}
582202719Sgabor		| DOT	{
583202719Sgabor				$$ = node(cs("l."), END_NODE);
584202719Sgabor			}
585202719Sgabor		| NUMBER
586202719Sgabor			{
587202719Sgabor				$$ = node(cs(" "), as($1), END_NODE);
588202719Sgabor			}
589202719Sgabor		| LPAR expression RPAR
590202719Sgabor			{
591202719Sgabor				$$ = $2;
592202719Sgabor			}
593202719Sgabor		| LETTER LPAR opt_argument_list RPAR
594202719Sgabor			{
595202719Sgabor				$$ = node($3, cs("l"),
596202719Sgabor				    function_node($1), cs("x"),
597202719Sgabor				    END_NODE);
598202719Sgabor				free($1);
599202719Sgabor			}
600202719Sgabor		| MINUS expression %prec UMINUS
601202719Sgabor			{
602202719Sgabor				$$ = node(cs(" 0"), $2, cs("-"),
603202719Sgabor				    END_NODE);
604202719Sgabor			}
605202719Sgabor		| expression PLUS expression
606202719Sgabor			{
607202719Sgabor				$$ = node($1, $3, cs("+"), END_NODE);
608202719Sgabor			}
609202719Sgabor		| expression MINUS expression
610202719Sgabor			{
611202719Sgabor				$$ = node($1, $3, cs("-"), END_NODE);
612202719Sgabor			}
613202719Sgabor		| expression MULTIPLY expression
614202719Sgabor			{
615202719Sgabor				$$ = node($1, $3, cs("*"), END_NODE);
616202719Sgabor			}
617202719Sgabor		| expression DIVIDE expression
618202719Sgabor			{
619202719Sgabor				$$ = node($1, $3, cs("/"), END_NODE);
620202719Sgabor			}
621202719Sgabor		| expression REMAINDER expression
622202719Sgabor			{
623202719Sgabor				$$ = node($1, $3, cs("%"), END_NODE);
624202719Sgabor			}
625202719Sgabor		| expression EXPONENT expression
626202719Sgabor			{
627202719Sgabor				$$ = node($1, $3, cs("^"), END_NODE);
628202719Sgabor			}
629202719Sgabor		| INCR named_expression
630202719Sgabor			{
631202719Sgabor				$$ = node($2.load, cs("1+d"), $2.store,
632202719Sgabor				    END_NODE);
633202719Sgabor			}
634202719Sgabor		| DECR named_expression
635202719Sgabor			{
636202719Sgabor				$$ = node($2.load, cs("1-d"),
637202719Sgabor				    $2.store, END_NODE);
638202719Sgabor			}
639202719Sgabor		| named_expression INCR
640202719Sgabor			{
641202719Sgabor				$$ = node($1.load, cs("d1+"),
642202719Sgabor				    $1.store, END_NODE);
643202719Sgabor			}
644202719Sgabor		| named_expression DECR
645202719Sgabor			{
646202719Sgabor				$$ = node($1.load, cs("d1-"),
647202719Sgabor				    $1.store, END_NODE);
648202719Sgabor			}
649202719Sgabor		| named_expression ASSIGN_OP expression
650202719Sgabor			{
651202719Sgabor				if ($2[0] == '\0')
652202719Sgabor					$$ = node($3, cs($2), cs("d"), $1.store,
653202719Sgabor					    END_NODE);
654202719Sgabor				else
655202719Sgabor					$$ = node($1.load, $3, cs($2), cs("d"),
656202719Sgabor					    $1.store, END_NODE);
657202719Sgabor			}
658202719Sgabor		| LENGTH LPAR expression RPAR
659202719Sgabor			{
660202719Sgabor				$$ = node($3, cs("Z"), END_NODE);
661202719Sgabor			}
662202719Sgabor		| SQRT LPAR expression RPAR
663202719Sgabor			{
664202719Sgabor				$$ = node($3, cs("v"), END_NODE);
665202719Sgabor			}
666202719Sgabor		| SCALE LPAR expression RPAR
667202719Sgabor			{
668202719Sgabor				$$ = node($3, cs("X"), END_NODE);
669202719Sgabor			}
670202719Sgabor		| BOOL_NOT expression
671202719Sgabor			{
672202719Sgabor				$$ = node($2, cs("N"), END_NODE);
673202719Sgabor			}
674202719Sgabor		| expression BOOL_AND alloc_macro pop_nesting expression
675202719Sgabor			{
676202719Sgabor				ssize_t n = node(cs("R"), $5, END_NODE);
677202719Sgabor				emit_macro($3, n);
678202719Sgabor				$$ = node($1, cs("d0!="), $3, END_NODE);
679202719Sgabor			}
680202719Sgabor		| expression BOOL_OR alloc_macro pop_nesting expression
681202719Sgabor			{
682202719Sgabor				ssize_t n = node(cs("R"), $5, END_NODE);
683202719Sgabor				emit_macro($3, n);
684202719Sgabor				$$ = node($1, cs("d0="), $3, END_NODE);
685202719Sgabor			}
686202719Sgabor		| expression EQUALS expression
687202719Sgabor			{
688202719Sgabor				$$ = node($1, $3, cs("G"), END_NODE);
689202719Sgabor			}
690202719Sgabor		| expression UNEQUALS expression
691202719Sgabor			{
692202719Sgabor				$$ = node($1, $3, cs("GN"), END_NODE);
693202719Sgabor			}
694202719Sgabor		| expression LESS expression
695202719Sgabor			{
696202719Sgabor				$$ = node($3, $1, cs("("), END_NODE);
697202719Sgabor			}
698202719Sgabor		| expression LESS_EQ expression
699202719Sgabor			{
700202719Sgabor				$$ = node($3, $1, cs("{"), END_NODE);
701202719Sgabor			}
702202719Sgabor		| expression GREATER expression
703202719Sgabor			{
704202719Sgabor				$$ = node($1, $3, cs("("), END_NODE);
705202719Sgabor			}
706202719Sgabor		| expression GREATER_EQ expression
707202719Sgabor			{
708202719Sgabor				$$ = node($1, $3, cs("{"), END_NODE);
709202719Sgabor			}
710202719Sgabor		;
711202719Sgabor
712202719Sgabornamed_expression
713202719Sgabor		: LETTER
714202719Sgabor			{
715202719Sgabor				$$.load = node(cs("l"), letter_node($1),
716202719Sgabor				    END_NODE);
717202719Sgabor				$$.store = node(cs("s"), letter_node($1),
718202719Sgabor				    END_NODE);
719202719Sgabor				free($1);
720202719Sgabor			}
721202719Sgabor		| LETTER LBRACKET expression RBRACKET
722202719Sgabor			{
723202719Sgabor				$$.load = node($3, cs(";"),
724202719Sgabor				    array_node($1), END_NODE);
725202719Sgabor				$$.store = node($3, cs(":"),
726202719Sgabor				    array_node($1), END_NODE);
727202719Sgabor				free($1);
728202719Sgabor			}
729202719Sgabor		| SCALE
730202719Sgabor			{
731202719Sgabor				$$.load = cs("K");
732202719Sgabor				$$.store = cs("k");
733202719Sgabor			}
734202719Sgabor		| IBASE
735202719Sgabor			{
736202719Sgabor				$$.load = cs("I");
737202719Sgabor				$$.store = cs("i");
738202719Sgabor			}
739202719Sgabor		| OBASE
740202719Sgabor			{
741202719Sgabor				$$.load = cs("O");
742202719Sgabor				$$.store = cs("o");
743202719Sgabor			}
744202719Sgabor		;
745202719Sgabor
746202719Sgaborprint_expression_list
747202719Sgabor		: print_expression
748202719Sgabor		| print_expression_list COMMA print_expression
749202719Sgabor			{
750202719Sgabor				$$ = node($1, $3, END_NODE);
751202719Sgabor			}
752202719Sgabor
753202719Sgaborprint_expression
754202719Sgabor		: expression
755202719Sgabor			{
756202719Sgabor				$$ = node($1, cs("ds.n"), END_NODE);
757202719Sgabor			}
758202719Sgabor		| STRING
759202719Sgabor			{
760202719Sgabor				char *p = escape($1);
761202719Sgabor				$$ = node(cs("["), as(p), cs("]n"), END_NODE);
762202719Sgabor				free(p);
763202719Sgabor			}
764202719Sgabor%%
765202719Sgabor
766202719Sgabor
767202719Sgaborstatic void
768202719Sgaborgrow(void)
769202719Sgabor{
770203443Sgabor	struct tree *p;
771203443Sgabor	size_t newsize;
772202719Sgabor
773202719Sgabor	if (current == instr_sz) {
774202719Sgabor		newsize = instr_sz * 2 + 1;
775202719Sgabor		p = realloc(instructions, newsize * sizeof(*p));
776202719Sgabor		if (p == NULL) {
777202719Sgabor			free(instructions);
778202719Sgabor			err(1, NULL);
779202719Sgabor		}
780202719Sgabor		instructions = p;
781202719Sgabor		instr_sz = newsize;
782202719Sgabor	}
783202719Sgabor}
784202719Sgabor
785202719Sgaborstatic ssize_t
786202719Sgaborcs(const char *str)
787202719Sgabor{
788203443Sgabor
789202719Sgabor	grow();
790202719Sgabor	instructions[current].index = CONST_STRING;
791202719Sgabor	instructions[current].u.cstr = str;
792202719Sgabor	return (current++);
793202719Sgabor}
794202719Sgabor
795202719Sgaborstatic ssize_t
796202719Sgaboras(const char *str)
797202719Sgabor{
798203443Sgabor
799202719Sgabor	grow();
800202719Sgabor	instructions[current].index = ALLOC_STRING;
801202719Sgabor	instructions[current].u.astr = strdup(str);
802202719Sgabor	if (instructions[current].u.astr == NULL)
803202719Sgabor		err(1, NULL);
804202719Sgabor	return (current++);
805202719Sgabor}
806202719Sgabor
807202719Sgaborstatic ssize_t
808202719Sgabornode(ssize_t arg, ...)
809202719Sgabor{
810203443Sgabor	va_list ap;
811203443Sgabor	ssize_t ret;
812202719Sgabor
813202719Sgabor	va_start(ap, arg);
814202719Sgabor
815202719Sgabor	ret = current;
816202719Sgabor	grow();
817202719Sgabor	instructions[current++].index = arg;
818202719Sgabor
819202719Sgabor	do {
820202719Sgabor		arg = va_arg(ap, ssize_t);
821202719Sgabor		grow();
822202719Sgabor		instructions[current++].index = arg;
823202719Sgabor	} while (arg != END_NODE);
824202719Sgabor
825202719Sgabor	va_end(ap);
826202719Sgabor	return (ret);
827202719Sgabor}
828202719Sgabor
829202719Sgaborstatic void
830202719Sgaboremit(ssize_t i)
831202719Sgabor{
832203443Sgabor
833202719Sgabor	if (instructions[i].index >= 0)
834202719Sgabor		while (instructions[i].index != END_NODE)
835202719Sgabor			emit(instructions[i++].index);
836202719Sgabor	else
837202719Sgabor		fputs(instructions[i].u.cstr, stdout);
838202719Sgabor}
839202719Sgabor
840202719Sgaborstatic void
841202719Sgaboremit_macro(int nodeidx, ssize_t code)
842202719Sgabor{
843203443Sgabor
844202719Sgabor	putchar('[');
845202719Sgabor	emit(code);
846202719Sgabor	printf("]s%s\n", instructions[nodeidx].u.cstr);
847202719Sgabor	nesting--;
848202719Sgabor}
849202719Sgabor
850202719Sgaborstatic void
851202719Sgaborfree_tree(void)
852202719Sgabor{
853203443Sgabor	ssize_t	i;
854202719Sgabor
855202719Sgabor	for (i = 0; i < current; i++)
856202719Sgabor		if (instructions[i].index == ALLOC_STRING)
857202719Sgabor			free(instructions[i].u.astr);
858202719Sgabor	current = 0;
859202719Sgabor}
860202719Sgabor
861202719Sgaborstatic ssize_t
862202719Sgabornumnode(int num)
863202719Sgabor{
864203443Sgabor	const char *p;
865202719Sgabor
866202719Sgabor	if (num < 10)
867202719Sgabor		p = str_table['0' + num];
868202719Sgabor	else if (num < 16)
869202719Sgabor		p = str_table['A' - 10 + num];
870202719Sgabor	else
871202719Sgabor		errx(1, "internal error: break num > 15");
872202719Sgabor	return (node(cs(" "), cs(p), END_NODE));
873202719Sgabor}
874202719Sgabor
875202719Sgabor
876202719Sgaborstatic ssize_t
877202719Sgaborlookup(char * str, size_t len, char type)
878202719Sgabor{
879203443Sgabor	ENTRY entry, *found;
880203443Sgabor	u_char *p;
881203443Sgabor	u_short num;
882202719Sgabor
883202719Sgabor	/* The scanner allocated an extra byte already */
884202719Sgabor	if (str[len-1] != type) {
885202719Sgabor		str[len] = type;
886202719Sgabor		str[len+1] = '\0';
887202719Sgabor	}
888202719Sgabor	entry.key = str;
889202719Sgabor	found = hsearch(entry, FIND);
890202719Sgabor	if (found == NULL) {
891202719Sgabor		if (var_count == MAX_VARIABLES)
892202719Sgabor			errx(1, "too many variables");
893202719Sgabor		p = malloc(4);
894202719Sgabor		if (p == NULL)
895202719Sgabor			err(1, NULL);
896202719Sgabor		num = var_count++;
897202719Sgabor		p[0] = 255;
898202719Sgabor		p[1] = ENCODE(num / VAR_BASE + 1);
899202719Sgabor		p[2] = ENCODE(num % VAR_BASE + 1);
900202719Sgabor		p[3] = '\0';
901202719Sgabor
902202719Sgabor		entry.data = (char *)p;
903202719Sgabor		entry.key = strdup(str);
904202719Sgabor		if (entry.key == NULL)
905202719Sgabor			err(1, NULL);
906202719Sgabor		found = hsearch(entry, ENTER);
907202719Sgabor		if (found == NULL)
908202719Sgabor			err(1, NULL);
909202719Sgabor	}
910202719Sgabor	return (cs(found->data));
911202719Sgabor}
912202719Sgabor
913202719Sgaborstatic ssize_t
914202719Sgaborletter_node(char *str)
915202719Sgabor{
916203443Sgabor	size_t len;
917202719Sgabor
918202719Sgabor	len = strlen(str);
919202719Sgabor	if (len == 1 && str[0] != '_')
920202719Sgabor		return (cs(str_table[(int)str[0]]));
921202719Sgabor	else
922202719Sgabor		return (lookup(str, len, 'L'));
923202719Sgabor}
924202719Sgabor
925202719Sgaborstatic ssize_t
926202719Sgaborarray_node(char *str)
927202719Sgabor{
928203443Sgabor	size_t len;
929202719Sgabor
930202719Sgabor	len = strlen(str);
931202719Sgabor	if (len == 1 && str[0] != '_')
932202719Sgabor		return (cs(str_table[(int)str[0] - 'a' + ARRAY_CHAR]));
933202719Sgabor	else
934202719Sgabor		return (lookup(str, len, 'A'));
935202719Sgabor}
936202719Sgabor
937202719Sgaborstatic ssize_t
938202719Sgaborfunction_node(char *str)
939202719Sgabor{
940203443Sgabor	size_t len;
941202719Sgabor
942202719Sgabor	len = strlen(str);
943202719Sgabor	if (len == 1 && str[0] != '_')
944202719Sgabor		return (cs(str_table[(int)str[0] - 'a' + FUNC_CHAR]));
945202719Sgabor	else
946202719Sgabor		return (lookup(str, len, 'F'));
947202719Sgabor}
948202719Sgabor
949202719Sgaborstatic void
950202719Sgaboradd_par(ssize_t n)
951202719Sgabor{
952203443Sgabor
953202719Sgabor	prologue = node(cs("S"), n, prologue, END_NODE);
954202719Sgabor	epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE);
955202719Sgabor}
956202719Sgabor
957202719Sgaborstatic void
958202719Sgaboradd_local(ssize_t n)
959202719Sgabor{
960203443Sgabor
961202719Sgabor	prologue = node(cs("0S"), n, prologue, END_NODE);
962202719Sgabor	epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE);
963202719Sgabor}
964202719Sgabor
965202719Sgaborvoid
966202719Sgaboryyerror(const char *s)
967202719Sgabor{
968203443Sgabor	char *p, *str;
969203443Sgabor	int n;
970202719Sgabor
971202719Sgabor	if (yyin != NULL && feof(yyin))
972202719Sgabor		n = asprintf(&str, "%s: %s:%d: %s: unexpected EOF",
973202719Sgabor		    __progname, filename, lineno, s);
974202719Sgabor	else if (isspace(yytext[0]) || !isprint(yytext[0]))
975202719Sgabor		n = asprintf(&str,
976202719Sgabor		    "%s: %s:%d: %s: ascii char 0x%02x unexpected",
977202719Sgabor		    __progname, filename, lineno, s, yytext[0]);
978202719Sgabor	else
979202719Sgabor		n = asprintf(&str, "%s: %s:%d: %s: %s unexpected",
980202719Sgabor		    __progname, filename, lineno, s, yytext);
981202719Sgabor	if (n == -1)
982202719Sgabor		err(1, NULL);
983202719Sgabor
984202719Sgabor	fputs("c[", stdout);
985202719Sgabor	for (p = str; *p != '\0'; p++) {
986202719Sgabor		if (*p == '[' || *p == ']' || *p =='\\')
987202719Sgabor			putchar('\\');
988202719Sgabor		putchar(*p);
989202719Sgabor	}
990202719Sgabor	fputs("]pc\n", stdout);
991202719Sgabor	free(str);
992202719Sgabor}
993202719Sgabor
994202719Sgaborvoid
995202719Sgaborfatal(const char *s)
996202719Sgabor{
997203443Sgabor
998202719Sgabor	errx(1, "%s:%d: %s", filename, lineno, s);
999202719Sgabor}
1000202719Sgabor
1001202719Sgaborstatic void
1002202719Sgaborwarning(const char *s)
1003202719Sgabor{
1004203443Sgabor
1005202719Sgabor	warnx("%s:%d: %s", filename, lineno, s);
1006202719Sgabor}
1007202719Sgabor
1008202719Sgaborstatic void
1009202719Sgaborinit(void)
1010202719Sgabor{
1011203443Sgabor	unsigned int i;
1012202719Sgabor
1013202719Sgabor	for (i = 0; i < UCHAR_MAX; i++) {
1014202719Sgabor		str_table[i][0] = i;
1015202719Sgabor		str_table[i][1] = '\0';
1016202719Sgabor	}
1017202719Sgabor	if (hcreate(1 << 16) == 0)
1018202719Sgabor		err(1, NULL);
1019202719Sgabor}
1020202719Sgabor
1021202719Sgabor
1022202719Sgaborstatic void
1023202719Sgaborusage(void)
1024202719Sgabor{
1025203443Sgabor
1026202845Sdelphij	fprintf(stderr, "usage: %s [-chlqv] [-e expression] [file ...]\n",
1027202719Sgabor	    __progname);
1028202719Sgabor	exit(1);
1029202719Sgabor}
1030202719Sgabor
1031202719Sgaborstatic char *
1032202719Sgaborescape(const char *str)
1033202719Sgabor{
1034203443Sgabor	char *p, *ret;
1035202719Sgabor
1036202719Sgabor	ret = malloc(strlen(str) + 1);
1037202719Sgabor	if (ret == NULL)
1038202719Sgabor		err(1, NULL);
1039202719Sgabor
1040202719Sgabor	p = ret;
1041202719Sgabor	while (*str != '\0') {
1042202719Sgabor		/*
1043202719Sgabor		 * We get _escaped_ strings here. Single backslashes are
1044202719Sgabor		 * already converted to double backslashes
1045202719Sgabor		 */
1046202719Sgabor		if (*str == '\\') {
1047202719Sgabor			if (*++str == '\\') {
1048202719Sgabor				switch (*++str) {
1049202719Sgabor				case 'a':
1050202719Sgabor					*p++ = '\a';
1051202719Sgabor					break;
1052202719Sgabor				case 'b':
1053202719Sgabor					*p++ = '\b';
1054202719Sgabor					break;
1055202719Sgabor				case 'f':
1056202719Sgabor					*p++ = '\f';
1057202719Sgabor					break;
1058202719Sgabor				case 'n':
1059202719Sgabor					*p++ = '\n';
1060202719Sgabor					break;
1061202719Sgabor				case 'q':
1062202719Sgabor					*p++ = '"';
1063202719Sgabor					break;
1064202719Sgabor				case 'r':
1065202719Sgabor					*p++ = '\r';
1066202719Sgabor					break;
1067202719Sgabor				case 't':
1068202719Sgabor					*p++ = '\t';
1069202719Sgabor					break;
1070202719Sgabor				case '\\':
1071202719Sgabor					*p++ = '\\';
1072202719Sgabor					break;
1073202719Sgabor				}
1074202719Sgabor				str++;
1075202719Sgabor			} else {
1076202719Sgabor				*p++ = '\\';
1077202719Sgabor				*p++ = *str++;
1078202719Sgabor			}
1079202719Sgabor		} else
1080202719Sgabor			*p++ = *str++;
1081202719Sgabor	}
1082202719Sgabor	*p = '\0';
1083202719Sgabor	return (ret);
1084202719Sgabor}
1085202719Sgabor
1086202719Sgabor/* ARGSUSED */
1087243075Seadlerstatic void
1088202719Sgaborsigchld(int signo)
1089202719Sgabor{
1090203443Sgabor	pid_t pid;
1091203443Sgabor	int status;
1092202719Sgabor
1093202719Sgabor	switch (signo) {
1094202719Sgabor	default:
1095202719Sgabor		for (;;) {
1096232994Skevlo			pid = waitpid(dc, &status, WUNTRACED);
1097202719Sgabor			if (pid == -1) {
1098202719Sgabor				if (errno == EINTR)
1099202719Sgabor					continue;
1100202719Sgabor				_exit(0);
1101202719Sgabor			}
1102202719Sgabor			if (WIFEXITED(status) || WIFSIGNALED(status))
1103202719Sgabor				_exit(0);
1104202719Sgabor			else
1105202719Sgabor				break;
1106202719Sgabor		}
1107202719Sgabor	}
1108202719Sgabor}
1109202719Sgabor
1110203498Sdelphijstatic const char *
1111203498Sdelphijdummy_prompt(void)
1112203498Sdelphij{
1113203498Sdelphij
1114203498Sdelphij        return ("");
1115203498Sdelphij}
1116203498Sdelphij
1117202719Sgaborint
1118202719Sgabormain(int argc, char *argv[])
1119202719Sgabor{
1120203443Sgabor	char *q;
1121203443Sgabor	int p[2];
1122203443Sgabor	int ch, i;
1123202719Sgabor
1124202719Sgabor	init();
1125202719Sgabor	setlinebuf(stdout);
1126202719Sgabor
1127202719Sgabor	sargv = malloc(argc * sizeof(char *));
1128202719Sgabor	if (sargv == NULL)
1129202719Sgabor		err(1, NULL);
1130202719Sgabor
1131202719Sgabor	if ((cmdexpr = strdup("")) == NULL)
1132202719Sgabor		err(1, NULL);
1133202719Sgabor	/* The d debug option is 4.4 BSD bc(1) compatible */
1134202719Sgabor	while ((ch = getopt_long(argc, argv, "cde:hlqv",
1135202719Sgabor	   long_options, NULL)) != -1) {
1136202719Sgabor		switch (ch) {
1137202719Sgabor		case 'c':
1138202719Sgabor		case 'd':
1139202719Sgabor			do_fork = false;
1140202719Sgabor			break;
1141202719Sgabor		case 'e':
1142202719Sgabor			q = cmdexpr;
1143202719Sgabor			if (asprintf(&cmdexpr, "%s%s\n", cmdexpr, optarg) == -1)
1144202719Sgabor				err(1, NULL);
1145202719Sgabor			free(q);
1146202719Sgabor			break;
1147202719Sgabor		case 'h':
1148202719Sgabor			usage();
1149202719Sgabor			break;
1150202719Sgabor		case 'l':
1151202719Sgabor			sargv[sargc++] = _PATH_LIBB;
1152202719Sgabor			break;
1153202719Sgabor		case 'q':
1154202719Sgabor			/* compatibility option */
1155202719Sgabor			break;
1156202719Sgabor		case 'v':
1157202719Sgabor			fprintf(stderr, "%s (BSD bc) %s\n", __progname, BC_VER);
1158202719Sgabor			exit(0);
1159202719Sgabor			break;
1160202719Sgabor		default:
1161202719Sgabor			usage();
1162202719Sgabor		}
1163202719Sgabor	}
1164202719Sgabor
1165202719Sgabor	argc -= optind;
1166202719Sgabor	argv += optind;
1167202719Sgabor
1168202719Sgabor	interactive = isatty(STDIN_FILENO);
1169202719Sgabor	for (i = 0; i < argc; i++)
1170202719Sgabor		sargv[sargc++] = argv[i];
1171202719Sgabor
1172202719Sgabor	if (do_fork) {
1173202719Sgabor		if (pipe(p) == -1)
1174202719Sgabor			err(1, "cannot create pipe");
1175202719Sgabor		dc = fork();
1176202719Sgabor		if (dc == -1)
1177202719Sgabor			err(1, "cannot fork");
1178202719Sgabor		else if (dc != 0) {
1179202719Sgabor			signal(SIGCHLD, sigchld);
1180202719Sgabor			close(STDOUT_FILENO);
1181202719Sgabor			dup(p[1]);
1182202719Sgabor			close(p[0]);
1183202719Sgabor			close(p[1]);
1184202719Sgabor		} else {
1185202719Sgabor			close(STDIN_FILENO);
1186202719Sgabor			dup(p[0]);
1187202719Sgabor			close(p[0]);
1188202719Sgabor			close(p[1]);
1189202719Sgabor			execl(_PATH_DC, "dc", "-x", (char *)NULL);
1190202719Sgabor			err(1, "cannot find dc");
1191202719Sgabor		}
1192202719Sgabor	}
1193232994Skevlo	if (interactive) {
1194232994Skevlo		el = el_init("bc", stdin, stderr, stderr);
1195232994Skevlo		hist = history_init();
1196232994Skevlo		history(hist, &he, H_SETSIZE, 100);
1197232994Skevlo		el_set(el, EL_HIST, history, hist);
1198232994Skevlo		el_set(el, EL_EDITOR, "emacs");
1199232994Skevlo		el_set(el, EL_SIGNAL, 1);
1200232994Skevlo		el_set(el, EL_PROMPT, dummy_prompt);
1201232994Skevlo		el_source(el, NULL);
1202232994Skevlo	}
1203202719Sgabor	yywrap();
1204202719Sgabor	return (yyparse());
1205202719Sgabor}
1206