freebsd-dev/usr.bin/dc/bcode.c
2018-09-19 07:08:27 +00:00

1776 lines
31 KiB
C

/* $OpenBSD: bcode.c,v 1.46 2014/10/08 03:59:56 doug 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"
/* #define DEBUGGING */
#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 source *readstack;
struct stack *reg;
struct stack stack;
u_int scale;
u_int obase;
u_int ibase;
size_t readsp;
size_t reg_array_size;
size_t readstack_sz;
bool extended_regs;
};
static struct bmachine bmachine;
static __inline int readch(void);
static __inline void unreadch(void);
static __inline char *readline(void);
static __inline void src_free(void);
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 print_err(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 },
{ 'e', print_err },
{ '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]))
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;
}
u_int
bmachine_scale(void)
{
return bmachine.scale;
}
/* 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 unsigned long factors[] = {
0, 10, 100, 1000, 10000, 100000, 1000000, 10000000,
100000000, 1000000000
};
/* Multiply n by 10^s */
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);
}
}
/* Change the scale of n to s. Reducing scale may truncate the mantissa */
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_set_negative(n->number, !BN_is_negative(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
print_err(void)
{
struct value *value = tos();
if (value != NULL) {
print_value(stderr, value, "", bmachine.obase);
(void)putc('\n', stderr);
}
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_is_negative(n->number))
warnx("scale must be a nonnegative number");
else {
scale = get_ulong(n);
if (scale != ULONG_MAX && 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 != ULONG_MAX && 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 != ULONG_MAX && 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)
{
struct number *n;
size_t i;
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 number *n;
struct value *value;
u_int scale = 0;
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 n->scale ? n->scale : 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 number *n = NULL;
struct value *value;
size_t digits;
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)
{
struct number *n;
struct value *value;
char str[2];
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 ch1, ch2, idx;
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)
{
struct number *n;
struct value *v;
struct value copy;
int idx;
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)
{
struct value *val;
int idx;
idx = readreg();
if (idx >= 0) {
val = pop();
if (val == NULL) {
return;
}
stack_set_tos(&bmachine.reg[idx], val);
}
}
static void
load_stack(void)
{
struct stack *stack;
struct value *value;
int idx;
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)
{
struct value *value;
int idx;
idx = readreg();
if (idx >= 0) {
value = pop();
if (value == NULL)
return;
stack_push(&bmachine.reg[idx], value);
}
}
static void
load_array(void)
{
struct number *inumber, *n;
struct stack *stack;
struct value *v;
struct value copy;
u_long idx;
int reg;
reg = readreg();
if (reg >= 0) {
inumber = pop_number();
if (inumber == NULL)
return;
idx = get_ulong(inumber);
if (BN_is_negative(inumber->number))
warnx("negative idx");
else if (idx == ULONG_MAX || 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)
{
struct number *inumber;
struct value *value;
struct stack *stack;
u_long idx;
int reg;
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_is_negative(inumber->number)) {
warnx("negative idx");
stack_free_value(value);
} else if (idx == ULONG_MAX || 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, *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, *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, u_int scale)
{
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);
r->scale = rscale;
if (rscale > bmachine.scale && rscale > ascale && rscale > bscale)
normalize(r, max(scale, max(ascale, bscale)));
}
static void
bmul(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();
bmul_number(r, a, b, bmachine.scale);
push_number(r);
free_number(a);
free_number(b);
}
static void
bdiv(void)
{
struct number *a, *b, *r;
a = pop_number();
if (a == NULL)
return;
b = pop_number();
if (b == NULL) {
push_number(a);
return;
}
r = div_number(b, a, bmachine.scale);
push_number(r);
free_number(a);
free_number(b);
}
static void
bmod(void)
{
struct number *a, *b, *r;
BN_CTX *ctx;
u_int scale;
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 = scale;
if (BN_is_zero(a->number))
warnx("remainder by zero");
else {
normalize(a, scale);
normalize(b, 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, *frac, *quotient, *rdiv, *remainder;
BN_CTX *ctx;
u_int scale;
a = pop_number();
if (a == NULL)
return;
b = pop_number();
if (b == NULL) {
push_number(a);
return;
}
rdiv = new_number();
quotient = new_number();
remainder = new_number();
scale = max(a->scale, b->scale);
rdiv->scale = 0;
remainder->scale = scale;
quotient->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);
ctx = BN_CTX_new();
bn_checkp(ctx);
/*
* Unlike other languages' divmod operations, dc is specified
* to return the remainder and the full quotient, rather than
* the remainder and the floored quotient. bn(3) has no
* function to calculate both. So we'll use BN_div to get the
* remainder and floored quotient, then calculate the full
* quotient from those.
*
* quotient = rdiv + remainder / divisor
*/
bn_check(BN_div(rdiv->number, remainder->number,
b->number, a->number, ctx));
frac = div_number(remainder, a, bmachine.scale);
normalize(rdiv, bmachine.scale);
normalize(remainder, scale);
bn_check(BN_add(quotient->number, rdiv->number, frac->number));
free_number(frac);
BN_CTX_free(ctx);
}
push_number(quotient);
push_number(remainder);
free_number(rdiv);
free_number(a);
free_number(b);
}
static void
bexp(void)
{
struct number *a, *p;
struct number *r;
bool neg;
u_int rscale;
p = pop_number();
if (p == NULL)
return;
a = pop_number();
if (a == NULL) {
push_number(p);
return;
}
if (p->scale != 0) {
BIGNUM *i, *f;
i = BN_new();
bn_checkp(i);
f = BN_new();
bn_checkp(f);
split_number(p, i, f);
if (!BN_is_zero(f))
warnx("Runtime warning: non-zero fractional part in exponent");
BN_free(i);
BN_free(f);
}
normalize(p, 0);
neg = false;
if (BN_is_negative(p->number)) {
neg = true;
negate(p);
rscale = 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);
rscale = a->scale * (u_int)b;
if (rscale > m || (a->scale > 0 && (b == ULONG_MAX ||
b > UINT_MAX)))
rscale = m;
}
if (BN_is_zero(p->number)) {
r = new_number();
bn_check(BN_one(r->number));
normalize(r, rscale);
} else {
u_int ascale, mscale;
ascale = a->scale;
while (!BN_is_bit_set(p->number, 0)) {
ascale *= 2;
bmul_number(a, a, a, ascale);
bn_check(BN_rshift1(p->number, p->number));
}
r = dup_number(a);
bn_check(BN_rshift1(p->number, p->number));
mscale = ascale;
while (!BN_is_zero(p->number)) {
ascale *= 2;
bmul_number(a, a, a, ascale);
if (BN_is_bit_set(p->number, 0)) {
mscale += ascale;
bmul_number(r, r, a, mscale);
}
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 + rscale);
if (BN_is_zero(r->number))
warnx("divide by zero");
else
bn_check(BN_div(r->number, NULL, one,
r->number, ctx));
BN_free(one);
BN_CTX_free(ctx);
r->scale = rscale;
} else
normalize(r, rscale);
}
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, *r;
BIGNUM *x, *y;
BN_CTX *ctx;
u_int onecount, scale;
onecount = 0;
n = pop_number();
if (n == NULL)
return;
if (BN_is_zero(n->number)) {
r = new_number();
push_number(r);
} else if (BN_is_negative(n->number))
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)
{
struct number *a, *b;
struct value *v;
int idx, elseidx;
bool ok;
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 == ULONG_MAX || 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 == ULONG_MAX)
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)
{
for (;;) {
switch (readch()) {
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 (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, bmachine.scale));
}
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 = reallocarray(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)
eval_string(p);
}
void
eval(void)
{
int ch;
for (;;) {
ch = readch();
if (ch == EOF) {
if (bmachine.readsp == 0)
return;
src_free();
bmachine.readsp--;
continue;
}
#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
}
}