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