xref: /openbsd/usr.bin/dc/inout.c (revision f4f5a46b)
1*f4f5a46bSotto /*	$OpenBSD: inout.c,v 1.24 2024/11/07 16:20:00 otto Exp $	*/
2a6ce4a44Sotto 
3a6ce4a44Sotto /*
4a6ce4a44Sotto  * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net>
5a6ce4a44Sotto  *
6a6ce4a44Sotto  * Permission to use, copy, modify, and distribute this software for any
7a6ce4a44Sotto  * purpose with or without fee is hereby granted, provided that the above
8a6ce4a44Sotto  * copyright notice and this permission notice appear in all copies.
9a6ce4a44Sotto  *
10a6ce4a44Sotto  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11a6ce4a44Sotto  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12a6ce4a44Sotto  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13a6ce4a44Sotto  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14a6ce4a44Sotto  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15a6ce4a44Sotto  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16a6ce4a44Sotto  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17a6ce4a44Sotto  */
18a6ce4a44Sotto 
19a6ce4a44Sotto #include <ctype.h>
20a6ce4a44Sotto #include <err.h>
21a6ce4a44Sotto #include <string.h>
22a6ce4a44Sotto 
23a6ce4a44Sotto #include "extern.h"
24a6ce4a44Sotto 
25d2aaa95fSotto #define MAX_CHARS_PER_LINE 68
26a6ce4a44Sotto 
27f0690440Sotto static int	lastchar;
28f0690440Sotto static int	charcount;
29a6ce4a44Sotto 
30a6ce4a44Sotto static int	src_getcharstream(struct source *);
31a1a9dfc3Sotto static void	src_ungetcharstream(struct source *);
32a6ce4a44Sotto static char	*src_getlinestream(struct source *);
33a6ce4a44Sotto static void	src_freestream(struct source *);
34a6ce4a44Sotto static int	src_getcharstring(struct source *);
35a1a9dfc3Sotto static void	src_ungetcharstring(struct source *);
36a6ce4a44Sotto static char	*src_getlinestring(struct source *);
37a6ce4a44Sotto static void	src_freestring(struct source *);
38f0690440Sotto static void	flushwrap(FILE *);
39a6ce4a44Sotto static void	putcharwrap(FILE *, int);
40a6ce4a44Sotto static void	printwrap(FILE *, const char *);
41*f4f5a46bSotto static void	get_digit(u_long, int, u_int, char *, size_t);
42a6ce4a44Sotto 
43a6ce4a44Sotto static struct vtable stream_vtable = {
44a6ce4a44Sotto 	src_getcharstream,
45a6ce4a44Sotto 	src_ungetcharstream,
46a6ce4a44Sotto 	src_getlinestream,
47a6ce4a44Sotto 	src_freestream
48a6ce4a44Sotto };
49a6ce4a44Sotto 
50a6ce4a44Sotto static struct vtable string_vtable = {
51a6ce4a44Sotto 	src_getcharstring,
52a6ce4a44Sotto 	src_ungetcharstring,
53a6ce4a44Sotto 	src_getlinestring,
54a6ce4a44Sotto 	src_freestring
55a6ce4a44Sotto };
56a6ce4a44Sotto 
57a6ce4a44Sotto void
src_setstream(struct source * src,FILE * stream)58a6ce4a44Sotto src_setstream(struct source *src, FILE *stream)
59a6ce4a44Sotto {
60a6ce4a44Sotto 	src->u.stream = stream;
61a6ce4a44Sotto 	src->vtable = &stream_vtable;
62a6ce4a44Sotto }
63a6ce4a44Sotto 
64a6ce4a44Sotto void
src_setstring(struct source * src,char * p)65a6ce4a44Sotto src_setstring(struct source *src, char *p)
66a6ce4a44Sotto {
67a6ce4a44Sotto 	src->u.string.buf = (u_char *)p;
68a6ce4a44Sotto 	src->u.string.pos = 0;
69a6ce4a44Sotto 	src->vtable = &string_vtable;
70a6ce4a44Sotto }
71a6ce4a44Sotto 
72a6ce4a44Sotto static int
src_getcharstream(struct source * src)73a6ce4a44Sotto src_getcharstream(struct source *src)
74a6ce4a44Sotto {
75a6ce4a44Sotto 	return src->lastchar = getc(src->u.stream);
76a6ce4a44Sotto }
77a6ce4a44Sotto 
78a1a9dfc3Sotto static void
src_ungetcharstream(struct source * src)79a6ce4a44Sotto src_ungetcharstream(struct source *src)
80a6ce4a44Sotto {
81a1a9dfc3Sotto 	(void)ungetc(src->lastchar, src->u.stream);
82a6ce4a44Sotto }
83a6ce4a44Sotto 
84a6ce4a44Sotto static void
src_freestream(struct source * src)85a6ce4a44Sotto src_freestream(struct source *src)
86a6ce4a44Sotto {
87a6ce4a44Sotto }
88a6ce4a44Sotto 
89a6ce4a44Sotto static char *
src_getlinestream(struct source * src)90a6ce4a44Sotto src_getlinestream(struct source *src)
91a6ce4a44Sotto {
92a6ce4a44Sotto 	char buf[BUFSIZ];
93a6ce4a44Sotto 
94a6ce4a44Sotto 	if (fgets(buf, BUFSIZ, src->u.stream) == NULL)
95a6ce4a44Sotto 		return bstrdup("");
96a6ce4a44Sotto 	return bstrdup(buf);
97a6ce4a44Sotto }
98a6ce4a44Sotto 
99a6ce4a44Sotto static int
src_getcharstring(struct source * src)100a6ce4a44Sotto src_getcharstring(struct source *src)
101a6ce4a44Sotto {
102a6ce4a44Sotto 	src->lastchar = src->u.string.buf[src->u.string.pos];
103a6ce4a44Sotto 	if (src->lastchar == '\0')
104a6ce4a44Sotto 		return EOF;
105a6ce4a44Sotto 	else {
106a6ce4a44Sotto 		src->u.string.pos++;
107a6ce4a44Sotto 		return src->lastchar;
108a6ce4a44Sotto 	}
109a6ce4a44Sotto }
110a6ce4a44Sotto 
111a1a9dfc3Sotto static void
src_ungetcharstring(struct source * src)112a6ce4a44Sotto src_ungetcharstring(struct source *src)
113a6ce4a44Sotto {
1140c698325Sotto 	if (src->u.string.pos > 0) {
1150c698325Sotto 		if (src->lastchar != '\0')
1160c698325Sotto 			--src->u.string.pos;
117a1a9dfc3Sotto 	}
118a6ce4a44Sotto }
119a6ce4a44Sotto 
120a6ce4a44Sotto static char *
src_getlinestring(struct source * src)121a6ce4a44Sotto src_getlinestring(struct source *src)
122a6ce4a44Sotto {
123a6ce4a44Sotto 	char buf[BUFSIZ];
124a6ce4a44Sotto 	int ch, i;
125a6ce4a44Sotto 
126a6ce4a44Sotto 	i = 0;
127a6ce4a44Sotto 	while (i < BUFSIZ-1) {
128a6ce4a44Sotto 		ch = src_getcharstring(src);
129a6ce4a44Sotto 		if (ch == EOF)
130a6ce4a44Sotto 			break;
131a6ce4a44Sotto 		buf[i++] = ch;
132a6ce4a44Sotto 		if (ch == '\n')
133a6ce4a44Sotto 			break;
134a6ce4a44Sotto 	}
135a6ce4a44Sotto 	buf[i] = '\0';
136a6ce4a44Sotto 	return bstrdup(buf);
137a6ce4a44Sotto }
138a6ce4a44Sotto 
139a6ce4a44Sotto static void
src_freestring(struct source * src)140a6ce4a44Sotto src_freestring(struct source *src)
141a6ce4a44Sotto {
142a6ce4a44Sotto 	free(src->u.string.buf);
143a6ce4a44Sotto }
144a6ce4a44Sotto 
145a6ce4a44Sotto static void
flushwrap(FILE * f)146f0690440Sotto flushwrap(FILE *f)
147f0690440Sotto {
148f0690440Sotto 	if (lastchar != -1)
149a1a9dfc3Sotto 		(void)putc(lastchar, f);
150f0690440Sotto }
151f0690440Sotto 
152f0690440Sotto static void
putcharwrap(FILE * f,int ch)153a6ce4a44Sotto putcharwrap(FILE *f, int ch)
154a6ce4a44Sotto {
155f0690440Sotto 	if (charcount >= MAX_CHARS_PER_LINE) {
156f0690440Sotto 		charcount = 0;
157a1a9dfc3Sotto 		(void)fputs("\\\n", f);
158a6ce4a44Sotto 	}
159f0690440Sotto 	if (lastchar != -1) {
160f0690440Sotto 		charcount++;
161a1a9dfc3Sotto 		(void)putc(lastchar, f);
162f0690440Sotto 	}
163f0690440Sotto 	lastchar = ch;
164a6ce4a44Sotto }
165a6ce4a44Sotto 
166a6ce4a44Sotto static void
printwrap(FILE * f,const char * p)167a6ce4a44Sotto printwrap(FILE *f, const char *p)
168a6ce4a44Sotto {
169a6ce4a44Sotto 	char	buf[12];
170a6ce4a44Sotto 	char	*q = buf;
171a6ce4a44Sotto 
172a1a9dfc3Sotto 	(void)strlcpy(buf, p, sizeof(buf));
173a6ce4a44Sotto 	while (*q)
174a6ce4a44Sotto 		putcharwrap(f, *q++);
175a6ce4a44Sotto }
176a6ce4a44Sotto 
177a6ce4a44Sotto struct number *
readnumber(struct source * src,u_int base)178a6ce4a44Sotto readnumber(struct source *src, u_int base)
179a6ce4a44Sotto {
180a6ce4a44Sotto 	struct number	*n;
181a6ce4a44Sotto 	int		ch;
182a6ce4a44Sotto 	bool		sign = false;
183a6ce4a44Sotto 	bool		dot = false;
184a6ce4a44Sotto 	BN_ULONG	v;
185be23059eSotto 	u_int		i;
186a6ce4a44Sotto 
187a6ce4a44Sotto 	n = new_number();
188c935c058Sotto 	bn_check(BN_set_word(n->number, 0));
189a6ce4a44Sotto 
190a6ce4a44Sotto 	while ((ch = (*src->vtable->readchar)(src)) != EOF) {
191a6ce4a44Sotto 
192a6ce4a44Sotto 		if ('0' <= ch && ch <= '9')
193a6ce4a44Sotto 			v = ch - '0';
194a6ce4a44Sotto 		else if ('A' <= ch && ch <= 'F')
195a6ce4a44Sotto 			v = ch - 'A' + 10;
196a6ce4a44Sotto 		else if (ch == '_') {
197a6ce4a44Sotto 			sign = true;
198a6ce4a44Sotto 			continue;
199a6ce4a44Sotto 		} else if (ch == '.') {
200a6ce4a44Sotto 			if (dot)
201a6ce4a44Sotto 				break;
202a6ce4a44Sotto 			dot = true;
203a6ce4a44Sotto 			continue;
204a6ce4a44Sotto 		} else {
205a6ce4a44Sotto 			(*src->vtable->unreadchar)(src);
206a6ce4a44Sotto 			break;
207a6ce4a44Sotto 		}
208a6ce4a44Sotto 		if (dot)
209a6ce4a44Sotto 			n->scale++;
210a6ce4a44Sotto 
211a6ce4a44Sotto 		bn_check(BN_mul_word(n->number, base));
212a6ce4a44Sotto 
21358936847Sotto #if 0
214a6ce4a44Sotto 		/* work around a bug in BN_add_word: 0 += 0 is buggy.... */
215a6ce4a44Sotto 		if (v > 0)
21658936847Sotto #endif
217a6ce4a44Sotto 			bn_check(BN_add_word(n->number, v));
218a6ce4a44Sotto 	}
219be23059eSotto 	if (base != 10) {
220be23059eSotto 		scale_number(n->number, n->scale);
221be23059eSotto 		for (i = 0; i < n->scale; i++)
222a1a9dfc3Sotto 			(void)BN_div_word(n->number, base);
223be23059eSotto 	}
224a6ce4a44Sotto 	if (sign)
225a6ce4a44Sotto 		negate(n);
226a6ce4a44Sotto 	return n;
227a6ce4a44Sotto }
228a6ce4a44Sotto 
229a6ce4a44Sotto char *
read_string(struct source * src)230a6ce4a44Sotto read_string(struct source *src)
231a6ce4a44Sotto {
23244845cb5Sotto 	int count, i, sz, new_sz, ch;
233a6ce4a44Sotto 	char *p;
234adedf6afSotto 	bool escape;
235a6ce4a44Sotto 
236adedf6afSotto 	escape = false;
237a6ce4a44Sotto 	count = 1;
238a6ce4a44Sotto 	i = 0;
239a6ce4a44Sotto 	sz = 15;
240a6ce4a44Sotto 	p = bmalloc(sz + 1);
241a6ce4a44Sotto 
242a6ce4a44Sotto 	while ((ch = (*src->vtable->readchar)(src)) != EOF) {
243adedf6afSotto 		if (!escape) {
244a6ce4a44Sotto 			if (ch == '[')
245a6ce4a44Sotto 				count++;
246a6ce4a44Sotto 			else if (ch == ']')
247a6ce4a44Sotto 				count--;
248a6ce4a44Sotto 			if (count == 0)
249a6ce4a44Sotto 				break;
250adedf6afSotto 		}
251adedf6afSotto 		if (ch == '\\' && !escape)
252adedf6afSotto 			escape = true;
253adedf6afSotto 		else {
254adedf6afSotto 			escape = false;
255a6ce4a44Sotto 			if (i == sz) {
25644845cb5Sotto 				new_sz = sz * 2;
2575805b5e9Sderaadt 				p = breallocarray(p, 1, new_sz + 1);
25844845cb5Sotto 				sz = new_sz;
259a6ce4a44Sotto 			}
260a6ce4a44Sotto 			p[i++] = ch;
261a6ce4a44Sotto 		}
262adedf6afSotto 	}
263a6ce4a44Sotto 	p[i] = '\0';
264a6ce4a44Sotto 	return p;
265a6ce4a44Sotto }
266a6ce4a44Sotto 
267*f4f5a46bSotto static void
get_digit(u_long num,int digits,u_int base,char * buf,size_t sz)268*f4f5a46bSotto get_digit(u_long num, int digits, u_int base, char *buf, size_t sz)
269a6ce4a44Sotto {
270a6ce4a44Sotto 	if (base <= 16) {
271*f4f5a46bSotto 		buf[0] = num >= 10 ? num + 'A' - 10 : num + '0';
272*f4f5a46bSotto 		buf[1] = '\0';
273a6ce4a44Sotto 	} else {
274*f4f5a46bSotto 		int ret = snprintf(buf, sz, "%0*lu", digits, num);
275*f4f5a46bSotto 		if (ret < 0 || (size_t)ret >= sz)
276*f4f5a46bSotto 			err(1, "truncation");
277a6ce4a44Sotto 	}
278a6ce4a44Sotto }
279a6ce4a44Sotto 
280a6ce4a44Sotto void
printnumber(FILE * f,const struct number * b,u_int base)281a6ce4a44Sotto printnumber(FILE *f, const struct number *b, u_int base)
282a6ce4a44Sotto {
283a6ce4a44Sotto 	struct number	*int_part, *fract_part;
284a6ce4a44Sotto 	int		digits;
285*f4f5a46bSotto 	char		buf[12], *str, *p;
286*f4f5a46bSotto 	size_t		allocated;
287a6ce4a44Sotto 	int		i;
288*f4f5a46bSotto 	BN_ULONG	*mem;
289a6ce4a44Sotto 
290f0690440Sotto 	charcount = 0;
291f0690440Sotto 	lastchar = -1;
292a6ce4a44Sotto 	if (BN_is_zero(b->number))
293a6ce4a44Sotto 		putcharwrap(f, '0');
294a6ce4a44Sotto 
295a6ce4a44Sotto 	int_part = new_number();
296a6ce4a44Sotto 	fract_part = new_number();
297a6ce4a44Sotto 	fract_part->scale = b->scale;
298a6ce4a44Sotto 
299a6ce4a44Sotto 	if (base <= 16)
300a6ce4a44Sotto 		digits = 1;
301a6ce4a44Sotto 	else {
302a6ce4a44Sotto 		digits = snprintf(buf, sizeof(buf), "%u", base-1);
303a6ce4a44Sotto 	}
304a6ce4a44Sotto 	split_number(b, int_part->number, fract_part->number);
305a6ce4a44Sotto 
306*f4f5a46bSotto 	if (base == 10 && !BN_is_zero(int_part->number)) {
307*f4f5a46bSotto 		str = BN_bn2dec(int_part->number);
308*f4f5a46bSotto 		bn_checkp(str);
309*f4f5a46bSotto 		p = str;
310*f4f5a46bSotto 		while (*p)
311*f4f5a46bSotto 			putcharwrap(f, *p++);
312*f4f5a46bSotto 		free(str);
313*f4f5a46bSotto 	} else if (base == 16 && !BN_is_zero(int_part->number)) {
314*f4f5a46bSotto 		str = BN_bn2hex(int_part->number);
315*f4f5a46bSotto 		bn_checkp(str);
316*f4f5a46bSotto 		p = str;
317*f4f5a46bSotto 		if (*p == '-')
318*f4f5a46bSotto 			putcharwrap(f, *p++);
319*f4f5a46bSotto 		/* skip leading zero's */
320*f4f5a46bSotto 		while (*p == '0')
321*f4f5a46bSotto 			p++;
322*f4f5a46bSotto 		while (*p)
323*f4f5a46bSotto 			putcharwrap(f, *p++);
324*f4f5a46bSotto 		free(str);
325*f4f5a46bSotto 	} else {
326a6ce4a44Sotto 		i = 0;
327*f4f5a46bSotto 		allocated = 1;
328*f4f5a46bSotto 		mem = breallocarray(NULL, allocated, sizeof(BN_ULONG));
329a6ce4a44Sotto 		while (!BN_is_zero(int_part->number)) {
330*f4f5a46bSotto 			if (i >= allocated) {
331*f4f5a46bSotto 				allocated *= 2;
332*f4f5a46bSotto 				mem = breallocarray(mem, allocated,
333*f4f5a46bSotto 				    sizeof(BN_ULONG));
334a6ce4a44Sotto 			}
335*f4f5a46bSotto 			mem[i++] = BN_div_word(int_part->number, base);
336*f4f5a46bSotto 		}
3378e9b8cc7Sotto 		if (BN_is_negative(b->number))
338a6ce4a44Sotto 			putcharwrap(f, '-');
339*f4f5a46bSotto 		for (i = i - 1; i >= 0; i--) {
340*f4f5a46bSotto 			get_digit(mem[i], digits, base, buf,
341*f4f5a46bSotto 			    sizeof(buf));
342a6ce4a44Sotto 			if (base > 16)
343a6ce4a44Sotto 				putcharwrap(f, ' ');
344*f4f5a46bSotto 			printwrap(f, buf);
345a6ce4a44Sotto 		}
346*f4f5a46bSotto 		free(mem);
347*f4f5a46bSotto 	}
348*f4f5a46bSotto 
349a6ce4a44Sotto 	if (b->scale > 0) {
350a6ce4a44Sotto 		struct number	*num_base;
3514368645dStb 		BIGNUM		*mult, *stop;
352a6ce4a44Sotto 
353a6ce4a44Sotto 		putcharwrap(f, '.');
354a6ce4a44Sotto 		num_base = new_number();
355a1a9dfc3Sotto 		bn_check(BN_set_word(num_base->number, base));
3564368645dStb 		mult = BN_new();
3574368645dStb 		bn_checkp(mult);
3584368645dStb 		bn_check(BN_one(mult));
3594368645dStb 		stop = BN_new();
3604368645dStb 		bn_checkp(stop);
3614368645dStb 		bn_check(BN_one(stop));
3624368645dStb 		scale_number(stop, b->scale);
363a6ce4a44Sotto 
364a6ce4a44Sotto 		i = 0;
3654368645dStb 		while (BN_cmp(mult, stop) < 0) {
366a6ce4a44Sotto 			u_long	rem;
367a6ce4a44Sotto 
368a6ce4a44Sotto 			if (i && base > 16)
369a6ce4a44Sotto 				putcharwrap(f, ' ');
370a6ce4a44Sotto 			i = 1;
371a6ce4a44Sotto 
3728b19764bSotto 			bmul_number(fract_part, fract_part, num_base,
3738b19764bSotto 			    bmachine_scale());
374a6ce4a44Sotto 			split_number(fract_part, int_part->number, NULL);
375a6ce4a44Sotto 			rem = BN_get_word(int_part->number);
376*f4f5a46bSotto 			get_digit(rem, digits, base, buf, sizeof(buf));
377a6ce4a44Sotto 			int_part->scale = 0;
378a6ce4a44Sotto 			normalize(int_part, fract_part->scale);
379a1a9dfc3Sotto 			bn_check(BN_sub(fract_part->number, fract_part->number,
380a1a9dfc3Sotto 			    int_part->number));
381*f4f5a46bSotto 			printwrap(f, buf);
3824368645dStb 			bn_check(BN_mul_word(mult, base));
383a6ce4a44Sotto 		}
384a6ce4a44Sotto 		free_number(num_base);
3854368645dStb 		BN_free(mult);
3864368645dStb 		BN_free(stop);
387a6ce4a44Sotto 	}
388f0690440Sotto 	flushwrap(f);
389a6ce4a44Sotto 	free_number(int_part);
390a6ce4a44Sotto 	free_number(fract_part);
391a6ce4a44Sotto }
392a6ce4a44Sotto 
393a6ce4a44Sotto void
print_value(FILE * f,const struct value * value,const char * prefix,u_int base)394a6ce4a44Sotto print_value(FILE *f, const struct value *value, const char *prefix, u_int base)
395a6ce4a44Sotto {
396a1a9dfc3Sotto 	(void)fputs(prefix, f);
397a6ce4a44Sotto 	switch (value->type) {
398a6ce4a44Sotto 	case BCODE_NONE:
399a6ce4a44Sotto 		if (value->array != NULL)
400a1a9dfc3Sotto 			(void)fputs("<array>", f);
401a6ce4a44Sotto 		break;
402a6ce4a44Sotto 	case BCODE_NUMBER:
403a6ce4a44Sotto 		printnumber(f, value->u.num, base);
404a6ce4a44Sotto 		break;
405a6ce4a44Sotto 	case BCODE_STRING:
406a1a9dfc3Sotto 		(void)fputs(value->u.string, f);
407a6ce4a44Sotto 		break;
408a6ce4a44Sotto 	}
409a6ce4a44Sotto }
410a6ce4a44Sotto 
411a6ce4a44Sotto void
print_ascii(FILE * f,const struct number * n)412a6ce4a44Sotto print_ascii(FILE *f, const struct number *n)
413a6ce4a44Sotto {
414a6ce4a44Sotto 	int numbits, i, ch;
415a6ce4a44Sotto 
4162d34f094Sotto 	numbits = BN_num_bytes(n->number) * 8;
417a6ce4a44Sotto 	while (numbits > 0) {
418a6ce4a44Sotto 		ch = 0;
419a6ce4a44Sotto 		for (i = 0; i < 8; i++)
4202d34f094Sotto 			ch |= BN_is_bit_set(n->number, numbits-i-1) << (7 - i);
421a1a9dfc3Sotto 		(void)putc(ch, f);
422a6ce4a44Sotto 		numbits -= 8;
423a6ce4a44Sotto 	}
424a6ce4a44Sotto }
425