diff --git a/App/tools/driver.makefile b/App/tools/driver.makefile index e29ae1606f1b411a5d352f46516d399b39ef0987..39f8da7abef372af4d916c8f7eb24fe49da16757 100644 --- a/App/tools/driver.makefile +++ b/App/tools/driver.makefile @@ -115,6 +115,8 @@ TEMPLATES= SOURCES= DBDS= HEADERS= +BASH_ENV= +ENV= # Default target is "build" for all versions. # Don't install anything (different from default EPICS make rules). @@ -331,9 +333,13 @@ ${CONFIG}/CONFIG: EB=${EPICS_BASE} TOP:=${EPICS_BASE} -include ${CONFIG}/CONFIG +BASE_CPPFLAGS= EPICS_BASE:=${EB} -SHRLIB_VERSION= COMMON_DIR = O.${EPICSVERSION}_Common +ifndef LEGACY_RSET +USR_CPPFLAGS+=-DUSE_TYPED_RSET +endif +SHRLIB_VERSION= # do not link *everything* with readline (and curses) COMMANDLINE_LIBRARY = # Relax (3.13) cross compilers (default is STRICT) to allow sloppier syntax. diff --git a/GNUmakefile b/GNUmakefile index d2388a219e0d0973c47d69ad64fe4465af84c8e9..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 @@ -23,7 +25,7 @@ HEADERS += require.h USR_INCLUDES_Linux=-idirafter ${EPICS_BASE}/include # Pass T_A to the code -USR_CFLAGS += -DT_A=${T_A} +USR_CFLAGS += -DT_A='"${T_A}"' # This should really go into some global WIN32 config file USR_CFLAGS_WIN32 += /D_WIN32_WINNT=0x501 diff --git a/Makefile b/Makefile index 4d5a232ede3dbdc83dbd2e899009197aacd2e07f..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 @@ -25,7 +28,7 @@ LIB_LIBS += $(EPICS_BASE_IOC_LIBS) USR_INCLUDES_Linux=-idirafter ${EPICS_BASE}/include # Pass T_A to the code -USR_CFLAGS += -DT_A=${T_A} +USR_CFLAGS += -DT_A='"${T_A}"' # This should really go into some global WIN32 config file USR_CFLAGS_WIN32 += /D_WIN32_WINNT=0x501 diff --git a/dbLoadTemplate.y b/dbLoadTemplate.y index 4b216bc35cde0cab3ca7fc477a7b52a603e3d9c3..648a01096ced1b156c4ab122b9cd2313af1dc391 100644 --- a/dbLoadTemplate.y +++ b/dbLoadTemplate.y @@ -17,7 +17,10 @@ #include <stddef.h> #include <string.h> #include <errno.h> + +#if !defined (_WIN32) #include <unistd.h> +#endif #include "macLib.h" #include "dbmf.h" diff --git a/expr.c b/expr.c new file mode 100644 index 0000000000000000000000000000000000000000..15738d60e1af98cb521979074d559c618db9aaac --- /dev/null +++ b/expr.c @@ -0,0 +1,280 @@ +#include <ctype.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include "expr.h" + +int exprDebug; + +static int parseSubExpr(const char** pp, long* v, int pr, int op); +#define parseExpr(pp,v) parseSubExpr(pp, v, 0, 0) + +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) 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},{"?",1},{":",0} +}; + +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 parseSubExpr(const char** pp, long* v, int pr, int o) +{ + const char *p = *pp; + long val = o ? *v : 0; + long val2; + int o2 = o; + + if (exprDebug) printf("parseExpr(%d): start %ld %s %s\n", pr, val, ops[o].str, p); + do { + if (!parseValue(&p, &val2)) + { + if (exprDebug) printf("parseExpr(%d): no value after %ld %s\n", pr, val, ops[o].str); + 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 = parseSubExpr(&p, &val2, ops[o].pr, o2)) < 0) + { + if (exprDebug) printf("parseExpr(%d): parse failed after %ld %s %ld\n", pr, val, ops[o].str, val2); + 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: if (!val) val = val2; break; + } + if (exprDebug) printf(" = %ld\n", val); + if (o2 == 23) /* ? ... : ... */ + { + long val3 = 0; + val2 = 1; + if (exprDebug) printf("parseExpr(%d) if %ld ...\n", pr, val); + if ((o2 = parseSubExpr(&p, &val2, 1, 0)) == 24) + o2 = parseSubExpr(&p, &val3, 1, 0); + else o2 = 0; + if (exprDebug) printf("parseExpr(%d) if %ld then %ld else %ld\n", pr, val, val2, val3); + val = val ? val2 : val3; + } + o = o2; + } while (ops[o].pr && pr <= ops[o].pr); + if (exprDebug) printf("parseExpr(%d): value = %ld return %d %s\n", pr, val, o, ops[o].str); + *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) + { + r = r2; + if (w > buffer && w[-1] == '(' && *r2++ == ')') + { + w--; + r = r2; + } + w += sprintf(w, f , val); + if (exprDebug) printf("formatted expression %s\n", s); + } + } + else if (parseExpr(&r, &val) == 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/iocsh b/iocsh index bde7ec7809031e41fcfd5abba27100e1d4d64792..0e7f982f0a3c53dd6db0a845ffe70389ec4a0a64 100755 --- a/iocsh +++ b/iocsh @@ -2,30 +2,39 @@ help () { { - echo "usage: iocsh [options] [files]" + echo "Usage: iocsh [options] [files] [macro=value] ..." echo "Start an EPICS iocsh and load files" - echo "Recognized filetypes: *.db *.dbt *.template *.subs *.subst *.dbd *.so" echo - echo "Possible options:" - echo " -3.xx.yy: Set EPICS base version." - echo " -32: Force 32 bit version (on 64 bit systems)." - echo " -? or -h or --help: Show this page and exit." - echo " -v or --version: Show version and exit." - echo " -c: The next string is executed as a command by the EPICS shell." - echo " -s: The next string is a sequencer program (and arguments), run with 'seq'." - echo " This forces an 'iocInit' before running the program." - echo " -r: The next string is a module (and version), loaded via 'require'." - echo " -n: The next string is the IOC name (used for prompt)." - echo " Default: dirname if parent dir is \"ioc\" otherwise hostname." + echo "Options:" + echo " -?, -h, --help Show this page and exit." + echo " -v, --version Show version and exit." + echo " -32 Force 32 bit version (on 64 bit systems)." + echo " -x[.z[.y]] Select EPICS base version x.z.y (e.g. 3.14.8, 3.15, 7)." + echo " -d, --debug Run IOC with gdb." + echo " -dv Run IOC with valgrind." + echo " -dp Run IOC with perf record." + echo " -c 'cmd args' Ioc shell command." + echo " -s 'prog m=v' Sequencer program (and arguments), run with 'seq'." + echo " This forces an 'iocInit' before running the program." + echo " -r module[,ver] Modue (optionally with version) loaded via 'require'." + echo " -n name Name of the IOC, used for prompt and \${IOC} variable." + echo " Default: dirname if parent dir is \"ioc\" otherwise hostname." + echo " @file More arguments are read from file." echo echo "Supported filetypes:" - echo "*.db, *.dbt and *.template are loaded via 'dbLoadRecords'." - echo " After the filename, you can specify substitutions like MACRO=value." - echo "*.subs and *.subst are loaded via 'dbLoadTemplate'." - echo "*.dbd is loaded via 'dbLoadDatabase'." - echo "*.so is loaded via 'ld' or 'dlload' (3.14.12 or higer)." - echo "If an argument is @file, more arguments are read from that file." + echo " *.db, *.dbt, *.template loaded via 'dbLoadRecords'" + echo " *.subs, *.subst loaded via 'dbLoadTemplate'" + echo " *.dbd loaded via 'dbLoadDatabase'" + echo " *.so loaded via 'dlload' (or 'ld' before 3.14.12)" echo "All other files are executed as startup scripts by the EPICS shell." + echo "After a file you can specify substitutions like m1=v1 m2=v1 for that file." + echo + echo "Examples:" + echo " iocsh st.cmd" + echo " iocsh my_database.template P=XY M=3" + echo " iocsh -r my_module,version -c 'initModule()'" + echo " iocsh -3.15.4 -dp st.cmd" + echo " iocsh -c 'var requireDebug 1' st.cmd" } >&2 exit } @@ -37,20 +46,6 @@ version () { exit } -case $1 in - ( -h | "-?" | -help | --help ) - help - ;; - ( -v | -ver | --ver | -version | --version ) - version - ;; - ( -3.* ) - unset EPICS_BASE - BASE=${1#-} - shift - ;; -esac - # realpath and readlink are not available on all systems, let's try what works... rp() { ( realpath $1 || readlink -f $1 || readlink $1 || (cd -P $1 && echo $PWD) || (x=$(\ls -ld $1) && echo ${x##* }) || echo $1 ) 2>/dev/null @@ -59,23 +54,31 @@ rp() { # if EPICS_HOST_ARCH is not set guess it if [ -z "$EPICS_HOST_ARCH" ] then - echo "EPICS_HOST_ARCH is not set" EPICS_HOST_ARCH=$(basename $(dirname $(rp $(which caRepeater)))) if [ -n "$EPICS_HOST_ARCH" ] then - echo "Guessing EPICS_HOST_ARCH=$EPICS_HOST_ARCH" + echo "Guessing EPICS_HOST_ARCH=$EPICS_HOST_ARCH" >&2 else + echo "EPICS_HOST_ARCH is not set" >&2 exit 1 fi fi -# check if user requested 32 bit version on a 64 bit system -case $1 in - ( -32 ) - EPICS_HOST_ARCH=${EPICS_HOST_ARCH%_64} - shift - ;; -esac +while true +do + case $1 in + ( -32 ) + EPICS_HOST_ARCH=${EPICS_HOST_ARCH%_64} + ;; + ( -[1-9]* ) + unset EPICS_BASE + BASE=${1#-} + ;; + ( * ) break + ;; + esac + shift +done # Either EPICS or EPICS_BASE should be set to the install directory if [ -z "$EPICS_BASE" ] @@ -87,25 +90,44 @@ then do if [ -d $EPICS ] then - break 2 + break fi - echo "Cannot find any EPICS installation directory." >&2 - echo "Try setting EPICS_BASE environment variable to full path" >&2 - exit 1 done + if [ ! -d "$EPICS" ] + then + EPICS=$(dirname $(dirname $(dirname $(dirname $(ldd $(which caRepeater) | awk '/libca/ {print $3}'))))) + echo "Guessing EPICS=$EPICS" + fi + if [ ! -d "$EPICS" ] + then + echo "Cannot find EPICS installation directory." >&2 + echo "Try setting EPICS environment variable." >&2 + exit 1 + fi + fi + if [ -z "$BASE" ] + then + EPICS_BASE=$(\ls -1vrd $EPICS/base/bin/{${EPICS_HOST_ARCH},${EPICS_HOST_ARCH%_64}} 2>/dev/null | head -n1) + else + # find highest (requested) EPICS version that supports our architecture (or its 32 bit version) + EPICS_BASE=$(\ls -1vrd $EPICS/base-$BASE*/bin/{${EPICS_HOST_ARCH},${EPICS_HOST_ARCH%_64}} 2>/dev/null | head -n1) fi - # find highest (requested) EPICS version that supports our architecture (or its 32 bit version) - EPICS_BASE=$(\ls -1vrd $EPICS/base*$BASE*/bin/{${EPICS_HOST_ARCH},${EPICS_HOST_ARCH%_64}} 2>/dev/null | head -n1) if [ -z $EPICS_BASE ] then - echo Cannot find any $BASE EPICS version for $EPICS_HOST_ARCH. >&2 + if [ -z "$(\ls -1vrd $EPICS/base-$BASE*/ 2>/dev/null)" ] + then + echo "No EPICS $BASE installed." >&2 + exit 1 + fi + echo EPICS $BASE not available for EPICS_HOST_ARCH=$EPICS_HOST_ARCH. >&2 exit 1 fi # maybe we need to change from 64 bit to 32 bit if [ $EPICS_HOST_ARCH != ${EPICS_BASE#*/bin/} ] then EPICS_HOST_ARCH=${EPICS_BASE#*/bin/} - echo "No 64 bit version in ${EPICS_BASE%bin*}. Switch to 32 bit version $EPICS_HOST_ARCH" + echo "No 64 bit version in ${EPICS_BASE%bin*}." >&2 + echo "Switching to 32 bit version $EPICS_HOST_ARCH." >&2 fi EPICS_BASE=$(rp ${EPICS_BASE%bin*}) fi @@ -116,28 +138,13 @@ then exit 1 fi -# Check revision -if [ -r $EPICS_BASE/configure/CONFIG_BASE_VERSION ] -then -BASE=$(awk -F '[ \t]*=[ \t]*' ' +# Get actual EPICS revision +eval $(awk -F '[ \t]*=[ \t]*' ' /^[ \t]*EPICS_VERSION[ \t]*=/ {v=$2} /^[ \t]*EPICS_REVISION[ \t]*=/ {r=$2} - /^[ \t]*EPICS_MODIFICATION[ \t]*=/ {m=$2} - END {print v"."r"."m}' < $EPICS_BASE/configure/CONFIG_BASE_VERSION) -else -BASE=$(basename $(rp $EPICS_BASE)) -BASE=${BASE#*base-} -fi -if [ "${BASE#3.}" = "$BASE" ] -then - echo "Cannot find any EPICS base version" >&2 - echo "Try setting EPICS_BASE environment variable to full path" >&2 - exit 1 -fi -export BASE -BASEMINOR=${BASE#3.} -BASEPATCH=${BASEMINOR#*.} -BASEMINOR=${BASEMINOR%.*} + /^[ \t]*EPICS_MODIFICATION[ \t]*=/ {m=$2+0} + END {print "BASE="v"."r"."m";BASECODE="v*10000+r*100+m} +' < $EPICS_BASE/configure/CONFIG_BASE_VERSION) # IOC name derives from hostname # (trailing possible '\r' under cygwin) @@ -154,7 +161,7 @@ export IOC # Check for 64 bit versions, default to 32 bit if [ ! -d $EPICS_BASE/lib/${EPICS_HOST_ARCH} -a -d $EPICS_BASE/lib/${EPICS_HOST_ARCH%_64} ] then - echo "No 64 bit EPICS installation found. Defaulting to 32 bit" + echo "No 64 bit EPICS installation found. Defaulting to 32 bit" >&2 EPICS_HOST_ARCH=${EPICS_HOST_ARCH%_64} fi export EPICS_HOST_ARCH @@ -186,27 +193,19 @@ then fi export EPICS_DRIVER_PATH -subst () { - subst="" - while [ "$#" -gt 1 ] - do - case $2 in - ( *=* ) - subst="$subst,$2"; shift - ;; - ( * ) - break - ;; - esac - done - echo ${subst#,} -} - loadFiles () { while [ "$#" -gt 0 ] do file=$1 case $file in + ( -32 ) + echo "-32 option must be set earlier" >&2 + exit 1 + ;; + ( -[1-9]* ) + echo "EPICS version $file option must be set earlier" >&2 + exit 1 + ;; ( -h | "-?" | -help | --help ) help ;; @@ -216,35 +215,14 @@ do ( @* ) loadFiles $(cat ${file#@}) ;; - ( *.db | *.template) - subst="" - while [ "$#" -gt 1 ] - do - case $2 in - ( *=* ) - subst="$subst,$2"; shift - ;; - ( * ) - break - ;; - esac - done - echo "dbLoadRecords \"$file\",\"${subst#,}\"" - ;; - ( *.subs | *.subst ) - echo "dbLoadTemplate \"$file\"" + ( -d | -dg | --debug ) + LOADER="gdb --eval-command run --args $LOADER" ;; - ( *.dbd ) - # some dbd files must be loaded before main to take effect - echo "dbLoadDatabase \"$file\",\"$DBD\"" + ( -dv ) + LOADER="valgrind --leak-check=full $LOADER" ;; - ( *.so ) - if [ $BASEMINOR -ge 15 -o $BASEPATCH -ge 12 ] - then - echo "dlload \"$file\"" - else - echo "ld \"$file\"" - fi + ( -dp ) + LOADER="perf record $LOADER" ;; ( -c ) shift @@ -279,24 +257,58 @@ do shift IOC="$1" ;; - ( -3.* ) - echo "Version $file must be first argument" >&2 - exit 1 - ;; - ( -32 ) - echo "-32 option must come before all others (except -3.xx.yy)" >&2 - exit 1 - ;; ( -* ) - { - echo "unknown option $1" - echo "try: $(basename $0) --help" - } >&2 + echo "Unknown option $1" >&2 + echo "Try: $(basename $0) --help" >&2 exit 1 ;; - ( * ) - echo "< \"$file\"" - if grep -q iocInit $file; then init=NO; fi + ( *.so ) + if [ $BASECODE -ge 31412 ] + then + echo "dlload \"$file\"" + else + echo "ld \"$file\"" + fi + ;; + ( *=* ) + echo -n $file | awk -F '=' '{printf "epicsEnvSet %s '\''%s'\''\n", $1, $2}' + ;; + ( * ) + subst="" + while [ "$#" -gt 1 ] + do + case $2 in + ( *=* ) + subst="$subst,$2"; shift + ;; + ( * ) + break + ;; + esac + done + subst=${subst#,} + case $file in + ( *.db | *.template) + echo "dbLoadRecords '$file','$subst'" + ;; + ( *.subs | *.subst ) + echo "dbLoadTemplate '$file','$subst'" + ;; + ( *.dbd ) + # some dbd files must be loaded before main to take effect + echo "dbLoadDatabase '$file','$DBD','$subst'" + ;; + ( * ) + if [ $BASECODE -ge 31500 ] + then + echo "iocshLoad '$file','$subst'" + else + echo -n $subst | awk -F '=' -v 'RS=,' '{printf "epicsEnvSet %s '\''%s'\''\n", $1, $2}' + echo "< '$file'" + fi + if grep -q iocInit $file; then init=NO; fi + ;; + esac ;; esac shift @@ -306,12 +318,12 @@ done startup=/tmp/iocsh.startup.$$ # clean up and kill the softIoc when killed by any signal -trap "kill -s SIGTERM 0; stty sane; echo; rm -f $startup; " EXIT +trap "kill -s SIGTERM 0; (stty sane && echo) 2>/dev/null; rm -f $startup; " EXIT { echo "# date=\"$(date)\"" echo "# user=\"${USER:-$(whoami)}\"" -for var in PWD BASE EPICS_HOST_ARCH SHELLBOX EPICS_CA_ADDR_LIST EPICS_DRIVER_PATH +for var in IOC PWD BASE EPICS_HOST_ARCH SHELLBOX EPICS_CA_ADDR_LIST EPICS_DRIVER_PATH do echo "# $var=\"${!var}\"" done @@ -329,14 +341,14 @@ else # old driver pool model REQUIRE_DBD=$INSTBASE/iocBoot/R$BASE/dbd/$REQUIRE.dbd fi -if [ $BASEMINOR -ge 15 -o $BASEPATCH -ge 12 ] +if [ $BASECODE -ge 31412 ] then EXE=$EPICS_BASE/bin/$EPICS_HOST_ARCH/softIoc ARGS="-D $EPICS_BASE/dbd/softIoc.dbd" LDCMD="dlload" else # get rid of the compiled-in rpath because at PSI that is a link pointing to current EPICS version. - LOADER=/lib/ld-linux.so.2 + LOADER="$LOADER /lib/ld-linux.so.2" LOADERARGS="--library-path $EPICS_BASE/lib/$EPICS_HOST_ARCH --inhibit-rpath ''" APP=ioc EXE=$EPICS_EXTENSIONS/bin/$EPICS_HOST_ARCH/$APP @@ -346,6 +358,12 @@ else echo "${APP}_registerRecordDeviceDriver(pdbbase)" fi +if [ ! -x $EXE ] +then + echo "$EXE not found or not executable." >&2 + exit 1 +fi + if [ ! -f "$REQUIRE_LIB" ] then echo "Library $REQUIRE_LIB not found." >&2 @@ -388,5 +406,6 @@ then fi echo $EXE $ARGS $startup +#enable core dumps ulimit -c unlimited eval "$LOADER $LOADERARGS $EXE" $ARGS "$startup" 2>&1 diff --git a/require.c b/require.c index 840c5e6ac930d0d928d51e0801204b0bb9680728..b19f91be4c1269c474bb042475419041b684808c 100644 --- a/require.c +++ b/require.c @@ -70,6 +70,7 @@ int requireDebug; #include <loadLib.h> #include <shellLib.h> #include <ioLib.h> + #include <fioLib.h> #include <envLib.h> #include <epicsAssert.h> #include "strdup.h" @@ -83,19 +84,54 @@ int requireDebug; #define getAddress(module, name) __extension__ \ ({SYM_TYPE t; char* a = NULL; symFindByName(sysSymTbl, (name), &a, &t); a;}) - /* vxWorks has no snprintf() */ - #define snprintf(s, maxchars, f, args...) __extension__ \ - ({int printed=sprintf(s, f, ## args); assert(printed < maxchars); printed;}) +#ifndef _WRS_VXWORKS_MAJOR + /* vxWorks 5 has no snprintf() */ + struct outStr_s { + char *str; + int free; + }; + + static STATUS outRoutine(char *buffer, int nchars, int outarg) { + struct outStr_s *poutStr = (struct outStr_s *) outarg; + int free = poutStr->free; + + if (free < 1) { /*let fioFormatV continue to count length*/ + return OK; + } else if (free > 1) { + if (nchars >= free) nchars = free-1; + strncpy(poutStr->str, buffer, nchars); + poutStr->str += nchars; + poutStr->free -= nchars; + } + /*make sure final string is null terminated*/ + *poutStr->str = 0; + return OK; + } + + static int snprintf(char *str, size_t size, const char *format, ...) + { + struct outStr_s outStr; + int nchars; + va_list ap; + + outStr.str = str; + outStr.free = size; + va_start(ap, format); + nchars = fioFormatV(format, ap, outRoutine, (int)&outStr); + va_end(ap); + return nchars; + } +#endif /* vxWorks has no realpath() -- at least make directory absolute */ static char* realpath(const char* path, char* buf) { size_t len = 0; - if (!buf) buf = malloc(MAX_FILENAME_LENGTH); + if (!buf) buf = malloc(PATH_MAX+1); if (!buf) return NULL; if (path[0] != OSI_PATH_SEPARATOR[0]) { - getcwd(buf, MAX_FILENAME_LENGTH); + getcwd(buf, PATH_MAX); len = strlen(buf); if (len && buf[len-1] != OSI_PATH_SEPARATOR[0]) buf[len++] = OSI_PATH_SEPARATOR[0]; @@ -164,7 +200,7 @@ int requireDebug; #include "asprintf.h" #define snprintf _snprintf #define setenv(name,value,overwrite) _putenv_s(name,value) - #define NAME_MAX MAX_PATH + #define PATH_MAX MAX_PATH #define PREFIX #define INFIX @@ -227,7 +263,17 @@ int requireDebug; #define LIBDIR "lib" OSI_PATH_SEPARATOR #define TEMPLATEDIR "db" + /* +#define TOSTR(s) TOSTR2(s) +#define TOSTR2(s) #s +const char epicsRelease[] = TOSTR(EPICS_VERSION)"."TOSTR(EPICS_REVISION)"."TOSTR(EPICS_MODIFICATION); +const char epicsBasetype[] = TOSTR(EPICS_VERSION)"."TOSTR(EPICS_REVISION); +#ifndef T_A +#error T_A not defined: Compile with USR_CFLAGS += -DT_A='"${T_A}"' +#endif +const char targetArch[] = T_A; + */ #ifndef OS_CLASS #error OS_CLASS not defined: Try to compile with USR_CFLAGS += -DOS_CLASS='"${OS_CLASS}"' @@ -696,7 +742,7 @@ static int findLibRelease ( char* p; char* version; char* symname; - char name[NAME_MAX + 11]; /* get space for library path + "LibRelease" */ + char name[PATH_MAX + 11]; /* get space for library path + "LibRelease" */ (void)data; /* unused */ if (size < sizeof(struct dl_phdr_info)) return 0; /* wrong version of struct dl_phdr_info */ @@ -771,7 +817,6 @@ static void registerExternalModules() } } - #else static void registerExternalModules() { @@ -1240,23 +1285,21 @@ require_priv(const char* module, const char* versionstr /* "-<version>" or "" (for old style only */ ) { - int status; - int ifexists = 0; - int releasediroffs; - int libdiroffs; - int extoffs; - - HMODULE libhandle; - const char* loaded = NULL; const char* found = NULL; + HMODULE libhandle; + int ifexists = 0; const char* driverpath; const char* dirname; const char *end; + + int releasediroffs; + int libdiroffs; + int extoffs; char* founddir = NULL; char* symbolname; - char filename[NAME_MAX]; + char filename[PATH_MAX]; int someVersionFound = 0; int someArchFound = 0; diff --git a/runScript.c b/runScript.c index 6017663981bb990095568d9456bfdc890a5836f4..daf79fa343ed257ac9be183d4447db286bf374a7 100644 --- a/runScript.c +++ b/runScript.c @@ -47,110 +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, int* v); - -static int parseValue(const char** pp, int* v) -{ - int val; - const char *p = *pp; - int neg = 0; - - while (isspace((unsigned char)*p)) p++; - if (*p == '+' || *p == '-') neg = *p++ == '-'; - while (isspace((unsigned char)*p)) p++; - if (*p == '(') - { - p++; - if (!parseExpr(&p, &val)) return 0; - if (*p++ != ')') return 0; - } - else - { - char* e; - val = strtol(p, &e, 0); - if (e == p) return 0; - p = e; - } - if (neg) val = -val; - if (*p == '?') - { - p++; - val = (val != 0); - } - *pp = p; - *v = val; - if (runScriptDebug > 1) printf("parseValue: %d rest=\"%s\"\n", *v, p); - return 1; -} - -static int parseExpr(const char** pp, int* v) -{ - const char *p = *pp; - const char *q; - int o; - int val; - int val2; - int status = 0; - - *v = 0; - do { - if (!parseValue(&p, &val)) return status; - if (runScriptDebug > 1) printf("parseExp 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 (o == '*') val *= val2; - else if (val2 == 0) val = 0; - else if (o == '/') val /= val2; - else val %= val2; - p = q; - while (isspace((unsigned char)*p)) p++; - o = *p; - } - status = 1; - *v += val; - *pp = p; - if (runScriptDebug > 1) printf("parseExpr: sum %d rest=\"%s\"\n", *v, p); - } while (o == '+' || o == '-'); - return 1; -} - -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++) == '%') - { - 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++; - 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; @@ -175,7 +76,7 @@ int runScript(const char* filename, const char* args) #endif char*[]){ "", "environ", NULL, NULL }) != 0) goto error; macSuppressWarning(mac, 1); -#if (EPICSVER<31400) +#if (EPICSVER<31403) /* Have no environment macro substitution, thus load envionment explicitly */ /* Actually, environmant macro substitution was introduced in 3.14.3 */ for (pairs = ppGlobalEnviron; *pairs; pairs++) @@ -299,70 +200,10 @@ int runScript(const char* filename, const char* args) if (*p == 0 || *p == '#') continue; /* find local variable assignments */ - if ((x = strpbrk(p, "=(, \t\n")) != NULL && *x=='=') + if ((x = strpbrk(p, "=(, \t\n\r")) != NULL && *x=='=') { - const char* r; - char* w; - int val; - *x++ = 0; - r = x; - w = line_raw; - while (*r) - { - if (runScriptDebug > 1) printf ("expr %s\n", r); - if (*r == '%') - { - const char* r2 = r; - const char* f; - if ((f = getFormat(&r2)) && parseExpr(&r2, &val)) - { - w += sprintf(w, f , val); - r = r2; - } - else - { - if (runScriptDebug > 1) printf ("skip %c\n", *r); - *w++ = *r++; - } - continue; - } - if (parseExpr(&r, &val)) - { - if (runScriptDebug > 1) printf ("val=%d, rest=%s\n", val, r); - w += sprintf(w, "%d", val); - if (runScriptDebug > 1) printf ("rest=%s\n", r); - } - else if (*r == '(' || *r == '+') - { - if (runScriptDebug > 1) printf ("skip %c\n", *r); - *w++ = *r++; - continue; - } - while (1) - { - if ((*r >= '0' && *r <= '9') || *r == '(' || *r == '%') break; - if (*r == '"' || *r == '\'') - { - char c = *r++; - if (runScriptDebug > 1) printf ("string %c\n", c); - while (*r && *r != c) { - *w++ = *r++; - } - *w = 0; - if (*r) r++; - if (*r == '+') - { - if (runScriptDebug > 1) printf ("skip %c\n", *r); - *w++ = *r++; - } - break; - } - if (runScriptDebug > 1) printf ("copy %c\n", *r); - if (!(*w++ = *r)) break; - r++; - }; - } + replaceExpressions(x, line_raw, line_raw_size); if (runScriptDebug) printf("runScript: assign %s=%s\n", p, line_raw); macPutValue(mac, p, line_raw); @@ -503,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) diff --git a/testscript b/testscript new file mode 100644 index 0000000000000000000000000000000000000000..168ee74c5e372b172527853358060dbe8b2b9c2d --- /dev/null +++ b/testscript @@ -0,0 +1,82 @@ +#var exprDebug 1 + +x=3, !3, -3, ~3, 3? +# $(x) should be: 3, 0, -3, -4, 1 + +x=10+3, 10-3, 10*3, 10/3, 10%3, 10**3 +# $(x) should be: 13, 7, 30, 3, 1, 1000 + +x=%x -10<<2, %x -10>>2, %x -10>>>2 +# $(x) should be: ffffffffffffffd8, fffffffffffffffd, 3ffffffffffffffd + +x=%x 0xaa & 0x0f, %x 0xaa | 0x0f, %x 0xaa ^ 0x0f +# $(x) should be: a, af, a5 + +x=10<20 10<=20 10==20 10>=20 10>20 10<=>20 +# $(x) should be: 1 1 0 0 0 -1 + +x=20<20 20<=20 20==20 20>=20 20>20 20<=>20 +# $(x) should be: 0 1 1 1 0 0 + +x=20<10 20<=10 20==10 20>=10 20>10 20<=>10 +# $(x) should be: 0 0 0 1 1 1 + +x= (0|0)(0|1)(1|0)(1|1) (0&0)(0&1)(1&0)(1&1) +# $(x) should be: 0111 0001 + +x=1*2*3+4*5*6-7-8 +# $(x) should be: 111 + +x=1*2*3+4*5*6-7*8 +# $(x) should be: 70 + +x=-3**2+1, -(3**2)+1, (-3)**2+1, -3**-2+1, (1+2), (1+2, 1+2) +# $(x) should be: 10, -8, 10, 1, 3, (1+2, 3) + +x=%#X 0x5555&0xfff0|0xaf<<8^0xa00a0>>4 +# $(x) should be: 0X5F5A + +x=7+4*2|32 +# $(x) should be: 47 + +x=3<5 ? 7+4*2|32 : 9 & 10 +# $(x) should be: 47 + +x=3>5 ? 7+4*2|32 : 9 & 10 +# $(x) should be: 8 + +x=7 ?: -1 +# $(x) should be: 7 + +x=0 ?: -1 +# $(x) should be: -1 + +x=7 ? -1 +# $(x) should be: -1 + +x=0 ? -1 +# $(x) should be: 0 + +x=1+0?0?1:2:3?4:5 +# $(x) should be: 2 + +x=1+0?7?1:2:3?4:5 +# $(x) should be: 1 + +x=1+1?0?1:2:3-3?4:5 +# $(x) should be: 2 + +x=1-1?0?1:2:3?4:5 +# $(x) should be: 4 + +x=1-1?0?1:2:3-3?4:5 +# $(x) should be: 5 + +x=0?; 1?; 2?; -4? +# $(x) should be: 0; 1; 1; 1 + +x=a030b a(030)b a( 030 )b "a"030"b" a(%x 030)b +# $(x) should be: a030b a24b a24b a24b a18b + +x=-2**2 0-2**2 0+-2**2 0--2**2 +# $(x) should be: 4 -4 4 -4