diff --git a/externalLinks b/externalLinks new file mode 100755 index 0000000..c83d693 --- /dev/null +++ b/externalLinks @@ -0,0 +1,428 @@ +#!/bin/bash + +# Author: Dirk Zimoch + +version () { + echo '$Author: zimoch $' >&2 + echo '$Date: 2008/12/22 17:11:32 $' >&2 + echo '$Revision: 1.1 $' >&2 + echo '$Source: /cvs/G/EPICS/App/scripts/externalLinks,v $' >&2 + exit 1 +} + +usage () { + echo "usage: externalLinks [options] " >&2 + echo "returns list of link targets that are not resolved internally" >&2 + echo "optionally returns list of required modules" >&2 + echo "also does some plausibility checks" >&2 + echo "options are:" >&2 + echo " -h | -? | --help print this text and exit" >&2 + echo " -v | --version print version and exit" >&2 + echo " -d | --debug print additional debug messages" >&2 + echo " -w | --where show record.link and target" >&2 + echo " -r | --require show required modules" >&2 + echo " --3.13 use EPICS 3.13 (default for SLS)" >&2 + echo " --3.14 use EPICS 3.14 (default for XFEL)" >&2 + echo " -- next argument is file, even if starting with -" >&2 + exit 1 +} + +shopt -s nullglob +shopt -s extglob + +export LANG=en_US.iso885915 +if [ "${INSTBASE%/*}" == "/psi-xfel" ] +then + EPICS=3.14.8 +else + EPICS=3.13.10 +fi + +while true +do + case "$1" in + ( -h | -\? | ?(-)-help ) usage ;; + ( -v | ?(-)-version ) version ;; + ( -d | ?(-)-debug ) DEBUG=1 ;; + ( -r | ?(-)-require ) REQUIRE=1 ;; + ( -w | ?(-)-where ) WHERE=1 ;; + ( ?(-)-3.13 ) EPICS=3.13.10 ;; + ( ?(-)-3.14 ) EPICS=3.14.8 ;; + ( -- | - ) shift; break ;; + ( -* ) echo "unknown option $1 ignored" >&2 ;; + ( * ) break ;; + esac + shift +done + +if [ $# = 0 ] +then + usage +fi + +function expandFile () { + if [ ! -r $1 ] + then + echo "can't read $1" >&2 + return + fi + while read line + do + case $line in + ( include* ) eval expandFile ${line#include} ;; + ( * ) echo $line + esac + done < $1 +} + +{ + CONVERT=dbd/slsConvert.dbd + if [ ! -r $CONVERT ] + then + CONVERT=$SLSBASE/epics/base/bin/R${EPICS}/slsConvert.dbd + fi + if [ -r $CONVERT ] + then + echo "FILENAME $CONVERT" + cat $CONVERT + fi + MAINDBD=$INSTBASE/iocBoot/R${EPICS}/dbd/base.dbd + if [ ! -r $MAINDBD ] + then + echo "can't find $MAINDBD" >&2 + exit 1 + fi + echo "FILENAME $MAINDBD base" + cat $MAINDBD + + for dbd in dbd/*.dbd + do + case $dbd in + ( */dbCommon.dbd ) ;; + ( */*_conv.dbd ) ;; + ( */slsConvert.dbd ) ;; + ( */slsMain.dbd ) ;; + ( */exampleApp.dbd ) ;; + ( * ) echo "FILENAME $dbd"; expandFile $dbd ;; + esac + done + + echo $INSTBASE/iocBoot/R${EPICS}/dbd/+([^-]).dbd + + for dbd in $INSTBASE/iocBoot/R${EPICS}/dbd/+([^-]).dbd + do + if [ -L $dbd ] + then + module=$(basename $dbd) + module=${module%.dbd} + if [ $module != base -a $module != slsMain -a $module != sls ] + then + echo "FILENAME $dbd $module"; cat $dbd + fi + fi + done + + for subs in $@ + do + echo "FILENAME $subs" + dbLoadTemplate $subs + done +} | awk -v where=$WHERE -v require=$REQUIRE -v debug=$DEBUG ' + /FILENAME/ { + filename = $2 + line = 0 + module = $3 + if (debug) { + print "reading file " filename > "/dev/stderr" + } + } + {line ++} + /^[ \t]*#/ { next } + /\{/ {context++} + /\}/ {context--} + /choice[ \t]*([ \t]*.*[ \t]*,[ \t]*".*"[ \t]*)/ { + if (valid) { + match($0,/\([ \t]*(.*)[ \t]*,[ \t]*"(.*)"/,a) + choice = a[2] + if (ischoice[menu,choice]) + { + if (debug) { + printf("duplicate choice %s for menu %s\n\t%s line %d\n\t%s\n", + choice, menu, filename, line, definition[menu,choice]) > "/dev/stderr" + } + } else { + ischoice[menu,choice] = 1 + enum[menu,choicenum[menu]++] = choice + choices[menu] = choices[menu] "\n\t\"" choice "\"" + modulename[menu,choice] = module + definition[menu,choice] = filename " line " line + } + } + } + /breaktable[ \t]*([ \t]*.*[ \t]*)/ { + if (valid) { + match($0,/\([ \t]*(.*)[ \t]*)/,a) + choice = a[1] + menu = "menuConvert" + if (ischoice[menu,choice]) + { + if (debug) { + printf("duplicate choice %s for menu %s\n\t%s line %d\n\t%s\n", + choice, menu, filename, line, definition[menu,choice]) > "/dev/stderr" + } + } else { + ischoice[menu,choice] = 1 + enum[menu,choicenum[menu]++] = choice + choices[menu] = choices[menu] "\n\t\"" choice "\"" + modulename[menu,choice] = module + definition[menu,choice] = filename " line " line + } + } + } + /recordtype\(.*\)/ { + match($0,/\((.*)\)/,a) + rtype = a[1] + if (isdefined[rtype]) { + if (debug) { + printf("duplicate recordtype %s\n\t%s line %d\n\t%s\n", + rtype, filename, line, definition[rtype]) > "/dev/stderr" + } + modulename[rtype] = modulename[rtype] "\" or \"" module + valid = 0 + } else { + isdefined[rtype] = 1 + modulename[rtype] = module + definition[rtype] = filename " line " line + valid = 1 + } + } + /field\(.*,DBF_.*\)/ {if (!valid) next } + /field\(.*,DBF_.*\)/ { + match($0,/\((.*),/,a) + field = a[1] + } + /field\(.*,DBF_NOACCESS\)/ { + isnoaccess[rtype,field] = 1 + next + } + /field\(.*,DBF_.*\)/ { + if (field != "NAME") { + fields[rtype] = fields[rtype] field " " + } + } + /field\(.*,DBF_.*LINK\)/ { + islink[rtype,field] = 1 + next + } + /field\(.*,DBF_STRING\)/ { + isstring[rtype,field] = 1 + next + } + /field\(.*,DBF_DEVICE\)/ { + isdevice[rtype,field] = 1 + next + } + /field\(.*,DBF_FLOAT\)/ { + isnumeric[rtype,field] = 1 + next + } + /field\(.*,DBF_DOUBLE\)/ { + isnumeric[rtype,field] = 1 + next + } + /field\(.*,DBF_ULONG\)/ { + isunsigned[rtype,field] = 1 + range[rtype,field] = 0xffffffff + next + } + /field\(.*,DBF_USHORT\)/ { + isunsigned[rtype,field] = 1 + range[rtype,field] = 0xffff + next + } + /field\(.*,DBF_UCHAR\)/ { + isunsigned[rtype,field] = 1 + range[rtype,field] = 0xff + next + } + /field\(.*,DBF_LONG\)/ { + isinteger[rtype,field] = 1 + range[rtype,field] = 0x7fffffff + next + } + /field\(.*,DBF_SHORT\)/ { + isinteger[rtype,field] = 1 + range[rtype,field] = 0x7fff + next + } + /field\(.*,DBF_CHAR\)/ { + isinteger[rtype,field] = 1 + range[rtype,field] = 0x7f + next + } + /field\(.*,DBF_ENUM\)/ { + isunsigned[rtype,field] = 1 + range[rtype,field] = 0xffff + next + } + /menu\(.*\)/ { + if (context == 1) { + match($0,/\([ \t]*(.*)[ \t]*\)/,a) + menu = a[1] + if (choices[menu] != "") { + #printf("menu %s redefined\n", menu) > "/dev/stderr" + valid = 0 + } else { + valid = 1 + choicenum[menu] = 0 + definition[menu] = filename " line " line + } + } else { + match($0,/\((.*)\)/,a) + menu = a[1] + menuname[rtype,field] = menu + } + } + /size\(.*\)/ { + if (valid) { + match($0,/\((.*)\)/,a) + stringsize[rtype,field] = a[1]-1 + if (field == "NAME" && !maxNameLen) { + maxNameLen = stringsize[rtype,field] + } + } + } + /device[ \t]*\(.*\)/ { + match($0,/\([ \t]*(.*)[ \t]*,[ \t]*(.*)[ \t]*,.*,[ \t]*"(.*)"[ \t]*\)/,a) + rtype = a[1] + ltype = a[2] + dtype = a[3] + if (ltype == "CONSTANT") { + issoft[rtype,dtype] = 1 } + if (isdefined[rtype, dtype]) { + if (debug) { + printf("duplicate device type %s for record type %s\n\t%s line %d\n\t%s\n", + dtype, rtype, filename, line, definition[rtype, dtype]) > "/dev/stderr" + } + modulename[rtype, dtype] = modulename[rtype, dtype] "\" or \"" module + } else { + isdefined[rtype, dtype] = 1 + modulename[rtype, dtype] = module + devices[rtype] = devices[rtype] "\n\t\"" dtype "\"" + definition[rtype, dtype] = filename " line " line + } + } + /record\(.*,".*"\)/ { + match($0,/\((.*),"(.*)"/,a) + rtype = a[1] + record = a[2] + if (recordtype[record] != "" && recordtype[record] != rtype) { + printf("record %s redefined with different type\n\t%s in %s\n\t%s in %s\n", + record, recordtype[record], definition[record], rtype, filename) > "/dev/stderr" + } else { + recordtype[record] = rtype + definition[record] = filename + if (!required[modulename[rtype]]) { + if (debug) { + printf ("recordtype %s requires \"%s\"\n", rtype, modulename[rtype]) > "/dev/stderr" + } + required[modulename[rtype]] = 1 + } + } + if (length(record)>maxNameLen) { + printf("record name \"%s\" too long (max %d)\n", + record, maxNameLen) > "/dev/stderr" + } + soft = 1 + } + /field\(.*, ".*\")/ { + if (!stringsize[rtype,"NAME"]) next; + match($0,/\((.*), "(([^-+0-9\.][^\. ]*)?.*)"\)/,a) + field = a[1] + value = a[2] + target = a[3] + menu = menuname[rtype,field] + if (isnoaccess[rtype,field]) { + printf("writing \"%s\" to NOACCESS field %s.%s\n", + value, record, field) > "/dev/stderr" + } else if (isdevice[rtype,field]) { + if (!isdefined[rtype,value]) { + printf("unknown device \"%s\" in %s.%s (%s)\n", + value, record, field, rtype) > "/dev/stderr" + } else { + if (!required[modulename[rtype,value]]) { + if (debug) { + printf ("DTYP \"%s\" for record type %s requires \"%s\"\n", + value, rtype, modulename[rtype,value]) > "/dev/stderr" + } + required[modulename[rtype,value]] = 1 + } + } + soft = issoft[rtype,value] + } else if (islink[rtype,field]) { + if (target != "" && \ + ((field != "INP" && field != "OUT") || soft)) { + reference[target] = filename ": "record "." field + } + } else if (menu != "") { + if (!ischoice[menu,value]) { + if (value != int(value) || value < 0) + { + printf("unknown choice \"%s\" in %s.%s (%s) should be one of (%s):%s\n", + value, record, field, rtype, menu, choices[menu]) > "/dev/stderr" + } else { + printf("better use choice \"%s\" (%s) instead of \"%s\" in %s.%s (%s)\n", + enum[menu,value], menu, value, + record, field, rtype) > "/dev/stderr" + } + } + } else if (isnumeric[rtype,field]) { + if (!match(value,/^ *[+-]?([0-9]+.?[0-9]*|[0-9]*.?[0-9]+)([eE][-+]?[0-9]+)? *$/)) { + printf("value \"%s\" in %s.%s (%s) should be numeric\n", + value, record, field, rtype) > "/dev/stderr" + } + } else if (isinteger[rtype,field]) { + if (!match(value,/^ *[+-]?(0[xX])?[0-9]+ *$/)) { + printf("value \"%s\" in %s.%s (%s) should be integer %d...%d\n", + value, record, field, rtype, -range[rtype,field]-1, range[rtype,field]) > "/dev/stderr" + } + if (value+0 > range[rtype,field] || value+1 < -range[rtype,field]) { + printf("value %s in %s.%s (%s) put of range %d...%d\n", + value, record, field, rtype, -range[rtype,field]-1, range[rtype,field]) > "/dev/stderr" + } + } else if (isunsigned[rtype,field]) { + if (!match(value,/^ *+?(0[xX])?[0-9]+ *$/)) { + printf("value \"%s\" in %s.%s (%s) should be unsigned integer 0...%d\n", + value, record, field, rtype, range[rtype,field]) > "/dev/stderr" + } + if (value+0 > range[rtype,field]) { + printf("value %s in %s.%s (%s) put of range 0...%d\n", + value, record, field, rtype, range[rtype,field]) > "/dev/stderr" + } + } else if (isstring[rtype,field]) { + if (length(value)>stringsize[rtype,field]) { + printf("string \"%s\" in %s.%s (%s) too long (max %d)\n", + value, record, field, rtype, + stringsize[rtype,field]) > "/dev/stderr" + } + } else { + printf("unknown field %s in record %s (%s) should be one of:\n%s\n\n", + field, record, rtype, fields[rtype]) > "/dev/stderr" + } + } + END { + for (target in reference) { + if (recordtype[target] == "") { + if (where) print reference[target],target + else print target + } + } + if (require) { + for (module in required) { + if (module != "base") { + print "require \"" module "\"" + } + } + } + } +'