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