/* $OpenBSD: inout.c,v 1.18 2014/12/01 13:13:00 deraadt Exp $ */
/*
* Copyright (c) 2003, Otto Moerbeek <otto@drijf.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/cdefs.h>
#include <openssl/ssl.h>
#include <ctype.h>
#include <err.h>
#include <string.h>
#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, u_int bscale)
{
struct number *n;
BN_ULONG v;
int ch;
u_int iscale = 0;
bool dot = false, sign = false;
n = new_number();
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)
iscale++;
bn_check(BN_mul_word(n->number, base));
bn_check(BN_add_word(n->number, v));
}
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_zero(base_n);
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));
mult = BN_new();
bn_checkp(mult);
bn_check(BN_one(mult));
stop = BN_new();
bn_checkp(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("<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 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);
}