summaryrefslogtreecommitdiffstats
path: root/usr.bin/bc/bc.y
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/bc/bc.y')
-rw-r--r--usr.bin/bc/bc.y1205
1 files changed, 1205 insertions, 0 deletions
diff --git a/usr.bin/bc/bc.y b/usr.bin/bc/bc.y
new file mode 100644
index 0000000..b00d5ac
--- /dev/null
+++ b/usr.bin/bc/bc.y
@@ -0,0 +1,1205 @@
+%{
+/* $OpenBSD: bc.y,v 1.33 2009/10/27 23:59:36 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 <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+#include "pathnames.h"
+
+#define BC_VER "1.0-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 yyparse(void);
+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);
+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);
+ 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)
+{
+
+ if (instructions[i].index >= 0)
+ while (instructions[i].index != END_NODE)
+ emit(instructions[i++].index);
+ else
+ fputs(instructions[i].u.cstr, stdout);
+}
+
+static void
+emit_macro(int nodeidx, ssize_t code)
+{
+
+ putchar('[');
+ emit(code);
+ 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 (isspace(yytext[0]) || !isprint(yytext[0]))
+ n = asprintf(&str,
+ "%s: %s:%d: %s: ascii char 0x%02x unexpected",
+ __progname, filename, lineno, s, yytext[0]);
+ 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 [-chlqv] [-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 */
+void
+sigchld(int signo)
+{
+ pid_t pid;
+ int status;
+
+ switch (signo) {
+ default:
+ for (;;) {
+ pid = waitpid(dc, &status, WCONTINUED);
+ if (pid == -1) {
+ if (errno == EINTR)
+ continue;
+ _exit(0);
+ }
+ if (WIFEXITED(status) || WIFSIGNALED(status))
+ _exit(0);
+ else
+ break;
+ }
+ }
+}
+
+static const char *
+dummy_prompt(void)
+{
+
+ return ("");
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *q;
+ int p[2];
+ int ch, i;
+
+ init();
+ setlinebuf(stdout);
+
+ 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]);
+ if (interactive) {
+ 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_source(el, NULL);
+ }
+ } 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");
+ }
+ }
+ yywrap();
+ return (yyparse());
+}
OpenPOWER on IntegriCloud