commit 121f6601f24e6f9e5218ebefeec388514a918664
parent 678853769b2c8f59c9678dfcedf4cc942981da39
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date: Fri, 21 Nov 2014 15:54:21 +0100
initial bc import (slightly changed Makefile)
Diffstat:
A | bc/Makefile | | | 24 | ++++++++++++++++++++++++ |
A | bc/bc.1 | | | 402 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | bc/bc.library | | | 272 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | bc/bc.y | | | 1170 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | bc/extern.h | | | 48 | ++++++++++++++++++++++++++++++++++++++++++++++++ |
A | bc/pathnames.h | | | 20 | ++++++++++++++++++++ |
A | bc/scan.l | | | 363 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | bc/tty.c | | | 64 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
8 files changed, 2363 insertions(+), 0 deletions(-)
diff --git a/bc/Makefile b/bc/Makefile
@@ -0,0 +1,24 @@
+# $OpenBSD: Makefile,v 1.7 2013/09/19 16:12:00 otto Exp $
+
+build: clean
+ yacc bc.y
+ flex scan.l
+ cc *.c -o bc -ledit -lcurses
+
+clean:
+ rm -f bc *.o
+
+#PROG= bc
+#SRCS= bc.y scan.l tty.c
+#CPPFLAGS+= -I. -I${.CURDIR}
+#CFLAGS+= -Wall -Wno-unused
+#YFLAGS+=
+#LDADD+= -ledit -lcurses
+#DPADD+= ${LIBEDIT} ${LIBCURSES}
+#
+#
+#beforeinstall:
+# install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/bc.library \
+# ${DESTDIR}/usr/share/misc
+#
+#.include <bsd.prog.mk>
diff --git a/bc/bc.1 b/bc/bc.1
@@ -0,0 +1,402 @@
+.\" $OpenBSD: bc.1,v 1.30 2014/01/14 07:42:42 jmc Exp $
+.\"
+.\" Copyright (C) Caldera International Inc. 2001-2002.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code and documentation must retain the above
+.\" copyright notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed or owned by Caldera
+.\" International, Inc.
+.\" 4. Neither the name of Caldera International, Inc. nor the names of other
+.\" contributors may be used to endorse or promote products derived from
+.\" this software without specific prior written permission.
+.\"
+.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+.\" INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+.\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" @(#)bc.1 6.8 (Berkeley) 8/8/91
+.\"
+.Dd $Mdocdate: January 14 2014 $
+.Dt BC 1
+.Os
+.Sh NAME
+.Nm bc
+.Nd arbitrary-precision arithmetic language and calculator
+.Sh SYNOPSIS
+.Nm bc
+.Op Fl cl
+.Op Fl e Ar expression
+.Op Ar file ...
+.Sh DESCRIPTION
+.Nm
+is an interactive processor for a language which resembles
+C but provides unlimited precision arithmetic.
+It takes input from any expressions on the command line and
+any files given, then reads the standard input.
+.Pp
+Options available:
+.Bl -tag -width Ds
+.It Fl c
+.Nm
+is actually a preprocessor for
+.Xr dc 1 ,
+which it invokes automatically, unless the
+.Fl c
+.Pq compile only
+option is present.
+In this case the generated
+.Xr dc 1
+instructions are sent to the standard output,
+instead of being interpreted by a running
+.Xr dc 1
+process.
+.It Fl e Ar expression
+Evaluate
+.Ar expression .
+If multiple
+.Fl e
+options are specified, they are processed in the order given,
+separated by newlines.
+.It Fl l
+Allow specification of an arbitrary precision math library.
+The definitions in the library are available to command line
+expressions.
+.El
+.Pp
+The syntax for
+.Nm
+programs is as follows:
+.Sq L
+means letter a-z;
+.Sq E
+means expression;
+.Sq S
+means statement.
+As a non-portable extension, it is possible to use long names
+in addition to single letter names.
+A long name is a sequence starting with a lowercase letter
+followed by any number of lowercase letters and digits.
+The underscore character
+.Pq Sq _
+counts as a letter.
+.Pp
+Comments
+.Bd -unfilled -offset indent -compact
+are enclosed in /* and */
+are enclosed in # and the next newline
+.Ed
+.Pp
+The newline is not part of the line comment,
+which in itself is a non-portable extension.
+.Pp
+Names
+.Bd -unfilled -offset indent -compact
+simple variables: L
+array elements: L [ E ]
+The words `ibase', `obase', and `scale'
+The word `last' or a single dot
+.Ed
+.Pp
+Other operands
+.Bd -unfilled -offset indent -compact
+arbitrarily long numbers with optional sign and decimal point
+( E )
+sqrt ( E )
+length ( E ) number of significant decimal digits
+scale ( E ) number of digits right of decimal point
+L ( E , ... , E )
+.Ed
+.Pp
+The sequence
+.Sq \e<newline><whitespace>
+is ignored within numbers.
+.Pp
+Operators
+.Pp
+The following arithmetic and logical operators can be used.
+The semantics of the operators is the same as in the C language.
+They are listed in order of decreasing precedence.
+Operators in the same group have the same precedence.
+.Bl -column "= += \-= *= /= %= ^=" "Associativity" "multiply, divide, modulus" -offset indent
+.It Sy "Operator" Ta Sy "Associativity" Ta Sy "Description"
+.It "++ \-\-" Ta "none" Ta "increment, decrement"
+.It "\-" Ta "none" Ta "unary minus"
+.It "^" Ta "right" Ta "power"
+.It "* / %" Ta "left" Ta "multiply, divide, modulus"
+.It "+ \-" Ta "left" Ta "plus, minus"
+.It "= += -= *= /= %= ^=" Ta "right" Ta "assignment"
+.It "== <= >= != < >" Ta "none" Ta "relational"
+.It "!" Ta "none" Ta "boolean not"
+.It "&&" Ta "left" Ta "boolean and"
+.It "||" Ta "left" Ta "boolean or"
+.El
+.Pp
+Note the following:
+.Bl -bullet -offset indent
+.It
+The relational operators may appear in any expression.
+The
+.St -p1003.1-2008
+standard only allows them in the conditional expression of an
+.Sq if ,
+.Sq while
+or
+.Sq for
+statement.
+.It
+The relational operators have a lower precedence than the assignment
+operators.
+This has the consequence that the expression
+.Sy a = b < c
+is interpreted as
+.Sy (a = b) < c ,
+which is probably not what the programmer intended.
+.It
+In contrast with the C language, the relational operators all have
+the same precedence, and are non-associative.
+The expression
+.Sy a < b < c
+will produce a syntax error.
+.It
+The boolean operators (!, && and ||) are non-portable extensions.
+.It
+The boolean not
+(!) operator has much lower precedence than the same operator in the
+C language.
+This has the consequence that the expression
+.Sy !a < b
+is interpreted as
+.Sy !(a < b) .
+Prudent programmers use parentheses when writing expressions involving
+boolean operators.
+.El
+.Pp
+Statements
+.Bd -unfilled -offset indent -compact
+E
+{ S ; ... ; S }
+if ( E ) S
+if ( E ) S else S
+while ( E ) S
+for ( E ; E ; E ) S
+null statement
+break
+continue
+quit
+a string of characters, enclosed in double quotes
+print E ,..., E
+.Ed
+.Pp
+A string may contain any character, except double quote.
+The if statement with an else branch is a non-portable extension.
+All three E's in a for statement may be empty.
+This is a non-portable extension.
+The continue and print statements are also non-portable extensions.
+.Pp
+The print statement takes a list of comma-separated expressions.
+Each expression in the list is evaluated and the computed
+value is printed and assigned to the variable `last'.
+No trailing newline is printed.
+The expression may also be a string enclosed in double quotes.
+Within these strings the following escape sequences may be used:
+.Sq \ea
+for bell (alert),
+.Sq \eb
+for backspace,
+.Sq \ef
+for formfeed,
+.Sq \en
+for newline,
+.Sq \er
+for carriage return,
+.Sq \et
+for tab,
+.Sq \eq
+for double quote and
+.Sq \e\e
+for backslash.
+Any other character following a backslash will be ignored.
+Strings will not be assigned to `last'.
+.Pp
+Function definitions
+.Bd -unfilled -offset indent
+define L ( L ,..., L ) {
+ auto L, ... , L
+ S; ... S
+ return ( E )
+}
+.Ed
+.Pp
+As a non-portable extension, the opening brace of the define statement
+may appear on the next line.
+The return statement may also appear in the following forms:
+.Bd -unfilled -offset indent
+return
+return ()
+return E
+.Ed
+.Pp
+The first two are equivalent to the statement
+.Dq return 0 .
+The last form is a non-portable extension.
+Not specifying a return statement is equivalent to writing
+.Dq return (0) .
+.Pp
+Functions available in the math library, which is loaded by specifying the
+.Fl l
+flag on the command line
+.Pp
+.Bl -tag -width j(n,x) -offset indent -compact
+.It s(x)
+sine
+.It c(x)
+cosine
+.It e(x)
+exponential
+.It l(x)
+log
+.It a(x)
+arctangent
+.It j(n,x)
+Bessel function
+.El
+.Pp
+All function arguments are passed by value.
+.Pp
+The value of a statement that is an expression is printed
+unless the main operator is an assignment.
+The value printed is assigned to the special variable `last'.
+This is a non-portable extension.
+A single dot may be used as a synonym for `last'.
+Either semicolons or newlines may separate statements.
+Assignment to
+.Ar scale
+influences the number of digits to be retained on arithmetic
+operations in the manner of
+.Xr dc 1 .
+Assignments to
+.Ar ibase
+or
+.Ar obase
+set the input and output number radix respectively.
+.Pp
+The same letter may be used as an array, a function,
+and a simple variable simultaneously.
+All variables are global to the program.
+`Auto' variables are pushed down during function calls.
+When using arrays as function arguments
+or defining them as automatic variables,
+empty square brackets must follow the array name.
+.Pp
+For example
+.Bd -literal -offset indent
+scale = 20
+define e(x){
+ auto a, b, c, i, s
+ a = 1
+ b = 1
+ s = 1
+ for(i=1; 1==1; i++){
+ a = a*x
+ b = b*i
+ c = a/b
+ if(c == 0) return(s)
+ s = s+c
+ }
+}
+.Ed
+.Pp
+defines a function to compute an approximate value of
+the exponential function and
+.Pp
+.Dl for(i=1; i<=10; i++) e(i)
+.Pp
+prints approximate values of the exponential function of
+the first ten integers.
+.Bd -literal -offset indent
+$ bc -l -e 'scale = 500; 2 * a(2^10000)' -e quit
+.Ed
+.Pp
+prints an approximation of pi.
+.Sh COMMAND LINE EDITING
+.Nm
+supports interactive command line editing, via the
+.Xr editline 3
+library.
+It is enabled by default if input is from a tty.
+Previous lines can be recalled and edited with the arrow keys,
+and other GNU Emacs-style editing keys may be used as well.
+.Pp
+The
+.Xr editline 3
+library is configured with a
+.Pa .editrc
+file \- refer to
+.Xr editrc 5
+for more information.
+.Sh FILES
+.Bl -tag -width /usr/share/misc/bc.library -compact
+.It Pa /usr/share/misc/bc.library
+math library, read when the
+.Fl l
+option is specified on the command line.
+.El
+.Sh SEE ALSO
+.Xr dc 1
+.Sh STANDARDS
+The
+.Nm
+utility is compliant with the
+.St -p1003.1-2008
+specification.
+.Pp
+The flags
+.Op Fl ce ,
+as well as the parts noted above,
+are extensions to that specification.
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.At v6 .
+A complete rewrite of the
+.Nm
+command first appeared in
+.Ox 3.5 .
+.Sh AUTHORS
+.An -nosplit
+The original version of the
+.Nm
+command was written by
+.An Robert Morris
+and
+.An Lorinda Cherry .
+The current version of the
+.Nm
+utility was written by
+.An Otto Moerbeek .
+.Sh BUGS
+.Ql Quit
+is interpreted when read, not when executed.
+.Pp
+Some non-portable extensions, as found in the GNU version of the
+.Nm
+utility are not implemented (yet).
diff --git a/bc/bc.library b/bc/bc.library
@@ -0,0 +1,272 @@
+/* $OpenBSD: bc.library,v 1.4 2012/03/14 07:35:53 otto Exp $ */
+
+/*
+ * Copyright (C) Caldera International Inc. 2001-2002.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code and documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed or owned by Caldera
+ * International, Inc.
+ * 4. Neither the name of Caldera International, Inc. nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * @(#)bc.library 5.1 (Berkeley) 4/17/91
+ */
+
+scale = 20
+define e(x) {
+ auto a, b, c, d, e, g, t, w, y, r
+
+ r = ibase
+ ibase = A
+ t = scale
+ scale = 0
+ if (x > 0) scale = (0.435*x)/1
+ scale = scale + t + length(scale + t) + 1
+
+ w = 0
+ if (x < 0) {
+ x = -x
+ w = 1
+ }
+ y = 0
+ while (x > 2) {
+ x = x/2
+ y = y + 1
+ }
+
+ a = 1
+ b = 1
+ c = b
+ d = 1
+ e = 1
+ for (a = 1; 1 == 1; a++) {
+ b = b*x
+ c = c*a + b
+ d = d*a
+ g = c/d
+ if (g == e) {
+ g = g/1
+ while (y--) {
+ g = g*g
+ }
+ scale = t
+ ibase = r
+ if (w == 1) return (1/g)
+ return (g/1)
+ }
+ e = g
+ }
+}
+
+define l(x) {
+ auto a, b, c, d, e, f, g, u, s, t, r
+ r = ibase
+ ibase = A
+ if (x <= 0) {
+ a = (1 - 10^scale)
+ ibase = r
+ return (a)
+ }
+ t = scale
+
+ f = 1
+ if (x < 1) {
+ s = scale(x)
+ } else {
+ s = length(x)-scale(x)
+ }
+ scale = 0
+ a = (2.31*s)/1 /* estimated integer part of the answer */
+ s = t + length(a) + 2 /* estimated length of the answer */
+ while (x > 2) {
+ scale = 0
+ scale = (length(x) + scale(x))/2 + 1
+ if (scale < s) scale = s
+ x = sqrt(x)
+ f = f*2
+ }
+ while (x < .5) {
+ scale = 0
+ scale = scale(x)/2 + 1
+ if (scale < s) scale = s
+ x = sqrt(x)
+ f = f*2
+ }
+
+ scale = 0
+ scale = t + length(f) + length((1.05*(t+length(f))/1)) + 1
+ u = (x - 1)/(x + 1)
+ s = u*u
+ scale = t + 2
+ b = 2*f
+ c = b
+ d = 1
+ e = 1
+ for (a = 3; 1 == 1 ; a = a + 2) {
+ b = b*s
+ c = c*a + d*b
+ d = d*a
+ g = c/d
+ if (g == e) {
+ scale = t
+ ibase = r
+ return (u*c/d)
+ }
+ e = g
+ }
+}
+
+define s(x) {
+ auto a, b, c, s, t, y, p, n, i, r
+ r = ibase
+ ibase = A
+ t = scale
+ y = x/.7853
+ s = t + length(y) - scale(y)
+ if (s < t) s = t
+ scale = s
+ p = a(1)
+
+ scale = 0
+ if (x >= 0) n = (x/(2*p) + 1)/2
+ if (x < 0) n = (x/(2*p) - 1)/2
+ x = x - 4*n*p
+ if (n % 2 != 0) x = -x
+
+ scale = t + length(1.2*t) - scale(1.2*t)
+ y = -x*x
+ a = x
+ b = 1
+ s = x
+ for (i =3 ; 1 == 1; i = i + 2) {
+ a = a*y
+ b = b*i*(i - 1)
+ c = a/b
+ if (c == 0) {
+ scale = t
+ ibase = r
+ return (s/1)
+ }
+ s = s + c
+ }
+}
+
+define c(x) {
+ auto t, r
+ r = ibase
+ ibase = A
+ t = scale
+ scale = scale + 1
+ x = s(x + 2*a(1))
+ scale = t
+ ibase = r
+ return (x/1)
+}
+
+define a(x) {
+ auto a, b, c, d, e, f, g, s, t, r
+ if (x == 0) return(0)
+
+ r = ibase
+ ibase = A
+ if (x == 1) {
+ if (scale < 52) {
+ a = .7853981633974483096156608458198757210492923498437764/1
+ ibase = r
+ return (a)
+ }
+ }
+ t = scale
+ f = 1
+ while (x > .5) {
+ scale = scale + 1
+ x = -(1 - sqrt(1. + x*x))/x
+ f = f*2
+ }
+ while (x < -.5) {
+ scale = scale + 1
+ x = -(1 - sqrt(1. + x*x))/x
+ f = f*2
+ }
+ s = -x*x
+ b = f
+ c = f
+ d = 1
+ e = 1
+ for (a = 3; 1 == 1; a = a + 2) {
+ b = b*s
+ c = c*a + d*b
+ d = d*a
+ g = c/d
+ if (g == e) {
+ ibase = r
+ scale = t
+ return (x*c/d)
+ }
+ e = g
+ }
+}
+
+define j(n,x) {
+ auto a, b, c, d, e, g, i, s, k, t, r
+
+ r = ibase
+ ibase = A
+ t = scale
+ k = 1.36*x + 1.16*t - n
+ k = length(k) - scale(k)
+ if (k > 0) scale = scale + k
+
+ s = -x*x/4
+ if (n < 0) {
+ n = -n
+ x = -x
+ }
+ a = 1
+ c = 1
+ for (i = 1; i <= n; i++) {
+ a = a*x
+ c = c*2*i
+ }
+ b = a
+ d = 1
+ e = 1
+ for (i = 1; 1; i++) {
+ a = a*s
+ b = b*i*(n + i) + a
+ c = c*i*(n + i)
+ g = b/c
+ if (g == e) {
+ ibase = r
+ scale = t
+ return (g/1)
+ }
+ e = g
+ }
+}
diff --git a/bc/bc.y b/bc/bc.y
@@ -0,0 +1,1170 @@
+%{
+/* $OpenBSD: bc.y,v 1.46 2014/10/14 15:35:18 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/types.h>
+#include <sys/wait.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <histedit.h>
+#include <limits.h>
+#include <search.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+#include "pathnames.h"
+
+#define END_NODE ((ssize_t) -1)
+#define CONST_STRING ((ssize_t) -2)
+#define ALLOC_STRING ((ssize_t) -3)
+
+struct tree {
+ ssize_t index;
+ union {
+ char *astr;
+ const char *cstr;
+ } u;
+};
+
+int yyparse(void);
+int yywrap(void);
+
+int fileindex;
+int sargc;
+char **sargv;
+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 __dead 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;
+
+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)
+
+%}
+
+%start program
+
+%union {
+ ssize_t node;
+ struct lvalue lvalue;
+ const char *str;
+ char *astr;
+}
+
+%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 = reallocarray(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 node, ssize_t code)
+{
+ putchar('[');
+ emit(code);
+ printf("]s%s\n", instructions[node].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_short num;
+ u_char *p;
+
+ /* 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(char *s)
+{
+ char *str, *p;
+ 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]);
+ 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)
+{
+ 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 __dead void
+usage(void)
+{
+ fprintf(stderr, "usage: %s [-cl] [-e expression] [file ...]\n",
+ __progname);
+ exit(1);
+}
+
+static char *
+escape(const char *str)
+{
+ char *ret, *p;
+
+ 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)
+{
+ 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[])
+{
+ int i, ch;
+ int p[2];
+ char *q;
+
+ init();
+ setlinebuf(stdout);
+
+ sargv = reallocarray(NULL, 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(argc, argv, "cde:l")) != -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 'l':
+ sargv[sargc++] = _PATH_LIBB;
+ 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, 0);
+ 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();
+}
diff --git a/bc/extern.h b/bc/extern.h
@@ -0,0 +1,48 @@
+/* $OpenBSD: extern.h,v 1.12 2014/04/17 19:07:14 otto 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.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+
+struct lvalue {
+ ssize_t load;
+ ssize_t store;
+};
+
+int yylex(void);
+void yyerror(char *);
+void fatal(const char *);
+void abort_line(int);
+struct termios;
+int gettty(struct termios *);
+void tstpcont(int);
+unsigned char bc_eof(EditLine *, int);
+
+extern int lineno;
+extern char *yytext;
+extern FILE *yyin;
+extern int fileindex;
+extern int sargc;
+extern char **sargv;
+extern char *filename;
+extern bool interactive;
+extern EditLine *el;
+extern History *hist;
+extern HistEvent he;
+extern char *cmdexpr;
+extern struct termios ttysaved;
diff --git a/bc/pathnames.h b/bc/pathnames.h
@@ -0,0 +1,20 @@
+/* $OpenBSD: pathnames.h,v 1.1 2003/09/25 19:32:44 otto 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.
+ */
+
+#define _PATH_LIBB "/usr/share/misc/bc.library"
+#define _PATH_DC "/usr/bin/dc"
diff --git a/bc/scan.l b/bc/scan.l
@@ -0,0 +1,363 @@
+%{
+/* $OpenBSD: scan.l,v 1.28 2013/09/19 16:12:01 otto 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.
+ */
+
+#include <err.h>
+#include <histedit.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+#include "pathnames.h"
+#include "y.tab.h"
+
+int lineno;
+bool interactive;
+
+HistEvent he;
+EditLine *el;
+History *hist;
+
+static char *strbuf = NULL;
+static size_t strbuf_sz = 1;
+static bool dot_seen;
+static int use_el;
+static volatile sig_atomic_t skipchars;
+
+static void init_strbuf(void);
+static void add_str(const char *);
+
+static int bc_yyinput(char *, int);
+
+#undef YY_INPUT
+#define YY_INPUT(buf,retval,max) \
+ (retval = bc_yyinput(buf, max))
+
+%}
+
+%option always-interactive
+
+DIGIT [0-9A-F]
+ALPHA [a-z_]
+ALPHANUM [a-z_0-9]
+
+%x comment string number
+
+%%
+
+"/*" BEGIN(comment);
+<comment>{
+ "*/" BEGIN(INITIAL);
+ \n lineno++;
+ \* ;
+ [^*\n]+ ;
+ <<EOF>> fatal("end of file in comment");
+}
+
+\" BEGIN(string); init_strbuf();
+<string>{
+ [^"\n\\\[\]]+ add_str(yytext);
+ \[ add_str("\\[");
+ \] add_str("\\]");
+ \\ add_str("\\\\");
+ \n add_str("\n"); lineno++;
+ \" BEGIN(INITIAL); yylval.str = strbuf; return STRING;
+ <<EOF>> fatal("end of file in string");
+}
+
+{DIGIT}+ {
+ BEGIN(number);
+ dot_seen = false;
+ init_strbuf();
+ add_str(yytext);
+ }
+\. {
+ BEGIN(number);
+ dot_seen = true;
+ init_strbuf();
+ add_str(".");
+ }
+<number>{
+ {DIGIT}+ add_str(yytext);
+ \. {
+ if (dot_seen) {
+ BEGIN(INITIAL);
+ yylval.str = strbuf;
+ unput('.');
+ return NUMBER;
+ } else {
+ dot_seen = true;
+ add_str(".");
+ }
+ }
+ \\\n[ \t]* lineno++;
+ [^0-9A-F\.] {
+ BEGIN(INITIAL);
+ unput(yytext[0]);
+ if (strcmp(strbuf, ".") == 0)
+ return DOT;
+ else {
+ yylval.str = strbuf;
+ return NUMBER;
+ }
+ }
+}
+
+"auto" return AUTO;
+"break" return BREAK;
+"continue" return CONTINUE;
+"define" return DEFINE;
+"else" return ELSE;
+"ibase" return IBASE;
+"if" return IF;
+"last" return DOT;
+"for" return FOR;
+"length" return LENGTH;
+"obase" return OBASE;
+"print" return PRINT;
+"quit" return QUIT;
+"return" return RETURN;
+"scale" return SCALE;
+"sqrt" return SQRT;
+"while" return WHILE;
+
+"^" return EXPONENT;
+"*" return MULTIPLY;
+"/" return DIVIDE;
+"%" return REMAINDER;
+
+"!" return BOOL_NOT;
+"&&" return BOOL_AND;
+"||" return BOOL_OR;
+
+"+" return PLUS;
+"-" return MINUS;
+
+"++" return INCR;
+"--" return DECR;
+
+"=" yylval.str = ""; return ASSIGN_OP;
+"+=" yylval.str = "+"; return ASSIGN_OP;
+"-=" yylval.str = "-"; return ASSIGN_OP;
+"*=" yylval.str = "*"; return ASSIGN_OP;
+"/=" yylval.str = "/"; return ASSIGN_OP;
+"%=" yylval.str = "%"; return ASSIGN_OP;
+"^=" yylval.str = "^"; return ASSIGN_OP;
+
+"==" return EQUALS;
+"<=" return LESS_EQ;
+">=" return GREATER_EQ;
+"!=" return UNEQUALS;
+"<" return LESS;
+">" return GREATER;
+
+"," return COMMA;
+";" return SEMICOLON;
+
+"(" return LPAR;
+")" return RPAR;
+
+"[" return LBRACKET;
+"]" return RBRACKET;
+
+"{" return LBRACE;
+"}" return RBRACE;
+
+{ALPHA}{ALPHANUM}* {
+ /* alloc an extra byte for the type marker */
+ char *p = malloc(yyleng + 2);
+ if (p == NULL)
+ err(1, NULL);
+ strlcpy(p, yytext, yyleng + 1);
+ yylval.astr = p;
+ return LETTER;
+ }
+
+\\\n lineno++;
+\n lineno++; return NEWLINE;
+
+#[^\n]* ;
+[ \t] ;
+<<EOF>> return QUIT;
+. yyerror("illegal character");
+
+%%
+
+static void
+init_strbuf(void)
+{
+ if (strbuf == NULL) {
+ strbuf = malloc(strbuf_sz);
+ if (strbuf == NULL)
+ err(1, NULL);
+ }
+ strbuf[0] = '\0';
+}
+
+static void
+add_str(const char *str)
+{
+ size_t arglen;
+
+ arglen = strlen(str);
+
+ if (strlen(strbuf) + arglen + 1 > strbuf_sz) {
+ size_t newsize;
+ char *p;
+
+ newsize = strbuf_sz + arglen + 1;
+ p = realloc(strbuf, newsize);
+ if (p == NULL) {
+ free(strbuf);
+ err(1, NULL);
+ }
+ strbuf_sz = newsize;
+ strbuf = p;
+ }
+ strlcat(strbuf, str, strbuf_sz);
+}
+
+/* ARGSUSED */
+void
+abort_line(int sig)
+{
+ static const char str1[] = "[\n]P\n";
+ static const char str2[] = "[^C\n]P\n";
+ int save_errno;
+ const LineInfo *info;
+
+ save_errno = errno;
+ if (use_el) {
+ write(STDOUT_FILENO, str2, sizeof(str2) - 1);
+ info = el_line(el);
+ skipchars = info->lastchar - info->buffer;
+ } else
+ write(STDOUT_FILENO, str1, sizeof(str1) - 1);
+ errno = save_errno;
+}
+
+/*
+ * Avoid the echo of ^D by the default code of editline and take
+ * into account skipchars to make ^D work when the cursor is at start of
+ * line after a ^C.
+ */
+unsigned char
+bc_eof(EditLine *e, int ch)
+{
+ const struct lineinfo *info = el_line(e);
+
+ if (info->buffer + skipchars == info->cursor &&
+ info->cursor == info->lastchar)
+ return (CC_EOF);
+ else
+ return (CC_ERROR);
+}
+
+int
+yywrap(void)
+{
+ static int state;
+ static YY_BUFFER_STATE buf;
+
+ if (fileindex == 0 && sargc > 0 && strcmp(sargv[0], _PATH_LIBB) == 0) {
+ filename = sargv[fileindex++];
+ yyin = fopen(filename, "r");
+ lineno = 1;
+ if (yyin == NULL)
+ err(1, "cannot open %s", filename);
+ return (0);
+ }
+ if (state == 0 && cmdexpr[0] != '\0') {
+ buf = yy_scan_string(cmdexpr);
+ state++;
+ lineno = 1;
+ filename = "command line";
+ return (0);
+ } else if (state == 1) {
+ yy_delete_buffer(buf);
+ free(cmdexpr);
+ state++;
+ }
+ if (yyin != NULL && yyin != stdin)
+ fclose(yyin);
+ if (fileindex < sargc) {
+ filename = sargv[fileindex++];
+ yyin = fopen(filename, "r");
+ lineno = 1;
+ if (yyin == NULL)
+ err(1, "cannot open %s", filename);
+ return (0);
+ } else if (fileindex == sargc) {
+ fileindex++;
+ yyin = stdin;
+ if (interactive) {
+ signal(SIGINT, abort_line);
+ signal(SIGTSTP, tstpcont);
+ }
+ lineno = 1;
+ filename = "stdin";
+ return (0);
+ }
+ return (1);
+}
+
+static int
+bc_yyinput(char *buf, int maxlen)
+{
+ int num;
+
+ if (el != NULL)
+ el_get(el, EL_EDITMODE, &use_el);
+
+ if (yyin == stdin && interactive && use_el) {
+ const char *bp;
+ sigset_t oset, nset;
+
+ if ((bp = el_gets(el, &num)) == NULL || num == 0)
+ return (0);
+ sigemptyset(&nset);
+ sigaddset(&nset, SIGINT);
+ sigprocmask(SIG_BLOCK, &nset, &oset);
+ if (skipchars < num) {
+ bp += skipchars;
+ num -= skipchars;
+ }
+ skipchars = 0;
+ sigprocmask(SIG_SETMASK, &oset, NULL);
+ if (num > maxlen) {
+ el_push(el, (char *)(void *)bp + maxlen);
+ num = maxlen;
+ }
+ memcpy(buf, bp, num);
+ history(hist, &he, H_ENTER, bp);
+ el_get(el, EL_EDITMODE, &use_el);
+ } else {
+ int c = '*';
+ for (num = 0; num < maxlen &&
+ (c = getc(yyin)) != EOF && c != '\n'; ++num)
+ buf[num] = (char) c;
+ if (c == '\n')
+ buf[num++] = (char) c;
+ if (c == EOF && ferror(yyin))
+ YY_FATAL_ERROR( "input in flex scanner failed" );
+ }
+ return (num);
+}
+
+
diff --git a/bc/tty.c b/bc/tty.c
@@ -0,0 +1,64 @@
+/* $OpenBSD: tty.c,v 1.2 2013/11/12 13:54:51 deraadt Exp $ */
+
+/*
+ * Copyright (c) 2013, 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.
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <histedit.h>
+#include <termios.h>
+#include "extern.h"
+
+struct termios ttysaved, ttyedit;
+
+static int
+settty(struct termios *t)
+{
+ int ret;
+
+ while ((ret = tcsetattr(0, TCSADRAIN, t) == -1) && errno == EINTR)
+ continue;
+ return ret;
+}
+
+int
+gettty(struct termios *t)
+{
+ int ret;
+
+ while ((ret = tcgetattr(0, t) == -1) && errno == EINTR)
+ continue;
+ return ret;
+}
+
+/* ARGSUSED */
+void
+tstpcont(int sig)
+{
+ int save_errno = errno;
+
+ if (sig == SIGTSTP) {
+ signal(SIGCONT, tstpcont);
+ gettty(&ttyedit);
+ settty(&ttysaved);
+ } else {
+ signal(SIGTSTP, tstpcont);
+ settty(&ttyedit);
+ }
+ signal(sig, SIG_DFL);
+ kill(0, sig);
+ errno = save_errno;
+}