diff options
Diffstat (limited to 'usr.bin/dc/bcode.c')
-rw-r--r-- | usr.bin/dc/bcode.c | 1775 |
1 files changed, 1775 insertions, 0 deletions
diff --git a/usr.bin/dc/bcode.c b/usr.bin/dc/bcode.c new file mode 100644 index 0000000..e80c635 --- /dev/null +++ b/usr.bin/dc/bcode.c @@ -0,0 +1,1775 @@ +/* $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 source *readstack; + struct stack *reg; + struct stack stack; + volatile sig_atomic_t interrupted; + 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 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) +{ + 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 (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, ©)); + } +} + +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_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, ©)); + } + 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_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, *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) +{ + 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, *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, *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(); + 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, *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 = 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, *rdiv, *rmod; + 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(); + 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, *r; + u_int scale; + bool neg; + + 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, *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_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) +{ + 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 == 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) +{ + + 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)); +} + +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 + } +} |