From 4dd59a05f09196b04ee9e89d44486e56e13e1007 Mon Sep 17 00:00:00 2001
From: Dirk Zimoch <dirk.zimoch@psi.ch>
Date: Wed, 18 Apr 2018 16:31:24 +0200
Subject: [PATCH] enable embedded expressions in ()

---
 runScript.c | 89 ++++++++++++++++++++++++++++++-----------------------
 1 file changed, 50 insertions(+), 39 deletions(-)

diff --git a/runScript.c b/runScript.c
index 4b820b45..1f97479b 100644
--- a/runScript.c
+++ b/runScript.c
@@ -57,18 +57,29 @@ static int parseValue(const char** pp, int* v)
 {
     int val;
     const char *p = *pp;
-    int neg = 0;
+    char o;
 
-    /* A value is optionally prefixed with a sign + -.
+    /* A value is optionally prefixed with an unary operator + - ! ~.
      * It is either a number (decimal, octal or hex)
      * or an expression in ().
      * Allowed chars after a number: operators, closing parenthesis, whitespace, quotes, end of string
-     */ 
-    while (isspace((unsigned char)*p)) p++;
-    if (*p == '+' || *p == '-') neg = *p++ == '-';
-    while (isspace((unsigned char)*p)) p++;
-    if (*p == '(')
+     */
+
+    do {
+        while (isspace((unsigned char)*p)) p++;
+    } while (*p == '+' && p++);
+    o = *p;
+    if (strchr("-~!", o))
     {
+        p++;
+        if (!parseValue(&p, &val)) return 0;
+        if (o == '-') val=-val;
+        else if (o == '~') val=~val;
+        else if (o == '!') val=!val;
+    }
+    else if (o == '(')
+    {
+        if (runScriptDebug > 1) printf("parseValue: subexpression '%s'\n", p);
         p++;
         if (!parseExpr(&p, &val)) return 0;
         while (isspace((unsigned char)*p)) p++;
@@ -82,20 +93,14 @@ static int parseValue(const char** pp, int* v)
         if (*e && !isspace((unsigned char)*e) && !strchr("+-*/%?)'\"",*e))
         {
             /* followed by rubbish */
-            if (runScriptDebug > 1) printf("parseValue: bail out from %s at %s\n", *pp, e);
+            if (runScriptDebug > 1) printf("parseValue: bail out from '%s' at '%s'\n", *pp, e);
             return 0; 
         }
         p = e;
     }
-    if (neg) val = -val;
-    if (*p == '?')
-    {
-        p++;
-        val = (val != 0);
-    }
+    if (runScriptDebug > 1) printf("parseValue: '%.*s' = %d rest '%s'\n", (int)(p-*pp), *pp, val, p);
     *pp = p;
     *v = val;
-    if (runScriptDebug > 1) printf("parseValue: %d rest=\"%s\"\n", *v, p);
     return 1;
 }
 
@@ -103,12 +108,10 @@ static int parseExpr(const char** pp, int* v)
 {
     const char *p = *pp;
     const char *q;
-    int o;
-    int val;
-    int val2;
+    int sum = 0, val, val2;
     int status = 0;
+    char o;
 
-    *v = 0;
     /* An expression is a value optionally followed by an operator and another value.
      * Outer loop: low priority operators + -
      * Inner loop: high priority operators * / %
@@ -117,14 +120,13 @@ static int parseExpr(const char** pp, int* v)
      */
     do {
         if (!parseValue(&p, &val)) return status;
-        if (runScriptDebug > 1) printf("parseExpr: val=%d rest=%s\n", val, p);
         q = p;
         while (isspace((unsigned char)*q)) q++;
         o = *q;
         while (o == '*' || o == '/' || o == '%')
         {
             q++;
-            if (!parseValue(&q, &val2)) break;
+            if (!parseValue(&q, &val2)) return status;
             if (o == '*') val *= val2;
             else if (val2 == 0) val = 0; /* define devision by zero as 0 */
             else if (o == '/') val /= val2;
@@ -134,10 +136,16 @@ static int parseExpr(const char** pp, int* v)
             o = *p;
         }
         status = 1;
-        *v += val;
-        if (runScriptDebug > 1) printf("parseExpr: sum %d rest=\"%s\"\n", *v, p);
+        sum += val;
     } while (o == '+' || o == '-');
+    if (*p == '?')
+    {
+        p++;
+        sum = (sum != 0);
+    }
+    if (runScriptDebug > 1) printf("parseExpr: '%.*s' = %d\n", (int)(p-*pp), *pp, sum);
     *pp = p;
+    *v = sum;
     return 1;
 }
 
