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