Rizin
unix-like reverse engineering framework and cli tools
calc.c
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2012-2021 pancake <pancake@nopcode.org>
2 // SPDX-License-Identifier: LGPL-3.0-only
3 
4 /* ported to C by pancake for r2 in 2012-2017 */
5 // TODO: integrate floating point support
6 // TODO: do not use global variables
7 /*
8  Reference Chapter 6:
9  "The C++ Programming Language", Special Edition.
10  Bjarne Stroustrup,Addison-Wesley Pub Co; 3 edition (February 15, 2000)
11  ISBN: 0201700735
12  */
13 
14 #include <rz_types.h>
15 #include <rz_util.h>
16 #include <ctype.h>
17 #include <stdio.h>
18 
19 /* accessors */
20 static inline RzNumCalcValue Nset(ut64 v) {
22  n.d = (double)v;
23  n.n = v;
24  return n;
25 }
26 static inline RzNumCalcValue Nsetf(double v) {
28  n.d = v;
29  n.n = (ut64)v;
30  return n;
31 }
32 // UNUSED static inline RzNumCalcValue Naddf(RzNumCalcValue n, double v) { n.d += v; n.n += (ut64)v; return n; }
34  n.d += (double)v;
35  n.n += v;
36  return n;
37 }
39  n.d -= (double)v;
40  n.n -= v;
41  return n;
42 }
44  n.n = ~n.n;
45  return n;
46 }
48  n.d = v.d;
49  n.n |= v.n;
50  return n;
51 }
53  n.d = v.d;
54  n.n ^= v.n;
55  return n;
56 }
58  n.d = v.d;
59  n.n = n.n < v.n;
60  return n;
61 }
63  n.d = v.d;
64  n.n = n.n > v.n;
65  return n;
66 }
68  n.d = v.d;
69  n.n &= v.n;
70  return n;
71 }
73  n.d += v.d;
74  n.n += v.n;
75  return n;
76 }
78  n.d -= v.d;
79  n.n -= v.n;
80  return n;
81 }
83  n.d *= v.d;
84  n.n *= v.n;
85  return n;
86 }
87 
89  n.d += v.d;
90  n.n <<= v.n;
91  return n;
92 }
94  n.d += v.d;
95  n.n >>= v.n;
96  return n;
97 }
99  n.d += v.d;
100  n.n = (n.n << v.n) | (n.n >> (sizeof(n.n) * 8 - v.n));
101  return n;
102 }
104  n.d += v.d;
105  n.n = (n.n >> v.n) | (n.n << (sizeof(n.n) * 8 - v.n));
106  return n;
107 }
109  if (v.d) {
110  n.d = (n.d - (n.d / v.d));
111  } else {
112  n.d = 0;
113  }
114  if (v.n) {
115  n.n %= v.n;
116  } else {
117  n.n = 0;
118  }
119  return n;
120 }
121 
123  if (v.d) {
124  n.d /= v.d;
125  } else {
126  n.d = 0;
127  }
128  if (v.n) {
129  n.n /= v.n;
130  } else {
131  n.n = 0;
132  }
133  return n;
134 }
135 
137  RzNumCalcValue exp_n = n;
138  if (v.d - (st64)v.n) {
139  RZ_LOG_WARN("floating point powers not yet supported\n");
140  }
141  if ((st64)v.n > 0) {
142  for (st64 i = 1; i < (st64)v.n; i++) {
143  n = Nmul(exp_n, n);
144  }
145  } else if ((st64)v.n < 0) {
146  for (st64 i = 1; i > (st64)v.n; i--) {
147  n = Ndiv(n, exp_n);
148  }
149  } else {
150  n = Ndiv(n, exp_n);
151  }
152  return n;
153 }
154 
155 static RzNumCalcValue expr(RzNum *, RzNumCalc *, int);
156 static RzNumCalcValue term(RzNum *, RzNumCalc *, int);
157 static void error(RzNum *, RzNumCalc *, const char *);
158 static RzNumCalcValue prim(RzNum *, RzNumCalc *, int);
160 
161 static void error(RzNum *num, RzNumCalc *nc, const char *s) {
162  nc->errors++;
163  nc->calc_err = s;
164  // fprintf (stderr, "error: %s\n", s);
165 }
166 
167 static RzNumCalcValue expr(RzNum *num, RzNumCalc *nc, int get) {
168  RzNumCalcValue left = term(num, nc, get);
169  for (;;) {
170  switch (nc->curr_tok) {
171  case RNCSHL: left = Nshl(left, term(num, nc, 1)); break;
172  case RNCSHR: left = Nshr(left, term(num, nc, 1)); break;
173  case RNCROL: left = Nrol(left, term(num, nc, 1)); break;
174  case RNCROR: left = Nror(left, term(num, nc, 1)); break;
175  case RNCPLUS: left = Nadd(left, term(num, nc, 1)); break;
176  case RNCMINUS: left = Nsub(left, term(num, nc, 1)); break;
177  case RNCXOR: left = Nxor(left, term(num, nc, 1)); break;
178  case RNCORR: left = Norr(left, term(num, nc, 1)); break;
179  case RNCAND: left = Nand(left, term(num, nc, 1)); break;
180  case RNCLT: left = Nlt(left, term(num, nc, 1)); break;
181  case RNCGT: left = Ngt(left, term(num, nc, 1)); break;
182  default:
183  return left;
184  }
185  }
186  return left;
187 }
188 
189 static RzNumCalcValue term(RzNum *num, RzNumCalc *nc, int get) {
190  RzNumCalcValue left = prim(num, nc, get);
191  for (;;) {
192  if (nc->curr_tok == RNCMUL) {
193  left = Nmul(left, prim(num, nc, 1));
194  } else if (nc->curr_tok == RNCMOD) {
195  RzNumCalcValue d = prim(num, nc, 1);
196  if (!d.d) {
197  // error (num, nc, "divide by 0");
198  return d;
199  }
200  left = Nmod(left, d);
201  } else if (nc->curr_tok == RNCDIV) {
202  RzNumCalcValue d = prim(num, nc, 1);
203  if (num != NULL && (!d.d || !d.n)) {
204  num->dbz = 1;
205  return d;
206  }
207  left = Ndiv(left, d);
208  } else if (nc->curr_tok == RNCEXP) {
209  left = Nexp(left, prim(num, nc, 1));
210  } else {
211  return left;
212  }
213  }
214 }
215 
216 static RzNumCalcValue prim(RzNum *num, RzNumCalc *nc, int get) {
217  RzNumCalcValue v = { 0 };
218  if (get) {
219  get_token(num, nc);
220  }
221  switch (nc->curr_tok) {
222  case RNCNUMBER:
223  v = nc->number_value;
224  get_token(num, nc);
225  return v;
226  case RNCNAME:
227  // fprintf (stderr, "error: unknown keyword (%s)\n", nc->string_value);
228  // double& v = table[nc->string_value];
230  v = Nset(rz_num_get(num, nc->string_value));
231  get_token(num, nc);
232  if (nc->curr_tok == RNCASSIGN) {
233  v = expr(num, nc, 1);
234  }
235  if (nc->curr_tok == RNCINC) {
236  Naddi(v, 1);
237  }
238  if (nc->curr_tok == RNCDEC) {
239  Nsubi(v, 1);
240  }
241  return v;
242  case RNCNEG:
243  get_token(num, nc);
244  return Nneg(nc->number_value); // prim (num, nc, 1), 1);
245  case RNCINC:
246  return Naddi(prim(num, nc, 1), 1);
247  case RNCDEC:
248  return Naddi(prim(num, nc, 1), -1);
249  case RNCORR:
250  return Norr(v, prim(num, nc, 1));
251  case RNCMINUS:
252  return Nsub(v, prim(num, nc, 1));
253  case RNCLEFTP:
254  v = expr(num, nc, 1);
255  if (nc->curr_tok == RNCRIGHTP) {
256  get_token(num, nc);
257  } else {
258  error(num, nc, " ')' expected");
259  }
260  return v;
261  case RNCLT:
262  case RNCGT:
263  case RNCEND:
264  case RNCXOR:
265  case RNCAND:
266  case RNCPLUS:
267  case RNCMOD:
268  case RNCMUL:
269  case RNCDIV:
270  case RNCEXP:
271  case RNCPRINT:
272  case RNCASSIGN:
273  case RNCRIGHTP:
274  case RNCSHL:
275  case RNCSHR:
276  case RNCROL:
277  case RNCROR:
278  return v;
279  // default: error (num, nc, "primary expected");
280  }
281  return v;
282 }
283 
284 static void cin_putback(RzNum *num, RzNumCalc *nc, char c) {
285  nc->oc = c;
286 }
287 
288 RZ_API const char *rz_num_calc_index(RzNum *num, const char *p) {
289  if (!num) {
290  return NULL;
291  }
292  if (p) {
293  num->nc.calc_buf = p;
294  num->nc.calc_len = strlen(p);
295  num->nc.calc_i = 0;
296  }
297  return num->nc.calc_buf + num->nc.calc_i;
298 }
299 
300 static int cin_get(RzNum *num, RzNumCalc *nc, char *c) {
301  if (nc->oc) {
302  *c = nc->oc;
303  nc->oc = 0;
304  } else {
305  if (!nc->calc_buf || !*nc->calc_buf) {
306  return 0;
307  }
308  *c = nc->calc_buf[nc->calc_i];
309  if (*c) {
310  nc->calc_i++;
311  } else {
312  return 0;
313  }
314  }
315  return 1;
316 }
317 
319  double d;
320  char str[RZ_NUMCALC_STRSZ + 1]; // TODO: move into the heap?
321  int i = 0;
322  char c;
323  str[0] = 0;
324  while (cin_get(num, nc, &c)) {
325  if (c != '_' && c != ':' && c != '.' && !isalnum((ut8)c)) {
326  cin_putback(num, nc, c);
327  break;
328  }
329  if (i < RZ_NUMCALC_STRSZ) {
330  str[i++] = c;
331  }
332  }
333  str[i] = 0;
334  *n = Nset(rz_num_get(num, str));
335  if (IS_DIGIT(*str) && strchr(str, '.')) {
336  if (sscanf(str, "%lf", &d) < 1) {
337  return 0;
338  }
339  if (n->n < d) {
340  *n = Nsetf(d);
341  }
342  n->d = d;
343  }
344  return 1;
345 }
346 
348  char ch = 0, c = 0;
349 
350  do {
351  if (!cin_get(num, nc, &ch)) {
352  return nc->curr_tok = RNCEND;
353  }
354  } while (ch != '\n' && isspace((ut8)ch));
355 
356  switch (ch) {
357  case 0:
358  case ';':
359  case '\n':
360  return nc->curr_tok = RNCEND;
361  case '+': // added for ++name and name++
362  if (cin_get(num, nc, &c) && c == '+') {
363  return nc->curr_tok = RNCINC;
364  }
365  cin_putback(num, nc, c);
366  return nc->curr_tok = (RzNumCalcToken)ch;
367  // negate hack
368  case '~':
369  if (cin_get(num, nc, &c) && c == '-') {
370  return nc->curr_tok = RNCNEG;
371  }
372  cin_putback(num, nc, c);
373  return nc->curr_tok = (RzNumCalcToken)ch;
374  // negative number
375  case '-':
376  if (cin_get(num, nc, &c) && c == '-') {
377  return nc->curr_tok = RNCDEC;
378  }
379  cin_putback(num, nc, c);
380  return nc->curr_tok = (RzNumCalcToken)ch;
381  case '<':
382  if (cin_get(num, nc, &c) && c == '<') { // "<<" = shift left
383  if (cin_get(num, nc, &c) && c == '<') { // "<<<" = rotate left
384  return nc->curr_tok = RNCROL;
385  }
386  cin_putback(num, nc, c);
387  return nc->curr_tok = RNCSHL;
388  }
389  cin_putback(num, nc, c);
390  return nc->curr_tok = RNCLT;
391  case '>':
392  if (cin_get(num, nc, &c) && c == '>') {
393  if (cin_get(num, nc, &c) && c == '>') {
394  return nc->curr_tok = RNCROR;
395  }
396  cin_putback(num, nc, c);
397  return nc->curr_tok = RNCSHR;
398  }
399  cin_putback(num, nc, c);
400  return nc->curr_tok = RNCGT;
401  case '^':
402  case '&':
403  case '|':
404  case '%':
405  case '/':
406  case '(':
407  case ')':
408  case '=':
409  return nc->curr_tok = (RzNumCalcToken)ch;
410  case '*':
411  if (cin_get(num, nc, &c) && c == '*') {
412  return nc->curr_tok = RNCEXP;
413  }
414  cin_putback(num, nc, c);
415  return nc->curr_tok = RNCMUL;
416  case '0':
417  case '1':
418  case '2':
419  case '3':
420  case '4':
421  case '5':
422  case '6':
423  case '7':
424  case '8':
425  case '9':
426  case '.':
427  cin_putback(num, nc, ch);
428  if (!cin_get_num(num, nc, &nc->number_value)) {
429  error(num, nc, "invalid number conversion");
430  return 1;
431  }
432  return nc->curr_tok = RNCNUMBER;
433 
434 #define isvalidchar(x) \
435  (isalnum(x) || (x) == ':' || (x) == '$' || (x) == '.' || (x) == '_' || (x) == '?' || (x) == '\\' || (x) == ' ' || (x) == '[' || (x) == ']' || (x) == '}' || (x) == '{' || ((x) >= '0' && (x) <= '9'))
436 
437  default: {
438  int i = 0;
439 #define stringValueAppend(x) \
440  { \
441  const size_t max = sizeof(nc->string_value) - 1; \
442  if (i < max) \
443  nc->string_value[i++] = x; \
444  else \
445  nc->string_value[max] = 0; \
446  }
447  stringValueAppend(ch);
448  if (ch == '[') {
449  while (cin_get(num, nc, &ch) && ch != ']') {
450  if (i > RZ_NUMCALC_STRSZ - 1) {
451  error(num, nc, "string too long");
452  return 0;
453  }
454  stringValueAppend(ch);
455  }
456  stringValueAppend(ch);
457  } else {
458  while (cin_get(num, nc, &ch) && isvalidchar((unsigned char)ch)) {
459  if (i >= RZ_NUMCALC_STRSZ) {
460  error(num, nc, "string too long");
461  return 0;
462  }
463  stringValueAppend(ch);
464  }
465  }
467  if (ch != '\'') {
468  cin_putback(num, nc, ch);
469  }
470  return nc->curr_tok = RNCNAME;
471  }
472  }
473 }
474 
475 static void load_token(RzNum *num, RzNumCalc *nc, const char *s) {
476  nc->calc_i = 0;
477  nc->calc_len = strlen(s);
478  nc->calc_buf = s;
479  nc->calc_err = NULL;
480 }
481 
482 RZ_API ut64 rz_num_calc(RzNum *num, const char *str, const char **err) {
484  RzNumCalc *nc, nc_local;
485  if (!str || !*str) {
486  return 0LL;
487  }
488  if (num) {
489  nc = &num->nc;
490  num->dbz = 0;
491  } else {
492  nc = &nc_local;
493  }
494  /* init */
495  nc->curr_tok = RNCPRINT;
496  nc->number_value.d = 0.0;
497  nc->number_value.n = 0LL;
498  nc->errors = 0;
499  nc->oc = 0;
500  nc->calc_err = NULL;
501  nc->calc_i = 0;
502  nc->calc_len = 0;
503  nc->calc_buf = NULL;
504  nc->under_calc = true;
505 
506  load_token(num, nc, str);
507  get_token(num, nc);
508  n = expr(num, nc, 0);
509  if (err) {
510  *err = nc->calc_err;
511  }
512  if (num) {
513  num->fvalue = n.d;
514  }
515  nc->under_calc = false;
516  return n.n;
517 }
lzma_index ** i
Definition: index.h:629
static bool err
Definition: armass.c:435
static RzNumCalcValue term(RzNum *, RzNumCalc *, int)
Definition: calc.c:189
static RzNumCalcValue Nexp(RzNumCalcValue n, RzNumCalcValue v)
Definition: calc.c:136
static RzNumCalcValue Nneg(RzNumCalcValue n)
Definition: calc.c:43
static RzNumCalcValue Nset(ut64 v)
Definition: calc.c:20
static int cin_get_num(RzNum *num, RzNumCalc *nc, RzNumCalcValue *n)
Definition: calc.c:318
#define stringValueAppend(x)
static RzNumCalcValue Nror(RzNumCalcValue n, RzNumCalcValue v)
Definition: calc.c:103
static RzNumCalcValue Nlt(RzNumCalcValue n, RzNumCalcValue v)
Definition: calc.c:57
static RzNumCalcValue Ngt(RzNumCalcValue n, RzNumCalcValue v)
Definition: calc.c:62
static RzNumCalcValue Nxor(RzNumCalcValue n, RzNumCalcValue v)
Definition: calc.c:52
static void cin_putback(RzNum *num, RzNumCalc *nc, char c)
Definition: calc.c:284
static RzNumCalcValue Nshl(RzNumCalcValue n, RzNumCalcValue v)
Definition: calc.c:88
static RzNumCalcValue Naddi(RzNumCalcValue n, ut64 v)
Definition: calc.c:33
static RzNumCalcValue Ndiv(RzNumCalcValue n, RzNumCalcValue v)
Definition: calc.c:122
static RzNumCalcValue Nadd(RzNumCalcValue n, RzNumCalcValue v)
Definition: calc.c:72
static void load_token(RzNum *num, RzNumCalc *nc, const char *s)
Definition: calc.c:475
static RzNumCalcValue Nmod(RzNumCalcValue n, RzNumCalcValue v)
Definition: calc.c:108
static RzNumCalcValue Nand(RzNumCalcValue n, RzNumCalcValue v)
Definition: calc.c:67
static RzNumCalcValue expr(RzNum *, RzNumCalc *, int)
Definition: calc.c:167
static RzNumCalcValue Nmul(RzNumCalcValue n, RzNumCalcValue v)
Definition: calc.c:82
static RzNumCalcValue prim(RzNum *, RzNumCalc *, int)
Definition: calc.c:216
static RzNumCalcValue Nsubi(RzNumCalcValue n, ut64 v)
Definition: calc.c:38
static RzNumCalcToken get_token(RzNum *, RzNumCalc *)
Definition: calc.c:347
#define isvalidchar(x)
static int cin_get(RzNum *num, RzNumCalc *nc, char *c)
Definition: calc.c:300
static RzNumCalcValue Nsetf(double v)
Definition: calc.c:26
static RzNumCalcValue Nrol(RzNumCalcValue n, RzNumCalcValue v)
Definition: calc.c:98
RZ_API const char * rz_num_calc_index(RzNum *num, const char *p)
Definition: calc.c:288
RZ_API ut64 rz_num_calc(RzNum *num, const char *str, const char **err)
Definition: calc.c:482
static RzNumCalcValue Norr(RzNumCalcValue n, RzNumCalcValue v)
Definition: calc.c:47
static RzNumCalcValue Nshr(RzNumCalcValue n, RzNumCalcValue v)
Definition: calc.c:93
static RzNumCalcValue Nsub(RzNumCalcValue n, RzNumCalcValue v)
Definition: calc.c:77
static void error(RzNum *, RzNumCalc *, const char *)
Definition: calc.c:161
#define RZ_API
#define NULL
Definition: cris-opc.c:27
const char * v
Definition: dsignal.c:12
uint8_t ut8
Definition: lh5801.h:11
void * p
Definition: libc.cpp:67
static static fork const void static count static fd const char const char static newpath char char char static envp time_t static t const char static mode static whence const char static dir time_t static t unsigned static seconds const char struct utimbuf static buf static inc static sig const char static mode static oldfd struct tms static buf static getgid static geteuid const char static filename static arg static mask struct ustat static ubuf static getppid static setsid static egid sigset_t static set struct timeval struct timezone static tz fd_set fd_set fd_set struct timeval static timeout const char char static bufsiz const char static swapflags void static offset const char static length static mode static who const char struct statfs static buf unsigned unsigned num
Definition: sflib.h:126
int n
Definition: mipsasm.c:19
static RzSocket * s
Definition: rtr.c:28
#define RZ_LOG_WARN(fmtstr,...)
Definition: rz_log.h:56
RZ_API ut64 rz_num_get(RzNum *num, const char *str)
Definition: unum.c:172
#define RZ_NUMCALC_STRSZ
Definition: rz_num.h:6
RzNumCalcToken
Definition: rz_num.h:17
@ RNCNEG
Definition: rz_num.h:32
@ RNCORR
Definition: rz_num.h:34
@ RNCROR
Definition: rz_num.h:43
@ RNCXOR
Definition: rz_num.h:35
@ RNCLT
Definition: rz_num.h:23
@ RNCAND
Definition: rz_num.h:33
@ RNCMINUS
Definition: rz_num.h:26
@ RNCNUMBER
Definition: rz_num.h:19
@ RNCRIGHTP
Definition: rz_num.h:39
@ RNCMOD
Definition: rz_num.h:30
@ RNCDEC
Definition: rz_num.h:22
@ RNCEXP
Definition: rz_num.h:28
@ RNCINC
Definition: rz_num.h:21
@ RNCPLUS
Definition: rz_num.h:25
@ RNCGT
Definition: rz_num.h:24
@ RNCLEFTP
Definition: rz_num.h:38
@ RNCMUL
Definition: rz_num.h:27
@ RNCROL
Definition: rz_num.h:42
@ RNCSHR
Definition: rz_num.h:41
@ RNCASSIGN
Definition: rz_num.h:37
@ RNCPRINT
Definition: rz_num.h:36
@ RNCDIV
Definition: rz_num.h:29
@ RNCEND
Definition: rz_num.h:20
@ RNCSHL
Definition: rz_num.h:40
@ RNCNAME
Definition: rz_num.h:18
RZ_API void rz_str_trim(RZ_NONNULL RZ_INOUT char *str)
Removes whitespace characters (space, tab, newline etc.) from the beginning and end of a string.
Definition: str_trim.c:190
#define IS_DIGIT(x)
Definition: rz_str_util.h:11
#define st64
Definition: rz_types_base.h:10
#define isspace(c)
Definition: safe-ctype.h:141
#define isalnum(c)
Definition: safe-ctype.h:127
#define d(i)
Definition: sha256.c:44
#define c(i)
Definition: sha256.c:43
double d
Definition: rz_num.h:13
bool under_calc
Definition: rz_num.h:56
RzNumCalcValue number_value
Definition: rz_num.h:48
const char * calc_buf
Definition: rz_num.h:54
RzNumCalcToken curr_tok
Definition: rz_num.h:47
int calc_i
Definition: rz_num.h:53
int calc_len
Definition: rz_num.h:55
char string_value[RZ_NUMCALC_STRSZ]
Definition: rz_num.h:49
const char * calc_err
Definition: rz_num.h:52
int errors
Definition: rz_num.h:50
char oc
Definition: rz_num.h:51
ut64(WINAPI *w32_GetEnabledXStateFeatures)()