@@ -149,13 +157,10 @@ const char* getFormat(const char** pp)
     if (runScriptDebug > 1) printf ("getFormat %s\n", p);
     if ((format[0] = *p++) == '%')
     {
-        if (runScriptDebug > 1) printf ("getFormat0 %s\n", p);
         while (i < sizeof(format) && strchr(" #-+0", *p))
             format[i++] = *p++;
-        if (runScriptDebug > 1) printf ("getFormat1 %s\n", p);
         while (i < sizeof(format) && strchr("0123456789", *p))
             format[i++] = *p++;
-        if (runScriptDebug > 1) printf ("getFormat2 %s\n", p);
         if (i < sizeof(format) && strchr("diouxXc", *p))
         {
             format[i++] = *p++;
@@ -293,6 +298,7 @@ int runScript(const char* filename, const char* args)
             if (fgets(line_raw + len, line_raw_size - len, file) == NULL) break;
         }
         while (len > 0 && isspace((unsigned char)line_raw[len-1])) line_raw[--len] = 0; /* get rid of '\n' and friends */
+        if (len == 0) continue;
         if (runScriptDebug)
                 printf("runScript raw line (%ld chars): '%s'\n", len, line_raw);
         /* expand and check the buffer size (different epics versions write different may number of bytes)*/
@@ -320,7 +326,7 @@ int runScript(const char* filename, const char* args)
         if ((x = strpbrk(p, "=(, \t\n\r")) != NULL && *x=='=')
         {
             const char* r;
-            const char* s;
+            char* s;
             char* w;
             int val;
 
@@ -329,17 +335,15 @@ int runScript(const char* filename, const char* args)
             w = line_raw;
             while (*r)
             {
-                /* Resolve any integer expression that is not embedded in a
-                 * string. A string is anything in single or double quotes or
-                 * a word that is not an integer expression. An expression
-                 * consists of an optional format specifier (such as %x),
-                 * numbers (including 0x prefixed hex numbers), arithmetic
-                 * operators (at the moment +-*%/) and and parentheses ().
+                /* Resolve integer expressions:
+                 * Any free standing expression.
+                 * Any expression in parentheses () embedded in an unquoted word.
+                 * Do not resolve expressions in single or double quoted strings.
+                 * An expression optionally starts with a format such as %x.
+                 * It consists of integer numbers (including 0x prefixed hex numbers),
+                 * unary (+-!~) and binary (+-*%/) oprators and parentheses ().
                  */
-                while (isspace((unsigned char)*r)) *w++ = *r++;
-                if (!*r) break;
                 s = w;
-
                 if (*r == '"' || *r == '\'')
                 {
                     /* quoted strings */
@@ -358,10 +362,16 @@ int runScript(const char* filename, const char* args)
                     /* formatted expression */
                     const char* r2 = r;
                     const char* f;
+                    if (runScriptDebug > 1) printf ("formatted expression after '%s'\n", s);
                     if ((f = getFormat(&r2)) && parseExpr(&r2, &val))
                     {
-                        w += sprintf(w, f , val);
                         r = r2;
+                        if (*s == '(' && *r2++ == ')')
+                        {
+                            w = s;
+                            r = r2;
+                        }
+                        w += sprintf(w, f , val);
                         if (runScriptDebug > 1) printf ("formatted expression %s\n", s);
                         continue;
                     }
@@ -376,9 +386,10 @@ int runScript(const char* filename, const char* args)
                 /* unquoted string (i.e plain word) */
                 do {
                     *w++ = *r++;
-                } while (*r && !strchr("(\"' \t\n",*r));
+                } while (*r && !strchr("%(\"', \t\n",*r));
+                while (isspace((unsigned char)*r)) *w++ = *r++;
                 *w = 0;
-                if (runScriptDebug > 1) printf ("plain word %s\n", s);
+                if (runScriptDebug > 1) printf ("plain word '%s'\n", s);
             }
             if (runScriptDebug)
                 printf("runScript: assign %s=%s\n", p, line_raw);
-- 
GitLab