busybox/miscutils/dc.c
<<
>>
Prefs
   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 check_under(void)
  60{
  61        if (pointer == 0)
  62                bb_error_msg_and_die("stack underflow");
  63}
  64
  65static void push(double a)
  66{
  67        if (pointer >= STACK_SIZE)
  68                bb_error_msg_and_die("stack overflow");
  69        stack[pointer++] = a;
  70}
  71
  72static double pop(void)
  73{
  74        check_under();
  75        return stack[--pointer];
  76}
  77
  78static void add(void)
  79{
  80        push(pop() + pop());
  81}
  82
  83static void sub(void)
  84{
  85        double subtrahend = pop();
  86
  87        push(pop() - subtrahend);
  88}
  89
  90static void mul(void)
  91{
  92        push(pop() * pop());
  93}
  94
  95#if ENABLE_FEATURE_DC_LIBM
  96static void power(void)
  97{
  98        double topower = pop();
  99
 100        push(pow(pop(), topower));
 101}
 102#endif
 103
 104static void divide(void)
 105{
 106        double divisor = pop();
 107
 108        push(pop() / divisor);
 109}
 110
 111static void mod(void)
 112{
 113        data_t d = pop();
 114
 115        push((data_t) pop() % d);
 116}
 117
 118static void and(void)
 119{
 120        push((data_t) pop() & (data_t) pop());
 121}
 122
 123static void or(void)
 124{
 125        push((data_t) pop() | (data_t) pop());
 126}
 127
 128static void eor(void)
 129{
 130        push((data_t) pop() ^ (data_t) pop());
 131}
 132
 133static void not(void)
 134{
 135        push(~(data_t) pop());
 136}
 137
 138static void set_output_base(void)
 139{
 140        static const char bases[] ALIGN1 = { 2, 8, 10, 16, 0 };
 141        unsigned b = (unsigned)pop();
 142
 143        base = *strchrnul(bases, b);
 144        if (base == 0) {
 145                bb_error_msg("error, base %u is not supported", b);
 146                base = 10;
 147        }
 148}
 149
 150static void print_base(double print)
 151{
 152        data_t x, i;
 153
 154        x = (data_t) print;
 155        if (base == 10) {
 156                if (x == print) /* exactly representable as unsigned integer */
 157                        printf("%"DATA_FMT"u\n", x);
 158                else
 159                        printf("%g\n", print);
 160                return;
 161        }
 162
 163        switch (base) {
 164        case 16:
 165                printf("%"DATA_FMT"x\n", x);
 166                break;
 167        case 8:
 168                printf("%"DATA_FMT"o\n", x);
 169                break;
 170        default: /* base 2 */
 171                i = MAXINT(data_t) - (MAXINT(data_t) >> 1);
 172                /* i is 100000...00000 */
 173                do {
 174                        if (x & i)
 175                                break;
 176                        i >>= 1;
 177                } while (i > 1);
 178                do {
 179                        bb_putchar('1' - !(x & i));
 180                        i >>= 1;
 181                } while (i);
 182                bb_putchar('\n');
 183        }
 184}
 185
 186static void print_stack_no_pop(void)
 187{
 188        unsigned i = pointer;
 189        while (i)
 190                print_base(stack[--i]);
 191}
 192
 193static void print_no_pop(void)
 194{
 195        check_under();
 196        print_base(stack[pointer-1]);
 197}
 198
 199struct op {
 200        const char name[4];
 201        void (*function) (void);
 202};
 203
 204static const struct op operators[] = {
 205#if ENABLE_FEATURE_DC_LIBM
 206        {"**",  power},
 207        {"exp", power},
 208        {"pow", power},
 209#endif
 210        {"%",   mod},
 211        {"mod", mod},
 212        {"and", and},
 213        {"or",  or},
 214        {"not", not},
 215        {"eor", eor},
 216        {"xor", eor},
 217        {"+",   add},
 218        {"add", add},
 219        {"-",   sub},
 220        {"sub", sub},
 221        {"*",   mul},
 222        {"mul", mul},
 223        {"/",   divide},
 224        {"div", divide},
 225        {"p", print_no_pop},
 226        {"f", print_stack_no_pop},
 227        {"o", set_output_base},
 228};
 229
 230/* Feed the stack machine */
 231static void stack_machine(const char *argument)
 232{
 233        char *end;
 234        double number;
 235        const struct op *o;
 236
 237 next:
 238        number = strtod(argument, &end);
 239        if (end != argument) {
 240                argument = end;
 241                push(number);
 242                goto next;
 243        }
 244
 245        /* We might have matched a digit, eventually advance the argument */
 246        argument = skip_whitespace(argument);
 247
 248        if (*argument == '\0')
 249                return;
 250
 251        o = operators;
 252        do {
 253                char *after_name = is_prefixed_with(argument, o->name);
 254                if (after_name) {
 255                        argument = after_name;
 256                        o->function();
 257                        goto next;
 258                }
 259                o++;
 260        } while (o != operators + ARRAY_SIZE(operators));
 261
 262        bb_error_msg_and_die("syntax error at '%s'", argument);
 263}
 264
 265int dc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 266int dc_main(int argc UNUSED_PARAM, char **argv)
 267{
 268        INIT_G();
 269
 270        argv++;
 271        if (!argv[0]) {
 272                /* take stuff from stdin if no args are given */
 273                char *line;
 274                while ((line = xmalloc_fgetline(stdin)) != NULL) {
 275                        stack_machine(line);
 276                        free(line);
 277                }
 278        } else {
 279                do {
 280                        stack_machine(*argv);
 281                } while (*++argv);
 282        }
 283        return EXIT_SUCCESS;
 284}
 285