dc.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  4. */
  5. #include "libbb.h"
  6. #include <math.h>
  7. /* Tiny RPN calculator, because "expr" didn't give me bitwise operations. */
  8. struct globals {
  9. unsigned pointer;
  10. unsigned base;
  11. double stack[1];
  12. } FIX_ALIASING;
  13. enum { STACK_SIZE = (COMMON_BUFSIZE - offsetof(struct globals, stack)) / sizeof(double) };
  14. #define G (*(struct globals*)&bb_common_bufsiz1)
  15. #define pointer (G.pointer )
  16. #define base (G.base )
  17. #define stack (G.stack )
  18. #define INIT_G() do { \
  19. base = 10; \
  20. } while (0)
  21. static void push(double a)
  22. {
  23. if (pointer >= STACK_SIZE)
  24. bb_error_msg_and_die("stack overflow");
  25. stack[pointer++] = a;
  26. }
  27. static double pop(void)
  28. {
  29. if (pointer == 0)
  30. bb_error_msg_and_die("stack underflow");
  31. return stack[--pointer];
  32. }
  33. static void add(void)
  34. {
  35. push(pop() + pop());
  36. }
  37. static void sub(void)
  38. {
  39. double subtrahend = pop();
  40. push(pop() - subtrahend);
  41. }
  42. static void mul(void)
  43. {
  44. push(pop() * pop());
  45. }
  46. #if ENABLE_FEATURE_DC_LIBM
  47. static void power(void)
  48. {
  49. double topower = pop();
  50. push(pow(pop(), topower));
  51. }
  52. #endif
  53. static void divide(void)
  54. {
  55. double divisor = pop();
  56. push(pop() / divisor);
  57. }
  58. static void mod(void)
  59. {
  60. unsigned d = pop();
  61. push((unsigned) pop() % d);
  62. }
  63. static void and(void)
  64. {
  65. push((unsigned) pop() & (unsigned) pop());
  66. }
  67. static void or(void)
  68. {
  69. push((unsigned) pop() | (unsigned) pop());
  70. }
  71. static void eor(void)
  72. {
  73. push((unsigned) pop() ^ (unsigned) pop());
  74. }
  75. static void not(void)
  76. {
  77. push(~(unsigned) pop());
  78. }
  79. static void set_output_base(void)
  80. {
  81. static const char bases[] ALIGN1 = { 2, 8, 10, 16, 0 };
  82. unsigned b = (unsigned)pop();
  83. base = *strchrnul(bases, b);
  84. if (base == 0) {
  85. bb_error_msg("error, base %u is not supported", b);
  86. base = 10;
  87. }
  88. }
  89. static void print_base(double print)
  90. {
  91. unsigned x, i;
  92. if (base == 10) {
  93. printf("%g\n", print);
  94. return;
  95. }
  96. x = (unsigned)print;
  97. switch (base) {
  98. case 16:
  99. printf("%x\n", x);
  100. break;
  101. case 8:
  102. printf("%o\n", x);
  103. break;
  104. default: /* base 2 */
  105. i = (unsigned)INT_MAX + 1;
  106. do {
  107. if (x & i) break;
  108. i >>= 1;
  109. } while (i > 1);
  110. do {
  111. bb_putchar('1' - !(x & i));
  112. i >>= 1;
  113. } while (i);
  114. bb_putchar('\n');
  115. }
  116. }
  117. static void print_stack_no_pop(void)
  118. {
  119. unsigned i = pointer;
  120. while (i)
  121. print_base(stack[--i]);
  122. }
  123. static void print_no_pop(void)
  124. {
  125. print_base(stack[pointer-1]);
  126. }
  127. struct op {
  128. const char name[4];
  129. void (*function) (void);
  130. };
  131. static const struct op operators[] = {
  132. {"+", add},
  133. {"add", add},
  134. {"-", sub},
  135. {"sub", sub},
  136. {"*", mul},
  137. {"mul", mul},
  138. {"/", divide},
  139. {"div", divide},
  140. #if ENABLE_FEATURE_DC_LIBM
  141. {"**", power},
  142. {"exp", power},
  143. {"pow", power},
  144. #endif
  145. {"%", mod},
  146. {"mod", mod},
  147. {"and", and},
  148. {"or", or},
  149. {"not", not},
  150. {"eor", eor},
  151. {"xor", eor},
  152. {"p", print_no_pop},
  153. {"f", print_stack_no_pop},
  154. {"o", set_output_base},
  155. { "", NULL }
  156. };
  157. static void stack_machine(const char *argument)
  158. {
  159. char *endPointer;
  160. double d;
  161. const struct op *o = operators;
  162. d = strtod(argument, &endPointer);
  163. if (endPointer != argument && *endPointer == '\0') {
  164. push(d);
  165. return;
  166. }
  167. while (o->function) {
  168. if (strcmp(o->name, argument) == 0) {
  169. o->function();
  170. return;
  171. }
  172. o++;
  173. }
  174. bb_error_msg_and_die("syntax error at '%s'", argument);
  175. }
  176. /* return pointer to next token in buffer and set *buffer to one char
  177. * past the end of the above mentioned token
  178. */
  179. static char *get_token(char **buffer)
  180. {
  181. char *current = skip_whitespace(*buffer);
  182. if (*current != '\0') {
  183. *buffer = skip_non_whitespace(current);
  184. return current;
  185. }
  186. return NULL;
  187. }
  188. int dc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  189. int dc_main(int argc UNUSED_PARAM, char **argv)
  190. {
  191. INIT_G();
  192. argv++;
  193. if (!argv[0]) {
  194. /* take stuff from stdin if no args are given */
  195. char *line;
  196. char *cursor;
  197. char *token;
  198. while ((line = xmalloc_fgetline(stdin)) != NULL) {
  199. cursor = line;
  200. while (1) {
  201. token = get_token(&cursor);
  202. if (!token)
  203. break;
  204. *cursor++ = '\0';
  205. stack_machine(token);
  206. }
  207. free(line);
  208. }
  209. } else {
  210. // why? it breaks "dc -2 2 * p"
  211. //if (argv[0][0] == '-')
  212. // bb_show_usage();
  213. do {
  214. stack_machine(*argv);
  215. } while (*++argv);
  216. }
  217. return EXIT_SUCCESS;
  218. }