dc.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  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. enum { STACK_SIZE = COMMON_BUFSIZE / sizeof(double) };
  9. #define stack ((double*)&bb_common_bufsiz1)
  10. static unsigned int pointer;
  11. static unsigned char base;
  12. static void push(double a)
  13. {
  14. if (pointer >= STACK_SIZE)
  15. bb_error_msg_and_die("stack overflow");
  16. stack[pointer++] = a;
  17. }
  18. static double pop(void)
  19. {
  20. if (pointer == 0)
  21. bb_error_msg_and_die("stack underflow");
  22. return stack[--pointer];
  23. }
  24. static void add(void)
  25. {
  26. push(pop() + pop());
  27. }
  28. static void sub(void)
  29. {
  30. double subtrahend = pop();
  31. push(pop() - subtrahend);
  32. }
  33. static void mul(void)
  34. {
  35. push(pop() * pop());
  36. }
  37. static void power(void)
  38. {
  39. double topower = pop();
  40. push(pow(pop(), topower));
  41. }
  42. static void divide(void)
  43. {
  44. double divisor = pop();
  45. push(pop() / divisor);
  46. }
  47. static void mod(void)
  48. {
  49. unsigned int d = pop();
  50. push((unsigned int) pop() % d);
  51. }
  52. static void and(void)
  53. {
  54. push((unsigned int) pop() & (unsigned int) pop());
  55. }
  56. static void or(void)
  57. {
  58. push((unsigned int) pop() | (unsigned int) pop());
  59. }
  60. static void eor(void)
  61. {
  62. push((unsigned int) pop() ^ (unsigned int) pop());
  63. }
  64. static void not(void)
  65. {
  66. push(~(unsigned int) pop());
  67. }
  68. static void set_output_base(void)
  69. {
  70. base = (unsigned char)pop();
  71. if ((base != 10) && (base != 16)) {
  72. bb_error_msg("error, base %d is not supported", base);
  73. base = 10;
  74. }
  75. }
  76. static void print_base(double print)
  77. {
  78. if (base == 16)
  79. printf("%x\n", (unsigned int)print);
  80. else
  81. printf("%g\n", print);
  82. }
  83. static void print_stack_no_pop(void)
  84. {
  85. unsigned int i = pointer;
  86. while (i)
  87. print_base(stack[--i]);
  88. }
  89. static void print_no_pop(void)
  90. {
  91. print_base(stack[pointer-1]);
  92. }
  93. struct op {
  94. const char name[4];
  95. void (*function) (void);
  96. };
  97. static const struct op operators[] = {
  98. {"+", add},
  99. {"add", add},
  100. {"-", sub},
  101. {"sub", sub},
  102. {"*", mul},
  103. {"mul", mul},
  104. {"/", divide},
  105. {"div", divide},
  106. {"**", power},
  107. {"exp", power},
  108. {"pow", power},
  109. {"%", mod},
  110. {"mod", mod},
  111. {"and", and},
  112. {"or", or},
  113. {"not", not},
  114. {"eor", eor},
  115. {"xor", eor},
  116. {"p", print_no_pop},
  117. {"f", print_stack_no_pop},
  118. {"o", set_output_base},
  119. {"", 0}
  120. };
  121. static void stack_machine(const char *argument)
  122. {
  123. char *endPointer = 0;
  124. double d;
  125. const struct op *o = operators;
  126. if (argument == 0)
  127. return;
  128. d = strtod(argument, &endPointer);
  129. if (endPointer != argument) {
  130. push(d);
  131. return;
  132. }
  133. while (o->name[0]) {
  134. if (strcmp(o->name, argument) == 0) {
  135. o->function();
  136. return;
  137. }
  138. o++;
  139. }
  140. bb_error_msg_and_die("%s: syntax error", argument);
  141. }
  142. /* return pointer to next token in buffer and set *buffer to one char
  143. * past the end of the above mentioned token
  144. */
  145. static char *get_token(char **buffer)
  146. {
  147. char *start = NULL;
  148. char *current;
  149. current = skip_whitespace(*buffer);
  150. if (*current != 0) {
  151. start = current;
  152. current = skip_non_whitespace(current);
  153. *buffer = current;
  154. }
  155. return start;
  156. }
  157. /* In Perl one might say, scalar m|\s*(\S+)\s*|g */
  158. static int number_of_tokens(char *buffer)
  159. {
  160. int i = 0;
  161. char *b = buffer;
  162. while (get_token(&b)) { i++; }
  163. return i;
  164. }
  165. int dc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  166. int dc_main(int argc, char **argv)
  167. {
  168. /* take stuff from stdin if no args are given */
  169. if (argc <= 1) {
  170. int i, len;
  171. char *line = NULL;
  172. char *cursor = NULL;
  173. char *token = NULL;
  174. while ((line = xmalloc_getline(stdin))) {
  175. cursor = line;
  176. len = number_of_tokens(line);
  177. for (i = 0; i < len; i++) {
  178. token = get_token(&cursor);
  179. *cursor++ = 0;
  180. stack_machine(token);
  181. }
  182. free(line);
  183. }
  184. } else {
  185. if (*argv[1] == '-')
  186. bb_show_usage();
  187. while (argc >= 2) {
  188. stack_machine(argv[1]);
  189. argv++;
  190. argc--;
  191. }
  192. }
  193. return EXIT_SUCCESS;
  194. }