+/*
+ * (C) Copyright 2016 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include "eval_arg.h"
+#include "common.h"
+#include <stdlib.h>
+#include <ctype.h>
+#include <setjmp.h>
+#include "print-utils.h"
+#include "command.h" /* jump_buf */
+
+static jmp_buf eval_jbuf;
+static char ch;
+static char *start_p;
+static char *bp;
+
+
+static long expr(void);
+
+static void print_error_pos(void)
+{
+ printf_P(PSTR("Arg: '%s'\n"
+ " "), start_p);
+ print_blanks(bp - start_p);
+ my_puts_P(PSTR("^syntax error!\n"));
+}
+
+static void error (void)
+{
+ --bp;
+ longjmp (eval_jbuf, 1);
+}
+
+static void next(void)
+{
+ do
+ ch = *bp++;
+ while (ch == ' ' || ch == '\n');
+}
+
+static long number (void)
+{
+ int base = 16;
+ char *end_p;
+ long n;
+
+ if (ch == '$') { /* FIXME: should be '#' */
+ next();
+ base = 10;
+ }
+ if (!isdigit(ch) && !(base == 16 && isxdigit(ch)))
+ error ();
+
+ n = strtoul(bp - 1, &end_p, base);
+
+ if (end_p == bp - 1)
+ error();
+ bp = end_p;
+ next();
+
+ return n;
+}
+
+static long factor (void)
+{
+ long f;
+
+ if (ch == '(')
+ {
+ next();
+ f = expr();
+ if (ch == ')')
+ next();
+ else
+ error ();
+ }
+ else
+ f = number ();
+
+ return f;
+}
+
+static long term (void)
+{
+ long t = factor();
+
+ for (;;)
+ switch (ch) {
+ case '*':
+ next();
+ t *= factor();
+ break;
+ case '/':
+ next();
+ t /= factor();
+ break;
+ case '%':
+ next();
+ t %= factor();
+ break;
+ default:
+ return t;
+ }
+}
+
+
+static long expr(void)
+{
+ long e;
+
+ if (ch == '+') {
+ next();
+ e = term ();
+ } else if (ch == '-') {
+ next();
+ e = - term ();
+ } else
+ e = term ();
+
+ while (ch == '+' || ch == '-') {
+ char op = ch;
+ next();
+ if (op == '-')
+ e -= term ();
+ else
+ e += term ();
+ }
+ return e;
+}
+
+long eval_arg(char *arg, char **end_ptr)
+{
+ long val;
+
+ start_p = arg;
+ bp = arg;
+ next();
+ if (setjmp (eval_jbuf) != 0) {
+ if (!end_ptr) {
+ print_error_pos();
+ longjmp(cmd_jbuf, 1);
+ }
+ val = -1;
+ } else {
+ val = expr ();
+ --bp;
+ }
+
+ if (!end_ptr) {
+ if (*bp != '\0') {
+ print_error_pos();
+ longjmp(cmd_jbuf, 1);
+ }
+ } else
+ *end_ptr = bp;
+
+ return val;
+}