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