Patch by Paul Whittaker, make busybox dc compatable with GNU dc.
[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 struct op {
118         const char *name;
119         void (*function) (void);
120 };
121
122 static const struct op operators[] = {
123         {"+",   add},
124         {"add", add},
125         {"-",   sub},
126         {"sub", sub},
127         {"*",   mul},
128         {"mul", mul},
129         {"/",   divide},
130         {"div", divide},
131         {"**",  power},
132         {"exp", power},
133         {"pow", power},
134         {"%",   mod},
135         {"mod", mod},
136         {"and", and},
137         {"or",  or},
138         {"not", not},
139         {"eor", eor},
140         {"xor", eor},
141         {"p", print_no_pop},
142         {"f", print_stack_no_pop},
143         {"o", set_output_base},
144         {0,     0}
145 };
146
147 static void stack_machine(const char *argument)
148 {
149         char *endPointer = 0;
150         double d;
151         const struct op *o = operators;
152
153         if (argument == 0)
154                 return;
155
156         d = strtod(argument, &endPointer);
157
158         if (endPointer != argument) {
159                 push(d);
160                 return;
161         }
162
163         while (o->name != 0) {
164                 if (strcmp(o->name, argument) == 0) {
165                         (*(o->function)) ();
166                         return;
167                 }
168                 o++;
169         }
170         bb_error_msg_and_die("%s: syntax error.", argument);
171 }
172
173 /* return pointer to next token in buffer and set *buffer to one char
174  * past the end of the above mentioned token
175  */
176 static char *get_token(char **buffer)
177 {
178         char *start   = NULL;
179         char *current = *buffer;
180
181         while (isspace(*current)) { current++; }
182         if (*current != 0) {
183                 start = current;
184                 while (!isspace(*current) && *current != 0) { current++; }
185                 *buffer = current;
186         }
187         return start;
188 }
189
190 /* In Perl one might say, scalar m|\s*(\S+)\s*|g */
191 static int number_of_tokens(char *buffer)
192 {
193         int   i = 0;
194         char *b = buffer;
195         while (get_token(&b)) { i++; }
196         return i;
197 }
198
199 int dc_main(int argc, char **argv)
200 {
201         /* take stuff from stdin if no args are given */
202         if (argc <= 1) {
203                 int i, len;
204                 char *line   = NULL;
205                 char *cursor = NULL;
206                 char *token  = NULL;
207                 while ((line = bb_get_chomped_line_from_file(stdin))) {
208                         cursor = line;
209                         len = number_of_tokens(line);
210                         for (i = 0; i < len; i++) {
211                                 token = get_token(&cursor);
212                                 *cursor++ = 0;
213                                 stack_machine(token);
214                         }
215                         free(line);
216                 }
217         } else {
218                 if (*argv[1]=='-')
219                         bb_show_usage();
220                 while (argc >= 2) {
221                         stack_machine(argv[1]);
222                         argv++;
223                         argc--;
224                 }
225         }
226         stack_machine(0);
227         return EXIT_SUCCESS;
228 }