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