#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 void src_freestream(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 void get_digit(u_long, int, u_int, char *, size_t);
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 void
src_ungetcharstream(struct source *src)
{
(void)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 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 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
flushwrap(FILE *f)
{
if (lastchar != -1)
(void)putc(lastchar, f);
}
static void
putcharwrap(FILE *f, int ch)
{
if (charcount >= MAX_CHARS_PER_LINE) {
charcount = 0;
(void)fputs("\\\n", f);
}
if (lastchar != -1) {
charcount++;
(void)putc(lastchar, f);
}
lastchar = ch;
}
static void
printwrap(FILE *f, const char *p)
{
char buf[12];
char *q = buf;
(void)strlcpy(buf, p, sizeof(buf));
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;
u_int i;
n = new_number();
bn_check(BN_set_word(n->number, 0));
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));
#if 0
if (v > 0)
#endif
bn_check(BN_add_word(n->number, v));
}
if (base != 10) {
scale_number(n->number, n->scale);
for (i = 0; i < n->scale; i++)
(void)BN_div_word(n->number, base);
}
if (sign)
negate(n);
return n;
}
char *
read_string(struct source *src)
{
int count, i, sz, new_sz, ch;
char *p;
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 void
get_digit(u_long num, int digits, u_int base, char *buf, size_t sz)
{
if (base <= 16) {
buf[0] = num >= 10 ? num + 'A' - 10 : num + '0';
buf[1] = '\0';
} else {
int ret = snprintf(buf, sz, "%0*lu", digits, num);
if (ret < 0 || (size_t)ret >= sz)
err(1, "truncation");
}
}
void
printnumber(FILE *f, const struct number *b, u_int base)
{
struct number *int_part, *fract_part;
int digits;
char buf[12], *str, *p;
size_t allocated;
int i;
BN_ULONG *mem;
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);
if (base == 10 && !BN_is_zero(int_part->number)) {
str = BN_bn2dec(int_part->number);
bn_checkp(str);
p = str;
while (*p)
putcharwrap(f, *p++);
free(str);
} else if (base == 16 && !BN_is_zero(int_part->number)) {
str = BN_bn2hex(int_part->number);
bn_checkp(str);
p = str;
if (*p == '-')
putcharwrap(f, *p++);
while (*p == '0')
p++;
while (*p)
putcharwrap(f, *p++);
free(str);
} else {
i = 0;
allocated = 1;
mem = breallocarray(NULL, allocated, sizeof(BN_ULONG));
while (!BN_is_zero(int_part->number)) {
if (i >= allocated) {
allocated *= 2;
mem = breallocarray(mem, allocated,
sizeof(BN_ULONG));
}
mem[i++] = BN_div_word(int_part->number, base);
}
if (BN_is_negative(b->number))
putcharwrap(f, '-');
for (i = i - 1; i >= 0; i--) {
get_digit(mem[i], digits, base, buf,
sizeof(buf));
if (base > 16)
putcharwrap(f, ' ');
printwrap(f, buf);
}
free(mem);
}
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);
get_digit(rem, digits, base, buf, sizeof(buf));
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, buf);
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)
{
(void)fputs(prefix, f);
switch (value->type) {
case BCODE_NONE:
if (value->array != NULL)
(void)fputs("<array>", f);
break;
case BCODE_NUMBER:
printnumber(f, value->u.num, base);
break;
case BCODE_STRING:
(void)fputs(value->u.string, f);
break;
}
}
void
print_ascii(FILE *f, const struct number *n)
{
int numbits, i, ch;
numbits = BN_num_bytes(n->number) * 8;
while (numbits > 0) {
ch = 0;
for (i = 0; i < 8; i++)
ch |= BN_is_bit_set(n->number, numbits-i-1) << (7 - i);
(void)putc(ch, f);
numbits -= 8;
}
}