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