Replace GNU bc/dc with BSDL versions ported from OpenBSD. They have a good

compatibility level with the GNU counterparts and have shown to be mature
enough. For now, the GNU versions aren't removed from the tree, just detached
from the build.

Sponsored by:		Google Summer of Code 2008
Portbuild run by:	erwin
Approved by:		delphij
This commit is contained in:
gabor 2010-01-20 21:30:52 +00:00
parent 572c61752a
commit bc4e1f6f87
23 changed files with 7792 additions and 3 deletions

View File

@ -14,6 +14,13 @@
# The file is partitioned: OLD_FILES first, then OLD_LIBS and OLD_DIRS last.
#
# 20100120: replacing GNU bc/dc with BSDL versions
OLD_FILES+=usr/share/examples/bc/ckbook.b
OLD_FILES+=usr/share/examples/bc/pi.b
OLD_FILES+=usr/share/examples/bc/primes.b
OLD_FILES+=usr/share/examples/bc/twins.b
OLD_FILES+=usr/share/info/dc.info.gz
OLD_DIRS+=usr/share/examples/bc
# 20100114: removal of ttyslot(3)
OLD_FILES+=usr/share/man/man3/ttyslot.3.gz
# 20100113: remove utmp.h, replace it by utmpx.h

View File

@ -2,12 +2,10 @@
.include <bsd.own.mk>
SUBDIR= bc \
${_binutils} \
SUBDIR= ${_binutils} \
${_cc} \
${_cpio} \
${_cvs} \
dc \
dialog \
diff \
diff3 \

View File

@ -18,6 +18,7 @@ SUBDIR= alias \
awk \
banner \
basename \
bc \
${_biff} \
${_bluetooth} \
brandelf \
@ -49,6 +50,7 @@ SUBDIR= alias \
${_csup} \
${_ctags} \
cut \
dc \
${_dig} \
dirname \
du \

17
usr.bin/bc/Makefile Normal file
View File

@ -0,0 +1,17 @@
# $FreeBSD$
# $OpenBSD: Makefile,v 1.4 2006/06/30 19:02:28 otto Exp $
PROG= bc
SRCS= bc.y scan.l
CFLAGS+= -I. -I${.CURDIR}
WARNS?= 6
#SUBDIR+= USD.doc
FILES+= bc.library
FILESDIR= ${SHAREDIR}/misc
#beforeinstall:
# install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/bc.library \
# ${DESTDIR}/usr/share/misc
.include <bsd.prog.mk>

View File

@ -0,0 +1,13 @@
# $FreeBSD$
# $OpenBSD: Makefile,v 1.3 2004/02/01 15:18:01 jmc Exp $
DOC= bc
DIR= usd/06.bc
SRCS= bc
MACROS= -ms
BINDIR= /usr/share/doc/papers
paper.txt: ${SRCS}
${ROFF} -Tascii ${SRCS} > ${.TARGET}
.include <bsd.doc.mk>

1241
usr.bin/bc/USD.doc/bc Normal file

File diff suppressed because it is too large Load Diff

400
usr.bin/bc/bc.1 Normal file
View File

@ -0,0 +1,400 @@
.\" $FreeBSD$
.\" $OpenBSD: bc.1,v 1.25 2010/01/02 19:48:56 schwarze 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 May 31 2007
.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
.It Fl d
.It Fl Fl debug
.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 exp
.It Fl Fl expression Ar exp
Evaluate
.Ar expression .
If multiple
.Fl e
options are specified, they are processed in the order given,
separated by newlines.
.It Fl h
.It Fl Fl help
Prints usage information.
.It Fl l
.It Fl Fl mathlib
Allow specification of an arbitrary precision math library.
The definitions in the library are available to command line
expressions.
.It Fl v
.It Fl Fl version
Prints version information.
.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 -offset indent "= += \-= *= /= %= ^=" "Associativity" \
"multiply, divide, modulus"
.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.2
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 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
.Pp
"BC \- An Arbitrary Precision Desk-Calculator Language",
.Pa /usr/share/doc/usd/06.bc/ .
.Sh STANDARDS
The
.Nm
utility is compliant with the
.St -p1003.1-2008
specification.
.Pp
The flags
.Op Fl ce
are extensions to that specification.
.Sh HISTORY
The
.Nm
first command 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).

263
usr.bin/bc/bc.library Normal file
View File

@ -0,0 +1,263 @@
/* $FreeBSD$ */
/* $OpenBSD: bc.library,v 1.3 2007/02/03 21:15:06 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 = t + .434*x + 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
scale = scale + scale(x) - length(x) + 1
s = scale
while (x > 2) {
s = s + (length(x) - scale(x))/2 + 1
if (s > 0) scale = s
x = sqrt(x)
f = f*2
}
while (x < .5) {
s = s + (length(x) - scale(x))/2 + 1
if (s > 0) scale = s
x = sqrt(x)
f = f*2
}
scale = t + length(f) - scale(f) + 1
u = (x - 1)/(x + 1)
scale = scale + 1.1*length(t) - 1.1*scale(t)
s = u*u
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
}
}

1179
usr.bin/bc/bc.y Normal file

File diff suppressed because it is too large Load Diff

38
usr.bin/bc/extern.h Normal file
View File

@ -0,0 +1,38 @@
/* $FreeBSD$ */
/* $OpenBSD: extern.h,v 1.6 2006/03/18 20:44:43 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 <stdio.h>
struct lvalue {
ssize_t load;
ssize_t store;
};
int yylex(void);
void yyerror(const char *);
void fatal(const char *);
void abort_line(int);
extern int lineno;
extern int fileindex;
extern int sargc;
extern const char **sargv;
extern const char *filename;
extern char *cmdexpr;
bool interactive;

21
usr.bin/bc/pathnames.h Normal file
View File

@ -0,0 +1,21 @@
/* $FreeBSD$ */
/* $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"

288
usr.bin/bc/scan.l Normal file
View File

@ -0,0 +1,288 @@
%{
/* $OpenBSD: scan.l,v 1.23 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <err.h>
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include "extern.h"
#include "bc.h"
#include "pathnames.h"
int lineno;
static char *strbuf = NULL;
static size_t strbuf_sz = 1;
static bool dot_seen;
static void init_strbuf(void);
static void add_str(const char *);
%}
%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 str[] = "[\n]P\n";
int save_errno;
switch (sig) {
default:
save_errno = errno;
YY_FLUSH_BUFFER; /* XXX signal race? */
write(STDOUT_FILENO, str, sizeof(str) - 1);
errno = save_errno;
}
}
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);
lineno = 1;
filename = "stdin";
return (0);
}
return (1);
}

13
usr.bin/dc/Makefile Normal file
View File

@ -0,0 +1,13 @@
# $FreeBSD$
# $OpenBSD: Makefile,v 1.2 2006/11/26 11:31:09 deraadt Exp $
PROG= dc
SRCS= dc.c bcode.c inout.c mem.c stack.c
LDADD= -lcrypto
DPADD= ${LIBCRYPTO}
#SUBDIR+= USD.doc
WARNS?= 6
.include <bsd.prog.mk>

View File

@ -0,0 +1,16 @@
# $FreeBSD$
# $OpenBSD: Makefile,v 1.2 2004/02/01 15:18:01 jmc Exp $
DOC= dc
DIR= usd/05.dc
SRCS= dc
MACROS= -ms
BINDIR= /usr/share/doc/papers
paper.ps: ${SRCS}
${EQN} ${SRCS} | ${ROFF} > ${.TARGET}
paper.txt: ${SRCS}
${EQN} -Tascii ${SRCS} | ${ROFF} -Tascii > ${.TARGET}
.include <bsd.doc.mk>

753
usr.bin/dc/USD.doc/dc Normal file
View File

