blob: 17fdda8fd9e5fbb78b6b4be2cf127e7eba883647 [file] [log] [blame]
Erik Andersene49d5ec2000-02-08 19:58:47 +00001/* vi: set sw=4 ts=4: */
"Robert P. J. Day"801ab142006-07-12 07:56:04 +00002/*
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02003 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
"Robert P. J. Day"801ab142006-07-12 07:56:04 +00004 */
Eric Andersencc8ed391999-10-05 16:24:54 +00005
Denys Vlasenko9ca9ef22018-12-06 11:31:14 +01006/* config/applet/usage bits are in bc.c */
Denys Vlasenkof88e3bf2016-11-22 23:54:17 +01007
Denys Vlasenko9ca9ef22018-12-06 11:31:14 +01008//#include "libbb.h"
9//#include "common_bufsiz.h"
Denys Vlasenkofb4da162016-11-22 23:14:24 +010010#include <math.h>
11
Denys Vlasenko7a07b0e2010-07-29 04:00:27 +020012#if 0
13typedef unsigned data_t;
14#define DATA_FMT ""
15#elif 0
16typedef unsigned long data_t;
17#define DATA_FMT "l"
18#else
19typedef unsigned long long data_t;
20#define DATA_FMT "ll"
21#endif
Eric Andersencc8ed391999-10-05 16:24:54 +000022
Denis Vlasenkob44c7902008-03-17 09:29:43 +000023struct globals {
24 unsigned pointer;
25 unsigned base;
26 double stack[1];
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +010027} FIX_ALIASING;
Denis Vlasenkob44c7902008-03-17 09:29:43 +000028enum { STACK_SIZE = (COMMON_BUFSIZE - offsetof(struct globals, stack)) / sizeof(double) };
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +020029#define G (*(struct globals*)bb_common_bufsiz1)
Denis Vlasenkob44c7902008-03-17 09:29:43 +000030#define pointer (G.pointer )
31#define base (G.base )
32#define stack (G.stack )
Denis Vlasenkod8850f22008-12-30 10:40:05 +000033#define INIT_G() do { \
Denys Vlasenko47cfbf32016-04-21 18:18:48 +020034 setup_common_bufsiz(); \
Denis Vlasenkod8850f22008-12-30 10:40:05 +000035 base = 10; \
36} while (0)
Denis Vlasenkob44c7902008-03-17 09:29:43 +000037
Denys Vlasenkoc4603fb2015-05-25 13:31:25 +020038static void check_under(void)
39{
40 if (pointer == 0)
41 bb_error_msg_and_die("stack underflow");
42}
43
Erik Andersene49d5ec2000-02-08 19:58:47 +000044static void push(double a)
Eric Andersencc8ed391999-10-05 16:24:54 +000045{
Denis Vlasenko5b27fbe2007-03-24 14:06:51 +000046 if (pointer >= STACK_SIZE)
Manuel Novoa III cad53642003-03-19 09:13:01 +000047 bb_error_msg_and_die("stack overflow");
Matt Kraai3e856ce2000-12-01 02:55:13 +000048 stack[pointer++] = a;
Eric Andersencc8ed391999-10-05 16:24:54 +000049}
50
Aaron Lehmann2dd2d7a2001-12-06 03:29:37 +000051static double pop(void)
Eric Andersencc8ed391999-10-05 16:24:54 +000052{
Denys Vlasenkoc4603fb2015-05-25 13:31:25 +020053 check_under();
Eric Andersencc8ed391999-10-05 16:24:54 +000054 return stack[--pointer];
55}
56
Aaron Lehmann2dd2d7a2001-12-06 03:29:37 +000057static void add(void)
Eric Andersencc8ed391999-10-05 16:24:54 +000058{
59 push(pop() + pop());
60}
61
Aaron Lehmann2dd2d7a2001-12-06 03:29:37 +000062static void sub(void)
Eric Andersencc8ed391999-10-05 16:24:54 +000063{
Erik Andersene49d5ec2000-02-08 19:58:47 +000064 double subtrahend = pop();
Eric Andersencc8ed391999-10-05 16:24:54 +000065
66 push(pop() - subtrahend);
67}
68
Aaron Lehmann2dd2d7a2001-12-06 03:29:37 +000069static void mul(void)
Eric Andersencc8ed391999-10-05 16:24:54 +000070{
71 push(pop() * pop());
72}
73
Denis Vlasenko07832302008-10-20 08:43:10 +000074#if ENABLE_FEATURE_DC_LIBM
Eric Andersena9287742003-10-22 11:24:39 +000075static void power(void)
76{
77 double topower = pop();
78
79 push(pow(pop(), topower));
80}
Denis Vlasenko07832302008-10-20 08:43:10 +000081#endif
Eric Andersena9287742003-10-22 11:24:39 +000082
Aaron Lehmann2dd2d7a2001-12-06 03:29:37 +000083static void divide(void)
Eric Andersencc8ed391999-10-05 16:24:54 +000084{
Erik Andersene49d5ec2000-02-08 19:58:47 +000085 double divisor = pop();
86
Eric Andersencc8ed391999-10-05 16:24:54 +000087 push(pop() / divisor);
88}
89
Eric Andersena9287742003-10-22 11:24:39 +000090static void mod(void)
91{
Denys Vlasenko7a07b0e2010-07-29 04:00:27 +020092 data_t d = pop();
Eric Andersena9287742003-10-22 11:24:39 +000093
Denys Vlasenko7a07b0e2010-07-29 04:00:27 +020094 push((data_t) pop() % d);
Eric Andersena9287742003-10-22 11:24:39 +000095}
96
Aaron Lehmann2dd2d7a2001-12-06 03:29:37 +000097static void and(void)
Eric Andersencc8ed391999-10-05 16:24:54 +000098{
Denys Vlasenko7a07b0e2010-07-29 04:00:27 +020099 push((data_t) pop() & (data_t) pop());
Eric Andersencc8ed391999-10-05 16:24:54 +0000100}
101
Aaron Lehmann2dd2d7a2001-12-06 03:29:37 +0000102static void or(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000103{
Denys Vlasenko7a07b0e2010-07-29 04:00:27 +0200104 push((data_t) pop() | (data_t) pop());
Eric Andersencc8ed391999-10-05 16:24:54 +0000105}
106
Aaron Lehmann2dd2d7a2001-12-06 03:29:37 +0000107static void eor(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000108{
Denys Vlasenko7a07b0e2010-07-29 04:00:27 +0200109 push((data_t) pop() ^ (data_t) pop());
Eric Andersencc8ed391999-10-05 16:24:54 +0000110}
111
Aaron Lehmann2dd2d7a2001-12-06 03:29:37 +0000112static void not(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000113{
Denys Vlasenko7a07b0e2010-07-29 04:00:27 +0200114 push(~(data_t) pop());
Eric Andersencc8ed391999-10-05 16:24:54 +0000115}
116
Glenn L McGrath6d074322002-12-12 10:31:53 +0000117static void set_output_base(void)
118{
Denis Vlasenko6b38e182008-10-30 23:25:50 +0000119 static const char bases[] ALIGN1 = { 2, 8, 10, 16, 0 };
120 unsigned b = (unsigned)pop();
121
122 base = *strchrnul(bases, b);
123 if (base == 0) {
124 bb_error_msg("error, base %u is not supported", b);
Denis Vlasenko5b27fbe2007-03-24 14:06:51 +0000125 base = 10;
Glenn L McGrath6d074322002-12-12 10:31:53 +0000126 }
127}
128
129static void print_base(double print)
130{
Denys Vlasenko7a07b0e2010-07-29 04:00:27 +0200131 data_t x, i;
Denis Vlasenko6b38e182008-10-30 23:25:50 +0000132
Denys Vlasenko7a07b0e2010-07-29 04:00:27 +0200133 x = (data_t) print;
Denis Vlasenko6b38e182008-10-30 23:25:50 +0000134 if (base == 10) {
Denys Vlasenko7a07b0e2010-07-29 04:00:27 +0200135 if (x == print) /* exactly representable as unsigned integer */
136 printf("%"DATA_FMT"u\n", x);
137 else
138 printf("%g\n", print);
Denis Vlasenko6b38e182008-10-30 23:25:50 +0000139 return;
140 }
141
Denis Vlasenko6b38e182008-10-30 23:25:50 +0000142 switch (base) {
143 case 16:
Denys Vlasenko7a07b0e2010-07-29 04:00:27 +0200144 printf("%"DATA_FMT"x\n", x);
Denis Vlasenko6b38e182008-10-30 23:25:50 +0000145 break;
146 case 8:
Denys Vlasenko7a07b0e2010-07-29 04:00:27 +0200147 printf("%"DATA_FMT"o\n", x);
Denis Vlasenko6b38e182008-10-30 23:25:50 +0000148 break;
149 default: /* base 2 */
Denys Vlasenko7a07b0e2010-07-29 04:00:27 +0200150 i = MAXINT(data_t) - (MAXINT(data_t) >> 1);
151 /* i is 100000...00000 */
Denis Vlasenko6b38e182008-10-30 23:25:50 +0000152 do {
Denys Vlasenko7a07b0e2010-07-29 04:00:27 +0200153 if (x & i)
154 break;
Denis Vlasenko6b38e182008-10-30 23:25:50 +0000155 i >>= 1;
156 } while (i > 1);
157 do {
158 bb_putchar('1' - !(x & i));
159 i >>= 1;
160 } while (i);
161 bb_putchar('\n');
162 }
Glenn L McGrath6d074322002-12-12 10:31:53 +0000163}
164
165static void print_stack_no_pop(void)
166{
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000167 unsigned i = pointer;
Glenn L McGrath6d074322002-12-12 10:31:53 +0000168 while (i)
169 print_base(stack[--i]);
170}
171
172static void print_no_pop(void)
173{
Denys Vlasenkoc4603fb2015-05-25 13:31:25 +0200174 check_under();
Glenn L McGrath6d074322002-12-12 10:31:53 +0000175 print_base(stack[pointer-1]);
176}
177
Eric Andersencc8ed391999-10-05 16:24:54 +0000178struct op {
Denis Vlasenko5b27fbe2007-03-24 14:06:51 +0000179 const char name[4];
Aaron Lehmann2dd2d7a2001-12-06 03:29:37 +0000180 void (*function) (void);
Eric Andersencc8ed391999-10-05 16:24:54 +0000181};
182
Erik Andersene49d5ec2000-02-08 19:58:47 +0000183static const struct op operators[] = {
Denis Vlasenko07832302008-10-20 08:43:10 +0000184#if ENABLE_FEATURE_DC_LIBM
Denys Vlasenkod0bc5fd2018-12-08 18:59:07 +0100185 {"^", power},
186// {"exp", power},
187// {"pow", power},
Denis Vlasenko07832302008-10-20 08:43:10 +0000188#endif
Eric Andersena9287742003-10-22 11:24:39 +0000189 {"%", mod},
Denys Vlasenkod0bc5fd2018-12-08 18:59:07 +0100190// {"mod", mod},
191 // logic ops are not standard, remove?
Erik Andersene49d5ec2000-02-08 19:58:47 +0000192 {"and", and},
John Beppuc0352542000-06-21 18:00:46 +0000193 {"or", or},
Erik Andersen5e1189e2000-04-15 16:34:54 +0000194 {"not", not},
Eric Andersena9287742003-10-22 11:24:39 +0000195 {"xor", eor},
Bernhard Reutner-Fischer70e30e82015-02-16 17:12:04 +0100196 {"+", add},
Denys Vlasenkod0bc5fd2018-12-08 18:59:07 +0100197// {"add", add},
Bernhard Reutner-Fischer70e30e82015-02-16 17:12:04 +0100198 {"-", sub},
Denys Vlasenkod0bc5fd2018-12-08 18:59:07 +0100199// {"sub", sub},
Bernhard Reutner-Fischer70e30e82015-02-16 17:12:04 +0100200 {"*", mul},
Denys Vlasenkod0bc5fd2018-12-08 18:59:07 +0100201// {"mul", mul},
Bernhard Reutner-Fischer70e30e82015-02-16 17:12:04 +0100202 {"/", divide},
Denys Vlasenkod0bc5fd2018-12-08 18:59:07 +0100203// {"div", divide},
Glenn L McGrath6d074322002-12-12 10:31:53 +0000204 {"p", print_no_pop},
205 {"f", print_stack_no_pop},
206 {"o", set_output_base},
Eric Andersencc8ed391999-10-05 16:24:54 +0000207};
208
Bernhard Reutner-Fischer70e30e82015-02-16 17:12:04 +0100209/* Feed the stack machine */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000210static void stack_machine(const char *argument)
Eric Andersencc8ed391999-10-05 16:24:54 +0000211{
Denys Vlasenko9daf33f2013-01-18 13:30:13 +0100212 char *end;
Bernhard Reutner-Fischer70e30e82015-02-16 17:12:04 +0100213 double number;
Denys Vlasenko9daf33f2013-01-18 13:30:13 +0100214 const struct op *o;
Eric Andersencc8ed391999-10-05 16:24:54 +0000215
Bernhard Reutner-Fischer70e30e82015-02-16 17:12:04 +0100216 next:
217 number = strtod(argument, &end);
218 if (end != argument) {
219 argument = end;
220 push(number);
221 goto next;
Eric Andersencc8ed391999-10-05 16:24:54 +0000222 }
223
Bernhard Reutner-Fischer70e30e82015-02-16 17:12:04 +0100224 /* We might have matched a digit, eventually advance the argument */
225 argument = skip_whitespace(argument);
226
227 if (*argument == '\0')
228 return;
229
Denys Vlasenko9daf33f2013-01-18 13:30:13 +0100230 o = operators;
231 do {
Denys Vlasenko8dff01d2015-03-12 17:48:34 +0100232 char *after_name = is_prefixed_with(argument, o->name);
233 if (after_name) {
234 argument = after_name;
Denis Vlasenko5b27fbe2007-03-24 14:06:51 +0000235 o->function();
Bernhard Reutner-Fischer70e30e82015-02-16 17:12:04 +0100236 goto next;
Eric Andersencc8ed391999-10-05 16:24:54 +0000237 }
238 o++;
Denys Vlasenko9daf33f2013-01-18 13:30:13 +0100239 } while (o != operators + ARRAY_SIZE(operators));
240
Denys Vlasenkobd1de182009-12-30 18:37:08 +0100241 bb_error_msg_and_die("syntax error at '%s'", argument);
Eric Andersencc8ed391999-10-05 16:24:54 +0000242}
243
Denys Vlasenkod0bc5fd2018-12-08 18:59:07 +0100244static void process_file(FILE *fp)
245{
246 char *line;
247 while ((line = xmalloc_fgetline(fp)) != NULL) {
248 stack_machine(line);
249 free(line);
250 }
251}
252
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000253int dc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000254int dc_main(int argc UNUSED_PARAM, char **argv)
Eric Andersencc8ed391999-10-05 16:24:54 +0000255{
Denys Vlasenkod0bc5fd2018-12-08 18:59:07 +0100256 bool script = 0;
257
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000258 INIT_G();
259
Denys Vlasenkod0bc5fd2018-12-08 18:59:07 +0100260 /* Run -e'SCRIPT' and -fFILE in order of appearance, then handle FILEs */
261 for (;;) {
262 int n = getopt(argc, argv, "e:f:");
263 if (n <= 0)
264 break;
265 switch (n) {
266 case 'e':
267 script = 1;
268 stack_machine(optarg);
269 break;
270 case 'f':
271 script = 1;
272 process_file(xfopen_for_read(optarg));
273 break;
274 default:
275 bb_show_usage();
John Beppu5db60a72000-06-12 22:59:12 +0000276 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000277 }
Denys Vlasenkod0bc5fd2018-12-08 18:59:07 +0100278 argv += optind;
279
280 if (*argv) {
281 do
282 process_file(xfopen_for_read(*argv++));
283 while (*argv);
284 } else if (!script) {
285 /* Take stuff from stdin if no args are given */
286 process_file(stdin);
287 }
288
Matt Kraai3e856ce2000-12-01 02:55:13 +0000289 return EXIT_SUCCESS;
Eric Andersencc8ed391999-10-05 16:24:54 +0000290}