diff options
author | Otto Moerbeek <otto@cvs.openbsd.org> | 2003-09-19 17:58:26 +0000 |
---|---|---|
committer | Otto Moerbeek <otto@cvs.openbsd.org> | 2003-09-19 17:58:26 +0000 |
commit | e7be052f8750f1f09aa7d6d1911ba6f583f00c0e (patch) | |
tree | 2cd0ab7174b8f7f74474f5fd8be26eb9fa4c06a2 /usr.bin | |
parent | 72b36c1cb795050afed11afb38f1654f5c4474cc (diff) |
A complete, BSD licensed rewrite of dc(1), using the bn(3) big number routines.
ok toby@ deraadt@
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/dc/Makefile | 8 | ||||
-rw-r--r-- | usr.bin/dc/bcode.c | 1392 | ||||
-rw-r--r-- | usr.bin/dc/bcode.h | 80 | ||||
-rw-r--r-- | usr.bin/dc/dc.c | 71 | ||||
-rw-r--r-- | usr.bin/dc/extern.h | 61 | ||||
-rw-r--r-- | usr.bin/dc/inout.c | 381 | ||||
-rw-r--r-- | usr.bin/dc/mem.c | 108 | ||||
-rw-r--r-- | usr.bin/dc/stack.c | 359 |
8 files changed, 2460 insertions, 0 deletions
diff --git a/usr.bin/dc/Makefile b/usr.bin/dc/Makefile new file mode 100644 index 00000000000..a97c5c2255f --- /dev/null +++ b/usr.bin/dc/Makefile @@ -0,0 +1,8 @@ +# $OpenBSD: Makefile,v 1.1 2003/09/19 17:58:25 otto Exp $ + +PROG= dc +SRCS= dc.c bcode.c inout.c mem.c stack.c +COPTS+= -Wall +LDADD= -lcrypto + +.include <bsd.prog.mk> diff --git a/usr.bin/dc/bcode.c b/usr.bin/dc/bcode.c new file mode 100644 index 00000000000..320ec49c088 --- /dev/null +++ b/usr.bin/dc/bcode.c @@ -0,0 +1,1392 @@ +/* $OpenBSD: bcode.c,v 1.1 2003/09/19 17:58: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. + */ + +#ifndef lint +static const char rcsid[] = "$OpenBSD: bcode.c,v 1.1 2003/09/19 17:58:25 otto Exp $"; +#endif /* not lint */ + +#include <ssl/ssl.h> +#include <err.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "extern.h" + +BIGNUM zero; +static bool trace = false; + +#define MAX_ARRAY_INDEX 2048 +#define MAX_RECURSION 100 + +struct bmachine { + struct stack stack; + u_int scale; + u_int obase; + u_int ibase; + int readsp; + struct stack reg[UCHAR_MAX]; + struct source readstack[MAX_RECURSION]; +}; + +static struct bmachine bmachine; + +static __inline int readch(void); +static __inline int 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 __inline void pop_print(void); +static __inline void print_stack(); +static __inline void dup(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 push_line(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 bexp(void); +static bool bsqrt_stop(const BIGNUM *, const BIGNUM *); +static void bsqrt(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 void compare(enum bcode_compare); +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 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[] = { + { '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 }, + { 'A', parse_number }, + { 'B', parse_number }, + { 'C', parse_number }, + { 'D', parse_number }, + { 'E', parse_number }, + { 'F', parse_number }, + { '_', parse_number }, + { '.', parse_number }, + { '+', badd }, + { '-', bsub }, + { '*', bmul }, + { '/', bdiv }, + { '%', bmod }, + { '^', bexp }, + { 's', store }, + { 'S', store_stack }, + { 'l', load }, + { 'L', load_stack }, + { 'd', dup }, + { 'p', print_tos }, + { 'P', pop_print }, + { 'f', print_stack }, + { 'x', eval_tos }, + { 'X', push_scale }, + { '[', push_line }, + { 'q', quit }, + { 'Q', quitN }, + { '<', less }, + { '>', greater }, + { '=', equal }, + { '!', not_compare }, + { 'v', bsqrt }, + { 'c', clear_stack }, + { 'i', set_ibase }, + { 'I', get_ibase }, + { 'o', set_obase }, + { 'O', get_obase }, + { 'k', set_scale }, + { 'K', get_scale }, + { 'z', stackdepth }, + { 'Z', num_digits }, + { '?', eval_line }, + { ';', load_array }, + { ':', store_array }, + { ' ', nop }, + { '\t', nop }, + { '\n', nop }, + { '\f', nop }, + { '\r', nop } +}; + +#define JUMP_TABLE_DATA_SIZE \ + (sizeof(jump_table_data)/sizeof(jump_table_data[0])) + +void +init_bmachine(void) +{ + int i; + + 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 < UCHAR_MAX; i++) + stack_init(&bmachine.reg[i]); + + bmachine.obase = bmachine.ibase = 10; + BN_init(&zero); + bn_check(BN_zero(&zero)); +} + +/* 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 int +unreadch(void) +{ + struct source *src = &bmachine.readstack[bmachine.readsp]; + + return 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); +} + +#if 1 +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) +{ + 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_zero(f); + else if (n->scale < sizeof(factors)/sizeof(factors[0])) { + rem = BN_div_word(i, factors[n->scale]); + if (f != NULL) + 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 __inline 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); + break; + case BCODE_STRING: + printf("%s", value->u.string); + break; + } + stack_free_value(value); + } +} + +static __inline void +dup(void) +{ + stack_dup(&bmachine.stack); +} + +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) + bmachine.scale = 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) + bmachine.obase = 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 = base; + else + warnx("input base must be a number between 2 and 16 " + "(inclusive)"); + free_number(n); + } +} + +static void +stackdepth(void) +{ + u_int 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; + u_int digits; + struct number *n; + + value = pop(); + if (value != NULL) { + switch (value->type) { + case BCODE_NONE: + break; + case BCODE_NUMBER: + digits = count_digits(value->u.num); + n = new_number(); + bn_check(BN_set_word(n->number, digits)); + /* free first, then reassign */ + BN_free(value->u.num->number); + push_number(n); + break; + case BCODE_STRING: + digits = strlen(value->u.string); + n = new_number(); + bn_check(BN_set_word(n->number, digits)); + /* free first, then reassign */ + free(value->u.string); + push_number(n); + break; + } + } +} + +static void +load(void) +{ + int index; + struct value *v, copy; + + index = readch(); + if (0 <= index && index < UCHAR_MAX) { + v = stack_tos(&bmachine.reg[index]); + if (v == NULL) + warnx("register '%c' (0%o) is empty", index, index); + else + push(stack_dup_value(v, ©)); + } else + warnx("internal error: reg num = %d", index); +} + +static void +store(void) +{ + int index; + struct value *val; + + index = readch(); + if (0 <= index && index < UCHAR_MAX) { + val = pop(); + if (val == NULL) { + return; + } + stack_set_tos(&bmachine.reg[index], val); + } else + warnx("internal error: reg num = %d", index); +} + +static void +load_stack(void) +{ + int index; + struct stack *stack; + struct value *value, copy; + + index = readch(); + if (0 <= index && index < UCHAR_MAX) { + stack = &bmachine.reg[index]; + value = NULL; + if (stack_size(stack) > 0) { + /*frame_free(stack);*/ + value = stack_pop(stack); + } + if (value != NULL) + push(stack_dup_value(value, ©)); + else + warnx("stack register '%c' (0%o) is empty", + index, index); + } else + warnx("internal error: reg num = %d", index); +} + +static void +store_stack(void) +{ + int index; + struct value *value; + + index = readch(); + if (0 <= index && index < UCHAR_MAX) { + value = pop(); + if (value == NULL) + return; + stack_push(&bmachine.reg[index], value); + } else + warnx("internal error: reg num = %d", index); +} + +static void +load_array(void) +{ + int reg; + struct number *inumber, *n; + u_long index; + struct stack *stack; + struct value *v, copy; + + reg = readch(); + if (0 <= reg && reg < UCHAR_MAX) { + inumber = pop_number(); + if (inumber == NULL) + return; + index = get_ulong(inumber); + if (BN_cmp(inumber->number, &zero) < 0) + warnx("negative index"); + else if (index == BN_MASK2 || index > MAX_ARRAY_INDEX) + warnx("index too big"); + else { + stack = &bmachine.reg[reg]; + v = frame_retrieve(stack, index); + if (v == NULL) { + n = new_number(); + bn_check(BN_zero(n->number)); + push_number(n); + } + else + push(stack_dup_value(v, ©)); + } + free_number(inumber); + } else + warnx("internal error: reg num = %d", reg); +} + +static void +store_array(void) +{ + int reg; + struct number *inumber; + u_long index; + struct value *value; + struct stack *stack; + + reg = readch(); + if (0 <= reg && reg < UCHAR_MAX) { + inumber = pop_number(); + value = pop(); + if (inumber == NULL) { + if (value != NULL) + stack_free_value(value); + return; + } + index = get_ulong(inumber); + if (BN_cmp(inumber->number, &zero) < 0) { + warnx("negative index"); + stack_free_value(value); + } else if (index == BN_MASK2 || index > MAX_ARRAY_INDEX) { + warnx("index too big"); + stack_free_value(value); + } else { + stack = &bmachine.reg[reg]; + frame_assign(stack, index, value); + } + free_number(inumber); + } else + warnx("internal error: reg num = %d", reg); +} + +static void +push_line(void) +{ + push_string(read_string(&bmachine.readstack[bmachine.readsp])); +} + +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(); + /* + * XXX gives incorrect results for scale > 0, but is AT&T and + * GNU compatible + */ + 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 +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 * b; + if (scale > m || b == BN_MASK2) + 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_one(one); + ctx = BN_CTX_new(); + bn_checkp(ctx); + r->scale = scale; + scale_number(one, r->scale); + bn_check(BN_div(r->number, NULL, one, r->number, ctx)); + BN_free(one); + BN_CTX_free(ctx); + } + } + push_number(r); + free_number(a); + free_number(p); +} + +static bool +bsqrt_stop(const BIGNUM *x, const BIGNUM *y) +{ + BIGNUM *r; + bool ret; + + r = BN_new(); + bn_checkp(r); + bn_check(BN_sub(r, x, y)); + ret = BN_is_one(r) || BN_is_zero(r); + BN_free(r); + return ret; +} + +static void +bsqrt(void) +{ + struct number *n; + struct number *r; + BIGNUM *x, *y; + u_int scale; + BN_CTX *ctx; + + 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)) + 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 +equal(void) +{ + compare(BCODE_EQUAL); +} + +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 void +compare(enum bcode_compare type) +{ + int index; + struct number *a, *b; + u_int scale; + int cmp; + bool ok; + struct value *v; + + index = readch(); + + a = pop_number(); + if (a == NULL) { + return; + } + b = pop_number(); + if (b == NULL) { + push_number(a); + return; + } + + scale = max(a->scale, b->scale); + if (scale > a->scale) + normalize(a, scale); + else if (scale > scale) + normalize(b, scale); + + cmp = BN_cmp(a->number, b->number); + + free_number(a); + free_number(b); + + ok = false; + switch (type) { + case BCODE_EQUAL: + ok = cmp == 0; + break; + case BCODE_NOT_EQUAL: + ok = cmp != 0; + break; + case BCODE_LESS: + ok = cmp < 0; + break; + case BCODE_NOT_LESS: + ok = cmp >= 0; + break; + case BCODE_GREATER: + ok = cmp > 0; + break; + case BCODE_NOT_GREATER: + ok = cmp <= 0; + break; + } + + if (ok) { + v = stack_tos(&bmachine.reg[index]); + if (v == NULL) + warn("stack empty"); + else { + switch(v->type) { + case BCODE_NONE: + warnx("register '%c' (0%o) is empty", + index, index); + 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); + 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 +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 == MAX_RECURSION) + errx(1, "recursion too deep"); + src_setstring(&bmachine.readstack[++bmachine.readsp], p); +} + +static void +eval_line(void) +{ + /* Always read from stdin */ + struct source in; + char *p; + + 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) + exit(0); + src_free(); + bmachine.readsp--; + continue; + } + if (trace) { + fprintf(stderr, "# %c\n", ch); + stack_print(stderr, &bmachine.stack, "* ", + bmachine.obase); + fprintf(stderr, "%d =>\n", bmachine.readsp); + } + + if (0 <= ch && ch < UCHAR_MAX) + (*jump_table[ch])(); + else + warnx("internal error: opcode %d", ch); + + if (trace) { + stack_print(stderr, &bmachine.stack, "* ", + bmachine.obase); + fprintf(stderr, "%d ==\n", bmachine.readsp); + } + } +} diff --git a/usr.bin/dc/bcode.h b/usr.bin/dc/bcode.h new file mode 100644 index 00000000000..a9eceafdb55 --- /dev/null +++ b/usr.bin/dc/bcode.h @@ -0,0 +1,80 @@ +#include <sys/types.h> +#include <ssl/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; + int sp; + int size; +}; + +struct source; + +struct vtable { + int (*readchar)(struct source *); + int (*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(void); +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; diff --git a/usr.bin/dc/dc.c b/usr.bin/dc/dc.c new file mode 100644 index 00000000000..70c6d3b3aa0 --- /dev/null +++ b/usr.bin/dc/dc.c @@ -0,0 +1,71 @@ +/* $OpenBSD: dc.c,v 1.1 2003/09/19 17:58: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. + */ + +#ifndef lint +static const char rcsid[] = "$OpenBSD: dc.c,v 1.1 2003/09/19 17:58:25 otto Exp $"; +#endif /* not lint */ + +#include <err.h> +#include <stdlib.h> +#include <string.h> + +#include "extern.h" + +static __dead void usage(void); + +extern char *__progname; + +static __dead void +usage(void) +{ + fprintf(stderr, "usage: %s [file]\n", __progname); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + FILE *file; + struct source src; + + if (argc > 2) + usage(); + + + init_bmachine(); + setlinebuf(stdout); + setlinebuf(stderr); + if (argc == 2 && strcmp(argv[1], "-") != 0) { + file = fopen(argv[1], "r"); + if (file == NULL) + err(1, "cannot open file %s", argv[1]); + src_setstream(&src, file); + reset_bmachine(&src); + eval(); + fclose(file); + } + /* + * BSD dc and Solaris dc continue with stdin after processing + * the file given as the argument. + */ + src_setstream(&src, stdin); + reset_bmachine(&src); + eval(); + + return 0; +} diff --git a/usr.bin/dc/extern.h b/usr.bin/dc/extern.h new file mode 100644 index 00000000000..3c0dc8de205 --- /dev/null +++ b/usr.bin/dc/extern.h @@ -0,0 +1,61 @@ +/* $OpenBSD: extern.h,v 1.1 2003/09/19 17:58: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 *); +int 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 *); */ diff --git a/usr.bin/dc/inout.c b/usr.bin/dc/inout.c new file mode 100644 index 00000000000..d3a9fb553df --- /dev/null +++ b/usr.bin/dc/inout.c @@ -0,0 +1,381 @@ +/* $OpenBSD: inout.c,v 1.1 2003/09/19 17:58: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. + */ + +#ifndef lint +static const char rcsid[] = "$OpenBSD: inout.c,v 1.1 2003/09/19 17:58:25 otto Exp $"; +#endif /* not lint */ + +#include <ssl/ssl.h> +#include <ctype.h> +#include <err.h> +#include <string.h> + +#include "extern.h" + +#define MAX_CHARS_PER_LINE 68 + +static int charCount; + + +static int src_getcharstream(struct source *); +static int src_ungetcharstream(struct source *); +static char *src_getlinestream(struct source *); +static void src_freestream(struct source *); +static int src_getcharstring(struct source *); +static int src_ungetcharstring(struct source *); +static char *src_getlinestring(struct source *); +static void src_freestring(struct source *); +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, + src_freestream +}; + +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 int +src_ungetcharstream(struct source *src) +{ + return ungetc(src->lastchar, src->u.stream); +} + +static void +src_freestream(struct source *src) +{ +} + +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 int +src_ungetcharstring(struct source *src) +{ + if (src->u.string.pos > 0) + return src->u.string.buf[--src->u.string.pos]; + else + return EOF; +} + +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 +putcharwrap(FILE *f, int ch) +{ + putc(ch, f); + if (++charCount > MAX_CHARS_PER_LINE) { + charCount = 0; + fputs("\\\n", f); + } +} + +static void +printwrap(FILE *f, const char *p) +{ + char buf[12]; + char *q = buf; + + snprintf(buf, sizeof(buf), "%s", p); + 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; + + 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)); + + /* work around a bug in BN_add_word: 0 += 0 is buggy.... */ + if (v > 0) + bn_check(BN_add_word(n->number, v)); + } + if (sign) + negate(n); + return n; +} + +char * +read_string(struct source *src) +{ + int count, i, sz, ch; + char *p; + + count = 1; + i = 0; + sz = 15; + p = bmalloc(sz + 1); + + while ((ch = (*src->vtable->readchar)(src)) != EOF) { + if (ch == '[') + count++; + else if (ch == ']') + count--; + if (count == 0) + break; + if (i == sz) { + sz *= 2; + p = brealloc(p, sz + 1); + } + 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, "cannot allocate string"); + } + 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; + int i; + struct stack stack; + char *p; + + 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; + charCount = 0; + 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_set_word(num_base->number, base); + BN_init(&mult); + BN_one(&mult); + BN_init(&stop); + 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_sub(fract_part->number, fract_part->number, + int_part->number); + printwrap(f, p); + free(p); + BN_mul_word(&mult, base); + } + free_number(num_base); + BN_free(&mult); + BN_free(&stop); + } + 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); +} diff --git a/usr.bin/dc/mem.c b/usr.bin/dc/mem.c new file mode 100644 index 00000000000..35a79d719c4 --- /dev/null +++ b/usr.bin/dc/mem.c @@ -0,0 +1,108 @@ +/* $OpenBSD: mem.c,v 1.1 2003/09/19 17:58: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. + */ + +#ifndef lint +static const char rcsid[] = "$OpenBSD: mem.c,v 1.1 2003/09/19 17:58:25 otto Exp $"; +#endif /* not lint */ + +#include <ssl/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, "cannot allocate number"); + 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, "malloc failed"); + return p; +} + +void * +brealloc(void *p, size_t sz) +{ + void *q; + + q = realloc(p, sz); + if (q == NULL) + err(1, "realloc failed"); + return q; +} + +char * +bstrdup(const char *p) +{ + char *q; + + q = strdup(p); + if (q == NULL) + err(1, "stdup failed"); + 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()); +} diff --git a/usr.bin/dc/stack.c b/usr.bin/dc/stack.c new file mode 100644 index 00000000000..4aa831271a6 --- /dev/null +++ b/usr.bin/dc/stack.c @@ -0,0 +1,359 @@ +/* $OpenBSD: stack.c,v 1.1 2003/09/19 17:58: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. + */ + +#ifndef lint +static const char rcsid[] = "$OpenBSD: stack.c,v 1.1 2003/09/19 17:58:25 otto Exp $"; +#endif /* not lint */ + +#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, "cannot dup string"); + break; + } + + copy->array = a->array == NULL ? NULL : array_dup(a->array); + + return copy; +} + +int +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, ©)); +} + +static void +stack_grow(struct stack *stack) +{ + int old_size, i; + + if (++stack->sp == stack->size) { + old_size = stack->size; + stack->size *= 2; + stack->stack = brealloc(stack->stack, + ++stack->size * sizeof(*stack->stack)); + for (i = old_size; i < stack->size; i++) + stack->stack[i].array = NULL; + } +} + +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) +{ + int 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) +{ + u_int 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; + u_int 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].array = NULL; + array->size = newsize; +} + +static __inline void +array_assign(struct array *array, size_t index, const struct value *v) +{ + if (index >= array->size) + array_grow(array, index+1); + array->data[index] = *v; +} + +static __inline struct value * +array_retrieve(const struct array *array, size_t index) +{ + if (index >= array->size) + return NULL; + return &array->data[index]; +} + +void +frame_assign(struct stack *stack, size_t index, 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, index, v); +} + +struct value * +frame_retrieve(const struct stack *stack, size_t index) +{ + struct array *a; + + a = stack->stack[stack->sp].array; + if (a == NULL) + a = stack->stack[stack->sp].array = array_new(); + return array_retrieve(a, index); +} + +/* +void +frame_free(struct stack *stack) +{ + struct array *a; + + a = stack->stack[stack->sp].array; + if (a != NULL) { + array_free(a); + stack->stack[stack->sp].array = NULL; + } +} +*/ |