source: branches/3.2/mindi-busybox/miscutils/dc.c @ 3232

Last change on this file since 3232 was 3232, checked in by bruno, 5 years ago
  • Update mindi-busybox to 1.21.1
File size: 5.0 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
4 */
5
6#include "libbb.h"
7#include <math.h>
8
9//usage:#define dc_trivial_usage
10//usage:       "EXPRESSION..."
11//usage:
12//usage:#define dc_full_usage "\n\n"
13//usage:       "Tiny RPN calculator. Operations:\n"
14//usage:       "+, add, -, sub, *, mul, /, div, %, mod, "IF_FEATURE_DC_LIBM("**, exp, ")"and, or, not, xor,\n"
15//usage:       "p - print top of the stack (without popping),\n"
16//usage:       "f - print entire stack,\n"
17//usage:       "o - pop the value and set output radix (must be 10, 16, 8 or 2).\n"
18//usage:       "Examples: 'dc 2 2 add p' -> 4, 'dc 8 8 mul 2 2 + / p' -> 16"
19//usage:
20//usage:#define dc_example_usage
21//usage:       "$ dc 2 2 + p\n"
22//usage:       "4\n"
23//usage:       "$ dc 8 8 \\* 2 2 + / p\n"
24//usage:       "16\n"
25//usage:       "$ dc 0 1 and p\n"
26//usage:       "0\n"
27//usage:       "$ dc 0 1 or p\n"
28//usage:       "1\n"
29//usage:       "$ echo 72 9 div 8 mul p | dc\n"
30//usage:       "64\n"
31
32#if 0
33typedef unsigned data_t;
34#define DATA_FMT ""
35#elif 0
36typedef unsigned long data_t;
37#define DATA_FMT "l"
38#else
39typedef unsigned long long data_t;
40#define DATA_FMT "ll"
41#endif
42
43
44struct globals {
45    unsigned pointer;
46    unsigned base;
47    double stack[1];
48} FIX_ALIASING;
49enum { STACK_SIZE = (COMMON_BUFSIZE - offsetof(struct globals, stack)) / sizeof(double) };
50#define G (*(struct globals*)&bb_common_bufsiz1)
51#define pointer   (G.pointer   )
52#define base      (G.base      )
53#define stack     (G.stack     )
54#define INIT_G() do { \
55    base = 10; \
56} while (0)
57
58
59static void push(double a)
60{
61    if (pointer >= STACK_SIZE)
62        bb_error_msg_and_die("stack overflow");
63    stack[pointer++] = a;
64}
65
66static double pop(void)
67{
68    if (pointer == 0)
69        bb_error_msg_and_die("stack underflow");
70    return stack[--pointer];
71}
72
73static void add(void)
74{
75    push(pop() + pop());
76}
77
78static void sub(void)
79{
80    double subtrahend = pop();
81
82    push(pop() - subtrahend);
83}
84
85static void mul(void)
86{
87    push(pop() * pop());
88}
89
90#if ENABLE_FEATURE_DC_LIBM
91static void power(void)
92{
93    double topower = pop();
94
95    push(pow(pop(), topower));
96}
97#endif
98
99static void divide(void)
100{
101    double divisor = pop();
102
103    push(pop() / divisor);
104}
105
106static void mod(void)
107{
108    data_t d = pop();
109
110    push((data_t) pop() % d);
111}
112
113static void and(void)
114{
115    push((data_t) pop() & (data_t) pop());
116}
117
118static void or(void)
119{
120    push((data_t) pop() | (data_t) pop());
121}
122
123static void eor(void)
124{
125    push((data_t) pop() ^ (data_t) pop());
126}
127
128static void not(void)
129{
130    push(~(data_t) pop());
131}
132
133static void set_output_base(void)
134{
135    static const char bases[] ALIGN1 = { 2, 8, 10, 16, 0 };
136    unsigned b = (unsigned)pop();
137
138    base = *strchrnul(bases, b);
139    if (base == 0) {
140        bb_error_msg("error, base %u is not supported", b);
141        base = 10;
142    }
143}
144
145static void print_base(double print)
146{
147    data_t x, i;
148
149    x = (data_t) print;
150    if (base == 10) {
151        if (x == print) /* exactly representable as unsigned integer */
152            printf("%"DATA_FMT"u\n", x);
153        else
154            printf("%g\n", print);
155        return;
156    }
157
158    switch (base) {
159    case 16:
160        printf("%"DATA_FMT"x\n", x);
161        break;
162    case 8:
163        printf("%"DATA_FMT"o\n", x);
164        break;
165    default: /* base 2 */
166        i = MAXINT(data_t) - (MAXINT(data_t) >> 1);
167        /* i is 100000...00000 */
168        do {
169            if (x & i)
170                break;
171            i >>= 1;
172        } while (i > 1);
173        do {
174            bb_putchar('1' - !(x & i));
175            i >>= 1;
176        } while (i);
177        bb_putchar('\n');
178    }
179}
180
181static void print_stack_no_pop(void)
182{
183    unsigned i = pointer;
184    while (i)
185        print_base(stack[--i]);
186}
187
188static void print_no_pop(void)
189{
190    print_base(stack[pointer-1]);
191}
192
193struct op {
194    const char name[4];
195    void (*function) (void);
196};
197
198static const struct op operators[] = {
199    {"+",   add},
200    {"add", add},
201    {"-",   sub},
202    {"sub", sub},
203    {"*",   mul},
204    {"mul", mul},
205    {"/",   divide},
206    {"div", divide},
207#if ENABLE_FEATURE_DC_LIBM
208    {"**",  power},
209    {"exp", power},
210    {"pow", power},
211#endif
212    {"%",   mod},
213    {"mod", mod},
214    {"and", and},
215    {"or",  or},
216    {"not", not},
217    {"eor", eor},
218    {"xor", eor},
219    {"p", print_no_pop},
220    {"f", print_stack_no_pop},
221    {"o", set_output_base},
222};
223
224static void stack_machine(const char *argument)
225{
226    char *end;
227    double d;
228    const struct op *o;
229
230    d = strtod(argument, &end);
231    if (end != argument && *end == '\0') {
232        push(d);
233        return;
234    }
235
236    o = operators;
237    do {
238        if (strcmp(o->name, argument) == 0) {
239            o->function();
240            return;
241        }
242        o++;
243    } while (o != operators + ARRAY_SIZE(operators));
244
245    bb_error_msg_and_die("syntax error at '%s'", argument);
246}
247
248int dc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
249int dc_main(int argc UNUSED_PARAM, char **argv)
250{
251    INIT_G();
252
253    argv++;
254    if (!argv[0]) {
255        /* take stuff from stdin if no args are given */
256        char *line;
257        char *cursor;
258        char *token;
259        while ((line = xmalloc_fgetline(stdin)) != NULL) {
260            cursor = line;
261            while (1) {
262                token = skip_whitespace(cursor);
263                if (*token == '\0')
264                    break;
265                cursor = skip_non_whitespace(token);
266                if (*cursor != '\0')
267                    *cursor++ = '\0';
268                stack_machine(token);
269            }
270            free(line);
271        }
272    } else {
273        // why? it breaks "dc -2 2 + p"
274        //if (argv[0][0] == '-')
275        //  bb_show_usage();
276        do {
277            stack_machine(*argv);
278        } while (*++argv);
279    }
280    return EXIT_SUCCESS;
281}
Note: See TracBrowser for help on using the repository browser.