Files
utilities/externalLinks
2013-12-05 15:30:27 +00:00

499 lines
16 KiB
Bash
Executable File

#!/bin/bash
# Author: Dirk Zimoch
version () {
echo '$Author: zimoch $' >&2
echo '$Date: 2013/12/05 15:30:27 $' >&2
echo '$Revision: 1.15 $' >&2
echo '$Source: /cvs/G/EPICS/App/scripts/externalLinks,v $' >&2
exit 1
}
usage () {
echo "usage: externalLinks [options] <substitutionFiles>" >&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 lastest EPICS 3.13 version (default for SLS)" >&2
echo " -3.14 use lastest EPICS 3.14 version (default for SwissFEL, HIPA)" >&2
echo " -3.x.x use specific EPICS version" >&2
echo " -- next argument is file, even if starting with -" >&2
exit 1
}
shopt -s nullglob
shopt -s extglob
export LANG=en_US.iso885915
INSTBASE=${INSTBASE%/}
INSTBASE=${INSTBASE#/import}
case "${INSTBASE}" in
( /work|/prod|/devl ) EPICS=3.13.10 ;;
( /hipa/* ) EPICS=3.14.12 ;;
( * ) EPICS=3.14.12 ;;
esac
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.12 ;;
( ?(-)-3.* ) EPICS=${1##*-} ;;
( -- | - ) 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
}
{
SCAN=dbd/menuScan.dbd
if [ ! -r $SCAN ]
then
SCAN=dbd/scan.dbd
fi
if [ -r $SCAN ]
then
echo "FILENAME $SCAN"
cat $SCAN
fi
CONVERT=dbd/slsConvert.dbd
if [ ! -r $CONVERT ]
then
CONVERT=$INSTBASE/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 ) ;;
( */menuScan.dbd ) ;;
( */scan.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 -v epics=$EPICS '
BEGIN {
epics3_14ver=gensub(/^3\.14\./,"",1,epics)+0
printf ("Info: Using EPICS version %s (see -? how to change)\n", epics) > "/dev/stderr"
}
/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]
isfield[rtype,field] = 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\" %d chars too long (max %d)\nmaybe this works with a higher EPICS version (this is %s)\n",
record, length(record)-maxNameLen, maxNameLen, epics) > "/dev/stderr"
}
if (record ~ /[ \t.]/) {
printf("whitespaces or dots in record name \"%s\" are not recommended\n", record) > "/dev/stderr"
}
soft = 1
}
/alias\(".*\",".*\")/ {
if (epics3_14ver < 11) {
printf ("%s requires EPICS version >= 3.14.11 (this is %s)\n",
$0, epics) > "/dev/stderr"
}
match($0,/\("(.*)","(.*)"/,a)
record = a[1]
aliasname = a[2]
if (recordtype[record] == "") {
printf ("alias \"%s\" defined for not existing record \"%s\"\n\tin %s\n",
aliasname, record, filename) > "/dev/stderr"
}
alias[aliasname] = record;
}
/field\(.*, ".*\")/ {
if (!stringsize[rtype,"NAME"]) next;
match($0,/\((.*), "(([^-+0-9\.][^\. ]*)?.*)"\)/,a)
field = a[1]
value = a[2]
menu = menuname[rtype,field]
if (fieldvalue[record,field] && fieldvalue[record,field] != value) {
printf("%s.%s redefined from \"%s\" to \"%s\"\n",
record, field, fieldvalue[record,field], value) > "/dev/stderr"
}
fieldvalue[record,field] = value
if (isnoaccess[rtype,field]) {
printf("writing \"%s\" to NOACCESS field %s.%s\n",
value, record, field) > "/dev/stderr"
} else if (isdevice[rtype,field]) {
if (iolink[record]) {
if (!dtyp[record]) {
printf("%s defined after %s in record %s\n",
field, iolink[record], record) > "/dev/stderr"
}
}
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 ("%s \"%s\" for record type %s requires \"%s\"\n",
field, value, rtype, modulename[rtype,value]) > "/dev/stderr"
}
required[modulename[rtype,value]] = 1
}
}
soft = issoft[rtype,value]
dtyp[record] = value
} else if (islink[rtype,field]) {
if (value != "" && \
tolower(value) !~ /^ *[+-]?(([0-9]+.?[0-9]*|[0-9]*.?[0-9]+)(e[-+]?[0-9]+)?|inf|nan) *$/ && \
((field != "INP" && field != "OUT") || soft)) {
reference[gensub(/ .*/,"",1,value)] = filename ": "record "." field
}
if (field == "INP" || field == "OUT") {
iolink[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(tolower(value),/^ *[+-]?(([0-9]+.?[0-9]*|[0-9]*.?[0-9]+)(e[-+]?[0-9]+)?|inf|nan) *$/)) {
printf("value \"%s\" in %s.%s (%s) should be numeric\n",
value, record, field, rtype) > "/dev/stderr"
}
} else if (isinteger[rtype,field]) {
if (!match(tolower(value),/^ *[+-]?(0x[0-9a-f]+|[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(tolower(value),/^ *+?(0x[0-9a-f]+|[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) %d chars too long (max %d)\n",
value, record, field, rtype,
length(value)-stringsize[rtype,field], 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) {
split(target,a,".")
record = a[1]
field = a[2]
if (alias[record] != "") { record = alias[record] }
if (recordtype[record] == "") {
if (where) print reference[target],target
else print target
} else {
if (field == "") { field = "VAL" }
rtype = recordtype[record]
split (reference[target],a," ")
link = a[2]
if (!isfield[rtype,field]) {
printf("link \"%s\" points to non-existing %s field \"%s.%s\"\n",
link, rtype, record, field) > "/dev/stderr"
} else if (isnoaccess[rtype,field]) {
printf("link \"%s\" points to NOACCESS %s field \"%s.%s\"\n",
link, rtype, record, field) > "/dev/stderr"
}
}
}
if (require) {
for (module in required) {
if (module != "base") {
print "require \"" module "\""
}
}
}
}
'