dc.c 5.7 KB

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