@ -0,0 +1,753 @@
.\" $FreeBSD$
.\" $OpenBSD: dc,v 1.2 2003/09/22 19:08:27 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.
.\"
.\" @(#)dc 8.1 (Berkeley) 6/8/93
.\"
.EH 'USD:5-%''DC \- An Interactive Desk Calculator'
.OH 'DC \- An Interactive Desk Calculator''USD:5-%'
.\".RP
.\" ....TM 75-1271-8 39199 39199-11
.ND
.TL
DC \- An Interactive Desk Calculator
.AU "MH 2C-524" 3878
Robert Morris
.AU
Lorinda Cherry
.AI
.\" .MH
.AB
DC is an interactive desk calculator program implemented
on the
.UX
time-sharing system to do arbitrary-precision
integer arithmetic.
It has provision for manipulating scaled fixed-point numbers and
for input and output in bases other than decimal.
.PP
The size of numbers that can be manipulated is limited
only by available core storage.
On typical implementations of
.UX ,
the size of numbers that
can be handled varies from several hundred digits on the smallest
systems to several thousand on the largest.
.AE
.PP
.SH
.PP
.ft I
Editor's note: the description of the implementation details of DC in this
paper is only valid for the original version of DC.
The current version of DC uses a different approach.
.ft
.PP
DC is an arbitrary precision arithmetic package implemented
on the
.UX
time-sharing system
in the form of an interactive desk calculator.
It works like a stacking calculator using reverse Polish notation.
Ordinarily DC operates on decimal integers, but one may
specify an input base, output base, and a number of fractional
digits to be maintained.
.PP
A language called BC [1] has been developed which accepts
programs written in the familiar style of higher-level
programming languages and compiles output which is
interpreted by DC.
Some of the commands described below were designed
for the compiler interface and are not easy for a human user
to manipulate.
.PP
Numbers that are typed into DC are put on a push-down
stack.
DC commands work by taking the top number or two
off the stack, performing the desired operation, and pushing the result
on the stack.
If an argument is given,
input is taken from that file until its end,
then from the standard input.
.SH
SYNOPTIC DESCRIPTION
.PP
Here we describe the DC commands that are intended
for use by people. The additional commands that are
intended to be invoked by compiled output are
described in the detailed description.
.PP
Any number of commands are permitted on a line.
Blanks and new-line characters are ignored except within numbers
and in places where a register name is expected.
.PP
The following constructions are recognized:
.SH
number
.IP
The value of the number is pushed onto the main stack.
A number is an unbroken string of the digits 0-9
and the capital letters A\-F which are treated as digits
with values 10\-15 respectively.
The number may be preceded by an underscore _ to input a
negative number.
Numbers may contain decimal points.
.SH
+ \- * % ^
.IP
The
top two values on the stack are added
(\fB+\fP),
subtracted
(\fB\-\fP),
multiplied (\fB*\fP),
divided (\fB/\fP),
remaindered (\fB%\fP),
or exponentiated (^).
The two entries are popped off the stack;
the result is pushed on the stack in their place.
The result of a division is an integer truncated toward zero.
See the detailed description below for the treatment of
numbers with decimal points.
An exponent must not have any digits after the decimal point.
.SH
s\fIx\fP
.IP
The
top of the main stack is popped and stored into
a register named \fIx\fP, where \fIx\fP may be any character.
If
the
.ft B
s
.ft
is capitalized,
.ft I
x
.ft
is treated as a stack and the value is pushed onto it.
Any character, even blank or new-line, is a valid register name.
.SH
l\fIx\fP
.IP
The
value in register
.ft I
x
.ft
is pushed onto the stack.
The register
.ft I
x
.ft
is not altered.
If the
.ft B
l
.ft
is capitalized,
register
.ft I
x
.ft
is treated as a stack and its top value is popped onto the main stack.
.LP
All registers start with empty value which is treated as a zero
by the command \fBl\fP and is treated as an error by the command \fBL\fP.
.SH
d
.IP
The
top value on the stack is duplicated.
.SH
p
.IP
The top value on the stack is printed.
The top value remains unchanged.
.SH
f
.IP
All values on the stack and in registers are printed.
.SH
x
.IP
treats the top element of the stack as a character string,
removes it from the stack, and
executes it as a string of DC commands.
.SH
[ ... ]
.IP
puts the bracketed character string onto the top of the stack.
.SH
q
.IP
exits the program.
If executing a string, the recursion level is
popped by two.
If
.ft B
q
.ft
is capitalized,
the top value on the stack is popped and the string execution level is popped
by that value.
.SH
<\fIx\fP >\fIx\fP =\fIx\fP !<\fIx\fP !>\fIx\fP !=\fIx\fP
.IP
The
top two elements of the stack are popped and compared.
Register
.ft I
x
.ft
is executed if they obey the stated
relation.
Exclamation point is negation.
.SH
v
.IP
replaces the top element on the stack by its square root.
The square root of an integer is truncated to an integer.
For the treatment of numbers with decimal points, see
the detailed description below.
.SH
!
.IP
interprets the rest of the line as a
.UX
command.
Control returns to DC when the
.UX
command terminates.
.SH
c
.IP
All values on the stack are popped; the stack becomes empty.
.SH
i
.IP
The top value on the stack is popped and used as the
number radix for further input.
If \fBi\fP is capitalized, the value of
the input base is pushed onto the stack.
No mechanism has been provided for the input of arbitrary
numbers in bases less than 1 or greater than 16.
.SH
o
.IP
The top value on the stack is popped and used as the
number radix for further output.
If \fBo\fP is capitalized, the value of the output
base is pushed onto the stack.
.SH
k
.IP
The top of the stack is popped, and that value is used as
a scale factor
that influences the number of decimal places
that are maintained during multiplication, division, and exponentiation.
The scale factor must be greater than or equal to zero and
less than 100.
If \fBk\fP is capitalized, the value of the scale factor
is pushed onto the stack.
.SH
z
.IP
The value of the stack level is pushed onto the stack.
.SH
?
.IP
A line of input is taken from the input source (usually the console)
and executed.
.SH
DETAILED DESCRIPTION
.SH
Internal Representation of Numbers
.PP
Numbers are stored internally using a dynamic storage allocator.
Numbers are kept in the form of a string
of digits to the base 100 stored one digit per byte
(centennial digits).
The string is stored with the low-order digit at the
beginning of the string.
For example, the representation of 157
is 57,1.
After any arithmetic operation on a number, care is taken
that all digits are in the range 0\-99 and that
the number has no leading zeros.
The number zero is represented by the empty string.
.PP
Negative numbers are represented in the 100's complement
notation, which is analogous to two's complement notation for binary
numbers.
The high order digit of a negative number is always \-1
and all other digits are in the range 0\-99.
The digit preceding the high order \-1 digit is never a 99.
The representation of \-157 is 43,98,\-1.
We shall call this the canonical form of a number.
The advantage of this kind of representation of negative
numbers is ease of addition. When addition is performed digit
by digit, the result is formally correct. The result need only
be modified, if necessary, to put it into canonical form.
.PP
Because the largest valid digit is 99 and the byte can
hold numbers twice that large, addition can be carried out
and the handling of carries done later when
that is convenient, as it sometimes is.
.PP
An additional byte is stored with each number beyond
the high order digit to indicate the number of
assumed decimal digits after the decimal point. The representation
of .001 is 1,\fI3\fP
where the scale has been italicized to emphasize the fact that it
is not the high order digit.
The value of this extra byte is called the
.ft B
scale factor
.ft
of the number.
.SH
The Allocator
.PP
DC uses a dynamic string storage allocator
for all of its internal storage.
All reading and writing of numbers internally is done through
the allocator.
Associated with each string in the allocator is a four-word header containing pointers
to the beginning of the string, the end of the string,
the next place to write, and the next place to read.
Communication between the allocator and DC
is done via pointers to these headers.
.PP
The allocator initially has one large string on a list
of free strings. All headers except the one pointing
to this string are on a list of free headers.
Requests for strings are made by size.
The size of the string actually supplied is the next higher
power of 2.
When a request for a string is made, the allocator
first checks the free list to see if there is
a string of the desired size.
If none is found, the allocator finds the next larger free string and splits it repeatedly until
it has a string of the right size.
Left-over strings are put on the free list.
If there are no larger strings,
the allocator tries to coalesce smaller free strings into
larger ones.
Since all strings are the result
of splitting large strings,
each string has a neighbor that is next to it in core
and, if free, can be combined with it to make a string twice as long.
This is an implementation of the `buddy system' of allocation
described in [2].
.PP
Failing to find a string of the proper length after coalescing,
the allocator asks the system for more space.
The amount of space on the system is the only limitation
on the size and number of strings in DC.
If at any time in the process of trying to allocate a string, the allocator runs out of
headers, it also asks the system for more space.
.PP
There are routines in the allocator for reading, writing, copying, rewinding,
forward-spacing, and backspacing strings.
All string manipulation is done using these routines.
.PP
The reading and writing routines
increment the read pointer or write pointer so that
the characters of a string are read or written in
succession by a series of read or write calls.
The write pointer is interpreted as the end of the
information-containing portion of a string and a call
to read beyond that point returns an end-of-string indication.
An attempt to write beyond the end of a string
causes the allocator to
allocate a larger space and then copy
the old string into the larger block.
.SH
Internal Arithmetic
.PP
All arithmetic operations are done on integers.
The operands (or operand) needed for the operation are popped
from the main stack and their scale factors stripped off.
Zeros are added or digits removed as necessary to get
a properly scaled result from the internal arithmetic routine.
For example, if the scale of the operands is different and decimal
alignment is required, as it is for
addition, zeros are appended to the operand with the smaller
scale.
After performing the required arithmetic operation,
the proper scale factor is appended to the end of the number before
it is pushed on the stack.
.PP
A register called \fBscale\fP plays a part
in the results of most arithmetic operations.
\fBscale\fP is the bound on the number of decimal places retained in
arithmetic computations.
\fBscale\fP may be set to the number on the top of the stack
truncated to an integer with the \fBk\fP command.
\fBK\fP may be used to push the value of \fBscale\fP on the stack.
\fBscale\fP must be greater than or equal to 0 and less than 100.
The descriptions of the individual arithmetic operations will
include the exact effect of \fBscale\fP on the computations.
.SH
Addition and Subtraction
.PP
The scales of the two numbers are compared and trailing
zeros are supplied to the number with the lower scale to give both
numbers the same scale. The number with the smaller scale is
multiplied by 10 if the difference of the scales is odd.
The scale of the result is then set to the larger of the scales
of the two operands.
.PP
Subtraction is performed by negating the number
to be subtracted and proceeding as in addition.
.PP
Finally, the addition is performed digit by digit from the
low order end of the number. The carries are propagated
in the usual way.
The resulting number is brought into canonical form, which may
require stripping of leading zeros, or for negative numbers
replacing the high-order configuration 99,\-1 by the digit \-1.
In any case, digits which are not in the range 0\-99 must
be brought into that range, propagating any carries or borrows
that result.
.SH
Multiplication
.PP
The scales are removed from the two operands and saved.
The operands are both made positive.
Then multiplication is performed in
a digit by digit manner that exactly mimics the hand method
of multiplying.
The first number is multiplied by each digit of the second
number, beginning with its low order digit. The intermediate
products are accumulated into a partial sum which becomes the
final product.
The product is put into the canonical form and its sign is
computed from the signs of the original operands.
.PP
The scale of the result is set equal to the sum
of the scales of the two operands.
If that scale is larger than the internal register
.ft B
scale
.ft
and also larger than both of the scales of the two operands,
then the scale of the result is set equal to the largest
of these three last quantities.
.SH
Division
.PP
The scales are removed from the two operands.
Zeros are appended or digits removed from the dividend to make
the scale of the result of the integer division equal to
the internal quantity
\fBscale\fP.
The signs are removed and saved.
.PP
Division is performed much as it would be done by hand.
The difference of the lengths of the two numbers
is computed.
If the divisor is longer than the dividend,
zero is returned.
Otherwise the top digit of the divisor is divided into the top
two digits of the dividend.
The result is used as the first (high-order) digit of the
quotient.
It may turn out be one unit too low, but if it is, the next
trial quotient will be larger than 99 and this will be
adjusted at the end of the process.
The trial digit is multiplied by the divisor and the result subtracted
from the dividend and the process is repeated to get
additional quotient digits until the remaining
dividend is smaller than the divisor.
At the end, the digits of the quotient are put into
the canonical form, with propagation of carry as needed.
The sign is set from the sign of the operands.
.SH
Remainder
.PP
The division routine is called and division is performed
exactly as described. The quantity returned is the remains of the
dividend at the end of the divide process.
Since division truncates toward zero, remainders have the same
sign as the dividend.
The scale of the remainder is set to
the maximum of the scale of the dividend and
the scale of the quotient plus the scale of the divisor.
.SH
Square Root
.PP
The scale is stripped from the operand.
Zeros are added if necessary to make the
integer result have a scale that is the larger of
the internal quantity
\fBscale\fP
and the scale of the operand.
.PP
The method used to compute sqrt(y) is Newton's method
with successive approximations by the rule
.EQ
x sub {n+1} ~=~ half ( x sub n + y over x sub n )
.EN
The initial guess is found by taking the integer square root
of the top two digits.
.SH
Exponentiation
.PP
Only exponents with zero scale factor are handled. If the exponent is
zero, then the result is 1. If the exponent is negative, then
it is made positive and the base is divided into one. The scale
of the base is removed.
.PP
The integer exponent is viewed as a binary number.
The base is repeatedly squared and the result is
obtained as a product of those powers of the base that
correspond to the positions of the one-bits in the binary
representation of the exponent.
Enough digits of the result
are removed to make the scale of the result the same as if the
indicated multiplication had been performed.
.SH
Input Conversion and Base
.PP
Numbers are converted to the internal representation as they are read
in.
The scale stored with a number is simply the number of fractional digits input.
Negative numbers are indicated by preceding the number with a \fB\_\fP (an
underscore).
The hexadecimal digits A\-F correspond to the numbers 10\-15 regardless of input base.
The \fBi\fP command can be used to change the base of the input numbers.
This command pops the stack, truncates the resulting number to an integer,
and uses it as the input base for all further input.
The input base is initialized to 10 but may, for example be changed to
8 or 16 to do octal or hexadecimal to decimal conversions.
The command \fBI\fP will push the value of the input base on the stack.
.SH
Output Commands
.PP
The command \fBp\fP causes the top of the stack to be printed.
It does not remove the top of the stack.
All of the stack and internal registers can be output
by typing the command \fBf\fP.
The \fBo\fP command can be used to change the output base.
This command uses the top of the stack, truncated to an integer as
the base for all further output.
The output base in initialized to 10.
It will work correctly for any base.
The command \fBO\fP pushes the value of the output base on the stack.
.SH
Output Format and Base
.PP
The input and output bases only affect
the interpretation of numbers on input and output; they have no
effect on arithmetic computations.
Large numbers are output with 70 characters per line;
a \\ indicates a continued line.
All choices of input and output bases work correctly, although not all are
useful.
A particularly useful output base is 100000, which has the effect of
grouping digits in fives.
Bases of 8 and 16 can be used for decimal-octal or decimal-hexadecimal
conversions.
.SH
Internal Registers
.PP
Numbers or strings may be stored in internal registers or loaded on the stack
from registers with the commands \fBs\fP and \fBl\fP.
The command \fBs\fIx\fR pops the top of the stack and
stores the result in register \fBx\fP.
\fIx\fP can be any character.
\fBl\fIx\fR puts the contents of register \fBx\fP on the top of the stack.
The \fBl\fP command has no effect on the contents of register \fIx\fP.
The \fBs\fP command, however, is destructive.
.SH
Stack Commands
.PP
The command \fBc\fP clears the stack.
The command \fBd\fP pushes a duplicate of the number on the top of the stack
on the stack.
The command \fBz\fP pushes the stack size on the stack.
The command \fBX\fP replaces the number on the top of the stack
with its scale factor.
The command \fBZ\fP replaces the top of the stack
with its length.
.SH
Subroutine Definitions and Calls
.PP
Enclosing a string in \fB[ ]\fP pushes the ascii string on the stack.
The \fBq\fP command quits or in executing a string, pops the recursion levels by two.
.SH
Internal Registers \- Programming DC
.PP
The load and store
commands together with \fB[ ]\fP to store strings, \fBx\fP to execute
and the testing commands `<', `>', `=', `!<', `!>', `!=' can be used to program
DC.
The \fBx\fP command assumes the top of the stack is an string of DC commands
and executes it.
The testing commands compare the top two elements on the stack and if the relation holds, execute the register
that follows the relation.
For example, to print the numbers 0-9,
.DS
[lip1+ si li10>a]sa
0si lax
.DE
.SH
Push-Down Registers and Arrays
.PP
These commands were designed for used by a compiler, not by
people.
They involve push-down registers and arrays.
In addition to the stack that commands work on, DC can be thought
of as having individual stacks for each register.
These registers are operated on by the commands \fBS\fP and \fBL\fP.
\fBS\fIx\fR pushes the top value of the main stack onto the stack for
the register \fIx\fP.
\fBL\fIx\fR pops the stack for register \fIx\fP and puts the result on the main
stack.
The commands \fBs\fP and \fBl\fP also work on registers but not as push-down
stacks.
\fBl\fP doesn't effect the top of the
register stack, and \fBs\fP destroys what was there before.
.PP
The commands to work on arrays are \fB:\fP and \fB;\fP.
\fB:\fIx\fR pops the stack and uses this value as an index into
the array \fIx\fP.
The next element on the stack is stored at this index in \fIx\fP.
An index must be greater than or equal to 0 and
less than 2048.
\fB;\fIx\fR is the command to load the main stack from the array \fIx\fP.
The value on the top of the stack is the index
into the array \fIx\fP of the value to be loaded.
.SH
Miscellaneous Commands
.PP
The command \fB!\fP interprets the rest of the line as a
.UX
command and passes it to
.UX
to execute.
One other compiler command is \fBQ\fP.
This command uses the top of the stack as the number of levels of recursion to skip.
.SH
DESIGN CHOICES
.PP
The real reason for the use of a dynamic storage allocator was
that a general purpose program could be (and in fact has been)
used for a variety of other tasks.
The allocator has some value for input and for compiling (i.e.
the bracket [...] commands) where it cannot be known in advance
how long a string will be.
The result was that at a modest
cost in execution time, all considerations of string allocation
and sizes of strings were removed from the remainder of the program
and debugging was made easier. The allocation method
used wastes approximately 25% of available space.
.PP
The choice of 100 as a base for internal arithmetic
seemingly has no compelling advantage. Yet the base cannot
exceed 127 because of hardware limitations and at the cost
of 5% in space, debugging was made a great deal easier and
decimal output was made much faster.
.PP
The reason for a stack-type arithmetic design was
to permit all DC commands from addition to subroutine execution
to be implemented in essentially the same way. The result
was a considerable degree of logical separation of the final
program into modules with very little communication between
modules.
.PP
The rationale for the lack of interaction between the scale and the bases
was to provide an understandable means of proceeding after
a change of base or scale when numbers had already been entered.
An earlier implementation which had global notions of
scale and base did not work out well.
If the value of
.ft B
scale
.ft
were to be interpreted in the current
input or output base,
then a change of base or scale in the midst of a
computation would cause great confusion in the interpretation
of the results.
The current scheme has the advantage that the value of
the input and output bases
are only used for input and output, respectively, and they
are ignored in all other operations.
The value of
scale
is not used for any essential purpose by any part of the program
and it is used only to prevent the number of
decimal places resulting from the arithmetic operations from
growing beyond all bounds.
.PP
The design rationale for the choices for the scales of
the results of arithmetic were that in no case should
any significant digits be thrown away if, on appearances, the
user actually wanted them. Thus, if the user wants
to add the numbers 1.5 and 3.517, it seemed reasonable to give
him the result 5.017 without requiring him to unnecessarily
specify his rather obvious requirements for precision.
.PP
On the other hand, multiplication and exponentiation produce
results with many more digits than their operands and it
seemed reasonable to give as a minimum the number of decimal
places in the operands but not to give more than that
number of digits
unless the user asked for them by specifying a value for \fBscale\fP.
Square root can be handled in just the same way as multiplication.
The operation of division gives arbitrarily many decimal places
and there is simply no way to guess how many places the user
wants.
In this case only, the user must
specify a \fBscale\fP to get any decimal places at all.
.PP
The scale of remainder was chosen to make it possible
to recreate the dividend from the quotient and remainder.
This is easy to implement; no digits are thrown away.
.SH
References
.IP [1]
L. L. Cherry, R. Morris,
.ft I
BC \- An Arbitrary Precision Desk-Calculator Language.
.ft
.IP [2]
K. C. Knowlton,
.ft I
A Fast Storage Allocator,
.ft
Comm. ACM \fB8\fP, pp. 623-625 (Oct. 1965).

1781
usr.bin/dc/bcode.c Normal file

File diff suppressed because it is too large Load Diff

98
usr.bin/dc/bcode.h Normal file
View File

@ -0,0 +1,98 @@
/* $FreeBSD$ */
/* $OpenBSD: bcode.h,v 1.5 2006/01/16 08:09:25 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 <sys/types.h>
#include <openssl/bn.h>
struct number {
BIGNUM *number;
u_int scale;
};
enum stacktype {
BCODE_NONE,
BCODE_NUMBER,
BCODE_STRING
};
enum bcode_compare {
BCODE_EQUAL,
BCODE_NOT_EQUAL,
BCODE_LESS,
BCODE_NOT_LESS,
BCODE_GREATER,
BCODE_NOT_GREATER
};
struct array;
struct value {
union {
struct number *num;
char *string;
} u;
struct array *array;
enum stacktype type;
};
struct array {
struct value *data;
size_t size;
};
struct stack {
struct value *stack;
ssize_t sp;
ssize_t size;
};
struct source;
struct vtable {
int (*readchar)(struct source *);
void (*unreadchar)(struct source *);
char *(*readline)(struct source *);
void (*free)(struct source *);
};
struct source {
struct vtable *vtable;
union {
FILE *stream;
struct {
u_char *buf;
size_t pos;
} string;
} u;
int lastchar;
};
void init_bmachine(bool);
void reset_bmachine(struct source *);
void scale_number(BIGNUM *, int);
void normalize(struct number *, u_int);
void eval(void);
void pn(const char *, const struct number *);
void pbn(const char *, const BIGNUM *);
void negate(struct number *);
void split_number(const struct number *, BIGNUM *, BIGNUM *);
void bmul_number(struct number *, struct number *,
struct number *);
extern BIGNUM zero;

552
usr.bin/dc/dc.1 Normal file
View File

@ -0,0 +1,552 @@
.\" $FreeBSD$
.\" $OpenBSD: dc.1,v 1.24 2010/01/02 19:48:56 schwarze 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.
.\"
.\" @(#)dc.1 8.1 (Berkeley) 6/6/93
.\"
.Dd Jan 28 2009
.Dt DC 1
.Os
.Sh NAME
.Nm dc
.Nd desk calculator
.Sh SYNOPSIS
.Nm
.Op Fl hxV
.Op Fl e Ar expression
.Op Fl f Ar filename
.Op Ar filename
.Sh DESCRIPTION
.Nm
is an arbitrary precision arithmetic package.
The overall structure of
.Nm
is
a stacking (reverse Polish) calculator i.e.\&
numbers are stored on a stack.
Adding a number pushes it onto the stack.
Arithmetic operations pop arguments off the stack
and push the results.
See also the
.Xr bc 1
utility, which is a preprocessor for
.Nm
providing infix notation and a C-like syntax
which implements functions and reasonable control
structures for programs.
The options are as follows:
.Bl -tag -width Ds
.It Fl e Ar expr
.It Fl Fl expression Ar expr
Evaluate
.Ar expression .
If multiple
.Fl e
options are specified, they will be processed in the order given.
If no
.Ar filename
argument is given, execution will stop after processing the expressions
given on the command line,
otherwise processing will continue with the contents of
.Ar filename .
.It Fl f Ar filename
.It Fl Fl file Ar filename
Process the content of the given file before further calculations are done.
If multiple
.Fl f
options are specified, they will be processed in the order given.
.It Fl h
.It Fl Fl help
Print short usage info.
.It Fl V
.It Fl Fl version
Print version info.
.It Fl x
Enable extended register mode.
This mode is used by
.Xr bc 1
to allow more than 256 registers.
See
.Sx Registers
for a more detailed description.
.El
.Pp
Ordinarily,
.Nm
operates on decimal integers,
but one may specify an input base, output base,
and a number of fractional digits (scale) to be maintained.
If an argument is given,
input is taken from that file until its end,
then from the standard input.
Whitespace is ignored, except where it signals the end of a number,
end of a line or when a register name is expected.
The following constructions are recognized:
.Bl -tag -width "number"
.It Va number
The value of the number is pushed on the stack.
A number is an unbroken string of the digits 0\-9 and letters A\-F.
It may be preceded by an underscore
.Pq Sq _
to input a negative number.
A number may contain a single decimal point.
A number may also contain the characters A\-F, with the values 10\-15.
.It Cm "+ - / * % ~ ^"
The
top two values on the stack are added
(+),
subtracted
(\-),
multiplied (*),
divided (/),
remaindered (%),
divided and remaindered (~),
or exponentiated (^).
The two entries are popped off the stack;
the result is pushed on the stack in their place.
Any fractional part of an exponent is ignored.
.Pp
For addition and subtraction, the scale of the result is the maximum
of scales of the operands.
For division the scale of the result is defined
by the scale set by the
.Ic k
operation.
For multiplication, the scale is defined by the expression
.Sy min(a+b,max(a,b,scale)) ,
where
.Sy a
and
.Sy b
are the scales of the operands, and
.Sy scale
is the scale defined by the
.Ic k
operation.
For exponentiation with a non-negative exponent, the scale of the result is
.Sy min(a*b,max(scale,a)) ,
where
.Sy a
is the scale of the base, and
.Sy b
is the
.Em value
of the exponent.
If the exponent is negative, the scale of the result is the scale
defined by the
.Ic k
operation.
.Pp
In the case of the division and modulus operator (~),
the resultant quotient is pushed first followed by the remainder.
This is a shorthand for the sequence:
.Bd -literal -offset indent -compact
x y / x y %
.Ed
The division and modulus operator is a non-portable extension.
.It Ic a
Pop the top value from the stack.
If that value is a number, compute the integer part of the number modulo 256.
If the result is zero, push an empty string.
Otherwise push a one character string by interpreting the computed value
as an
.Tn ASCII
character.
.Pp
If the top value is a string, push a string containing the first character
of the original string.
If the original string is empty, an empty string is pushed back.
The
.Ic a
operator is a non-portable extension.
.It Ic c
All values on the stack are popped.
.It Ic d
The top value on the stack is duplicated.
.It Ic f
All values on the stack are printed, separated by newlines.
.It Ic G
The top two numbers are popped from the stack and compared.
A one is pushed if the top of the stack is equal to the second number
on the stack.
A zero is pushed otherwise.
This is a non-portable extension.
.It Ic I
Pushes the input base on the top of the stack.
.It Ic i
The top value on the stack is popped and used as the
base for further input.
The initial input base is 10.
.It Ic J
Pop the top value from the stack.
The recursion level is popped by that value and, following that,
the input is skipped until the first occurrence of the
.Ic M
operator.
The
.Ic J
operator is a non-portable extension, used by the
.Xr bc 1
command.
.It Ic K
The current scale factor is pushed onto the stack.
.It Ic k
The top of the stack is popped, and that value is used as
a non-negative scale factor:
the appropriate number of places
are printed on output,
and maintained during multiplication, division, and exponentiation.
The interaction of scale factor,
input base, and output base will be reasonable if all are changed
together.
.It Ic L Ns Ar x
Register
.Ar x
is treated as a stack and its top value is popped onto the main stack.
.It Ic l Ns Ar x
The
value in register
.Ar x
is pushed on the stack.
The register
.Ar x
is not altered.
Initially, all registers contain the value zero.
.It Ic M
Mark used by the
.Ic J
operator.
The
.Ic M
operator is a non-portable extensions, used by the
.Xr bc 1
command.
.It Ic N
The top of the stack is replaced by one if the top of the stack
is equal to zero.
If the top of the stack is unequal to zero, it is replaced by zero.
This is a non-portable extension.
.It Ic n
The top value on the stack is popped and printed without a newline.
This is a non-portable extension.
.It Ic O
Pushes the output base on the top of the stack.
.It Ic o
The top value on the stack is popped and used as the
base for further output.
The initial output base is 10.
.It Ic P
The top of the stack is popped.
If the top of the stack is a string, it is printed without a trailing newline.
If the top of the stack is a number, it is interpreted as a
base 256 number, and each digit of this base 256 number is printed as
an
.Tn ASCII
character, without a trailing newline.
.It Ic p
The top value on the stack is printed with a trailing newline.
The top value remains unchanged.
.It Ic Q
The top value on the stack is popped and the string execution level is popped
by that value.
.It Ic q
Exits the program.
If executing a string, the recursion level is
popped by two.
.It Ic R
The top of the stack is removed (popped).
This is a non-portable extension.
.It Ic r
The top two values on the stack are reversed (swapped).
This is a non-portable extension.
.It Ic S Ns Ar x
Register
.Ar x
is treated as a stack.
The top value of the main stack is popped and pushed on it.
.It Ic s Ns Ar x
The
top of the stack is popped and stored into
a register named
.Ar x .
.It Ic v
Replaces the top element on the stack by its square root.
The scale of the result is the maximum of the scale of the argument
and the current value of scale.
.It Ic X
Replaces the number on the top of the stack with its scale factor.
If the top of the stack is a string, replace it with the integer 0.
.It Ic x
Treats the top element of the stack as a character string
and executes it as a string of
.Nm
commands.
.It Ic Z
Replaces the number on the top of the stack with its length.
The length of a string is its number of characters.
The length of a number is its number of digits, not counting the minus sign
and decimal point.
.It Ic z
The stack level is pushed onto the stack.
.It Cm [ Ns ... Ns Cm ]
Puts the bracketed
.Tn ASCII
string onto the top of the stack.
If the string includes brackets, these must be properly balanced.
The backslash character
.Pq Sq \e
may be used as an escape character, making it
possible to include unbalanced brackets in strings.
To include a backslash in a string, use a double backslash.
.It Xo
.Cm < Ns Va x
.Cm > Ns Va x
.Cm = Ns Va x
.Cm !< Ns Va x
.Cm !> Ns Va x
.Cm != Ns Va x
.Xc
The top two elements of the stack are popped and compared.
Register
.Ar x
is executed if they obey the stated
relation.
.It Xo
.Cm < Ns Va x Ns e Ns Va y
.Cm > Ns Va x Ns e Ns Va y
.Cm = Ns Va x Ns e Ns Va y
.Cm !< Ns Va x Ns e Ns Va y
.Cm !> Ns Va x Ns e Ns Va y
.Cm != Ns Va x Ns e Ns Va y
.Xc
These operations are variants of the comparison operations above.
The first register name is followed by the letter
.Sq e
and another register name.
Register
.Ar x
will be executed if the relation is true, and register
.Ar y
will be executed if the relation is false.
This is a non-portable extension.
.It Ic \&(
The top two numbers are popped from the stack and compared.
A one is pushed if the top of the stack is less than the second number
on the stack.
A zero is pushed otherwise.
This is a non-portable extension.
.It Ic {
The top two numbers are popped from the stack and compared.
A one is pushed if the top of stack is less than or equal to the
second number on the stack.
A zero is pushed otherwise.
This is a non-portable extension.
.It Ic \&!
Interprets the rest of the line as a
.Ux
command.
.It Ic \&?
A line of input is taken from the input source (usually the terminal)
and executed.
.It Ic : Ns Ar r
Pop two values from the stack.
The second value on the stack is stored into the array
.Ar r
indexed by the top of stack.
.It Ic ; Ns Ar r
Pop a value from the stack.
The value is used as an index into register
.Ar r .
The value in this register is pushed onto the stack.
.Pp
Array elements initially have the value zero.
Each level of a stacked register has its own array associated with
it.
The command sequence
.Bd -literal -offset indent
[first] 0:a [dummy] Sa [second] 0:a 0;a p La 0;a p
.Ed
.Pp
will print
.Bd -literal -offset indent
second
first
.Ed
.Pp
since the string
.Ql second
is written in an array that is later popped, to reveal the array that
stored
.Ql first .
.It Ic #
Skip the rest of the line.
This is a non-portable extension.
.El
.Ss Registers
Registers have a single character name
.Ar x ,
where
.Ar x
may be any character, including space, tab or any other special character.
If extended register mode is enabled using the
.Fl x
option and the register identifier
.Ar x
has the value 255, the next two characters are interpreted as a
two-byte register index.
The set of standard single character registers and the set of extended
registers do not overlap.
Extended register mode is a non-portable extension.
.Sh EXAMPLES
An example which prints the first ten values of
.Ic n! :
.Bd -literal -offset indent
[la1+dsa*pla10>y]sy
0sa1
lyx
.Ed
.Pp
Independent of the current input base, the command
.Bd -literal -offset indent
Ai
.Ed
.Pp
will reset the input base to decimal 10.
.Sh DIAGNOSTICS
.Bl -diag
.It %c (0%o) is unimplemented
an undefined operation was called.
.It stack empty
for not enough elements on the stack to do what was asked.
.It stack register '%c' (0%o) is empty
for an
.Ar L
operation from a stack register that is empty.
.It Runtime warning: non-zero scale in exponent
for a fractional part of an exponent that is being ignored.
.It divide by zero
for trying to divide by zero.
.It remainder by zero
for trying to take a remainder by zero.
.It square root of negative number
for trying to take the square root of a negative number.
.It index too big
for an array index that is larger than 2048.
.It negative index
for a negative array index.
.It "input base must be a number between 2 and 16"
for trying to set an illegal input base.
.It output base must be a number greater than 1
for trying to set an illegal output base.
.It scale must be a nonnegative number
for trying to set a negative or zero scale.
.It scale too large
for trying to set a scale that is too large.
A scale must be representable as a 32-bit unsigned number.
.It Q command argument exceeded string execution depth
for trying to pop the recursion level more than the current
recursion level.
.It Q command requires a number >= 1
for trying to pop an illegal number of recursion levels.
.It recursion too deep
for too many levels of nested execution.
.Pp
The recursion level is increased by one if the
.Ar x
or
.Ar ?\&
operation or one of the compare operations resulting in the execution
of register is executed.
As an exception, the recursion level is not increased if the operation
is executed as the last command of a string.
For example, the commands
.Bd -literal -offset indent
[lax]sa
1 lax
.Ed
.Pp
will execute an endless loop, while the commands
.Bd -literal -offset indent
[laxp]sa
1 lax
.Ed
.Pp
will terminate because of a too deep recursion level.
.It J command argument exceeded string execution depth
for trying to pop the recursion level more than the current
recursion level.
.It mark not found
for a failed scan for an occurrence of the
.Ic M
operator.
.El
.Sh SEE ALSO
.Xr bc 1
.Rs
.%B USD:05
.%A L. L. Cherry
.%A R. Morris
.%T "DC \- An Interactive Desk Calculator"
.Re
.Sh STANDARDS
The arithmetic operations of the
.Nm
utility are expected to conform to the definition listed in the
.Xr bc 1
section of the
.St -p1003.2
specification.
.Sh HISTORY
The
.Nm
command first appeared in
.At v6 .
A complete rewrite of the
.Nm
command using the
.Xr bn 3
big number routines 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 .

140
usr.bin/dc/dc.c Normal file
View File

@ -0,0 +1,140 @@
/* $OpenBSD: dc.c,v 1.11 2009/10/27 23:59:37 deraadt Exp $ */
/*
* Copyright (c) 2003, Otto Moerbeek <otto@drijf.net>
* Copyright (c) 2009, Gabor Kovesdan <gabor@FreeBSD.org>
*
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/stat.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "extern.h"
#define DC_VER "1.3-FreeBSD"
static void usage(void);
extern char *__progname;
struct source src;
struct option long_options[] =
{
{"expression", required_argument, NULL, 'e'},
{"file", required_argument, NULL, 'f'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'}
};
static void
usage(void)
{
fprintf(stderr, "usage: %s [-hVx] [-e expression] [file]\n",
__progname);
exit(1);
}
static void
procfile(char *fname) {
FILE *file;
struct stat st;
file = fopen(fname, "r");
if (file == NULL)
err(1, "cannot open file %s", fname);
if (fstat(fileno(file), &st) == -1)
err(1, "%s", fname);
if (S_ISDIR(st.st_mode)) {
errno = EISDIR;
err(1, "%s", fname);
}
src_setstream(&src, file);
reset_bmachine(&src);
eval();
fclose(file);
}
int
main(int argc, char *argv[])
{
int ch;
bool extended_regs = false;
char *buf;
bool preproc_done = false;
if ((buf = strdup("")) == NULL)
err(1, NULL);
init_bmachine(extended_regs);
setlinebuf(stdout);
setlinebuf(stderr);
/* accept and ignore a single dash to be 4.4BSD dc(1) compatible */
while ((ch = getopt_long(argc, argv, "e:f:Vx", long_options, NULL)) != -1) {
switch (ch) {
case 'e':
src_setstring(&src, optarg);
reset_bmachine(&src);
eval();
preproc_done = true;
break;
case 'f':
procfile(optarg);
preproc_done = true;
break;
case 'x':
extended_regs = true;
break;
case 'V':
fprintf(stderr, "%s (BSD bc) %s\n", __progname, DC_VER);
exit(0);
break;
case '-':
break;
case 'h':
/* FALLTHROUGH */
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc > 1)
usage();
if (argc == 1) {
procfile(argv[0]);
preproc_done = true;
}
if (preproc_done)
return (0);
src_setstream(&src, stdin);
reset_bmachine(&src);
eval();
return (0);
}

63
usr.bin/dc/extern.h Normal file
View File

@ -0,0 +1,63 @@
/* $FreeBSD$ */
/* $OpenBSD: extern.h,v 1.3 2006/01/16 08:09:25 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 "bcode.h"
/* inout.c */
void src_setstream(struct source *, FILE *);
void src_setstring(struct source *, char *);
struct number *readnumber(struct source *, u_int);
void printnumber(FILE *, const struct number *, u_int);
char *read_string(struct source *);
void print_value(FILE *, const struct value *, const char *, u_int);
void print_ascii(FILE *, const struct number *);
/* mem.c */
struct number *new_number(void);
void free_number(struct number *);
struct number *dup_number(const struct number *);
void *bmalloc(size_t);
void *brealloc(void *, size_t);
char *bstrdup(const char *p);
void bn_check(int);
void bn_checkp(const void *);
/* stack.c */
void stack_init(struct stack *);
void stack_free_value(struct value *);
struct value *stack_dup_value(const struct value *, struct value *);
void stack_swap(struct stack *);
size_t stack_size(const struct stack *);
void stack_dup(struct stack *);
void stack_pushnumber(struct stack *, struct number *);
void stack_pushstring(struct stack *stack, char *);
void stack_push(struct stack *, struct value *);
void stack_set_tos(struct stack *, struct value *);
struct value *stack_tos(const struct stack *);
struct value *stack_pop(struct stack *);
struct number *stack_popnumber(struct stack *);
char *stack_popstring(struct stack *);
void stack_clear(struct stack *);
void stack_print(FILE *, const struct stack *, const char *,
u_int base);
void frame_assign(struct stack *, size_t, const struct value *);
struct value *frame_retrieve(const struct stack *, size_t);
/* void frame_free(struct stack *); */

417
usr.bin/dc/inout.c Normal file
View File

@ -0,0 +1,417 @@
/* $OpenBSD: inout.c,v 1.15 2009/10/27 23:59:37 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <openssl/ssl.h>
#include <ctype.h>
#include <err.h>
#include <string.h>
#include "extern.h"
#define MAX_CHARS_PER_LINE 68
static int lastchar;
static int charcount;
static int src_getcharstream(struct source *);
static void src_ungetcharstream(struct source *);
static char *src_getlinestream(struct source *);
static int src_getcharstring(struct source *);
static void src_ungetcharstring(struct source *);
static char *src_getlinestring(struct source *);
static void src_freestring(struct source *);
static void flushwrap(FILE *);
static void putcharwrap(FILE *, int);
static void printwrap(FILE *, const char *);
static char *get_digit(u_long, int, u_int);
static struct vtable stream_vtable = {
src_getcharstream,
src_ungetcharstream,
src_getlinestream,
NULL
};
static struct vtable string_vtable = {
src_getcharstring,
src_ungetcharstring,
src_getlinestring,
src_freestring
};
void
src_setstream(struct source *src, FILE *stream)
{
src->u.stream = stream;
src->vtable = &stream_vtable;
}
void
src_setstring(struct source *src, char *p)
{
src->u.string.buf = (u_char *)p;
src->u.string.pos = 0;
src->vtable = &string_vtable;
}
static int
src_getcharstream(struct source *src)
{
return (src->lastchar = getc(src->u.stream));
}
static void
src_ungetcharstream(struct source *src)
{
ungetc(src->lastchar, src->u.stream);
}
static char *
src_getlinestream(struct source *src)
{
char buf[BUFSIZ];
if (fgets(buf, BUFSIZ, src->u.stream) == NULL)
return (bstrdup(""));
return bstrdup(buf);
}
static int
src_getcharstring(struct source *src)
{
src->lastchar = src->u.string.buf[src->u.string.pos];
if (src->lastchar == '\0')
return (EOF);
else {
src->u.string.pos++;
return (src->lastchar);
}
}
static void
src_ungetcharstring(struct source *src)
{
if (src->u.string.pos > 0) {
if (src->lastchar != '\0')
--src->u.string.pos;
}
}
static char *
src_getlinestring(struct source *src)
{
char buf[BUFSIZ];
int ch, i;
i = 0;
while (i < BUFSIZ-1) {
ch = src_getcharstring(src);
if (ch == EOF)
break;
buf[i++] = ch;
if (ch == '\n')
break;
}
buf[i] = '\0';
return (bstrdup(buf));
}
static void
src_freestring(struct source *src)
{
free(src->u.string.buf);
}
static void
flushwrap(FILE *f)
{
if (lastchar != -1)
putc(lastchar, f);
}
static void
putcharwrap(FILE *f, int ch)
{
if (charcount >= MAX_CHARS_PER_LINE) {
charcount = 0;
fputs("\\\n", f);
}
if (lastchar != -1) {
charcount++;
putc(lastchar, f);
}
lastchar = ch;
}
static void
printwrap(FILE *f, const char *p)
{
char buf[12];
char *q = buf;
strlcpy(buf, p, sizeof(buf));
while (*q)
putcharwrap(f, *q++);
}
struct number *
readnumber(struct source *src, u_int base)
{
struct number *n;
int ch;
bool sign = false;
bool dot = false;
BN_ULONG v;
u_int i;
n = new_number();
bn_check(BN_zero(n->number));
while ((ch = (*src->vtable->readchar)(src)) != EOF) {
if ('0' <= ch && ch <= '9')
v = ch - '0';
else if ('A' <= ch && ch <= 'F')
v = ch - 'A' + 10;
else if (ch == '_') {
sign = true;
continue;
} else if (ch == '.') {
if (dot)
break;
dot = true;
continue;
} else {
(*src->vtable->unreadchar)(src);
break;
}
if (dot)
n->scale++;
bn_check(BN_mul_word(n->number, base));
#if 0
/* work around a bug in BN_add_word: 0 += 0 is buggy.... */
if (v > 0)
#endif
bn_check(BN_add_word(n->number, v));
}
if (base != 10) {
scale_number(n->number, n->scale);
for (i = 0; i < n->scale; i++)
BN_div_word(n->number, base);
}
if (sign)
negate(n);
return (n);
}
char *
read_string(struct source *src)
{
int count, i, sz, new_sz, ch;
char *p;
bool escape;
escape = false;
count = 1;
i = 0;
sz = 15;
p = bmalloc(sz + 1);
while ((ch = (*src->vtable->readchar)(src)) != EOF) {
if (!escape) {
if (ch == '[')
count++;
else if (ch == ']')
count--;
if (count == 0)
break;
}
if (ch == '\\' && !escape)
escape = true;
else {
escape = false;
if (i == sz) {
new_sz = sz * 2;
p = brealloc(p, new_sz + 1);
sz = new_sz;
}
p[i++] = ch;
}
}
p[i] = '\0';
return (p);
}
static char *
get_digit(u_long num, int digits, u_int base)
{
char *p;
if (base <= 16) {
p = bmalloc(2);
p[0] = num >= 10 ? num + 'A' - 10 : num + '0';
p[1] = '\0';
} else {
if (asprintf(&p, "%0*lu", digits, num) == -1)
err(1, NULL);
}
return (p);
}
void
printnumber(FILE *f, const struct number *b, u_int base)
{
struct number *int_part, *fract_part;
int digits;
char buf[11];
size_t sz;
unsigned int i;
struct stack stack;
char *p;
charcount = 0;
lastchar = -1;
if (BN_is_zero(b->number))
putcharwrap(f, '0');
int_part = new_number();
fract_part = new_number();
fract_part->scale = b->scale;
if (base <= 16)
digits = 1;
else {
digits = snprintf(buf, sizeof(buf), "%u", base-1);
}
split_number(b, int_part->number, fract_part->number);
i = 0;
stack_init(&stack);
while (!BN_is_zero(int_part->number)) {
BN_ULONG rem = BN_div_word(int_part->number, base);
stack_pushstring(&stack, get_digit(rem, digits, base));
i++;
}
sz = i;
if (BN_cmp(b->number, &zero) < 0)
putcharwrap(f, '-');
for (i = 0; i < sz; i++) {
p = stack_popstring(&stack);
if (base > 16)
putcharwrap(f, ' ');
printwrap(f, p);
free(p);
}
stack_clear(&stack);
if (b->scale > 0) {
struct number *num_base;
BIGNUM mult, stop;
putcharwrap(f, '.');
num_base = new_number();
bn_check(BN_set_word(num_base->number, base));
BN_init(&mult);
bn_check(BN_one(&mult));
BN_init(&stop);
bn_check(BN_one(&stop));
scale_number(&stop, b->scale);
i = 0;
while (BN_cmp(&mult, &stop) < 0) {
u_long rem;
if (i && base > 16)
putcharwrap(f, ' ');
i = 1;
bmul_number(fract_part, fract_part, num_base);
split_number(fract_part, int_part->number, NULL);
rem = BN_get_word(int_part->number);
p = get_digit(rem, digits, base);
int_part->scale = 0;
normalize(int_part, fract_part->scale);
bn_check(BN_sub(fract_part->number, fract_part->number,
int_part->number));
printwrap(f, p);
free(p);
bn_check(BN_mul_word(&mult, base));
}
free_number(num_base);
BN_free(&mult);
BN_free(&stop);
}
flushwrap(f);
free_number(int_part);
free_number(fract_part);
}
void
print_value(FILE *f, const struct value *value, const char *prefix, u_int base)
{
fputs(prefix, f);
switch (value->type) {
case BCODE_NONE:
if (value->array != NULL)
fputs("<array>", f);
break;
case BCODE_NUMBER:
printnumber(f, value->u.num, base);
break;
case BCODE_STRING:
fputs(value->u.string, f);
break;
}
}
void
print_ascii(FILE *f, const struct number *n)
{
BIGNUM *v;
int numbits, i, ch;
v = BN_dup(n->number);
bn_checkp(v);
if (BN_cmp(v, &zero) < 0)
bn_check(BN_sub(v, &zero, v));
numbits = BN_num_bytes(v) * 8;
while (numbits > 0) {
ch = 0;
for (i = 0; i < 8; i++)
ch |= BN_is_bit_set(v, numbits-i-1) << (7 - i);
putc(ch, f);
numbits -= 8;
}
BN_free(v);
}

110
usr.bin/dc/mem.c Normal file
View File

@ -0,0 +1,110 @@
/* $OpenBSD: mem.c,v 1.5 2009/10/27 23:59:37 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <openssl/err.h>
#include <err.h>
#include <stdlib.h>
#include <string.h>
#include "extern.h"
struct number *
new_number(void)
{
struct number *n;
n = bmalloc(sizeof(*n));
n->scale = 0;
n->number = BN_new();
if (n->number == NULL)
err(1, NULL);
return (n);
}
void
free_number(struct number *n)
{
BN_free(n->number);
free(n);
}
struct number *
dup_number(const struct number *a)
{
struct number *n;
n = bmalloc(sizeof(*n));
n->scale = a->scale;
n->number = BN_dup(a->number);
bn_checkp(n->number);
return (n);
}
void *
bmalloc(size_t sz)
{
void *p;
p = malloc(sz);
if (p == NULL)
err(1, NULL);
return (p);
}
void *
brealloc(void *p, size_t sz)
{
void *q;
q = realloc(p, sz);
if (q == NULL)
err(1, NULL);
return (q);
}
char *
bstrdup(const char *p)
{
char *q;
q = strdup(p);
if (q == NULL)
err(1, NULL);
return (q);
}
void
bn_check(int x) \
{
if (x == 0)
err(1, "big number failure %lx", ERR_get_error());
}
void
bn_checkp(const void *p) \
{
if (p == NULL)
err(1, "allocation failure %lx", ERR_get_error());
}

379
usr.bin/dc/stack.c Normal file
View File

@ -0,0 +1,379 @@
/* $OpenBSD: stack.c,v 1.11 2009/10/27 23:59:37 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <err.h>
#include <stdlib.h>
#include <string.h>
#include "extern.h"
static __inline bool stack_empty(const struct stack *);
static void stack_grow(struct stack *);
static struct array *array_new(void);
static __inline void array_free(struct array *);
static struct array *array_dup(const struct array *);
static __inline void array_grow(struct array *, size_t);
static __inline void array_assign(struct array *, size_t, const struct value *);
static __inline struct value *array_retrieve(const struct array *, size_t);
void
stack_init(struct stack *stack)
{
stack->size = 0;
stack->sp = -1;
stack->stack = NULL;
}
static __inline bool
stack_empty(const struct stack *stack)
{
bool empty = stack->sp == -1;
if (empty)
warnx("stack empty");
return empty;
}
/* Clear number or string, but leave value itself */
void
stack_free_value(struct value *v)
{
switch (v->type) {
case BCODE_NONE:
break;
case BCODE_NUMBER:
free_number(v->u.num);
break;
case BCODE_STRING:
free(v->u.string);
break;
}
if (v->array != NULL) {
array_free(v->array);
v->array = NULL;
}
}
/* Copy number or string content into already allocated target */
struct value *
stack_dup_value(const struct value *a, struct value *copy)
{
copy->type = a->type;
switch (a->type) {
case BCODE_NONE:
break;
case BCODE_NUMBER:
copy->u.num = dup_number(a->u.num);
break;
case BCODE_STRING:
copy->u.string = strdup(a->u.string);
if (copy->u.string == NULL)
err(1, NULL);
break;
}
copy->array = a->array == NULL ? NULL : array_dup(a->array);
return (copy);
}
size_t
stack_size(const struct stack *stack)
{
return (stack->sp + 1);
}
void
stack_dup(struct stack *stack)
{
struct value *value;
struct value copy;
value = stack_tos(stack);
if (value == NULL) {
warnx("stack empty");
return;
}
stack_push(stack, stack_dup_value(value, &copy));
}
void
stack_swap(struct stack *stack)
{
struct value copy;
if (stack->sp < 1) {
warnx("stack empty");
return;
}
copy = stack->stack[stack->sp];
stack->stack[stack->sp] = stack->stack[stack->sp-1];
stack->stack[stack->sp-1] = copy;
}
static void
stack_grow(struct stack *stack)
{
size_t new_size, i;
if (++stack->sp == stack->size) {
new_size = stack->size * 2 + 1;
stack->stack = brealloc(stack->stack,
new_size * sizeof(*stack->stack));
for (i = stack->size; i < new_size; i++)
stack->stack[i].array = NULL;
stack->size = new_size;
}
}
void
stack_pushnumber(struct stack *stack, struct number *b)
{
stack_grow(stack);
stack->stack[stack->sp].type = BCODE_NUMBER;
stack->stack[stack->sp].u.num = b;
}
void
stack_pushstring(struct stack *stack, char *string)
{
stack_grow(stack);
stack->stack[stack->sp].type = BCODE_STRING;
stack->stack[stack->sp].u.string = string;
}
void
stack_push(struct stack *stack, struct value *v)
{
switch (v->type) {
case BCODE_NONE:
stack_grow(stack);
stack->stack[stack->sp].type = BCODE_NONE;
break;
case BCODE_NUMBER:
stack_pushnumber(stack, v->u.num);
break;
case BCODE_STRING:
stack_pushstring(stack, v->u.string);
break;
}
stack->stack[stack->sp].array = v->array == NULL ?
NULL : array_dup(v->array);
}
struct value *
stack_tos(const struct stack *stack)
{
if (stack->sp == -1)
return (NULL);
return &stack->stack[stack->sp];
}
void
stack_set_tos(struct stack *stack, struct value *v)
{
if (stack->sp == -1)
stack_push(stack, v);
else {
stack_free_value(&stack->stack[stack->sp]);
stack->stack[stack->sp] = *v;
stack->stack[stack->sp].array = v->array == NULL ?
NULL : array_dup(v->array);
}
}
struct value *
stack_pop(struct stack *stack)
{
if (stack_empty(stack))
return (NULL);
return &stack->stack[stack->sp--];
}
struct number *
stack_popnumber(struct stack *stack)
{
if (stack_empty(stack))
return (NULL);
if (stack->stack[stack->sp].array != NULL) {
array_free(stack->stack[stack->sp].array);
stack->stack[stack->sp].array = NULL;
}
if (stack->stack[stack->sp].type != BCODE_NUMBER) {
warnx("not a number"); /* XXX remove */
return (NULL);
}
return stack->stack[stack->sp--].u.num;
}
char *
stack_popstring(struct stack *stack)
{
if (stack_empty(stack))
return (NULL);
if (stack->stack[stack->sp].array != NULL) {
array_free(stack->stack[stack->sp].array);
stack->stack[stack->sp].array = NULL;
}
if (stack->stack[stack->sp].type != BCODE_STRING) {
warnx("not a string"); /* XXX remove */
return (NULL);
}
return stack->stack[stack->sp--].u.string;
}
void
stack_clear(struct stack *stack)
{
while (stack->sp >= 0) {
stack_free_value(&stack->stack[stack->sp--]);
}
free(stack->stack);
stack_init(stack);
}
void
stack_print(FILE *f, const struct stack *stack, const char *prefix, u_int base)
{
ssize_t i;
for (i = stack->sp; i >= 0; i--) {
print_value(f, &stack->stack[i], prefix, base);
putc('\n', f);
}
}
static struct array *
array_new(void)
{
struct array *a;
a = bmalloc(sizeof(*a));
a->data = NULL;
a->size = 0;
return a;
}
static __inline void
array_free(struct array *a)
{
size_t i;
if (a == NULL)
return;
for (i = 0; i < a->size; i++)
stack_free_value(&a->data[i]);
free(a->data);
free(a);
}
static struct array *
array_dup(const struct array *a)
{
struct array *n;
size_t i;
if (a == NULL)
return (NULL);
n = array_new();
array_grow(n, a->size);
for (i = 0; i < a->size; i++)
stack_dup_value(&a->data[i], &n->data[i]);
return (n);
}
static __inline void
array_grow(struct array *array, size_t newsize)
{
size_t i;
array->data = brealloc(array->data, newsize * sizeof(*array->data));
for (i = array->size; i < newsize; i++) {
array->data[i].type = BCODE_NONE;
array->data[i].array = NULL;
}
array->size = newsize;
}
static __inline void
array_assign(struct array *array, size_t i, const struct value *v)
{
if (i >= array->size)
array_grow(array, i + 1);
stack_free_value(&array->data[i]);
array->data[i] = *v;
}
static __inline struct value *
array_retrieve(const struct array *array, size_t i)
{
if (i >= array->size)
return (NULL);
return &array->data[i];
}
void
frame_assign(struct stack *stack, size_t i, const struct value *v)
{
struct array *a;
struct value n;
if (stack->sp == -1) {
n.type = BCODE_NONE;
n.array = NULL;
stack_push(stack, &n);
}
a = stack->stack[stack->sp].array;
if (a == NULL)
a = stack->stack[stack->sp].array = array_new();
array_assign(a, i, v);
}
struct value *
frame_retrieve(const struct stack *stack, size_t i)
{
struct array *a;
if (stack->sp == -1)
return (NULL);
a = stack->stack[stack->sp].array;
if (a == NULL)
a = stack->stack[stack->sp].array = array_new();
return array_retrieve(a, i);
}