1 /* vi: set sw=4 ts=4: */
2 /*
3  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
4  */
5 
6 /* config/applet/usage bits are in bc.c */
7 
8 //#include "libbb.h"
9 //#include "common_bufsiz.h"
10 #include <math.h>
11 
12 #if 0
13 typedef unsigned data_t;
14 #define DATA_FMT ""
15 #elif 0
16 typedef unsigned long data_t;
17 #define DATA_FMT "l"
18 #else
19 typedef unsigned long long data_t;
20 #define DATA_FMT "ll"
21 #endif
22 
23 struct globals {
24 	unsigned pointer;
25 	unsigned base;
26 	double stack[1];
27 } FIX_ALIASING;
28 enum { STACK_SIZE = (COMMON_BUFSIZE - offsetof(struct globals, stack)) / sizeof(double) };
29 #define G (*(struct globals*)bb_common_bufsiz1)
30 #define pointer   (G.pointer   )
31 #define base      (G.base      )
32 #define stack     (G.stack     )
33 #define INIT_G() do { \
34 	setup_common_bufsiz(); \
35 	base = 10; \
36 } while (0)
37 
check_under(void)38 static unsigned check_under(void)
39 {
40 	unsigned p = pointer;
41 	if (p == 0)
42 		bb_simple_error_msg_and_die("stack underflow");
43 	return p - 1;
44 }
45 
push(double a)46 static void push(double a)
47 {
48 	if (pointer >= STACK_SIZE)
49 		bb_simple_error_msg_and_die("stack overflow");
50 	stack[pointer++] = a;
51 }
52 
pop(void)53 static double pop(void)
54 {
55 	unsigned p = check_under();
56 	pointer = p;
57 	return stack[p];
58 }
59 
add(void)60 static void add(void)
61 {
62 	push(pop() + pop());
63 }
64 
sub(void)65 static void sub(void)
66 {
67 	double subtrahend = pop();
68 
69 	push(pop() - subtrahend);
70 }
71 
mul(void)72 static void mul(void)
73 {
74 	push(pop() * pop());
75 }
76 
77 #if ENABLE_FEATURE_DC_LIBM
power(void)78 static void power(void)
79 {
80 	double topower = pop();
81 
82 	push(pow(pop(), topower));
83 }
84 #endif
85 
divide(void)86 static void divide(void)
87 {
88 	double divisor = pop();
89 
90 	push(pop() / divisor);
91 }
92 
mod(void)93 static void mod(void)
94 {
95 	data_t d = pop();
96 
97 	/* compat with dc (GNU bc 1.07.1) 1.4.1:
98 	 * $ dc -e '4 0 % p'
99 	 * dc: remainder by zero
100 	 * 0
101 	 */
102 	if (d == 0) {
103 		bb_simple_error_msg("remainder by zero");
104 		pop();
105 		push(0);
106 		return;
107 	}
108 	/* ^^^^ without this, we simply get SIGFPE and die */
109 
110 	push((data_t) pop() % d);
111 }
112 
and(void)113 static void and(void)
114 {
115 	push((data_t) pop() & (data_t) pop());
116 }
117 
or(void)118 static void or(void)
119 {
120 	push((data_t) pop() | (data_t) pop());
121 }
122 
eor(void)123 static void eor(void)
124 {
125 	push((data_t) pop() ^ (data_t) pop());
126 }
127 
not(void)128 static void not(void)
129 {
130 	push(~(data_t) pop());
131 }
132 
set_output_base(void)133 static void set_output_base(void)
134 {
135 	static const char bases[] ALIGN1 = { 2, 8, 10, 16, 0 };
136 	unsigned b = (unsigned)pop();
137 
138 	base = *strchrnul(bases, b);
139 	if (base == 0) {
140 		bb_error_msg("error, base %u is not supported", b);
141 		base = 10;
142 	}
143 }
144 
print_base(double print)145 static void print_base(double print)
146 {
147 	data_t x, i;
148 
149 	x = (data_t) print;
150 	if (base == 10) {
151 		if (x == print) /* exactly representable as unsigned integer */
152 			printf("%"DATA_FMT"u\n", x);
153 		else
154 			printf("%g\n", print);
155 		return;
156 	}
157 
158 	switch (base) {
159 	case 16:
160 		printf("%"DATA_FMT"x\n", x);
161 		break;
162 	case 8:
163 		printf("%"DATA_FMT"o\n", x);
164 		break;
165 	default: /* base 2 */
166 		i = MAXINT(data_t) - (MAXINT(data_t) >> 1);
167 		/* i is 100000...00000 */
168 		do {
169 			if (x & i)
170 				break;
171 			i >>= 1;
172 		} while (i > 1);
173 		do {
174 			bb_putchar('1' - !(x & i));
175 			i >>= 1;
176 		} while (i);
177 		bb_putchar('\n');
178 	}
179 }
180 
print_stack_no_pop(void)181 static void print_stack_no_pop(void)
182 {
183 	unsigned i = pointer;
184 	while (i)
185 		print_base(stack[--i]);
186 }
187 
print_no_pop(void)188 static void print_no_pop(void)
189 {
190 	print_base(stack[check_under()]);
191 }
192 
193 struct op {
194 	const char name[4];
195 	void (*function) (void);
196 };
197 
198 static const struct op operators[] ALIGN_PTR = {
199 #if ENABLE_FEATURE_DC_LIBM
200 	{"^",   power},
201 //	{"exp", power},
202 //	{"pow", power},
203 #endif
204 	{"%",   mod},
205 //	{"mod", mod},
206 	// logic ops are not standard, remove?
207 	{"and", and},
208 	{"or",  or},
209 	{"not", not},
210 	{"xor", eor},
211 	{"+",   add},
212 //	{"add", add},
213 	{"-",   sub},
214 //	{"sub", sub},
215 	{"*",   mul},
216 //	{"mul", mul},
217 	{"/",   divide},
218 //	{"div", divide},
219 	{"p", print_no_pop},
220 	{"f", print_stack_no_pop},
221 	{"o", set_output_base},
222 };
223 
224 /* Feed the stack machine */
stack_machine(const char * argument)225 static void stack_machine(const char *argument)
226 {
227 	char *end;
228 	double number;
229 	const struct op *o;
230 
231  next:
232 //TODO: needs setlocale(LC_NUMERIC, "C")?
233 	number = strtod(argument, &end);
234 	if (end != argument) {
235 		argument = end;
236 		push(number);
237 		goto next;
238 	}
239 
240 	/* We might have matched a digit, eventually advance the argument */
241 	argument = skip_whitespace(argument);
242 
243 	if (*argument == '\0')
244 		return;
245 
246 	o = operators;
247 	do {
248 		char *after_name = is_prefixed_with(argument, o->name);
249 		if (after_name) {
250 			argument = after_name;
251 			o->function();
252 			goto next;
253 		}
254 		o++;
255 	} while (o != operators + ARRAY_SIZE(operators));
256 
257 	bb_error_msg_and_die("syntax error at '%s'", argument);
258 }
259 
process_file(FILE * fp)260 static void process_file(FILE *fp)
261 {
262 	char *line;
263 	while ((line = xmalloc_fgetline(fp)) != NULL) {
264 		stack_machine(line);
265 		free(line);
266 	}
267 }
268 
269 int dc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
dc_main(int argc UNUSED_PARAM,char ** argv)270 int dc_main(int argc UNUSED_PARAM, char **argv)
271 {
272 	bool script = 0;
273 
274 	INIT_G();
275 
276 	/* Run -e'SCRIPT' and -fFILE in order of appearance, then handle FILEs */
277 	for (;;) {
278 		int n = getopt(argc, argv, "e:f:");
279 		if (n <= 0)
280 			break;
281 		switch (n) {
282 		case 'e':
283 			script = 1;
284 			stack_machine(optarg);
285 			break;
286 		case 'f':
287 			script = 1;
288 			process_file(xfopen_for_read(optarg));
289 			break;
290 		default:
291 			bb_show_usage();
292 		}
293 	}
294 	argv += optind;
295 
296 	if (*argv) {
297 		do
298 			process_file(xfopen_for_read(*argv++));
299 		while (*argv);
300 	} else if (!script) {
301 		/* Take stuff from stdin if no args are given */
302 		process_file(stdin);
303 	}
304 
305 	return EXIT_SUCCESS;
306 }
307