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//config:config DC
   6//config:       bool "dc (4.2 kb)"
   7//config:       default y
   8//config:       help
   9//config:       Dc is a reverse-polish desk calculator which supports unlimited
  10//config:       precision arithmetic.
  11//config:
  12//config:config FEATURE_DC_LIBM
  13//config:       bool "Enable power and exp functions (requires libm)"
  14//config:       default y
  15//config:       depends on DC
  16//config:       help
  17//config:       Enable power and exp functions.
  18//config:       NOTE: This will require libm to be present for linking.
  19
  20//applet:IF_DC(APPLET(dc, BB_DIR_USR_BIN, BB_SUID_DROP))
  21
  22//kbuild:lib-$(CONFIG_DC) += dc.o
  23
  24//usage:#define dc_trivial_usage
  25//usage:       "EXPRESSION..."
  26//usage:
  27//usage:#define dc_full_usage "\n\n"
  28//usage:       "Tiny RPN calculator. Operations:\n"
  29//usage:       "+, add, -, sub, *, mul, /, div, %, mod, "IF_FEATURE_DC_LIBM("**, exp, ")"and, or, not, xor,\n"
  30//usage:       "p - print top of the stack (without popping),\n"
  31//usage:       "f - print entire stack,\n"
  32//usage:       "o - pop the value and set output radix (must be 10, 16, 8 or 2).\n"
  33//usage:       "Examples: 'dc 2 2 add p' -> 4, 'dc 8 8 mul 2 2 + / p' -> 16"
  34//usage:
  35//usage:#define dc_example_usage
  36//usage:       "$ dc 2 2 + p\n"
  37//usage:       "4\n"
  38//usage:       "$ dc 8 8 \\* 2 2 + / p\n"
  39//usage:       "16\n"
  40//usage:       "$ dc 0 1 and p\n"
  41//usage:       "0\n"
  42//usage:       "$ dc 0 1 or p\n"
  43//usage:       "1\n"
  44//usage:       "$ echo 72 9 div 8 mul p | dc\n"
  45//usage:       "64\n"
  46
  47#include "libbb.h"
  48#include "common_bufsiz.h"
  49#include <math.h>
  50
  51#if 0
  52typedef unsigned data_t;
  53#define DATA_FMT ""
  54#elif 0
  55typedef unsigned long data_t;
  56#define DATA_FMT "l"
  57#else
  58typedef unsigned long long data_t;
  59#define DATA_FMT "ll"
  60#endif
  61
  62
  63struct globals {
  64        unsigned pointer;
  65        unsigned base;
  66        double stack[1];
  67} FIX_ALIASING;
  68enum { STACK_SIZE = (COMMON_BUFSIZE - offsetof(struct globals, stack)) / sizeof(double) };
  69#define G (*(struct globals*)bb_common_bufsiz1)
  70#define pointer   (G.pointer   )
  71#define base      (G.base      )
  72#define stack     (G.stack     )
  73#define INIT_G() do { \
  74        setup_common_bufsiz(); \
  75        base = 10; \
  76} while (0)
  77
  78
  79static void check_under(void)
  80{
  81        if (pointer == 0)
  82                bb_error_msg_and_die("stack underflow");
  83}
  84
  85static void push(double a)
  86{
  87        if (pointer >= STACK_SIZE)
  88                bb_error_msg_and_die("stack overflow");
  89        stack[pointer++] = a;
  90}
  91
  92static double pop(void)
  93{
  94        check_under();
  95        return stack[--pointer];
  96}
  97
  98static void add(void)
  99{
 100        push(pop() + pop());
 101}
 102
 103static void sub(void)
 104{
 105        double subtrahend = pop();
 106
 107        push(pop() - subtrahend);
 108}
 109
 110static void mul(void)
 111{
 112        push(pop() * pop());
 113}
 114
 115#if ENABLE_FEATURE_DC_LIBM
 116static void power(void)
 117{
 118        double topower = pop();
 119
 120        push(pow(pop(), topower));
 121}
 122#endif
 123
 124static void divide(void)
 125{
 126        double divisor = pop();
 127
 128        push(pop() / divisor);
 129}
 130
 131static void mod(void)
 132{
 133        data_t d = pop();
 134
 135        push((data_t) pop() % d);
 136}
 137
 138static void and(void)
 139{
 140        push((data_t) pop() & (data_t) pop());
 141}
 142
 143static void or(void)
 144{
 145        push((data_t) pop() | (data_t) pop());
 146}
 147
 148static void eor(void)
 149{
 150        push((data_t) pop() ^ (data_t) pop());
 151}
 152
 153static void not(void)
 154{
 155        push(~(data_t) pop());
 156}
 157
 158static void set_output_base(void)
 159{
 160        static const char bases[] ALIGN1 = { 2, 8, 10, 16, 0 };
 161        unsigned b = (unsigned)pop();
 162
 163        base = *strchrnul(bases, b);
 164        if (base == 0) {
 165                bb_error_msg("error, base %u is not supported", b);
 166                base = 10;
 167        }
 168}
 169
 170static void print_base(double print)
 171{
 172        data_t x, i;
 173
 174        x = (data_t) print;
 175        if (base == 10) {
 176                if (x == print) /* exactly representable as unsigned integer */
 177                        printf("%"DATA_FMT"u\n", x);
 178                else
 179                        printf("%g\n", print);
 180                return;
 181        }
 182
 183        switch (base) {
 184        case 16:
 185                printf("%"DATA_FMT"x\n", x);
 186                break;
 187        case 8:
 188                printf("%"DATA_FMT"o\n", x);
 189                break;
 190        default: /* base 2 */
 191                i = MAXINT(data_t) - (MAXINT(data_t) >> 1);
 192                /* i is 100000...00000 */
 193                do {
 194                        if (x & i)
 195                                break;
 196                        i >>= 1;
 197                } while (i > 1);
 198                do {
 199                        bb_putchar('1' - !(x & i));
 200                        i >>= 1;
 201                } while (i);
 202                bb_putchar('\n');
 203        }
 204}
 205
 206static void print_stack_no_pop(void)
 207{
 208        unsigned i = pointer;
 209        while (i)
 210                print_base(stack[--i]);
 211}
 212
 213static void print_no_pop(void)
 214{
 215        check_under();
 216        print_base(stack[pointer-1]);
 217}
 218
 219struct op {
 220        const char name[4];
 221        void (*function) (void);
 222};
 223
 224static const struct op operators[] = {
 225#if ENABLE_FEATURE_DC_LIBM
 226        {"**",  power},
 227        {"exp", power},
 228        {"pow", power},
 229#endif
 230        {"%",   mod},
 231        {"mod", mod},
 232        {"and", and},
 233        {"or",  or},
 234        {"not", not},
 235        {"eor", eor},
 236        {"xor", eor},
 237        {"+",   add},
 238        {"add", add},
 239        {"-",   sub},
 240        {"sub", sub},
 241        {"*",   mul},
 242        {"mul", mul},
 243        {"/",   divide},
 244        {"div", divide},
 245        {"p", print_no_pop},
 246        {"f", print_stack_no_pop},
 247        {"o", set_output_base},
 248};
 249
 250/* Feed the stack machine */
 251static void stack_machine(const char *argument)
 252{
 253        char *end;
 254        double number;
 255        const struct op *o;
 256
 257 next:
 258        number = strtod(argument, &end);
 259        if (end != argument) {
 260                argument = end;
 261                push(number);
 262                goto next;
 263        }
 264
 265        /* We might have matched a digit, eventually advance the argument */
 266        argument = skip_whitespace(argument);
 267
 268        if (*argument == '\0')
 269                return;
 270
 271        o = operators;
 272        do {
 273                char *after_name = is_prefixed_with(argument, o->name);
 274                if (after_name) {
 275                        argument = after_name;
 276                        o->function();
 277                        goto next;
 278                }
 279                o++;
 280        } while (o != operators + ARRAY_SIZE(operators));
 281
 282        bb_error_msg_and_die("syntax error at '%s'", argument);
 283}
 284
 285int dc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 286int dc_main(int argc UNUSED_PARAM, char **argv)
 287{
 288        INIT_G();
 289
 290        argv++;
 291        if (!argv[0]) {
 292                /* take stuff from stdin if no args are given */
 293                char *line;
 294                while ((line = xmalloc_fgetline(stdin)) != NULL) {
 295                        stack_machine(line);
 296                        free(line);
 297                }
 298        } else {
 299                do {
 300                        stack_machine(*argv);
 301                } while (*++argv);
 302        }
 303        return EXIT_SUCCESS;
 304}
 305