Index: head/etc/mtree/BSD.tests.dist =================================================================== --- head/etc/mtree/BSD.tests.dist (revision 326555) +++ head/etc/mtree/BSD.tests.dist (revision 326556) @@ -1,768 +1,770 @@ # $FreeBSD$ # # Please see the file src/etc/mtree/README before making changes to this file. # /set type=dir uname=root gname=wheel mode=0755 . bin cat .. chflags .. chmod .. date .. dd .. echo .. expr .. ln .. ls .. mkdir .. mv .. pax .. pkill .. pwait .. rmdir .. sh builtins .. errors .. execution .. expansion .. invocation .. parameters .. parser .. set-e .. .. sleep .. test .. .. cddl lib .. sbin .. usr.bin ctfconvert .. .. usr.sbin dtrace common aggs .. arithmetic .. arrays .. assocs .. begin .. bitfields .. buffering .. builtinvar .. cg .. clauses .. cpc .. decls .. drops .. dtraceUtil .. end .. env .. enum .. error .. exit .. fbtprovider .. funcs .. grammar .. include .. inline .. io .. ip .. java_api .. json .. lexer .. llquantize .. mdb .. mib .. misc .. multiaggs .. offsetof .. operators .. pid .. plockstat .. pointers .. pragma .. predicates .. preprocessor .. print .. printa .. printf .. privs .. probes .. proc .. profile-n .. providers .. raise .. rates .. safety .. scalars .. sched .. scripting .. sdt .. sizeof .. speculation .. stability .. stack .. stackdepth .. stop .. strlen .. strtoll .. struct .. sugar .. syscall .. sysevent .. tick-n .. trace .. tracemem .. translators .. typedef .. types .. uctf .. union .. usdt .. ustack .. vars .. version .. .. .. zfsd .. .. .. etc rc.d .. .. games .. gnu lib .. usr.bin diff .. .. .. lib atf libatf-c detail .. .. libatf-c++ detail .. .. test-programs .. .. libarchive .. libc c063 .. db .. gen execve .. posix_spawn .. .. hash data .. .. iconv .. inet .. locale .. net getaddrinfo data .. .. .. nss .. regex data .. .. resolv .. rpc .. ssp .. setjmp .. stdio .. stdlib .. string .. sys .. time .. tls dso .. .. termios .. ttyio .. .. libcam .. libcasper services cap_dns .. cap_grp .. cap_pwd .. cap_sysctl .. .. .. libcrypt .. libdevdctl .. libkvm .. libmp .. libnv .. libproc .. librt .. libsbuf .. libthr dlopen .. .. libutil .. libxo .. msun .. .. libexec atf atf-check .. atf-sh .. .. rtld-elf .. .. sbin dhclient .. devd .. growfs .. ifconfig .. mdconfig .. pfctl files .. .. .. secure lib .. libexec .. usr.bin .. usr.sbin .. .. share examples tests atf .. plain .. tap .. .. .. zoneinfo .. .. sys acl .. aio .. fifo .. file .. fs tmpfs .. .. geom class concat .. eli .. gate .. gpt .. mirror .. nop .. raid3 .. shsec .. stripe .. uzip etalon .. .. .. .. kern acct .. execve .. pipe .. .. kqueue libkqueue .. .. mac bsdextended .. portacl .. .. mqueue .. netinet .. netipsec tunnel .. .. netpfil pf .. .. opencrypto .. pjdfstest chflags .. chmod .. chown .. ftruncate .. granular .. link .. mkdir .. mkfifo .. mknod .. open .. rename .. rmdir .. symlink .. truncate .. unlink .. utimensat .. .. posixshm .. sys .. vfs .. vm .. .. usr.bin apply .. basename .. bmake archives fmt_44bsd .. fmt_44bsd_mod .. fmt_oldbsd .. .. basic t0 .. t1 .. t2 .. t3 .. .. execution ellipsis .. empty .. joberr .. plus .. .. shell builtin .. meta .. path .. path_select .. replace .. select .. .. suffixes basic .. src_wild1 .. src_wild2 .. .. syntax directive-t0 .. enl .. funny-targets .. semi .. .. sysmk t0 2 1 .. .. mk .. .. t1 2 1 .. .. mk .. .. t2 2 1 .. .. mk .. .. .. variables modifier_M .. modifier_t .. opt_V .. t0 .. .. .. bsdcat .. calendar .. cmp .. compress .. cpio .. col .. comm .. csplit .. cut .. + dc + .. diff .. dirname .. du .. file2c .. fold .. getconf .. grep .. gzip .. head .. hexdump .. ident .. indent .. join .. jot .. lastcomm .. limits .. m4 .. mkimg .. ncal .. opensm .. pr .. printf .. procstat .. rs .. sdiff .. sed regress.multitest.out .. .. soelim .. stat .. tail .. tar .. timeout .. tr .. truncate .. units .. uudecode .. uuencode .. uniq .. xargs .. xinstall .. xo .. yacc yacc .. .. .. usr.sbin chown .. etcupdate .. extattr .. fstyp .. makefs .. newsyslog .. nmtree .. pw .. rpcbind .. sa .. .. .. # vim: set expandtab ts=4 sw=4: Index: head/usr.bin/dc/Makefile =================================================================== --- head/usr.bin/dc/Makefile (revision 326555) +++ head/usr.bin/dc/Makefile (revision 326556) @@ -1,8 +1,13 @@ # $FreeBSD$ # $OpenBSD: Makefile,v 1.2 2006/11/26 11:31:09 deraadt Exp $ +.include + PROG= dc SRCS= dc.c bcode.c inout.c mem.c stack.c LIBADD= crypto + +HAS_TESTS= +SUBDIR.${MK_TESTS}+= tests .include Index: head/usr.bin/dc/bcode.c =================================================================== --- head/usr.bin/dc/bcode.c (revision 326555) +++ head/usr.bin/dc/bcode.c (revision 326556) @@ -1,1777 +1,1756 @@ /* $OpenBSD: bcode.c,v 1.46 2014/10/08 03:59:56 doug Exp $ */ /* * Copyright (c) 2003, Otto Moerbeek * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #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 __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 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 __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 }; +/* 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 != 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 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, ©)); } } 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 == 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_is_negative(inumber->number)) { 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, 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; - 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); + r = div_number(b, a, bmachine.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; 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 == BN_MASK2 || 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 == 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)); + 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 } } Index: head/usr.bin/dc/bcode.h =================================================================== --- head/usr.bin/dc/bcode.h (revision 326555) +++ head/usr.bin/dc/bcode.h (revision 326556) @@ -1,97 +1,104 @@ /* $FreeBSD$ */ /* $OpenBSD: bcode.h,v 1.7 2012/11/07 11:06:14 otto Exp $ */ /* * Copyright (c) 2003, Otto Moerbeek * * 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 #include struct number { BIGNUM *number; u_int scale; }; enum stacktype { BCODE_NONE, BCODE_NUMBER, BCODE_STRING }; enum bcode_compare { BCODE_EQUAL, BCODE_NOT_EQUAL, BCODE_LESS, BCODE_NOT_LESS, BCODE_GREATER, BCODE_NOT_GREATER }; struct array; struct value { union { struct number *num; char *string; } u; struct array *array; enum stacktype type; }; struct array { struct value *data; size_t size; }; struct stack { struct value *stack; ssize_t size; ssize_t sp; }; struct source; struct vtable { int (*readchar)(struct source *); void (*unreadchar)(struct source *); char *(*readline)(struct source *); void (*free)(struct source *); }; struct source { union { struct { u_char *buf; size_t pos; } string; FILE *stream; } u; struct vtable *vtable; int lastchar; }; void init_bmachine(bool); void reset_bmachine(struct source *); u_int bmachine_scale(void); 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 *, u_int scale); + +static __inline u_int +max(u_int a, u_int b) +{ + + return (a > b ? a : b); +} Index: head/usr.bin/dc/extern.h =================================================================== --- head/usr.bin/dc/extern.h (revision 326555) +++ head/usr.bin/dc/extern.h (revision 326556) @@ -1,63 +1,64 @@ /* $FreeBSD$ */ /* $OpenBSD: extern.h,v 1.4 2014/12/01 13:13:00 deraadt Exp $ */ /* * Copyright (c) 2003, Otto Moerbeek * * 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 #include "bcode.h" /* inout.c */ void src_setstream(struct source *, FILE *); void src_setstring(struct source *, char *); -struct number *readnumber(struct source *, u_int); +struct number *readnumber(struct source *, u_int, 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 *div_number(struct number *, struct number *, u_int scale); struct number *dup_number(const struct number *); void *bmalloc(size_t); void *breallocarray(void *, size_t, size_t); char *bstrdup(const char *p); void bn_check(int); void bn_checkp(const void *); /* stack.c */ void stack_init(struct stack *); void stack_free_value(struct value *); struct value *stack_dup_value(const struct value *, struct value *); void stack_swap(struct stack *); size_t stack_size(const struct stack *); void stack_dup(struct stack *); void stack_pushnumber(struct stack *, struct number *); void stack_pushstring(struct stack *stack, char *); void stack_push(struct stack *, struct value *); void stack_set_tos(struct stack *, struct value *); struct value *stack_tos(const struct stack *); struct value *stack_pop(struct stack *); struct number *stack_popnumber(struct stack *); char *stack_popstring(struct stack *); void stack_clear(struct stack *); void stack_print(FILE *, const struct stack *, const char *, u_int base); void frame_assign(struct stack *, size_t, const struct value *); struct value *frame_retrieve(const struct stack *, size_t); /* void frame_free(struct stack *); */ Index: head/usr.bin/dc/inout.c =================================================================== --- head/usr.bin/dc/inout.c (revision 326555) +++ head/usr.bin/dc/inout.c (revision 326556) @@ -1,413 +1,446 @@ /* $OpenBSD: inout.c,v 1.18 2014/12/01 13:13:00 deraadt Exp $ */ /* * Copyright (c) 2003, Otto Moerbeek * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include "extern.h" #define MAX_CHARS_PER_LINE 68 static int lastchar; static int charcount; static int src_getcharstream(struct source *); static void src_ungetcharstream(struct source *); static char *src_getlinestream(struct source *); static int src_getcharstring(struct source *); static void src_ungetcharstring(struct source *); static char *src_getlinestring(struct source *); static void src_freestring(struct source *); static void flushwrap(FILE *); static void putcharwrap(FILE *, int); static void printwrap(FILE *, const char *); static char *get_digit(u_long, int, u_int); static struct vtable stream_vtable = { src_getcharstream, src_ungetcharstream, src_getlinestream, NULL }; static struct vtable string_vtable = { src_getcharstring, src_ungetcharstring, src_getlinestring, src_freestring }; void src_setstream(struct source *src, FILE *stream) { src->u.stream = stream; src->vtable = &stream_vtable; } void src_setstring(struct source *src, char *p) { src->u.string.buf = (u_char *)p; src->u.string.pos = 0; src->vtable = &string_vtable; } static int src_getcharstream(struct source *src) { return (src->lastchar = getc(src->u.stream)); } static void src_ungetcharstream(struct source *src) { ungetc(src->lastchar, src->u.stream); } static char * src_getlinestream(struct source *src) { char buf[BUFSIZ]; if (fgets(buf, BUFSIZ, src->u.stream) == NULL) return (bstrdup("")); return bstrdup(buf); } static int src_getcharstring(struct source *src) { src->lastchar = src->u.string.buf[src->u.string.pos]; if (src->lastchar == '\0') return (EOF); else { src->u.string.pos++; return (src->lastchar); } } static void src_ungetcharstring(struct source *src) { if (src->u.string.pos > 0) { if (src->lastchar != '\0') --src->u.string.pos; } } static char * src_getlinestring(struct source *src) { char buf[BUFSIZ]; int i, ch; i = 0; while (i < BUFSIZ-1) { ch = src_getcharstring(src); if (ch == EOF) break; buf[i++] = ch; if (ch == '\n') break; } buf[i] = '\0'; return (bstrdup(buf)); } static void src_freestring(struct source *src) { free(src->u.string.buf); } static void flushwrap(FILE *f) { if (lastchar != -1) putc(lastchar, f); } static void putcharwrap(FILE *f, int ch) { if (charcount >= MAX_CHARS_PER_LINE) { charcount = 0; fputs("\\\n", f); } if (lastchar != -1) { charcount++; putc(lastchar, f); } lastchar = ch; } static void printwrap(FILE *f, const char *p) { char *q; char buf[12]; q = buf; strlcpy(buf, p, sizeof(buf)); while (*q) putcharwrap(f, *q++); } struct number * -readnumber(struct source *src, u_int base) +readnumber(struct source *src, u_int base, u_int bscale) { struct number *n; BN_ULONG v; - u_int i; int ch; + u_int iscale = 0; bool dot = false, sign = false; 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++; + iscale++; bn_check(BN_mul_word(n->number, base)); bn_check(BN_add_word(n->number, v)); } - if (base != 10) { - scale_number(n->number, n->scale); - for (i = 0; i < n->scale; i++) - BN_div_word(n->number, base); + if (base == 10) { + n->scale = iscale; + } else { + /* At this point, the desired result is n->number / base^iscale*/ + struct number *quotient, *divisor, *_n; + BIGNUM *base_n, *exponent; + BN_CTX *ctx; + + ctx = BN_CTX_new(); + base_n = BN_new(); + exponent = BN_new(); + divisor = new_number(); + bn_check(BN_zero(base_n)); + bn_check(BN_zero(exponent)); + + bn_check(BN_add_word(base_n, base)); + bn_check(BN_add_word(exponent, iscale)); + bn_check(BN_exp(divisor->number, base_n, exponent, ctx)); + divisor->scale = 0; + quotient = div_number(n, divisor, bscale); + _n = n; + n = quotient; + + /* + * Trim off trailing zeros to yield the smallest scale without + * loss of accuracy + */ + while ( n->scale > 0 && + BN_mod_word(n->number, 10) == 0) { + normalize(n, n->scale - 1); + } + + free_number(_n); + free_number(divisor); + BN_CTX_free(ctx); + BN_free(base_n); + BN_free(exponent); } if (sign) negate(n); return (n); } char * read_string(struct source *src) { char *p; int count, ch, i, new_sz, sz; bool escape; escape = false; count = 1; i = 0; sz = 15; p = bmalloc(sz + 1); while ((ch = (*src->vtable->readchar)(src)) != EOF) { if (!escape) { if (ch == '[') count++; else if (ch == ']') count--; if (count == 0) break; } if (ch == '\\' && !escape) escape = true; else { escape = false; if (i == sz) { new_sz = sz * 2; p = breallocarray(p, 1, new_sz + 1); sz = new_sz; } p[i++] = ch; } } p[i] = '\0'; return (p); } static char * get_digit(u_long num, int digits, u_int base) { char *p; if (base <= 16) { p = bmalloc(2); p[0] = num >= 10 ? num + 'A' - 10 : num + '0'; p[1] = '\0'; } else { if (asprintf(&p, "%0*lu", digits, num) == -1) err(1, NULL); } return (p); } void printnumber(FILE *f, const struct number *b, u_int base) { struct number *fract_part, *int_part; struct stack stack; char *p; char buf[11]; size_t sz; unsigned int i; int digits; charcount = 0; lastchar = -1; if (BN_is_zero(b->number)) putcharwrap(f, '0'); int_part = new_number(); fract_part = new_number(); fract_part->scale = b->scale; if (base <= 16) digits = 1; else { digits = snprintf(buf, sizeof(buf), "%u", base-1); } split_number(b, int_part->number, fract_part->number); i = 0; stack_init(&stack); while (!BN_is_zero(int_part->number)) { BN_ULONG rem = BN_div_word(int_part->number, base); stack_pushstring(&stack, get_digit(rem, digits, base)); i++; } sz = i; if (BN_is_negative(b->number)) putcharwrap(f, '-'); for (i = 0; i < sz; i++) { p = stack_popstring(&stack); if (base > 16) putcharwrap(f, ' '); printwrap(f, p); free(p); } stack_clear(&stack); if (b->scale > 0) { struct number *num_base; BIGNUM mult, stop; putcharwrap(f, '.'); num_base = new_number(); bn_check(BN_set_word(num_base->number, base)); BN_init(&mult); bn_check(BN_one(&mult)); BN_init(&stop); bn_check(BN_one(&stop)); scale_number(&stop, b->scale); i = 0; while (BN_cmp(&mult, &stop) < 0) { u_long rem; if (i && base > 16) putcharwrap(f, ' '); i = 1; bmul_number(fract_part, fract_part, num_base, bmachine_scale()); split_number(fract_part, int_part->number, NULL); rem = BN_get_word(int_part->number); p = get_digit(rem, digits, base); int_part->scale = 0; normalize(int_part, fract_part->scale); bn_check(BN_sub(fract_part->number, fract_part->number, int_part->number)); printwrap(f, p); free(p); bn_check(BN_mul_word(&mult, base)); } free_number(num_base); BN_free(&mult); BN_free(&stop); } flushwrap(f); free_number(int_part); free_number(fract_part); } void print_value(FILE *f, const struct value *value, const char *prefix, u_int base) { fputs(prefix, f); switch (value->type) { case BCODE_NONE: if (value->array != NULL) fputs("", 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 ch, i, numbits; v = BN_dup(n->number); bn_checkp(v); if (BN_is_negative(v)) BN_set_negative(v, 0); 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); } Index: head/usr.bin/dc/mem.c =================================================================== --- head/usr.bin/dc/mem.c (revision 326555) +++ head/usr.bin/dc/mem.c (revision 326556) @@ -1,110 +1,142 @@ /* $OpenBSD: mem.c,v 1.6 2014/12/01 13:13:00 deraadt Exp $ */ /* * Copyright (c) 2003, Otto Moerbeek * * 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 __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include "extern.h" struct number * new_number(void) { struct number *n; n = bmalloc(sizeof(*n)); n->scale = 0; n->number = BN_new(); if (n->number == NULL) err(1, NULL); return (n); } void free_number(struct number *n) { BN_free(n->number); free(n); +} + +/* + * Divide dividend by divisor, returning the result. Retain bscale places of + * precision. + * The result must be freed when no longer in use + */ +struct number * +div_number(struct number *dividend, struct number *divisor, u_int bscale) +{ + struct number *quotient; + BN_CTX *ctx; + u_int scale; + + quotient = new_number(); + quotient->scale = bscale; + scale = max(divisor->scale, dividend->scale); + + if (BN_is_zero(divisor->number)) + warnx("divide by zero"); + else { + normalize(divisor, scale); + normalize(dividend, scale + quotient->scale); + + ctx = BN_CTX_new(); + bn_checkp(ctx); + bn_check(BN_div(quotient->number, NULL, dividend->number, + divisor->number, ctx)); + BN_CTX_free(ctx); + } + return (quotient); } struct number * dup_number(const struct number *a) { struct number *n; n = bmalloc(sizeof(*n)); n->scale = a->scale; n->number = BN_dup(a->number); bn_checkp(n->number); return (n); } void * bmalloc(size_t sz) { void *p; p = malloc(sz); if (p == NULL) err(1, NULL); return (p); } void * breallocarray(void *p, size_t nmemb, size_t size) { void *q; q = reallocarray(p, nmemb, size); if (q == NULL) err(1, NULL); return (q); } char * bstrdup(const char *p) { char *q; q = strdup(p); if (q == NULL) err(1, NULL); return (q); } void bn_check(int x) \ { if (x == 0) err(1, "big number failure %lx", ERR_get_error()); } void bn_checkp(const void *p) \ { if (p == NULL) err(1, "allocation failure %lx", ERR_get_error()); } Index: head/usr.bin/dc/tests/Makefile =================================================================== --- head/usr.bin/dc/tests/Makefile (nonexistent) +++ head/usr.bin/dc/tests/Makefile (revision 326556) @@ -0,0 +1,7 @@ +# $FreeBSD$ + +PACKAGE= tests + +ATF_TESTS_SH= inout + +.include Property changes on: head/usr.bin/dc/tests/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/usr.bin/dc/tests/inout.sh =================================================================== --- head/usr.bin/dc/tests/inout.sh (nonexistent) +++ head/usr.bin/dc/tests/inout.sh (revision 326556) @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: BSD-2-Clause-FreeBSD +# +# Copyright (c) 2017 Alan Somers +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +atf_test_case base16_input +base16_input_head() +{ + atf_set "descr" "Input hexadecimal numbers" +} +base16_input_body() +{ + cat > input.dc << EOF +4k # set scale to 4 decimal places +16i # switch to base 16 +0 p +10 p +1 p +1. p # The '.' should have no effect +1.0 p # Unlike with decimal, should not change the result's scale +.8 p # Can input fractions +# Check that we can input fractions that need more scale in base 10 than in 16 +# See PR 206230 +.1 p +.10 p # Result should be .0625, with scale=4 +.01 p # Result should be truncated to scale=4 +8k # Increase scale to 8 places +.01 p # Result should be exact again +0.1 p # Leading zeros are ignored +00.1 p # Leading zeros are ignored +EOF +dc input.dc > output.txt +cat > expect.txt << EOF +0 +16 +1 +1 +1 +.5 +.0625 +.0625 +.0039 +.00390625 +.0625 +.0625 +EOF + atf_check cmp expect.txt output.txt +} + +atf_test_case base3_input +base3_input_head() +{ + atf_set "descr" "Input ternary numbers" +} +base3_input_body() +{ + cat > input.dc << EOF +4k # 4 digits of precision +3i # Base 3 input +0 p +1 p +10 p +.1 p # Repeating fractions get truncated +EOF +dc input.dc > output.txt +cat > expect.txt << EOF +0 +1 +3 +.3333 +EOF + atf_check cmp expect.txt output.txt +} + +atf_init_test_cases() +{ + atf_add_test_case base16_input + atf_add_test_case base3_input +} Property changes on: head/usr.bin/dc/tests/inout.sh ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property