68ecd8a3f8e690325bcdb77c0da3effc77c0fff7
[oweals/busybox.git] / miscutils / dc.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
4  */
5
6 #include "libbb.h"
7 #include <math.h>
8
9 /* Tiny RPN calculator, because "expr" didn't give me bitwise operations. */
10
11
12 struct globals {
13         unsigned pointer;
14         unsigned base;
15         double stack[1];
16 };
17 enum { STACK_SIZE = (COMMON_BUFSIZE - offsetof(struct globals, stack)) / sizeof(double) };
18 #define G (*(struct globals*)&bb_common_bufsiz1)
19 #define pointer   (G.pointer   )
20 #define base      (G.base      )
21 #define stack     (G.stack     )
22
23
24 static void push(double a)
25 {
26         if (pointer >= STACK_SIZE)
27                 bb_error_msg_and_die("stack overflow");
28         stack[pointer++] = a;
29 }
30
31 static double pop(void)
32 {
33         if (pointer == 0)
34                 bb_error_msg_and_die("stack underflow");
35         return stack[--pointer];
36 }
37
38 static void add(void)
39 {
40         push(pop() + pop());
41 }
42
43 static void sub(void)
44 {
45         double subtrahend = pop();
46
47         push(pop() - subtrahend);
48 }
49
50 static void mul(void)
51 {
52         push(pop() * pop());
53 }
54
55 static void power(void)
56 {
57         double topower = pop();
58
59         push(pow(pop(), topower));
60 }
61
62 static void divide(void)
63 {
64         double divisor = pop();
65
66         push(pop() / divisor);
67 }
68
69 static void mod(void)
70 {
71         unsigned d = pop();
72
73         push((unsigned) pop() % d);
74 }
75
76 static void and(void)
77 {
78         push((unsigned) pop() & (unsigned) pop());
79 }
80
81 static void or(void)
82 {
83         push((unsigned) pop() | (unsigned) pop());
84 }
85
86 static void eor(void)
87 {
88         push((unsigned) pop() ^ (unsigned) pop());
89 }
90
91 static void not(void)
92 {
93         push(~(unsigned) pop());
94 }
95
96 static void set_output_base(void)
97 {
98         base = (unsigned)pop();
99         if ((base != 10) && (base != 16)) {
100                 bb_error_msg("error, base %d is not supported", base);
101                 base = 10;
102         }
103 }
104
105 static void print_base(double print)
106 {
107         if (base == 16)
108                 printf("%x\n", (unsigned)print);
109         else
110                 printf("%g\n", print);
111 }
112
113 static void print_stack_no_pop(void)
114 {
115         unsigned i = pointer;
116         while (i)
117                 print_base(stack[--i]);
118 }
119
120 static void print_no_pop(void)
121 {
122         print_base(stack[pointer-1]);
123 }
124
125 struct op {
126         const char name[4];
127         void (*function) (void);
128 };
129
130 static const struct op operators[] = {
131         {"+",   add},
132         {"add", add},
133         {"-",   sub},
134         {"sub", sub},
135         {"*",   mul},
136         {"mul", mul},
137         {"/",   divide},
138         {"div", divide},
139         {"**",  power},
140         {"exp", power},
141         {"pow", power},
142         {"%",   mod},
143         {"mod", mod},
144         {"and", and},
145         {"or",  or},
146         {"not", not},
147         {"eor", eor},
148         {"xor", eor},
149         {"p", print_no_pop},
150         {"f", print_stack_no_pop},
151         {"o", set_output_base},
152         { /* zero filled */ }
153 };
154
155 static void stack_machine(const char *argument)
156 {
157         char *endPointer;
158         double d;
159         const struct op *o = operators;
160
161         if (argument == 0)
162                 return;
163
164         d = strtod(argument, &endPointer);
165
166         if (endPointer != argument) {
167                 push(d);
168                 return;
169         }
170
171         while (o->name[0]) {
172                 if (strcmp(o->name, argument) == 0) {
173                         o->function();
174                         return;
175                 }
176                 o++;
177         }
178         bb_error_msg_and_die("%s: syntax error", argument);
179 }
180
181 /* return pointer to next token in buffer and set *buffer to one char
182  * past the end of the above mentioned token
183  */
184 static char *get_token(char **buffer)
185 {
186         char *current = skip_whitespace(*buffer);
187         if (*current != '\0') {
188                 *buffer = skip_non_whitespace(current);
189                 return current;
190         }
191         return NULL;
192 }
193
194 int dc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
195 int dc_main(int argc ATTRIBUTE_UNUSED, char **argv)
196 {
197         argv++;
198         if (!argv[0]) {
199                 /* take stuff from stdin if no args are given */
200                 char *line;
201                 char *cursor;
202                 char *token;
203                 while ((line = xmalloc_getline(stdin)) != NULL) {
204                         cursor = line;
205                         while (1) {
206                                 token = get_token(&cursor);
207                                 if (!token) break;
208                                 *cursor++ = '\0';
209                                 stack_machine(token);
210                         }
211                         free(line);
212                 }
213         } else {
214                 if (argv[0][0] == '-')
215                         bb_show_usage();
216                 do {
217                         stack_machine(*argv);
218                 } while (*++argv);
219         }
220         return EXIT_SUCCESS;
221 }