#!/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 "\"" } } } } '