1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-12 09:58:36 +00:00
freebsd/usr.bin/dc/bcode.c
Gabor Kovesdan fdf1f88bce 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
2010-01-20 21:30:52 +00:00

1782 lines
31 KiB
C

/* $OpenBSD: bcode.c,v 1.40 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 <limits.h>
#include <openssl/ssl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "extern.h"
BIGNUM zero;
#define __inline
#define MAX_ARRAY_INDEX 2048
#define READSTACK_SIZE 8
#define NO_ELSE -2 /* -1 is EOF */
#define REG_ARRAY_SIZE_SMALL (UCHAR_MAX + 1)
#define REG_ARRAY_SIZE_BIG (UCHAR_MAX + 1 + USHRT_MAX + 1)
struct bmachine {
struct stack stack;
u_int scale;
u_int obase;
u_int ibase;
size_t readsp;
bool extended_regs;
size_t reg_array_size;
struct stack *reg;
volatile sig_atomic_t interrupted;
struct source *readstack;
size_t readstack_sz;
};
static struct bmachine bmachine;
static void sighandler(int);
static __inline int readch(void);
static __inline void unreadch(void);
static __inline char *readline(void);
static __inline void src_free(void);
static __inline u_int max(u_int, u_int);
static u_long get_ulong(struct number *);
static __inline void push_number(struct number *);
static __inline void push_string(char *);
static __inline void push(struct value *);
static __inline struct value *tos(void);
static __inline struct number *pop_number(void);
static __inline char *pop_string(void);
static __inline void clear_stack(void);
static __inline void print_tos(void);
static void pop_print(void);
static void pop_printn(void);
static __inline void print_stack(void);
static __inline void dup(void);
static void swap(void);
static void drop(void);
static void get_scale(void);
static void set_scale(void);
static void get_obase(void);
static void set_obase(void);
static void get_ibase(void);
static void set_ibase(void);
static void stackdepth(void);
static void push_scale(void);
static u_int count_digits(const struct number *);
static void num_digits(void);
static void to_ascii(void);
static void push_line(void);
static void comment(void);
static void bexec(char *);
static void badd(void);
static void bsub(void);
static void bmul(void);
static void bdiv(void);
static void bmod(void);
static void bdivmod(void);
static void bexp(void);
static bool bsqrt_stop(const BIGNUM *, const BIGNUM *, u_int *);
static void bsqrt(void);
static void not(void);
static void equal_numbers(void);
static void less_numbers(void);
static void lesseq_numbers(void);
static void equal(void);
static void not_equal(void);
static void less(void);
static void not_less(void);
static void greater(void);
static void not_greater(void);
static void not_compare(void);
static bool compare_numbers(enum bcode_compare, struct number *,
struct number *);
static void compare(enum bcode_compare);
static int readreg(void);
static void load(void);
static void store(void);
static void load_stack(void);
static void store_stack(void);
static void load_array(void);
static void store_array(void);
static void nop(void);
static void quit(void);
static void quitN(void);
static void skipN(void);
static void skip_until_mark(void);
static void parse_number(void);
static void unknown(void);
static void eval_string(char *);
static void eval_line(void);
static void eval_tos(void);
typedef void (*opcode_function)(void);
struct jump_entry {
u_char ch;
opcode_function f;
};
static opcode_function jump_table[UCHAR_MAX];
static const struct jump_entry jump_table_data[] = {
{ ' ', nop },
{ '!', not_compare },
{ '#', comment },
{ '%', bmod },
{ '(', less_numbers },
{ '*', bmul },
{ '+', badd },
{ '-', bsub },
{ '.', parse_number },
{ '/', bdiv },
{ '0', parse_number },
{ '1', parse_number },
{ '2', parse_number },
{ '3', parse_number },
{ '4', parse_number },
{ '5', parse_number },
{ '6', parse_number },
{ '7', parse_number },
{ '8', parse_number },
{ '9', parse_number },
{ ':', store_array },
{ ';', load_array },
{ '<', less },
{ '=', equal },
{ '>', greater },
{ '?', eval_line },
{ 'A', parse_number },
{ 'B', parse_number },
{ 'C', parse_number },
{ 'D', parse_number },
{ 'E', parse_number },
{ 'F', parse_number },
{ 'G', equal_numbers },
{ 'I', get_ibase },
{ 'J', skipN },
{ 'K', get_scale },
{ 'L', load_stack },
{ 'M', nop },
{ 'N', not },
{ 'O', get_obase },
{ 'P', pop_print },
{ 'Q', quitN },
{ 'R', drop },
{ 'S', store_stack },
{ 'X', push_scale },
{ 'Z', num_digits },
{ '[', push_line },
{ '\f', nop },
{ '\n', nop },
{ '\r', nop },
{ '\t', nop },
{ '^', bexp },
{ '_', parse_number },
{ 'a', to_ascii },
{ 'c', clear_stack },
{ 'd', dup },
{ 'f', print_stack },
{ 'i', set_ibase },
{ 'k', set_scale },
{ 'l', load },
{ 'n', pop_printn },
{ 'o', set_obase },
{ 'p', print_tos },
{ 'q', quit },
{ 'r', swap },
{ 's', store },
{ 'v', bsqrt },
{ 'x', eval_tos },
{ 'z', stackdepth },
{ '{', lesseq_numbers },
{ '~', bdivmod }
};
#define JUMP_TABLE_DATA_SIZE \
(sizeof(jump_table_data)/sizeof(jump_table_data[0]))
static void
sighandler(int ignored)
{
switch (ignored)
{
default:
bmachine.interrupted = true;
}
}
void
init_bmachine(bool extended_registers)
{
unsigned int i;
bmachine.extended_regs = extended_registers;
bmachine.reg_array_size = bmachine.extended_regs ?
REG_ARRAY_SIZE_BIG : REG_ARRAY_SIZE_SMALL;
bmachine.reg = calloc(bmachine.reg_array_size,
sizeof(bmachine.reg[0]));
if (bmachine.reg == NULL)
err(1, NULL);
for (i = 0; i < UCHAR_MAX; i++)
jump_table[i] = unknown;
for (i = 0; i < JUMP_TABLE_DATA_SIZE; i++)
jump_table[jump_table_data[i].ch] = jump_table_data[i].f;
stack_init(&bmachine.stack);
for (i = 0; i < bmachine.reg_array_size; i++)
stack_init(&bmachine.reg[i]);
bmachine.readstack_sz = READSTACK_SIZE;
bmachine.readstack = calloc(sizeof(struct source),
bmachine.readstack_sz);
if (bmachine.readstack == NULL)
err(1, NULL);
bmachine.obase = bmachine.ibase = 10;
BN_init(&zero);
bn_check(BN_zero(&zero));
signal(SIGINT, sighandler);
}
/* Reset the things needed before processing a (new) file */
void
reset_bmachine(struct source *src)
{
bmachine.readsp = 0;
bmachine.readstack[0] = *src;
}
static __inline int
readch(void)
{
struct source *src = &bmachine.readstack[bmachine.readsp];
return (src->vtable->readchar(src));
}
static __inline void
unreadch(void)
{
struct source *src = &bmachine.readstack[bmachine.readsp];
src->vtable->unreadchar(src);
}
static __inline char *
readline(void)
{
struct source *src = &bmachine.readstack[bmachine.readsp];
return (src->vtable->readline(src));
}
static __inline void
src_free(void)
{
struct source *src = &bmachine.readstack[bmachine.readsp];
src->vtable->free(src);
}
#ifdef DEBUGGING
void
pn(const char *str, const struct number *n)
{
char *p = BN_bn2dec(n->number);
if (p == NULL)
err(1, "BN_bn2dec failed");
fputs(str, stderr);
fprintf(stderr, " %s (%u)\n" , p, n->scale);
OPENSSL_free(p);
}
void
pbn(const char *str, const BIGNUM *n)
{
char *p = BN_bn2dec(n);
if (p == NULL)
err(1, "BN_bn2dec failed");
fputs(str, stderr);
fprintf(stderr, " %s\n", p);
OPENSSL_free(p);
}
#endif
static __inline u_int
max(u_int a, u_int b)
{
return (a > b ? a : b);
}
static unsigned long factors[] = {
0, 10, 100, 1000, 10000, 100000, 1000000, 10000000,
100000000, 1000000000
};
void
scale_number(BIGNUM *n, int s)
{
unsigned int abs_scale;
if (s == 0)
return;
abs_scale = s > 0 ? s : -s;
if (abs_scale < sizeof(factors)/sizeof(factors[0])) {
if (s > 0)
bn_check(BN_mul_word(n, factors[abs_scale]));
else
BN_div_word(n, factors[abs_scale]);
} else {
BIGNUM *a, *p;
BN_CTX *ctx;
a = BN_new();
bn_checkp(a);
p = BN_new();
bn_checkp(p);
ctx = BN_CTX_new();
bn_checkp(ctx);
bn_check(BN_set_word(a, 10));
bn_check(BN_set_word(p, abs_scale));
bn_check(BN_exp(a, a, p, ctx));
if (s > 0)
bn_check(BN_mul(n, n, a, ctx));
else
bn_check(BN_div(n, NULL, n, a, ctx));
BN_CTX_free(ctx);
BN_free(a);
BN_free(p);
}
}
void
split_number(const struct number *n, BIGNUM *i, BIGNUM *f)
{
u_long rem;
bn_checkp(BN_copy(i, n->number));
if (n->scale == 0 && f != NULL)
bn_check(BN_zero(f));
else if (n->scale < sizeof(factors)/sizeof(factors[0])) {
rem = BN_div_word(i, factors[n->scale]);
if (f != NULL)
bn_check(BN_set_word(f, rem));
} else {
BIGNUM *a, *p;
BN_CTX *ctx;
a = BN_new();
bn_checkp(a);
p = BN_new();
bn_checkp(p);
ctx = BN_CTX_new();
bn_checkp(ctx);
bn_check(BN_set_word(a, 10));
bn_check(BN_set_word(p, n->scale));
bn_check(BN_exp(a, a, p, ctx));
bn_check(BN_div(i, f, n->number, a, ctx));
BN_CTX_free(ctx);
BN_free(a);
BN_free(p);
}
}
__inline void
normalize(struct number *n, u_int s)
{
scale_number(n->number, s - n->scale);
n->scale = s;
}
static u_long
get_ulong(struct number *n)
{
normalize(n, 0);
return (BN_get_word(n->number));
}
void
negate(struct number *n)
{
bn_check(BN_sub(n->number, &zero, n->number));
}
static __inline void
push_number(struct number *n)
{
stack_pushnumber(&bmachine.stack, n);
}
static __inline void
push_string(char *string)
{
stack_pushstring(&bmachine.stack, string);
}
static __inline void
push(struct value *v)
{
stack_push(&bmachine.stack, v);
}
static __inline struct value *
tos(void)
{
return (stack_tos(&bmachine.stack));
}
static __inline struct value *
pop(void)
{
return (stack_pop(&bmachine.stack));
}
static __inline struct number *
pop_number(void)
{
return (stack_popnumber(&bmachine.stack));
}
static __inline char *
pop_string(void)
{
return (stack_popstring(&bmachine.stack));
}
static __inline void
clear_stack(void)
{
stack_clear(&bmachine.stack);
}
static __inline void
print_stack(void)
{
stack_print(stdout, &bmachine.stack, "", bmachine.obase);
}
static __inline void
print_tos(void)
{
struct value *value = tos();
if (value != NULL) {
print_value(stdout, value, "", bmachine.obase);
putchar('\n');
}
else
warnx("stack empty");
}
static void
pop_print(void)
{
struct value *value = pop();
if (value != NULL) {
switch (value->type) {
case BCODE_NONE:
break;
case BCODE_NUMBER:
normalize(value->u.num, 0);
print_ascii(stdout, value->u.num);
fflush(stdout);
break;
case BCODE_STRING:
fputs(value->u.string, stdout);
fflush(stdout);
break;
}
stack_free_value(value);
}
}
static void
pop_printn(void)
{
struct value *value = pop();
if (value != NULL) {
print_value(stdout, value, "", bmachine.obase);
fflush(stdout);
stack_free_value(value);
}
}
static __inline void
dup(void)
{
stack_dup(&bmachine.stack);
}
static void
swap(void)
{
stack_swap(&bmachine.stack);
}
static void
drop(void)
{
struct value *v = pop();
if (v != NULL)
stack_free_value(v);
}
static void
get_scale(void)
{
struct number *n;
n = new_number();
bn_check(BN_set_word(n->number, bmachine.scale));
push_number(n);
}
static void
set_scale(void)
{
struct number *n;
u_long scale;
n = pop_number();
if (n != NULL) {
if (BN_cmp(n->number, &zero) < 0)
warnx("scale must be a nonnegative number");
else {
scale = get_ulong(n);
if (scale != BN_MASK2 && scale <= UINT_MAX)
bmachine.scale = (u_int)scale;
else
warnx("scale too large");
}
free_number(n);
}
}
static void
get_obase(void)
{
struct number *n;
n = new_number();
bn_check(BN_set_word(n->number, bmachine.obase));
push_number(n);
}
static void
set_obase(void)
{
struct number *n;
u_long base;
n = pop_number();
if (n != NULL) {
base = get_ulong(n);
if (base != BN_MASK2 && base > 1 && base <= UINT_MAX)
bmachine.obase = (u_int)base;
else
warnx("output base must be a number greater than 1");
free_number(n);
}
}
static void
get_ibase(void)
{
struct number *n;
n = new_number();
bn_check(BN_set_word(n->number, bmachine.ibase));
push_number(n);
}
static void
set_ibase(void)
{
struct number *n;
u_long base;
n = pop_number();
if (n != NULL) {
base = get_ulong(n);
if (base != BN_MASK2 && 2 <= base && base <= 16)
bmachine.ibase = (u_int)base;
else
warnx("input base must be a number between 2 and 16 "
"(inclusive)");
free_number(n);
}
}
static void
stackdepth(void)
{
size_t i;
struct number *n;
i = stack_size(&bmachine.stack);
n = new_number();
bn_check(BN_set_word(n->number, i));
push_number(n);
}
static void
push_scale(void)
{
struct value *value;
u_int scale = 0;
struct number *n;
value = pop();
if (value != NULL) {
switch (value->type) {
case BCODE_NONE:
return;
case BCODE_NUMBER:
scale = value->u.num->scale;
break;
case BCODE_STRING:
break;
}
stack_free_value(value);
n = new_number();
bn_check(BN_set_word(n->number, scale));
push_number(n);
}
}
static u_int
count_digits(const struct number *n)
{
struct number *int_part, *fract_part;
u_int i;
if (BN_is_zero(n->number))
return (1);
int_part = new_number();
fract_part = new_number();
fract_part->scale = n->scale;
split_number(n, int_part->number, fract_part->number);
i = 0;
while (!BN_is_zero(int_part->number)) {
BN_div_word(int_part->number, 10);
i++;
}
free_number(int_part);
free_number(fract_part);
return (i + n->scale);
}
static void
num_digits(void)
{
struct value *value;
size_t digits;
struct number *n = NULL;
value = pop();
if (value != NULL) {
switch (value->type) {
case BCODE_NONE:
return;
case BCODE_NUMBER:
digits = count_digits(value->u.num);
n = new_number();
bn_check(BN_set_word(n->number, digits));
break;
case BCODE_STRING:
digits = strlen(value->u.string);
n = new_number();
bn_check(BN_set_word(n->number, digits));
break;
}
stack_free_value(value);
push_number(n);
}
}
static void
to_ascii(void)
{
char str[2];
struct value *value;
struct number *n;
value = pop();
if (value != NULL) {
str[1] = '\0';
switch (value->type) {
case BCODE_NONE:
return;
case BCODE_NUMBER:
n = value->u.num;
normalize(n, 0);
if (BN_num_bits(n->number) > 8)
bn_check(BN_mask_bits(n->number, 8));
str[0] = (char)BN_get_word(n->number);
break;
case BCODE_STRING:
str[0] = value->u.string[0];
break;
}
stack_free_value(value);
push_string(bstrdup(str));
}
}
static int
readreg(void)
{
int idx, ch1, ch2;
idx = readch();
if (idx == 0xff && bmachine.extended_regs) {
ch1 = readch();
ch2 = readch();
if (ch1 == EOF || ch2 == EOF) {
warnx("unexpected eof");
idx = -1;
} else
idx = (ch1 << 8) + ch2 + UCHAR_MAX + 1;
}
if (idx < 0 || (unsigned)idx >= bmachine.reg_array_size) {
warnx("internal error: reg num = %d", idx);
idx = -1;
}
return (idx);
}
static void
load(void)
{
int idx;
struct value *v, copy;
struct number *n;
idx = readreg();
if (idx >= 0) {
v = stack_tos(&bmachine.reg[idx]);
if (v == NULL) {
n = new_number();
bn_check(BN_zero(n->number));
push_number(n);
} else
push(stack_dup_value(v, &copy));
}
}
static void
store(void)
{
int idx;
struct value *val;
idx = readreg();
if (idx >= 0) {
val = pop();
if (val == NULL) {
return;
}
stack_set_tos(&bmachine.reg[idx], val);
}
}
static void
load_stack(void)
{
int idx;
struct stack *stack;
struct value *value;
idx = readreg();
if (idx >= 0) {
stack = &bmachine.reg[idx];
value = NULL;
if (stack_size(stack) > 0) {
value = stack_pop(stack);
}
if (value != NULL)
push(value);
else
warnx("stack register '%c' (0%o) is empty",
idx, idx);
}
}
static void
store_stack(void)
{
int idx;
struct value *value;
idx = readreg();
if (idx >= 0) {
value = pop();
if (value == NULL)
return;
stack_push(&bmachine.reg[idx], value);
}
}
static void
load_array(void)
{
int reg;
struct number *inumber, *n;
u_long idx;
struct stack *stack;
struct value *v, copy;
reg = readreg();
if (reg >= 0) {
inumber = pop_number();
if (inumber == NULL)
return;
idx = get_ulong(inumber);
if (BN_cmp(inumber->number, &zero) < 0)
warnx("negative idx");
else if (idx == BN_MASK2 || idx > MAX_ARRAY_INDEX)
warnx("idx too big");
else {
stack = &bmachine.reg[reg];
v = frame_retrieve(stack, idx);
if (v == NULL || v->type == BCODE_NONE) {
n = new_number();
bn_check(BN_zero(n->number));
push_number(n);
}
else
push(stack_dup_value(v, &copy));
}
free_number(inumber);
}
}
static void
store_array(void)
{
int reg;
struct number *inumber;
u_long idx;
struct value *value;
struct stack *stack;
reg = readreg();
if (reg >= 0) {
inumber = pop_number();
if (inumber == NULL)
return;
value = pop();
if (value == NULL) {
free_number(inumber);
return;
}
idx = get_ulong(inumber);
if (BN_cmp(inumber->number, &zero) < 0) {
warnx("negative idx");
stack_free_value(value);
} else if (idx == BN_MASK2 || idx > MAX_ARRAY_INDEX) {
warnx("idx too big");
stack_free_value(value);
} else {
stack = &bmachine.reg[reg];
frame_assign(stack, idx, value);
}
free_number(inumber);
}
}
static void
push_line(void)
{
push_string(read_string(&bmachine.readstack[bmachine.readsp]));
}
static void
comment(void)
{
free(readline());
}
static void
bexec(char *line)
{
system(line);
free(line);
}
static void
badd(void)
{
struct number *a, *b;
struct number *r;
a = pop_number();
if (a == NULL) {
return;
}
b = pop_number();
if (b == NULL) {
push_number(a);
return;
}
r = new_number();
r->scale = max(a->scale, b->scale);
if (r->scale > a->scale)
normalize(a, r->scale);
else if (r->scale > b->scale)
normalize(b, r->scale);
bn_check(BN_add(r->number, a->number, b->number));
push_number(r);
free_number(a);
free_number(b);
}
static void
bsub(void)
{
struct number *a, *b;
struct number *r;
a = pop_number();
if (a == NULL) {
return;
}
b = pop_number();
if (b == NULL) {
push_number(a);
return;
}
r = new_number();
r->scale = max(a->scale, b->scale);
if (r->scale > a->scale)
normalize(a, r->scale);
else if (r->scale > b->scale)
normalize(b, r->scale);
bn_check(BN_sub(r->number, b->number, a->number));
push_number(r);
free_number(a);
free_number(b);
}
void
bmul_number(struct number *r, struct number *a, struct number *b)
{
BN_CTX *ctx;
/* Create copies of the scales, since r might be equal to a or b */
u_int ascale = a->scale;
u_int bscale = b->scale;
u_int rscale = ascale + bscale;
ctx = BN_CTX_new();
bn_checkp(ctx);
bn_check(BN_mul(r->number, a->number, b->number, ctx));
BN_CTX_free(ctx);
if (rscale > bmachine.scale && rscale > ascale && rscale > bscale) {
r->scale = rscale;
normalize(r, max(bmachine.scale, max(ascale, bscale)));
} else
r->scale = rscale;
}
static void
bmul(void)
{
struct number *a, *b;
struct number *r;
a = pop_number();
if (a == NULL) {
return;
}
b = pop_number();
if (b == NULL) {
push_number(a);
return;
}
r = new_number();
bmul_number(r, a, b);
push_number(r);
free_number(a);
free_number(b);
}
static void
bdiv(void)
{
struct number *a, *b;
struct number *r;
u_int scale;
BN_CTX *ctx;
a = pop_number();
if (a == NULL) {
return;
}
b = pop_number();
if (b == NULL) {
push_number(a);
return;
}
r = new_number();
r->scale = bmachine.scale;
scale = max(a->scale, b->scale);
if (BN_is_zero(a->number))
warnx("divide by zero");
else {
normalize(a, scale);
normalize(b, scale + r->scale);
ctx = BN_CTX_new();
bn_checkp(ctx);
bn_check(BN_div(r->number, NULL, b->number, a->number, ctx));
BN_CTX_free(ctx);
}
push_number(r);
free_number(a);
free_number(b);
}
static void
bmod(void)
{
struct number *a, *b;
struct number *r;
u_int scale;
BN_CTX *ctx;
a = pop_number();
if (a == NULL) {
return;
}
b = pop_number();
if (b == NULL) {
push_number(a);
return;
}
r = new_number();
scale = max(a->scale, b->scale);
r->scale = max(b->scale, a->scale + bmachine.scale);
if (BN_is_zero(a->number))
warnx("remainder by zero");
else {
normalize(a, scale);
normalize(b, scale + bmachine.scale);
ctx = BN_CTX_new();
bn_checkp(ctx);
bn_check(BN_mod(r->number, b->number, a->number, ctx));
BN_CTX_free(ctx);
}
push_number(r);
free_number(a);
free_number(b);
}
static void
bdivmod(void)
{
struct number *a, *b;
struct number *rdiv, *rmod;
u_int scale;
BN_CTX *ctx;
a = pop_number();
if (a == NULL) {
return;
}
b = pop_number();
if (b == NULL) {
push_number(a);
return;
}
rdiv = new_number();
rmod = new_number();
rdiv->scale = bmachine.scale;
rmod->scale = max(b->scale, a->scale + bmachine.scale);
scale = max(a->scale, b->scale);
if (BN_is_zero(a->number))
warnx("divide by zero");
else {
normalize(a, scale);
normalize(b, scale + bmachine.scale);
ctx = BN_CTX_new();
bn_checkp(ctx);
bn_check(BN_div(rdiv->number, rmod->number,
b->number, a->number, ctx));
BN_CTX_free(ctx);
}
push_number(rdiv);
push_number(rmod);
free_number(a);
free_number(b);
}
static void
bexp(void)
{
struct number *a, *p;
struct number *r;
bool neg;
u_int scale;
p = pop_number();
if (p == NULL) {
return;
}
a = pop_number();
if (a == NULL) {
push_number(p);
return;
}
if (p->scale != 0)
warnx("Runtime warning: non-zero scale in exponent");
normalize(p, 0);
neg = false;
if (BN_cmp(p->number, &zero) < 0) {
neg = true;
negate(p);
scale = bmachine.scale;
} else {
/* Posix bc says min(a.scale * b, max(a.scale, scale) */
u_long b;
u_int m;
b = BN_get_word(p->number);
m = max(a->scale, bmachine.scale);
scale = a->scale * (u_int)b;
if (scale > m || (a->scale > 0 && (b == BN_MASK2 ||
b > UINT_MAX)))
scale = m;
}
if (BN_is_zero(p->number)) {
r = new_number();
bn_check(BN_one(r->number));
normalize(r, scale);
} else {
while (!BN_is_bit_set(p->number, 0)) {
bmul_number(a, a, a);
bn_check(BN_rshift1(p->number, p->number));
}
r = dup_number(a);
normalize(r, scale);
bn_check(BN_rshift1(p->number, p->number));
while (!BN_is_zero(p->number)) {
bmul_number(a, a, a);
if (BN_is_bit_set(p->number, 0))
bmul_number(r, r, a);
bn_check(BN_rshift1(p->number, p->number));
}
if (neg) {
BN_CTX *ctx;
BIGNUM *one;
one = BN_new();
bn_checkp(one);
bn_check(BN_one(one));
ctx = BN_CTX_new();
bn_checkp(ctx);
scale_number(one, r->scale + scale);
normalize(r, scale);
bn_check(BN_div(r->number, NULL, one, r->number, ctx));
BN_free(one);
BN_CTX_free(ctx);
} else
normalize(r, scale);
}
push_number(r);
free_number(a);
free_number(p);
}
static bool
bsqrt_stop(const BIGNUM *x, const BIGNUM *y, u_int *onecount)
{
BIGNUM *r;
bool ret;
r = BN_new();
bn_checkp(r);
bn_check(BN_sub(r, x, y));
if (BN_is_one(r))
(*onecount)++;
ret = BN_is_zero(r);
BN_free(r);
return (ret || *onecount > 1);
}
static void
bsqrt(void)
{
struct number *n;
struct number *r;
BIGNUM *x, *y;
u_int scale, onecount;
BN_CTX *ctx;
onecount = 0;
n = pop_number();
if (n == NULL) {
return;
}
if (BN_is_zero(n->number)) {
r = new_number();
push_number(r);
} else if (BN_cmp(n->number, &zero) < 0)
warnx("square root of negative number");
else {
scale = max(bmachine.scale, n->scale);
normalize(n, 2*scale);
x = BN_dup(n->number);
bn_checkp(x);
bn_check(BN_rshift(x, x, BN_num_bits(x)/2));
y = BN_new();
bn_checkp(y);
ctx = BN_CTX_new();
bn_checkp(ctx);
for (;;) {
bn_checkp(BN_copy(y, x));
bn_check(BN_div(x, NULL, n->number, x, ctx));
bn_check(BN_add(x, x, y));
bn_check(BN_rshift1(x, x));
if (bsqrt_stop(x, y, &onecount))
break;
}
r = bmalloc(sizeof(*r));
r->scale = scale;
r->number = y;
BN_free(x);
BN_CTX_free(ctx);
push_number(r);
}
free_number(n);
}
static void
not(void)
{
struct number *a;
a = pop_number();
if (a == NULL) {
return;
}
a->scale = 0;
bn_check(BN_set_word(a->number, BN_get_word(a->number) ? 0 : 1));
push_number(a);
}
static void
equal(void)
{
compare(BCODE_EQUAL);
}
static void
equal_numbers(void)
{
struct number *a, *b, *r;
a = pop_number();
if (a == NULL) {
return;
}
b = pop_number();
if (b == NULL) {
push_number(a);
return;
}
r = new_number();
bn_check(BN_set_word(r->number,
compare_numbers(BCODE_EQUAL, a, b) ? 1 : 0));
push_number(r);
}
static void
less_numbers(void)
{
struct number *a, *b, *r;
a = pop_number();
if (a == NULL) {
return;
}
b = pop_number();
if (b == NULL) {
push_number(a);
return;
}
r = new_number();
bn_check(BN_set_word(r->number,
compare_numbers(BCODE_LESS, a, b) ? 1 : 0));
push_number(r);
}
static void
lesseq_numbers(void)
{
struct number *a, *b, *r;
a = pop_number();
if (a == NULL) {
return;
}
b = pop_number();
if (b == NULL) {
push_number(a);
return;
}
r = new_number();
bn_check(BN_set_word(r->number,
compare_numbers(BCODE_NOT_GREATER, a, b) ? 1 : 0));
push_number(r);
}
static void
not_equal(void)
{
compare(BCODE_NOT_EQUAL);
}
static void
less(void)
{
compare(BCODE_LESS);
}
static void
not_compare(void)
{
switch (readch()) {
case '<':
not_less();
break;
case '>':
not_greater();
break;
case '=':
not_equal();
break;
default:
unreadch();
bexec(readline());
break;
}
}
static void
not_less(void)
{
compare(BCODE_NOT_LESS);
}
static void
greater(void)
{
compare(BCODE_GREATER);
}
static void
not_greater(void)
{
compare(BCODE_NOT_GREATER);
}
static bool
compare_numbers(enum bcode_compare type, struct number *a, struct number *b)
{
u_int scale;
int cmp;
scale = max(a->scale, b->scale);
if (scale > a->scale)
normalize(a, scale);
else if (scale > b->scale)
normalize(b, scale);
cmp = BN_cmp(a->number, b->number);
free_number(a);
free_number(b);
switch (type) {
case BCODE_EQUAL:
return (cmp == 0);
case BCODE_NOT_EQUAL:
return (cmp != 0);
case BCODE_LESS:
return (cmp < 0);
case BCODE_NOT_LESS:
return (cmp >= 0);
case BCODE_GREATER:
return (cmp > 0);
case BCODE_NOT_GREATER:
return (cmp <= 0);
}
return (false);
}
static void
compare(enum bcode_compare type)
{
int idx, elseidx;
struct number *a, *b;
bool ok;
struct value *v;
elseidx = NO_ELSE;
idx = readreg();
if (readch() == 'e')
elseidx = readreg();
else
unreadch();
a = pop_number();
if (a == NULL)
return;
b = pop_number();
if (b == NULL) {
push_number(a);
return;
}
ok = compare_numbers(type, a, b);
if (!ok && elseidx != NO_ELSE)
idx = elseidx;
if (idx >= 0 && (ok || (!ok && elseidx != NO_ELSE))) {
v = stack_tos(&bmachine.reg[idx]);
if (v == NULL)
warnx("register '%c' (0%o) is empty", idx, idx);
else {
switch(v->type) {
case BCODE_NONE:
warnx("register '%c' (0%o) is empty", idx, idx);
break;
case BCODE_NUMBER:
warn("eval called with non-string argument");
break;
case BCODE_STRING:
eval_string(bstrdup(v->u.string));
break;
}
}
}
}
static void
nop(void)
{
}
static void
quit(void)
{
if (bmachine.readsp < 2)
exit(0);
src_free();
bmachine.readsp--;
src_free();
bmachine.readsp--;
}
static void
quitN(void)
{
struct number *n;
u_long i;
n = pop_number();
if (n == NULL)
return;
i = get_ulong(n);
free_number(n);
if (i == BN_MASK2 || i == 0)
warnx("Q command requires a number >= 1");
else if (bmachine.readsp < i)
warnx("Q command argument exceeded string execution depth");
else {
while (i-- > 0) {
src_free();
bmachine.readsp--;
}
}
}
static void
skipN(void)
{
struct number *n;
u_long i;
n = pop_number();
if (n == NULL)
return;
i = get_ulong(n);
if (i == BN_MASK2)
warnx("J command requires a number >= 0");
else if (i > 0 && bmachine.readsp < i)
warnx("J command argument exceeded string execution depth");
else {
while (i-- > 0) {
src_free();
bmachine.readsp--;
}
skip_until_mark();
}
}
static void
skip_until_mark(void)
{
int ch;
for (;;) {
ch = readch();
switch (ch) {
case 'M':
return;
case EOF:
errx(1, "mark not found");
return;
case 'l':
case 'L':
case 's':
case 'S':
case ':':
case ';':
case '<':
case '>':
case '=':
readreg();
if (readch() == 'e')
readreg();
else
unreadch();
break;
case '[':
free(read_string(&bmachine.readstack[bmachine.readsp]));
break;
case '!':
switch (ch = readch()) {
case '<':
case '>':
case '=':
readreg();
if (readch() == 'e')
readreg();
else
unreadch();
break;
default:
free(readline());
break;
}
break;
default:
break;
}
}
}
static void
parse_number(void)
{
unreadch();
push_number(readnumber(&bmachine.readstack[bmachine.readsp],
bmachine.ibase));
}
static void
unknown(void)
{
int ch = bmachine.readstack[bmachine.readsp].lastchar;
warnx("%c (0%o) is unimplemented", ch, ch);
}
static void
eval_string(char *p)
{
int ch;
if (bmachine.readsp > 0) {
/* Check for tail call. Do not recurse in that case. */
ch = readch();
if (ch == EOF) {
src_free();
src_setstring(&bmachine.readstack[bmachine.readsp], p);
return;
} else
unreadch();
}
if (bmachine.readsp == bmachine.readstack_sz - 1) {
size_t newsz = bmachine.readstack_sz * 2;
struct source *stack;
stack = realloc(bmachine.readstack, newsz *
sizeof(struct source));
if (stack == NULL)
err(1, "recursion too deep");
bmachine.readstack_sz = newsz;
bmachine.readstack = stack;
}
src_setstring(&bmachine.readstack[++bmachine.readsp], p);
}
static void
eval_line(void)
{
/* Always read from stdin */
struct source in;
char *p;
clearerr(stdin);
src_setstream(&in, stdin);
p = (*in.vtable->readline)(&in);
eval_string(p);
}
static void
eval_tos(void)
{
char *p;
p = pop_string();
if (p == NULL)
return;
eval_string(p);
}
void
eval(void)
{
int ch;
for (;;) {
ch = readch();
if (ch == EOF) {
if (bmachine.readsp == 0)
return;
src_free();
bmachine.readsp--;
continue;
}
if (bmachine.interrupted) {
if (bmachine.readsp > 0) {
src_free();
bmachine.readsp--;
continue;
} else
bmachine.interrupted = false;
}
#ifdef DEBUGGING
fprintf(stderr, "# %c\n", ch);
stack_print(stderr, &bmachine.stack, "* ",
bmachine.obase);
fprintf(stderr, "%zd =>\n", bmachine.readsp);
#endif
if (0 <= ch && ch < (signed)UCHAR_MAX)
(*jump_table[ch])();
else
warnx("internal error: opcode %d", ch);
#ifdef DEBUGGING
stack_print(stderr, &bmachine.stack, "* ",
bmachine.obase);
fprintf(stderr, "%zd ==\n", bmachine.readsp);
#endif
}
}