freebsd-dev/usr.bin/bc/bc.y
Pedro F. Giffuni 6659f97630 bc(1): Fix memory corruption issues
Fix crashes and hangs found by AFL.
Improve handling of non-ascii chars.

Obtained from:	OpenBSD (CVS rev 1.49)
2015-11-24 04:15:13 +00:00

1217 lines
23 KiB
Plaintext

%{
/* $OpenBSD: bc.y,v 1.44 2013/11/20 21:33:54 deraadt Exp $ */
/*
* Copyright (c) 2003, Otto Moerbeek <otto@drijf.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This implementation of bc(1) uses concepts from the original 4.4
* BSD bc(1). The code itself is a complete rewrite, based on the
* Posix defined bc(1) grammar. Other differences include type safe
* usage of pointers to build the tree of emitted code, typed yacc
* rule values, dynamic allocation of all data structures and a
* completely rewritten lexical analyzer using lex(1).
*
* Some effort has been made to make sure that the generated code is
* the same as the code generated by the older version, to provide
* easy regression testing.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <getopt.h>
#include <histedit.h>
#include <limits.h>
#include <search.h>
#include <signal.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "extern.h"
#include "pathnames.h"
#define BC_VER "1.1-FreeBSD"
#define END_NODE ((ssize_t) -1)
#define CONST_STRING ((ssize_t) -2)
#define ALLOC_STRING ((ssize_t) -3)
extern char *yytext;
extern FILE *yyin;
struct tree {
union {
char *astr;
const char *cstr;
} u;
ssize_t index;
};
int yywrap(void);
int fileindex;
int sargc;
const char **sargv;
const char *filename;
char *cmdexpr;
static void grow(void);
static ssize_t cs(const char *);
static ssize_t as(const char *);
static ssize_t node(ssize_t, ...);
static void emit(ssize_t, int);
static void emit_macro(int, ssize_t);
static void free_tree(void);
static ssize_t numnode(int);
static ssize_t lookup(char *, size_t, char);
static ssize_t letter_node(char *);
static ssize_t array_node(char *);
static ssize_t function_node(char *);
static void add_par(ssize_t);
static void add_local(ssize_t);
static void warning(const char *);
static void init(void);
static void usage(void);
static char *escape(const char *);
static ssize_t instr_sz = 0;
static struct tree *instructions = NULL;
static ssize_t current = 0;
static int macro_char = '0';
static int reset_macro_char = '0';
static int nesting = 0;
static int breakstack[16];
static int breaksp = 0;
static ssize_t prologue;
static ssize_t epilogue;
static bool st_has_continue;
static char str_table[UCHAR_MAX][2];
static bool do_fork = true;
static u_short var_count;
static pid_t dc;
static void sigchld(int);
extern char *__progname;
#define BREAKSTACK_SZ (sizeof(breakstack)/sizeof(breakstack[0]))
/* These values are 4.4BSD bc compatible */
#define FUNC_CHAR 0x01
#define ARRAY_CHAR 0xa1
/* Skip '\0', [, \ and ] */
#define ENCODE(c) ((c) < '[' ? (c) : (c) + 3);
#define VAR_BASE (256-4)
#define MAX_VARIABLES (VAR_BASE * VAR_BASE)
const struct option long_options[] =
{
{"expression", required_argument, NULL, 'e'},
{"help", no_argument, NULL, 'h'},
{"mathlib", no_argument, NULL, 'l'},
/* compatibility option */
{"quiet", no_argument, NULL, 'q'},
{"version", no_argument, NULL, 'v'},
{NULL, no_argument, NULL, 0}
};
%}
%start program
%union {
struct lvalue lvalue;
const char *str;
char *astr;
ssize_t node;
}
%token COMMA SEMICOLON LPAR RPAR LBRACE RBRACE LBRACKET RBRACKET DOT
%token NEWLINE
%token <astr> LETTER
%token <str> NUMBER STRING
%token DEFINE BREAK QUIT LENGTH
%token RETURN FOR IF WHILE SQRT
%token SCALE IBASE OBASE AUTO
%token CONTINUE ELSE PRINT
%left BOOL_OR
%left BOOL_AND
%nonassoc BOOL_NOT
%nonassoc EQUALS LESS_EQ GREATER_EQ UNEQUALS LESS GREATER
%right <str> ASSIGN_OP
%left PLUS MINUS
%left MULTIPLY DIVIDE REMAINDER
%right EXPONENT
%nonassoc UMINUS
%nonassoc INCR DECR
%type <lvalue> named_expression
%type <node> argument_list
%type <node> alloc_macro
%type <node> expression
%type <node> function
%type <node> function_header
%type <node> input_item
%type <node> opt_argument_list
%type <node> opt_expression
%type <node> opt_relational_expression
%type <node> opt_statement
%type <node> print_expression
%type <node> print_expression_list
%type <node> relational_expression
%type <node> return_expression
%type <node> semicolon_list
%type <node> statement
%type <node> statement_list
%%
program : /* empty */
| program input_item
;
input_item : semicolon_list NEWLINE
{
emit($1, 0);
macro_char = reset_macro_char;
putchar('\n');
free_tree();
st_has_continue = false;
}
| function
{
putchar('\n');
free_tree();
st_has_continue = false;
}
| error NEWLINE
{
yyerrok;
}
| error QUIT
{
yyerrok;
}
;
semicolon_list : /* empty */
{
$$ = cs("");
}
| statement
| semicolon_list SEMICOLON statement
{
$$ = node($1, $3, END_NODE);
}
| semicolon_list SEMICOLON
;
statement_list : /* empty */
{
$$ = cs("");
}
| statement
| statement_list NEWLINE
| statement_list NEWLINE statement
{
$$ = node($1, $3, END_NODE);
}
| statement_list SEMICOLON
| statement_list SEMICOLON statement
{
$$ = node($1, $3, END_NODE);
}
;
opt_statement : /* empty */
{
$$ = cs("");
}
| statement
;
statement : expression
{
$$ = node($1, cs("ps."), END_NODE);
}
| named_expression ASSIGN_OP expression
{
if ($2[0] == '\0')
$$ = node($3, cs($2), $1.store,
END_NODE);
else
$$ = node($1.load, $3, cs($2), $1.store,
END_NODE);
}
| STRING
{
$$ = node(cs("["), as($1),
cs("]P"), END_NODE);
}
| BREAK
{
if (breaksp == 0) {
warning("break not in for or while");
YYERROR;
} else {
$$ = node(
numnode(nesting -
breakstack[breaksp-1]),
cs("Q"), END_NODE);
}
}
| CONTINUE
{
if (breaksp == 0) {
warning("continue not in for or while");
YYERROR;
} else {
st_has_continue = true;
$$ = node(numnode(nesting -
breakstack[breaksp-1] - 1),
cs("J"), END_NODE);
}
}
| QUIT
{
sigset_t mask;
putchar('q');
fflush(stdout);
if (dc) {
sigprocmask(SIG_BLOCK, NULL, &mask);
sigsuspend(&mask);
} else
exit(0);
}
| RETURN return_expression
{
if (nesting == 0) {
warning("return must be in a function");
YYERROR;
}
$$ = $2;
}
| FOR LPAR alloc_macro opt_expression SEMICOLON
opt_relational_expression SEMICOLON
opt_expression RPAR opt_statement pop_nesting
{
ssize_t n;
if (st_has_continue)
n = node($10, cs("M"), $8, cs("s."),
$6, $3, END_NODE);
else
n = node($10, $8, cs("s."), $6, $3,
END_NODE);
emit_macro($3, n);
$$ = node($4, cs("s."), $6, $3, cs(" "),
END_NODE);
}
| IF LPAR alloc_macro pop_nesting relational_expression RPAR
opt_statement
{
emit_macro($3, $7);
$$ = node($5, $3, cs(" "), END_NODE);
}
| IF LPAR alloc_macro pop_nesting relational_expression RPAR
opt_statement ELSE alloc_macro pop_nesting opt_statement
{
emit_macro($3, $7);
emit_macro($9, $11);
$$ = node($5, $3, cs("e"), $9, cs(" "),
END_NODE);
}
| WHILE LPAR alloc_macro relational_expression RPAR
opt_statement pop_nesting
{
ssize_t n;
if (st_has_continue)
n = node($6, cs("M"), $4, $3, END_NODE);
else
n = node($6, $4, $3, END_NODE);
emit_macro($3, n);
$$ = node($4, $3, cs(" "), END_NODE);
}
| LBRACE statement_list RBRACE
{
$$ = $2;
}
| PRINT print_expression_list
{
$$ = $2;
}
;
alloc_macro : /* empty */
{
$$ = cs(str_table[macro_char]);
macro_char++;
/* Do not use [, \ and ] */
if (macro_char == '[')
macro_char += 3;
/* skip letters */
else if (macro_char == 'a')
macro_char = '{';
else if (macro_char == ARRAY_CHAR)
macro_char += 26;
else if (macro_char == 255)
fatal("program too big");
if (breaksp == BREAKSTACK_SZ)
fatal("nesting too deep");
breakstack[breaksp++] = nesting++;
}
;
pop_nesting : /* empty */
{
breaksp--;
}
;
function : function_header opt_parameter_list RPAR opt_newline
LBRACE NEWLINE opt_auto_define_list
statement_list RBRACE
{
int n = node(prologue, $8, epilogue,
cs("0"), numnode(nesting),
cs("Q"), END_NODE);
emit_macro($1, n);
reset_macro_char = macro_char;
nesting = 0;
breaksp = 0;
}
;
function_header : DEFINE LETTER LPAR
{
$$ = function_node($2);
free($2);
prologue = cs("");
epilogue = cs("");
nesting = 1;
breaksp = 0;
breakstack[breaksp] = 0;
}
;
opt_newline : /* empty */
| NEWLINE
;
opt_parameter_list
: /* empty */
| parameter_list
;
parameter_list : LETTER
{
add_par(letter_node($1));
free($1);
}
| LETTER LBRACKET RBRACKET
{
add_par(array_node($1));
free($1);
}
| parameter_list COMMA LETTER
{
add_par(letter_node($3));
free($3);
}
| parameter_list COMMA LETTER LBRACKET RBRACKET
{
add_par(array_node($3));
free($3);
}
;
opt_auto_define_list
: /* empty */
| AUTO define_list NEWLINE
| AUTO define_list SEMICOLON
;
define_list : LETTER
{
add_local(letter_node($1));
free($1);
}
| LETTER LBRACKET RBRACKET
{
add_local(array_node($1));
free($1);
}
| define_list COMMA LETTER
{
add_local(letter_node($3));
free($3);
}
| define_list COMMA LETTER LBRACKET RBRACKET
{
add_local(array_node($3));
free($3);
}
;
opt_argument_list
: /* empty */
{
$$ = cs("");
}
| argument_list
;
argument_list : expression
| argument_list COMMA expression
{
$$ = node($1, $3, END_NODE);
}
| argument_list COMMA LETTER LBRACKET RBRACKET
{
$$ = node($1, cs("l"), array_node($3),
END_NODE);
free($3);
}
;
opt_relational_expression
: /* empty */
{
$$ = cs(" 0 0=");
}
| relational_expression
;
relational_expression
: expression EQUALS expression
{
$$ = node($1, $3, cs("="), END_NODE);
}
| expression UNEQUALS expression
{
$$ = node($1, $3, cs("!="), END_NODE);
}
| expression LESS expression
{
$$ = node($1, $3, cs(">"), END_NODE);
}
| expression LESS_EQ expression
{
$$ = node($1, $3, cs("!<"), END_NODE);
}
| expression GREATER expression
{
$$ = node($1, $3, cs("<"), END_NODE);
}
| expression GREATER_EQ expression
{
$$ = node($1, $3, cs("!>"), END_NODE);
}
| expression
{
$$ = node($1, cs(" 0!="), END_NODE);
}
;
return_expression
: /* empty */
{
$$ = node(cs("0"), epilogue,
numnode(nesting), cs("Q"), END_NODE);
}
| expression
{
$$ = node($1, epilogue,
numnode(nesting), cs("Q"), END_NODE);
}
| LPAR RPAR
{
$$ = node(cs("0"), epilogue,
numnode(nesting), cs("Q"), END_NODE);
}
;
opt_expression : /* empty */
{
$$ = cs(" 0");
}
| expression
;
expression : named_expression
{
$$ = node($1.load, END_NODE);
}
| DOT {
$$ = node(cs("l."), END_NODE);
}
| NUMBER
{
$$ = node(cs(" "), as($1), END_NODE);
}
| LPAR expression RPAR
{
$$ = $2;
}
| LETTER LPAR opt_argument_list RPAR
{
$$ = node($3, cs("l"),
function_node($1), cs("x"),
END_NODE);
free($1);
}
| MINUS expression %prec UMINUS
{
$$ = node(cs(" 0"), $2, cs("-"),
END_NODE);
}
| expression PLUS expression
{
$$ = node($1, $3, cs("+"), END_NODE);
}
| expression MINUS expression
{
$$ = node($1, $3, cs("-"), END_NODE);
}
| expression MULTIPLY expression
{
$$ = node($1, $3, cs("*"), END_NODE);
}
| expression DIVIDE expression
{
$$ = node($1, $3, cs("/"), END_NODE);
}
| expression REMAINDER expression
{
$$ = node($1, $3, cs("%"), END_NODE);
}
| expression EXPONENT expression
{
$$ = node($1, $3, cs("^"), END_NODE);
}
| INCR named_expression
{
$$ = node($2.load, cs("1+d"), $2.store,
END_NODE);
}
| DECR named_expression
{
$$ = node($2.load, cs("1-d"),
$2.store, END_NODE);
}
| named_expression INCR
{
$$ = node($1.load, cs("d1+"),
$1.store, END_NODE);
}
| named_expression DECR
{
$$ = node($1.load, cs("d1-"),
$1.store, END_NODE);
}
| named_expression ASSIGN_OP expression
{
if ($2[0] == '\0')
$$ = node($3, cs($2), cs("d"), $1.store,
END_NODE);
else
$$ = node($1.load, $3, cs($2), cs("d"),
$1.store, END_NODE);
}
| LENGTH LPAR expression RPAR
{
$$ = node($3, cs("Z"), END_NODE);
}
| SQRT LPAR expression RPAR
{
$$ = node($3, cs("v"), END_NODE);
}
| SCALE LPAR expression RPAR
{
$$ = node($3, cs("X"), END_NODE);
}
| BOOL_NOT expression
{
$$ = node($2, cs("N"), END_NODE);
}
| expression BOOL_AND alloc_macro pop_nesting expression
{
ssize_t n = node(cs("R"), $5, END_NODE);
emit_macro($3, n);
$$ = node($1, cs("d0!="), $3, END_NODE);
}
| expression BOOL_OR alloc_macro pop_nesting expression
{
ssize_t n = node(cs("R"), $5, END_NODE);
emit_macro($3, n);
$$ = node($1, cs("d0="), $3, END_NODE);
}
| expression EQUALS expression
{
$$ = node($1, $3, cs("G"), END_NODE);
}
| expression UNEQUALS expression
{
$$ = node($1, $3, cs("GN"), END_NODE);
}
| expression LESS expression
{
$$ = node($3, $1, cs("("), END_NODE);
}
| expression LESS_EQ expression
{
$$ = node($3, $1, cs("{"), END_NODE);
}
| expression GREATER expression
{
$$ = node($1, $3, cs("("), END_NODE);
}
| expression GREATER_EQ expression
{
$$ = node($1, $3, cs("{"), END_NODE);
}
;
named_expression
: LETTER
{
$$.load = node(cs("l"), letter_node($1),
END_NODE);
$$.store = node(cs("s"), letter_node($1),
END_NODE);
free($1);
}
| LETTER LBRACKET expression RBRACKET
{
$$.load = node($3, cs(";"),
array_node($1), END_NODE);
$$.store = node($3, cs(":"),
array_node($1), END_NODE);
free($1);
}
| SCALE
{
$$.load = cs("K");
$$.store = cs("k");
}
| IBASE
{
$$.load = cs("I");
$$.store = cs("i");
}
| OBASE
{
$$.load = cs("O");
$$.store = cs("o");
}
;
print_expression_list
: print_expression
| print_expression_list COMMA print_expression
{
$$ = node($1, $3, END_NODE);
}
print_expression
: expression
{
$$ = node($1, cs("ds.n"), END_NODE);
}
| STRING
{
char *p = escape($1);
$$ = node(cs("["), as(p), cs("]n"), END_NODE);
free(p);
}
%%
static void
grow(void)
{
struct tree *p;
size_t newsize;
if (current == instr_sz) {
newsize = instr_sz * 2 + 1;
p = realloc(instructions, newsize * sizeof(*p));
if (p == NULL) {
free(instructions);
err(1, NULL);
}
instructions = p;
instr_sz = newsize;
}
}
static ssize_t
cs(const char *str)
{
grow();
instructions[current].index = CONST_STRING;
instructions[current].u.cstr = str;
return (current++);
}
static ssize_t
as(const char *str)
{
grow();
instructions[current].index = ALLOC_STRING;
instructions[current].u.astr = strdup(str);
if (instructions[current].u.astr == NULL)
err(1, NULL);
return (current++);
}
static ssize_t
node(ssize_t arg, ...)
{
va_list ap;
ssize_t ret;
va_start(ap, arg);
ret = current;
grow();
instructions[current++].index = arg;
do {
arg = va_arg(ap, ssize_t);
grow();
instructions[current++].index = arg;
} while (arg != END_NODE);
va_end(ap);
return (ret);
}
static void
emit(ssize_t i, int level)
{
if (level > 1000)
errx(1, "internal error: tree level > 1000");
if (instructions[i].index >= 0) {
while (instructions[i].index != END_NODE &&
instructions[i].index != i) {
emit(instructions[i].index, level + 1);
i++;
}
} else if (instructions[i].index != END_NODE)
fputs(instructions[i].u.cstr, stdout);
}
static void
emit_macro(int nodeidx, ssize_t code)
{
putchar('[');
emit(code, 0);
printf("]s%s\n", instructions[nodeidx].u.cstr);
nesting--;
}
static void
free_tree(void)
{
ssize_t i;
for (i = 0; i < current; i++)
if (instructions[i].index == ALLOC_STRING)
free(instructions[i].u.astr);
current = 0;
}
static ssize_t
numnode(int num)
{
const char *p;
if (num < 10)
p = str_table['0' + num];
else if (num < 16)
p = str_table['A' - 10 + num];
else
errx(1, "internal error: break num > 15");
return (node(cs(" "), cs(p), END_NODE));
}
static ssize_t
lookup(char * str, size_t len, char type)
{
ENTRY entry, *found;
u_char *p;
u_short num;
/* The scanner allocated an extra byte already */
if (str[len-1] != type) {
str[len] = type;
str[len+1] = '\0';
}
entry.key = str;
found = hsearch(entry, FIND);
if (found == NULL) {
if (var_count == MAX_VARIABLES)
errx(1, "too many variables");
p = malloc(4);
if (p == NULL)
err(1, NULL);
num = var_count++;
p[0] = 255;
p[1] = ENCODE(num / VAR_BASE + 1);
p[2] = ENCODE(num % VAR_BASE + 1);
p[3] = '\0';
entry.data = (char *)p;
entry.key = strdup(str);
if (entry.key == NULL)
err(1, NULL);
found = hsearch(entry, ENTER);
if (found == NULL)
err(1, NULL);
}
return (cs(found->data));
}
static ssize_t
letter_node(char *str)
{
size_t len;
len = strlen(str);
if (len == 1 && str[0] != '_')
return (cs(str_table[(int)str[0]]));
else
return (lookup(str, len, 'L'));
}
static ssize_t
array_node(char *str)
{
size_t len;
len = strlen(str);
if (len == 1 && str[0] != '_')
return (cs(str_table[(int)str[0] - 'a' + ARRAY_CHAR]));
else
return (lookup(str, len, 'A'));
}
static ssize_t
function_node(char *str)
{
size_t len;
len = strlen(str);
if (len == 1 && str[0] != '_')
return (cs(str_table[(int)str[0] - 'a' + FUNC_CHAR]));
else
return (lookup(str, len, 'F'));
}
static void
add_par(ssize_t n)
{
prologue = node(cs("S"), n, prologue, END_NODE);
epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE);
}
static void
add_local(ssize_t n)
{
prologue = node(cs("0S"), n, prologue, END_NODE);
epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE);
}
void
yyerror(const char *s)
{
char *p, *str;
int n;
if (yyin != NULL && feof(yyin))
n = asprintf(&str, "%s: %s:%d: %s: unexpected EOF",
__progname, filename, lineno, s);
else if (yytext[0] == '\n')
n = asprintf(&str,
"%s: %s:%d: %s: newline unexpected",
__progname, filename, lineno, s);
else if (isspace((unsigned char)yytext[0]) ||
!isprint((unsigned char)yytext[0]))
n = asprintf(&str,
"%s: %s:%d: %s: ascii char 0x%02x unexpected",
__progname, filename, lineno, s, yytext[0] & 0xff);
else
n = asprintf(&str, "%s: %s:%d: %s: %s unexpected",
__progname, filename, lineno, s, yytext);
if (n == -1)
err(1, NULL);
fputs("c[", stdout);
for (p = str; *p != '\0'; p++) {
if (*p == '[' || *p == ']' || *p =='\\')
putchar('\\');
putchar(*p);
}
fputs("]pc\n", stdout);
free(str);
}
void
fatal(const char *s)
{
errx(1, "%s:%d: %s", filename, lineno, s);
}
static void
warning(const char *s)
{
warnx("%s:%d: %s", filename, lineno, s);
}
static void
init(void)
{
unsigned int i;
for (i = 0; i < UCHAR_MAX; i++) {
str_table[i][0] = i;
str_table[i][1] = '\0';
}
if (hcreate(1 << 16) == 0)
err(1, NULL);
}
static void
usage(void)
{
fprintf(stderr, "usage: %s [-chlv] [-e expression] [file ...]\n",
__progname);
exit(1);
}
static char *
escape(const char *str)
{
char *p, *ret;
ret = malloc(strlen(str) + 1);
if (ret == NULL)
err(1, NULL);
p = ret;
while (*str != '\0') {
/*
* We get _escaped_ strings here. Single backslashes are
* already converted to double backslashes
*/
if (*str == '\\') {
if (*++str == '\\') {
switch (*++str) {
case 'a':
*p++ = '\a';
break;
case 'b':
*p++ = '\b';
break;
case 'f':
*p++ = '\f';
break;
case 'n':
*p++ = '\n';
break;
case 'q':
*p++ = '"';
break;
case 'r':
*p++ = '\r';
break;
case 't':
*p++ = '\t';
break;
case '\\':
*p++ = '\\';
break;
}
str++;
} else {
*p++ = '\\';
*p++ = *str++;
}
} else
*p++ = *str++;
}
*p = '\0';
return (ret);
}
/* ARGSUSED */
static void
sigchld(int signo __unused)
{
pid_t pid;
int status, save_errno = errno;
for (;;) {
pid = waitpid(dc, &status, WCONTINUED | WNOHANG);
if (pid == -1) {
if (errno == EINTR)
continue;
_exit(0);
} else if (pid == 0)
break;
if (WIFEXITED(status) || WIFSIGNALED(status))
_exit(0);
else
break;
}
errno = save_errno;
}
static const char *
dummy_prompt(void)
{
return ("");
}
int
main(int argc, char *argv[])
{
char *q;
int p[2];
int ch, i;
init();
setvbuf(stdout, NULL, _IOLBF, 0);
sargv = malloc(argc * sizeof(char *));
if (sargv == NULL)
err(1, NULL);
if ((cmdexpr = strdup("")) == NULL)
err(1, NULL);
/* The d debug option is 4.4 BSD bc(1) compatible */
while ((ch = getopt_long(argc, argv, "cde:hlqv",
long_options, NULL)) != -1) {
switch (ch) {
case 'c':
case 'd':
do_fork = false;
break;
case 'e':
q = cmdexpr;
if (asprintf(&cmdexpr, "%s%s\n", cmdexpr, optarg) == -1)
err(1, NULL);
free(q);
break;
case 'h':
usage();
break;
case 'l':
sargv[sargc++] = _PATH_LIBB;
break;
case 'q':
/* compatibility option */
break;
case 'v':
fprintf(stderr, "%s (BSD bc) %s\n", __progname, BC_VER);
exit(0);
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
interactive = isatty(STDIN_FILENO);
for (i = 0; i < argc; i++)
sargv[sargc++] = argv[i];
if (do_fork) {
if (pipe(p) == -1)
err(1, "cannot create pipe");
dc = fork();
if (dc == -1)
err(1, "cannot fork");
else if (dc != 0) {
signal(SIGCHLD, sigchld);
close(STDOUT_FILENO);
dup(p[1]);
close(p[0]);
close(p[1]);
} else {
close(STDIN_FILENO);
dup(p[0]);
close(p[0]);
close(p[1]);
execl(_PATH_DC, "dc", "-x", (char *)NULL);
err(1, "cannot find dc");
}
}
if (interactive) {
gettty(&ttysaved);
el = el_init("bc", stdin, stderr, stderr);
hist = history_init();
history(hist, &he, H_SETSIZE, 100);
el_set(el, EL_HIST, history, hist);
el_set(el, EL_EDITOR, "emacs");
el_set(el, EL_SIGNAL, 1);
el_set(el, EL_PROMPT, dummy_prompt);
el_set(el, EL_ADDFN, "bc_eof", "", bc_eof);
el_set(el, EL_BIND, "^D", "bc_eof", NULL);
el_source(el, NULL);
}
yywrap();
return (yyparse());
}