Give beppu credit for fixing grep "Line too long" bug.
[oweals/busybox.git] / math.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 math_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         {"-", sub},
97         {"*", mul},
98         {"/", divide},
99         {"and", and},
100         {"or", or},
101         {"not", not},
102         {"eor", eor},
103         {0, 0}
104 };
105
106 static void stack_machine(const char *argument)
107 {
108         char *endPointer = 0;
109         double d;
110         const struct op *o = operators;
111
112         if (argument == 0) {
113                 print();
114                 return;
115         }
116
117         d = strtod(argument, &endPointer);
118
119         if (endPointer != argument) {
120                 push(d);
121                 return;
122         }
123
124         while (o->name != 0) {
125                 if (strcmp(o->name, argument) == 0) {
126                         (*(o->function)) ();
127                         return;
128                 }
129                 o++;
130         }
131         fprintf(stderr, "math: %s: syntax error.\n", argument);
132         exit(-1);
133 }
134
135 /* return pointer to next token in buffer and set *buffer to one char
136  * past the end of the above mentioned token 
137  */
138 static char *get_token(char **buffer)
139 {
140         char *start   = NULL;
141         char *current = *buffer;
142
143         while (isspace(*current)) { current++; }
144         if (*current != 0) {
145                 start = current;
146                 while (!isspace(*current) && current != 0) { current++; }
147                 *buffer = current;
148         }
149         return start;
150 }
151
152 /* In Perl one might say, scalar m|\s*(\S+)\s*|g */
153 static int number_of_tokens(char *buffer)
154 {
155         int   i = 0;
156         char *b = buffer;
157         while (get_token(&b)) { i++; }
158         return i;
159 }
160
161 int math_main(int argc, char **argv)
162 {
163         /* take stuff from stdin if no args are given */
164         if (argc <= 1) {
165                 int i, len;
166                 char *line   = NULL;
167                 char *cursor = NULL;
168                 char *token  = NULL;
169                 while ((line = cstring_lineFromFile(stdin))) {
170                         cursor = line;
171                         len = number_of_tokens(line);
172                         for (i = 0; i < len; i++) {
173                                 token = get_token(&cursor);
174                                 *cursor++ = 0;
175                                 stack_machine(token);
176                         }
177                         free(line);
178                 }
179         } else {
180                 if (*argv[1]=='-')
181                         usage(math_usage);
182                 while (argc >= 2) {
183                         stack_machine(argv[1]);
184                         argv++;
185                         argc--;
186                 }
187         }
188         stack_machine(0);
189         exit( TRUE);
190 }