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