dc.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  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. };
  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. { /* zero filled */ }
  156. };
  157. static void stack_machine(const char *argument)
  158. {
  159. char *endPointer;
  160. double d;
  161. const struct op *o = operators;
  162. if (argument == 0)
  163. return;
  164. d = strtod(argument, &endPointer);
  165. if (endPointer != argument) {
  166. push(d);
  167. return;
  168. }
  169. while (o->name[0]) {
  170. if (strcmp(o->name, argument) == 0) {
  171. o->function();
  172. return;
  173. }
  174. o++;
  175. }
  176. bb_error_msg_and_die("%s: syntax error", argument);
  177. }
  178. /* return pointer to next token in buffer and set *buffer to one char
  179. * past the end of the above mentioned token
  180. */
  181. static char *get_token(char **buffer)
  182. {
  183. char *current = skip_whitespace(*buffer);
  184. if (*current != '\0') {
  185. *buffer = skip_non_whitespace(current);
  186. return current;
  187. }
  188. return NULL;
  189. }
  190. int dc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  191. int dc_main(int argc UNUSED_PARAM, char **argv)
  192. {
  193. INIT_G();
  194. argv++;
  195. if (!argv[0]) {
  196. /* take stuff from stdin if no args are given */
  197. char *line;
  198. char *cursor;
  199. char *token;
  200. while ((line = xmalloc_fgetline(stdin)) != NULL) {
  201. cursor = line;
  202. while (1) {
  203. token = get_token(&cursor);
  204. if (!token) break;
  205. *cursor++ = '\0';
  206. stack_machine(token);
  207. }
  208. free(line);
  209. }
  210. } else {
  211. if (argv[0][0] == '-')
  212. bb_show_usage();
  213. do {
  214. stack_machine(*argv);
  215. } while (*++argv);
  216. }
  217. return EXIT_SUCCESS;
  218. }