Move shell descriptions to the config system
[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 divide(void)
48 {
49         double divisor = pop();
50
51         push(pop() / divisor);
52 }
53
54 static void and(void)
55 {
56         push((unsigned int) pop() & (unsigned int) pop());
57 }
58
59 static void or(void)
60 {
61         push((unsigned int) pop() | (unsigned int) pop());
62 }
63
64 static void eor(void)
65 {
66         push((unsigned int) pop() ^ (unsigned int) pop());
67 }
68
69 static void not(void)
70 {
71         push(~(unsigned int) pop());
72 }
73
74 static void set_output_base(void)
75 {
76         base=(unsigned char)pop();      
77         if ((base != 10) && (base != 16)) {
78                 fprintf(stderr, "Error: base = %d is not supported.\n", base);
79                 base=10;
80         }
81 }
82
83 static void print_base(double print)
84 {
85         if (base == 16) 
86                 printf("%x\n", (unsigned int)print);
87         else
88         printf("%g\n", print);
89 }
90
91 static void print_stack_no_pop(void)
92 {
93         unsigned int i=pointer;
94         while (i)
95                 print_base(stack[--i]);
96 }
97
98 static void print_no_pop(void)
99 {
100         print_base(stack[pointer-1]);
101 }
102
103 static void print(void)
104 {
105         print_base(pop());
106 }
107
108 struct op {
109         const char *name;
110         void (*function) (void);
111 };
112
113 static const struct op operators[] = {
114         {"+",   add},
115         {"add", add},
116         {"-",   sub},
117         {"sub", sub},
118         {"*",   mul},
119         {"mul", mul},
120         {"/",   divide},
121         {"div", divide},
122         {"and", and},
123         {"or",  or},
124         {"not", not},
125         {"eor", eor},
126         {"p", print_no_pop},
127         {"f", print_stack_no_pop},
128         {"o", set_output_base},
129         {0,     0}
130 };
131
132 static void stack_machine(const char *argument)
133 {
134         char *endPointer = 0;
135         double d;
136         const struct op *o = operators;
137
138         if (argument == 0) {
139                 print();
140                 return;
141         }
142
143         d = strtod(argument, &endPointer);
144
145         if (endPointer != argument) {
146                 push(d);
147                 return;
148         }
149
150         while (o->name != 0) {
151                 if (strcmp(o->name, argument) == 0) {
152                         (*(o->function)) ();
153                         return;
154                 }
155                 o++;
156         }
157         bb_error_msg_and_die("%s: syntax error.", argument);
158 }
159
160 /* return pointer to next token in buffer and set *buffer to one char
161  * past the end of the above mentioned token 
162  */
163 static char *get_token(char **buffer)
164 {
165         char *start   = NULL;
166         char *current = *buffer;
167
168         while (isspace(*current)) { current++; }
169         if (*current != 0) {
170                 start = current;
171                 while (!isspace(*current) && current != 0) { current++; }
172                 *buffer = current;
173         }
174         return start;
175 }
176
177 /* In Perl one might say, scalar m|\s*(\S+)\s*|g */
178 static int number_of_tokens(char *buffer)
179 {
180         int   i = 0;
181         char *b = buffer;
182         while (get_token(&b)) { i++; }
183         return i;
184 }
185
186 int dc_main(int argc, char **argv)
187 {
188         /* take stuff from stdin if no args are given */
189         if (argc <= 1) {
190                 int i, len;
191                 char *line   = NULL;
192                 char *cursor = NULL;
193                 char *token  = NULL;
194                 while ((line = bb_get_chomped_line_from_file(stdin))) {
195                         cursor = line;
196                         len = number_of_tokens(line);
197                         for (i = 0; i < len; i++) {
198                                 token = get_token(&cursor);
199                                 *cursor++ = 0;
200                                 stack_machine(token);
201                         }
202                         free(line);
203                 }
204         } else {
205                 if (*argv[1]=='-')
206                         bb_show_usage();
207                 while (argc >= 2) {
208                         stack_machine(argv[1]);
209                         argv++;
210                         argc--;
211                 }
212         }
213         stack_machine(0);
214         return EXIT_SUCCESS;
215 }