diff --git a/GNUmakefile b/GNUmakefile index bd9bb5f7e0268f3bfd08b605be972c508b773932..d56841bd83d13cb70f425480c5009718dfc2ca12 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -10,6 +10,8 @@ SOURCES += require.c DBDS += require.dbd SOURCES += runScript.c DBDS += runScript.dbd +SOURCES += expr.c +#DBDS += expr.dbd SOURCES += dbLoadTemplate.y DBDS += dbLoadTemplate.dbd diff --git a/Makefile b/Makefile index ea3af0d84193dd346d987bebe498558475d88a66..2f85c3b20e84a1b3d5a5a9b6968c2885eae64037 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,9 @@ requireSup_DBD += require.dbd LIB_SRCS += runScript.c requireSup_DBD += runScript.dbd +LIB_SRCS += expr.c +#requireSup_DBD += expr.dbd + LIB_SRCS += dbLoadTemplate.y requireSup_DBD += dbLoadTemplate.dbd diff --git a/expr.c b/expr.c new file mode 100644 index 0000000000000000000000000000000000000000..a3eee063fd1b8163bc909838af5fc9aea11088dc --- /dev/null +++ b/expr.c @@ -0,0 +1,260 @@ +#include <ctype.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include "expr.h" + +int exprDebug; + +static int parseExpr(const char** pp, long* v, int op); + +static int parseValue(const char** pp, long* v) +{ + long val; + const char *p = *pp; + char o; + + /* 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 + */ + + /* first look for value */ + while (isspace((unsigned char)*p)) p++; + o = *p; + if (memchr("+-~!", o, 4)) + { + /* handle unary operators */ + p++; + if (!parseValue(&p, &val)) return 0; /* no valid value */ + if (o == '-') val=-val; + else if (o == '~') val=~val; + else if (o == '!') val=!val; + } + else if (o == '(') + { + /* handle sub-expression */ + p++; + if (parseExpr(&p, &val, 0) < 0) return 0; /* no valid expression */ + while (isspace((unsigned char)*p)) p++; + if (*p++ != ')') return 0; /* missing ) */ + } + else + { + /* get number */ + char* e; + val = strtol(p, &e, 0); + if (e == p) return 0; /* no number */ + + if (isalpha((unsigned char)*p)) + { + /* followed by rubbish */ + return 0; + } + p = e; + } + *pp = p; + *v = val; + return 1; +} + +static long ipow(long base, long exp) +{ + long v; + if (exp < 0) return 0; + if (exp == 0) return 1; + v = base; + while (--exp) v *= base; /* optimize this! */ + return v; +} + +struct {char str[4]; int pr;} ops[] = { + {"<-",0}, + {"**",11}, + {"*", 10},{"/",10},{"%",10}, + {"+",9},{"-",9}, + {"<<",8},{">>>",8},{">>",8}, + {"<=>",7},{"<=",7},{">=",7},{"<",7},{">",7}, + {"==",6},{"!=",6}, + {"&&",5},{"||",5}, + {"&",4},{"^",3},{"|",2},{"?",1} +}; + +static int startsWith(const char* p, const char* s) +{ + int i = 0; + while (*s) { i++; if (*p++ != *s++) return 0; } + return i; +} + +static int parseOp(const char** pp) +{ + const char *p = *pp; + int o, l; + + while (isspace((unsigned char)*p)) p++; + if (ispunct((unsigned char)*p)) + { + for (o = 1; o < (int)(sizeof(ops)/sizeof(ops[0])); o++) + { + if ((l = startsWith(p, ops[o].str))) + { + /* operator found */ + *pp = p+l; + return o; + } + } + } + return 0; +} + +static int parseExpr(const char** pp, long* v, int o) +{ + const char *p = *pp; + long val = *v; + long val2; + int o2 = o; + int pr = ops[o].pr; + + if (exprDebug) printf("parseExpr(%s %d): start %ld %s %s\n", ops[o].str, pr, val, ops[o].str, p); + do { + if (!parseValue(&p, &val2)) return -1; + if ((o2 = parseOp(&p))) + { + if (exprDebug) printf("parseExpr(%d): %ld %s %ld %s %s\n", pr, val, ops[o].str, val2, ops[o2].str, p); + if (o && ops[o2].pr > ops[o].pr) + { + if ((o2 = parseExpr(&p, &val2, o2)) < 0) return -1; + } + } + if (exprDebug) printf("parseExpr(%d): %ld %s %ld", pr, val, ops[o].str, val2); + switch(o) + { + case 0: val = val2; break; + case 1: val = ipow(val, val2); break; + case 2: val *= val2; break; + case 3: val /= val2; break; + case 4: val %= val2; break; + case 5: val += val2; break; + case 6: val -= val2; break; + case 7: val <<= val2; break; + case 8: val = (unsigned long)val >> val2; break; + case 9: val >>= val2; break; + case 10: val = val < val2 ? -1 : val == val2 ? 0 : 1; break; + case 11: val = val <= val2; break; + case 12: val = val >= val2; break; + case 13: val = val < val2; break; + case 14: val = val > val2; break; + case 15: val = val == val2; break; + case 16: val = val != val2; break; + case 17: val = val && val2; break; + case 18: val = val || val2; break; + case 19: val &= val2; break; + case 20: val ^= val2; break; + case 21: val |= val2; break; + case 22: val = (val != 0); break; + } + if (exprDebug) printf(" = %ld\n", val); + o = o2; + } while (o && pr <= ops[o].pr); + if (exprDebug) printf("parseExpr(%d): result %ld\n", pr, val); + *pp = p; + *v = val; + return o; +} + +const char* getFormat(const char** pp) +{ + static char format [20]; + const char* p = *pp; + unsigned int i = 1; + if (exprDebug) printf("getFormat %s\n", p); + if ((format[0] = *p++) == '%') + { + while (i < sizeof(format) && memchr(" #-+0", *p, 5)) + format[i++] = *p++; + while (i < sizeof(format) && *p >= '0' && *p <= '9') + format[i++] = *p++; + if (i < sizeof(format)) + format[i++] = 'l'; + if (i < sizeof(format) && memchr("diouxXc", *p, 7)) + { + format[i++] = *p++; + format[i] = 0; + *pp = p; + if (exprDebug) printf("format = '%s'\n", format); + return format; + } + } + if (exprDebug) printf("no format\n"); + return NULL; +} + +size_t replaceExpressions(const char* r, char* buffer, size_t buffersize) +{ + long val; + char* w = buffer; + char* s; + + while (*r) + { + s = w; + if (*r == '"' || *r == '\'') + { + /* quoted strings */ + char c = *w++ = *r++; + while (*r && *r != c) { + if (*r == '\\' && !(*w++ = *r++)) break; + *w++ = *r++; + } + if (*r) *w++ = *r++; + *w = 0; + if (exprDebug) printf("quoted string %s\n", s); + } + else if (*r == '%') + { + /* formatted expression */ + const char* r2 = r; + const char* f; + if (exprDebug) printf("formatted expression after '%s'\n", s); + if ((f = getFormat(&r2)) && parseExpr(&r2, &val, 0) == 0) + { + r = r2; + if (*s == '(' && *r2++ == ')') + { + w = s; + r = r2; + } + w += sprintf(w, f , val); + if (exprDebug) printf("formatted expression %s\n", s); + } + } + else if (parseExpr(&r, &val, 0) == 0) + { + /* unformatted expression */ + w += sprintf(w, "%ld", val); + *w = 0; + if (exprDebug) printf("simple expression %s\n", s); + } + else if (*r == ',') + { + /* single comma */ + *w++ = *r++; + } + else { + /* unquoted string (i.e plain word) */ + do { + *w++ = *r++; + } while (*r && !strchr("%(\"', \t\n", *r)); + *w = 0; + if (exprDebug) printf("plain word '%s'\n", s); + } + /* copy space */ + while (isspace((unsigned char)*r)) *w++ = *r++; + /* terminate */ + *w = 0; + } + return w - buffer; +} diff --git a/expr.h b/expr.h new file mode 100644 index 0000000000000000000000000000000000000000..d3ee4edaf5126b50accd9ed5a144625506589191 --- /dev/null +++ b/expr.h @@ -0,0 +1,21 @@ +#ifndef expr_h +#define expr_h + +#ifdef __cplusplus +extern { +#endif + +extern int exprDebug; + +size_t replaceExpressions(const char* source, char* buffer, size_t buffersize); +/* Resolve integer expressions that are either free standing + * or in parentheses () embedded in an unquoted word. + * Do not resolve expressions in single or double quoted strings. + * An expression optionally starts with a integer format such as %x. + * It consists of integer numbers, operators and parentheses (). + */ + +#ifdef __cplusplus +} +#endif +#endif diff --git a/runScript.c b/runScript.c index ebe0976e3b0cc276c6ab830fe31b23ccbbcd941c..daf79fa343ed257ac9be183d4447db286bf374a7 100644 --- a/runScript.c +++ b/runScript.c @@ -47,193 +47,11 @@ epicsShareFunc int epicsShareAPI iocshCmd(const char *cmd); #define IS_ABS_PATH(filename) (filename[0] == OSI_PATH_SEPARATOR[0]) /* may be different for other OS ? */ +#include "expr.h" #include "require.h" int runScriptDebug=0; -static int parseExpr(const char** pp, long* v, int op); - -static int parseValue(const char** pp, long* v) -{ - long val; - const char *p = *pp; - char o; - - /* 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 - */ - - /* first look for value */ - while (isspace((unsigned char)*p)) p++; - o = *p; - if (memchr("+-~!", o, 4)) - { - /* handle unary operators */ - p++; - if (!parseValue(&p, &val)) return 0; /* no valid value */ - if (o == '-') val=-val; - else if (o == '~') val=~val; - else if (o == '!') val=!val; - } - else if (o == '(') - { - /* handle sub-expression */ - if (runScriptDebug > 1) printf("parseValue: subexpression '%s'\n", p); - p++; - if (parseExpr(&p, &val, 0) < 0) return 0; /* no valid expression */ - while (isspace((unsigned char)*p)) p++; - if (*p++ != ')') return 0; /* missing ) */ - } - else - { - /* get number */ - char* e; - val = strtol(p, &e, 0); - if (e == p) return 0; /* no number */ - - if (isalpha((unsigned char)*p)) - { - /* followed by rubbish */ - if (runScriptDebug > 1) printf("parseValue: bail out from '%s' at '%s'\n", *pp, e); - return 0; - } - p = e; - } - if (runScriptDebug > 1) printf("parseValue: '%.*s' = %ld rest '%s'\n", (int)(p-*pp), *pp, val, p); - *pp = p; - *v = val; - return 1; -} - -static long ipow(long base, long exp) -{ - long v; - if (exp < 0) return 0; - if (exp == 0) return 1; - v = base; - while (--exp) v *= base; /* optimize this! */ - return v; -} - -struct {char str[3]; char pr;} ops[] = { - {"",0}, - {"**",2}, - {"*", 3},{"/",3},{"%",3}, - {"+",4},{"-",4}, - {"<<",5},{">>>",5},{">>",5}, - {"<=>",6},{"<=",6},{">=",6},{"<",6},{">",6}, - {"==",7},{"!=",7}, - {"&&",11},{"||",12}, - {"&",8},{"^",9},{"|",10} -}; - -static int startsWith(const char* p, const char* s) -{ - int i = 0; - while (*s) { i++; if (*p++ != *s++) return 0; } - return i; -} - -static int parseOp(const char** pp) -{ - const char *p = *pp; - int o, l; - - while (isspace((unsigned char)*p)) p++; - if (runScriptDebug > 1) printf("parseOp: check %s\n", p); - if (ispunct((unsigned char)*p)) - { - for (o = 1; o < (int)(sizeof(ops)/sizeof(ops[0])); o++) - { - if ((l = startsWith(p, ops[o].str))) - { - /* operator found */ - if (runScriptDebug > 1) printf("parseOp: %.3s\n", ops[0].str); - *pp = p+l; - return o; - } - } - } - if (runScriptDebug > 1) printf("parseOp: end\n"); - return 0; -} - -static int parseExpr(const char** pp, long* v, int o) -{ - const char *p = *pp; - long val = 0; - long val2; - int o2 = o; - int pr = ops[o].pr; - - if (runScriptDebug > 1) printf("parseExpr: o = %.3s\n", ops[o].str); - do { - if (!parseValue(&p, &val2)) return -1; - if ((o2 = parseOp(&p)) != 0 && ops[o2].pr < pr) - if ((o2 = parseExpr(&p, &val2, o2)) < 0) return -1; - if (runScriptDebug > 1) printf("parseExpr: %ld %.3s %ld\n", val, ops[o].str, val2); - switch(o) - { - case 0: val = val2; break; - case 1: val = ipow(val, val2); break; - case 2: val *= val2; break; - case 3: val /= val2; break; - case 4: val %= val2; break; - case 5: val += val2; break; - case 6: val -= val2; break; - case 7: val <<= val2; break; - case 8: val = (unsigned long)val >> val2; break; - case 9: val >>= val2; break; - case 10: val = val < val2 ? -1 : val == val2 ? 0 : 1; break; - case 11: val = val <= val2; break; - case 12: val = val >= val2; break; - case 13: val = val < val2; break; - case 14: val = val > val2; break; - case 15: val = val == val2; break; - case 16: val = val != val2; break; - case 17: val = val && val2; break; - case 18: val = val || val2; break; - case 19: val &= val2; break; - case 20: val ^= val2; break; - case 21: val |= val2; break; - } - o = o2; - } while (o && pr <= ops[o].pr); - if (runScriptDebug > 1) printf("parseExpr: result %ld\n", val); - *pp = p; - *v = val; - return o; -} - -const char* getFormat(const char** pp) -{ - static char format [20]; - const char* p = *pp; - unsigned int i = 1; - if (runScriptDebug > 1) printf("getFormat %s\n", p); - if ((format[0] = *p++) == '%') - { - while (i < sizeof(format) && memchr(" #-+0", *p, 5)) - format[i++] = *p++; - while (i < sizeof(format) && *p >= '0' && *p <= '9') - format[i++] = *p++; - if (i < sizeof(format)) - format[i++] = 'l'; - if (i < sizeof(format) && memchr("diouxXc", *p, 7)) - { - format[i++] = *p++; - format[i] = 0; - *pp = p; - if (runScriptDebug > 1) printf("format = '%s'\n", format); - return format; - } - } - if (runScriptDebug > 1) printf("no format\n"); - return NULL; -} - int runScript(const char* filename, const char* args) { MAC_HANDLE *mac = NULL; @@ -384,78 +202,8 @@ int runScript(const char* filename, const char* args) /* find local variable assignments */ if ((x = strpbrk(p, "=(, \t\n\r")) != NULL && *x=='=') { - const char* r; - char* s; - char* w; - long val; - *x++ = 0; - r = x; - w = line_raw; - while (*r) - { - /* Resolve integer expressions that are either free standing - * or in parentheses () embedded in an unquoted word. - * Do not resolve expressions in single or double quoted strings. - * An expression optionally starts with a integer format such as %x. - * It consists of integer numbers, operators and parentheses (). - */ - s = w; - if (*r == '"' || *r == '\'') - { - /* quoted strings */ - char c = *w++ = *r++; - while (*r && *r != c) { - if (*r == '\\' && !(*w++ = *r++)) break; - *w++ = *r++; - } - if (*r) *w++ = *r++; - *w = 0; - if (runScriptDebug > 1) printf("quoted string %s\n", s); - } - else if (*r == '%') - { - /* 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, 0) == 0) - { - r = r2; - if (*s == '(' && *r2++ == ')') - { - w = s; - r = r2; - } - w += sprintf(w, f , val); - if (runScriptDebug > 1) printf("formatted expression %s\n", s); - } - } - else if (parseExpr(&r, &val, 0) == 0) - { - /* unformatted expression */ - w += sprintf(w, "%ld", val); - *w = 0; - if (runScriptDebug > 1) printf("simple expression %s\n", s); - } - else if (*r == ',') - { - /* single comma */ - *w++ = *r++; - } - else { - /* unquoted string (i.e plain word) */ - do { - *w++ = *r++; - } while (*r && !strchr("%(\"', \t\n", *r)); - *w = 0; - if (runScriptDebug > 1) printf("plain word '%s'\n", s); - } - /* copy space */ - while (isspace((unsigned char)*r)) *w++ = *r++; - /* terminate */ - *w = 0; - } + replaceExpressions(x, line_raw, line_raw_size); if (runScriptDebug) printf("runScript: assign %s=%s\n", p, line_raw); macPutValue(mac, p, line_raw); @@ -596,6 +344,7 @@ int afterInit(char* cmd, char* a1, char* a2, char* a3, char* a4, char* a5, char* } epicsExportAddress(int, runScriptDebug); +epicsExportAddress(int, exprDebug); static const iocshFuncDef runScriptDef = { "runScript", 2, (const iocshArg *[]) { diff --git a/runScript.dbd b/runScript.dbd index a16c6c234ee2e199481112243cedeb2bfa66b388..ab7b21dc3715dc551931416a2a39d9e7719e02a3 100644 --- a/runScript.dbd +++ b/runScript.dbd @@ -1,2 +1,3 @@ registrar(runScriptRegister) variable(runScriptDebug,int) +variable(exprDebug,int)