417 lines
8.1 KiB
C
417 lines
8.1 KiB
C
/* $NetBSD: inout.c,v 1.3 2018/02/06 17:58:19 christos Exp $ */
|
|
/* $OpenBSD: inout.c,v 1.20 2017/02/26 11:29:55 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 <sys/cdefs.h>
|
|
__RCSID("$NetBSD: inout.c,v 1.3 2018/02/06 17:58:19 christos Exp $");
|
|
|
|
#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 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 void
|
|
src_ungetcharstream(struct source *src)
|
|
{
|
|
(void)ungetc(src->lastchar, src->u.stream);
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
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++] = (char)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 = (BN_ULONG)(ch - '0');
|
|
else if ('A' <= ch && ch <= 'F')
|
|
v = (BN_ULONG)(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
|
|
/* work around a bug in BN_add_word: 0 += 0 is buggy.... */
|
|
if (v > 0)
|
|
#endif
|
|
bn_check(BN_add_word(n->number, v));
|
|
}
|
|
if (base != 10) {
|
|
scale_number(n->number, (int)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)
|
|
{
|
|
size_t count, i, sz, new_sz;
|
|
int 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++] = (char)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] = (char)(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 *int_part, *fract_part;
|
|
int digits;
|
|
char buf[11];
|
|
size_t sz, i;
|
|
struct stack stack;
|
|
char *p;
|
|
|
|
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();
|
|
stop = BN_new();
|
|
if (mult == NULL || stop == NULL)
|
|
err(1, NULL);
|
|
bn_check(BN_one(mult));
|
|
bn_check(BN_one(stop));
|
|
scale_number(stop, (int)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)
|
|
{
|
|
(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)
|
|
{
|
|
BIGNUM *v;
|
|
int numbits, i, ch;
|
|
|
|
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);
|
|
(void)putc(ch, f);
|
|
numbits -= 8;
|
|
}
|
|
BN_free(v);
|
|
}
|