diff --git a/.ci/travis-build.sh b/.ci/travis-build.sh
index e7c2531b3..39eb0ea5f 100755
--- a/.ci/travis-build.sh
+++ b/.ci/travis-build.sh
@@ -6,18 +6,9 @@ die() {
exit 1
}
-ticker() {
- while true
- do
- sleep 60
- date -R
- [ -r "$1" ] && tail -n10 "$1"
- done
-}
-
CACHEKEY=1
-EPICS_HOST_ARCH=`sh startup/EpicsHostArch`
+export EPICS_HOST_ARCH=`perl src/tools/EpicsHostArch.pl`
[ -e configure/os/CONFIG_SITE.Common.linux-x86 ] || die "Wrong location: $PWD"
@@ -57,28 +48,24 @@ EOF
fi
# set RTEMS to eg. "4.9" or "4.10"
-# requires qemu, bison, flex, texinfo, install-info
if [ -n "$RTEMS" ]
then
echo "Cross RTEMS${RTEMS} for pc386"
- install -d /home/travis/.cache
- curl -L "https://github.com/mdavidsaver/rsb/releases/download/travis-20160306-2/rtems${RTEMS}-i386-trusty-20190306-2.tar.gz" \
- | tar -C /home/travis/.cache -xj
+ curl -L "https://github.com/mdavidsaver/rsb/releases/download/20171203-${RTEMS}/i386-rtems${RTEMS}-trusty-20171203-${RTEMS}.tar.bz2" \
+ | tar -C / -xmj
sed -i -e '/^RTEMS_VERSION/d' -e '/^RTEMS_BASE/d' configure/os/CONFIG_SITE.Common.RTEMS
cat << EOF >> configure/os/CONFIG_SITE.Common.RTEMS
RTEMS_VERSION=$RTEMS
-RTEMS_BASE=/home/travis/.cache/rtems${RTEMS}-i386
+RTEMS_BASE=$HOME/.rtems
EOF
cat << EOF >> configure/CONFIG_SITE
-CROSS_COMPILER_TARGET_ARCHS+=RTEMS-pc386
+CROSS_COMPILER_TARGET_ARCHS += RTEMS-pc386-qemu
EOF
# find local qemu-system-i386
- export PATH="$HOME/.cache/qemu/usr/bin:$PATH"
echo -n "Using QEMU: "
type qemu-system-i386 || echo "Missing qemu"
- EXTRA=RTEMS_QEMU_FIXUPS=YES
fi
make -j2 $EXTRA
diff --git a/.ci/travis-prepare.sh b/.ci/travis-prepare.sh
deleted file mode 100755
index 0107c2c0c..000000000
--- a/.ci/travis-prepare.sh
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/sh
-set -e -x
-
-die() {
- echo "$1" >&2
- exit 1
-}
-
-CURDIR="$PWD"
-
-QDIR="$HOME/.cache/qemu"
-
-if [ -n "$RTEMS" -a "$TEST" = "YES" ]
-then
- git clone --quiet --branch vme --depth 10 https://github.com/mdavidsaver/qemu.git "$HOME/.build/qemu"
- cd "$HOME/.build/qemu"
-
- HEAD=`git log -n1 --pretty=format:%H`
- echo "HEAD revision $HEAD"
-
- [ -e "$HOME/.cache/qemu/built" ] && BUILT=`cat "$HOME/.cache/qemu/built"`
- echo "Cached revision $BUILT"
-
- if [ "$HEAD" != "$BUILT" ]
- then
- echo "Building QEMU"
- git submodule --quiet update --init
-
- install -d "$HOME/.build/qemu/build"
- cd "$HOME/.build/qemu/build"
-
- "$HOME/.build/qemu/configure" --prefix="$HOME/.cache/qemu/usr" --target-list=i386-softmmu --disable-werror
- make -j2
- make install
-
- echo "$HEAD" > "$HOME/.cache/qemu/built"
- fi
-fi
-
-cd "$CURDIR"
-
-cat <> configure/CONFIG_SITE
-USR_CPPFLAGS += $USR_CPPFLAGS
-USR_CFLAGS += $USR_CFLAGS
-USR_CXXFLAGS += $USR_CXXFLAGS
-EOF
diff --git a/.travis.yml b/.travis.yml
index ba866bb9f..d6130c753 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -11,23 +11,17 @@ addons:
- perl
- clang
- g++-mingw-w64-i686
- - bison
- - flex
- - texinfo
- - install-info
-cache:
- directories:
- - $HOME/.cache
-install:
- - ./.ci/travis-prepare.sh
+ - qemu-system-x86
script:
- - ./.ci/travis-build.sh
+ - .ci/travis-build.sh
env:
- - BRCORE=master BRLIBCOM=master BRCA=master
- - CMPLR=clang
- - USR_CXXFLAGS=-std=c++11
- - CMPLR=clang USR_CXXFLAGS=-std=c++11
- - WINE=32 TEST=NO STATIC=YES
- - WINE=32 TEST=NO STATIC=NO
- - RTEMS=4.10 TEST=NO
- - RTEMS=4.9 TEST=NO
+ - CMPLR=gcc
+ - CMPLR=clang
+ - CMPLR=gcc STATIC=YES
+ - CMPLR=clang STATIC=YES
+ - CMPLR=gcc EXTRA=CMD_CXXFLAGS=-std=c++11
+ - CMPLR=clang EXTRA=CMD_CXXFLAGS=-std=c++11
+ - WINE=32 TEST=NO STATIC=YES
+ - WINE=32 TEST=NO STATIC=NO
+ - RTEMS=4.10 TEST=YES
+ - RTEMS=4.9 TEST=YES
diff --git a/configure/CONFIG b/configure/CONFIG
index 435ff9e77..e40d3f5f7 100644
--- a/configure/CONFIG
+++ b/configure/CONFIG
@@ -20,11 +20,14 @@ else
endif
# Provide a default if the user hasn't set EPICS_HOST_ARCH
-ifeq ($(strip $(EPICS_HOST_ARCH)),)
- # NB: We must set the environment variable for submodules to include
- # the correct modules/RELEASE..local file to set EPICS_BASE,
- # they can't do this for themselves since CONFIG is relative to it:
- export EPICS_HOST_ARCH := $(shell $(CONFIG)/../startup/EpicsHostArch.pl)
+#
+ifeq ($(origin EPICS_HOST_ARCH), undefined)
+ # Bootstrapping ...
+ EHA := $(firstword $(wildcard $(EPICS_BASE)/lib/perl/EpicsHostArch.pl \
+ $(TOP)/src/tools/EpicsHostArch.pl))
+ # NB: We use a simply expanded variable here for performance:
+ export EPICS_HOST_ARCH := $(shell perl $(EHA))
+ EHA :=
endif
-include $(CONFIG)/RELEASE
diff --git a/configure/CONFIG.gnuCommon b/configure/CONFIG.gnuCommon
index 025391548..c4fd8cedd 100644
--- a/configure/CONFIG.gnuCommon
+++ b/configure/CONFIG.gnuCommon
@@ -24,9 +24,14 @@ LD = $(GNU_BIN)/$(CMPLR_PREFIX)ld$(CMPLR_SUFFIX) -r
CPP = $(CC) -x c -E
RANLIB = $(GNU_BIN)/$(CMPLR_PREFIX)ranlib$(CMPLR_SUFFIX)
+# Requires at least GCC 4.8 or LLVM (clang) 3.1
+ASAN_FLAGS_YES = -fsanitize=address
+ASAN_LDFLAGS_YES = $(ASAN_FLAGS_YES)
+
PROF_CFLAGS_YES = -p
GPROF_CFLAGS_YES = -pg
CODE_CFLAGS = $(PROF_CFLAGS_$(PROFILE)) $(GPROF_CFLAGS_$(GPROF))
+CODE_CFLAGS += $(ASAN_FLAGS_$(ENABLE_ASAN))
WARN_CFLAGS_YES = -Wall
WARN_CFLAGS_NO = -w
OPT_CFLAGS_YES = -O3
@@ -35,12 +40,14 @@ OPT_CFLAGS_NO = -g
PROF_CXXFLAGS_YES = -p
GPROF_CXXFLAGS_YES = -pg
CODE_CXXFLAGS = $(PROF_CXXFLAGS_$(PROFILE)) $(GPROF_CXXFLAGS_$(GPROF))
+CODE_CXXFLAGS += $(ASAN_FLAGS_$(ENABLE_ASAN))
WARN_CXXFLAGS_YES = -Wall
WARN_CXXFLAGS_NO = -w
OPT_CXXFLAGS_YES = -O3
OPT_CXXFLAGS_NO = -g
CODE_LDFLAGS = $(PROF_CXXFLAGS_$(PROFILE)) $(GPROF_CXXFLAGS_$(GPROF))
+CODE_LDFLAGS += $(ASAN_LDFLAGS_$(ENABLE_ASAN))
PIPE_CFLAGS_YES_YES = -pipe
PIPE_CFLAGS = $(PIPE_CFLAGS_$(GCC_PIPE)_$(GNU))
diff --git a/configure/CONFIG_BASE b/configure/CONFIG_BASE
index 6d0416369..137ce0098 100644
--- a/configure/CONFIG_BASE
+++ b/configure/CONFIG_BASE
@@ -42,7 +42,7 @@ PODTOHTML = $(PERL) $(TOOLS)/podToHtml.pl
CONVERTRELEASE = $(PERL) $(call FIND_TOOL,convertRelease.pl)
FULLPATHNAME = $(PERL) $(TOOLS)/fullPathName.pl
TAPTOJUNIT = $(PERL) $(TOOLS)/tap-to-junit-xml.pl
-GENVERSIONHEADER = $(PERL) $(TOOLS)/genVersionHeader.pl $(QUIET_FLAG)
+GENVERSIONHEADER = $(PERL) $(TOOLS)/genVersionHeader.pl $(QUIET_FLAG) $(QUESTION_FLAG)
#---------------------------------------------------------------
# tools for installing libraries and products
diff --git a/configure/CONFIG_BASE_VERSION b/configure/CONFIG_BASE_VERSION
index 677fb5acf..715fc168c 100644
--- a/configure/CONFIG_BASE_VERSION
+++ b/configure/CONFIG_BASE_VERSION
@@ -15,17 +15,26 @@
# EPICS_SITE_VERSION is defined in CONFIG_SITE for sites that want a local
# version number to be included in the reported version string.
-# We define BASE_3_14 and BASE_3_15 as NO and BASE_3_16 as YES, so
+# We define convenience macros for our release series to be NO or YES, so
+# Makefiles can detect 'Series X or later', or 'Series X only' like this:
+#
# ifdef BASE_3_14
# true for 3.14 or later
# ifdef BASE_3_15
# true for 3.15 or later
+# ifdef BASE_3_16
+# true for 3.16 or later
+# ifdef BASE_7_0
+# true for 7.0 or later
+#
# ifeq ($(BASE_3_14),YES)
# true for 3.14.x only
# ifeq ($(BASE_3_15),YES)
-# true for 3.15 only
+# true for 3.15.x only
# ifeq ($(BASE_3_16),YES)
-# true for 3.16 only.
+# true for 3.16.x only.
+# ifeq ($(BASE_7_0),YES)
+# true for 7.0.x only.
BASE_3_14 = NO
BASE_3_15 = NO
@@ -39,11 +48,11 @@ EPICS_VERSION = 7
EPICS_REVISION = 0
# EPICS_MODIFICATION must be a number >=0 and <256
-EPICS_MODIFICATION = 1
+EPICS_MODIFICATION = 2
# EPICS_PATCH_LEVEL must be a number (win32 resource file requirement)
# Not included if zero
-EPICS_PATCH_LEVEL = 2
+EPICS_PATCH_LEVEL = 0
# This will end in -DEV between official releases
EPICS_DEV_SNAPSHOT=-DEV
diff --git a/configure/CONFIG_COMMON b/configure/CONFIG_COMMON
index 67cf33e1d..d3a66968f 100644
--- a/configure/CONFIG_COMMON
+++ b/configure/CONFIG_COMMON
@@ -76,10 +76,14 @@ COMMON_DIR = ../O.Common
IOCS_APPL_TOP = $(shell $(FULLPATHNAME) $(INSTALL_LOCATION))
#-------------------------------------------------------
-# Make echo output - suppress echoing if make's '-s' flag is set
+# Silencing the build - suppress messages during 'make -s'
NOP = :
-ECHO = @$(if $(findstring s,$(patsubst T_A=%,,$(MAKEFLAGS))),$(NOP),echo)
-QUIET_FLAG := $(if $(findstring s,$(MAKEFLAGS)),-q,)
+ECHO = @$(if $(findstring s,$(MFLAGS)),$(NOP),echo)
+QUIET_FLAG := $(if $(findstring s,$(MFLAGS)),-q,)
+
+#-------------------------------------------------------
+# Convert 'make -q' flag into '-i' for genVersionHeader.pl
+QUESTION_FLAG := $(if $(findstring q,$(MFLAGS)),-i,)
#-------------------------------------------------------
ifdef T_A
@@ -89,7 +93,7 @@ INSTALL_SHRLIB = $(INSTALL_LOCATION_LIB)/$(T_A)
INSTALL_TCLLIB = $(INSTALL_LOCATION_LIB)/$(T_A)
INSTALL_BIN = $(INSTALL_LOCATION_BIN)/$(T_A)
-#Directories for libraries
+# Directories for libraries
SHRLIB_SEARCH_DIRS = $(INSTALL_LIB)
#-------------------------------------------------------
diff --git a/configure/RULES b/configure/RULES
index bdc895a4d..e4ae22497 100644
--- a/configure/RULES
+++ b/configure/RULES
@@ -1,11 +1,10 @@
#*************************************************************************
-# Copyright (c) 2002 The University of Chicago, as Operator of Argonne
+# Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne
# National Laboratory.
# Copyright (c) 2002 The Regents of the University of California, as
# Operator of Los Alamos National Laboratory.
-# EPICS BASE Versions 3.13.7
-# and higher are distributed subject to a Software License Agreement found
-# in file LICENSE that is included with this distribution.
+# EPICS BASE is distributed subject to a Software License Agreement found
+# in the file LICENSE that is included with this distribution.
#*************************************************************************
ifndef T_A
diff --git a/configure/RULES.Db b/configure/RULES.Db
index 65212ec29..89f98b6a7 100644
--- a/configure/RULES.Db
+++ b/configure/RULES.Db
@@ -4,9 +4,10 @@
# Copyright (c) 2002 The Regents of the University of California, as
# Operator of Los Alamos National Laboratory.
# EPICS BASE is distributed subject to a Software License Agreement found
-# in file LICENSE that is included with this distribution.
+# in the file LICENSE that is included with this distribution.
#*************************************************************************
-#RULES.Db
+
+# RULES.Db
# Set db substitutions and template file suffixes
SUBST_SUFFIX ?= .substitutions
diff --git a/configure/RULES.ioc b/configure/RULES.ioc
index fa0482ec0..385e9e6de 100644
--- a/configure/RULES.ioc
+++ b/configure/RULES.ioc
@@ -4,9 +4,10 @@
# Copyright (c) 2002 The Regents of the University of California, as
# Operator of Los Alamos National Laboratory.
# EPICS BASE is distributed subject to a Software License Agreement found
-# in file LICENSE that is included with this distribution.
+# in the file LICENSE that is included with this distribution.
#*************************************************************************
-#RULES.ioc
+
+# RULES.ioc
include $(CONFIG)/RULES_DIRS
diff --git a/configure/RULES_ARCHS b/configure/RULES_ARCHS
index dc3fa04fe..4aaa75870 100644
--- a/configure/RULES_ARCHS
+++ b/configure/RULES_ARCHS
@@ -90,6 +90,4 @@ realclean:
.PHONY : $(BUILD_ARCHS) rebuild archsCommonClean
.PHONY : $(ACTIONS) clean realclean archclean host all
-# User specific rules
-#
--include $(HOME)/configure/RULES_USER
+include $(CONFIG)/RULES_COMMON
diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD
index daaae86f6..2b6386041 100644
--- a/configure/RULES_BUILD
+++ b/configure/RULES_BUILD
@@ -6,11 +6,12 @@
# EPICS BASE is distributed subject to a Software License Agreement found
# in the file LICENSE that is included with this distribution.
#*************************************************************************
+
+# RULES_BUILD
+
+# Rules for making things specified in a Makefile
#
-# Rules for making things specified in Makefile
-#
-# we are in O.$(T_A), but most sources are elsewhere
-#
+# CWD is O.$(T_A), but most sources are elsewhere
ifndef BASE_RULES_BUILD
BASE_RULES_BUILD=1
@@ -42,6 +43,7 @@ LOADABLE_LIBRARY += $(LOADABLE_LIBRARY_HOST)
OBJS += $(OBJS_HOST)
PROD += $(PROD_HOST)
SCRIPTS += $(SCRIPTS_HOST)
+TARGETS += $(TARGETS_HOST)
TESTLIBRARY += $(TESTLIBRARY_HOST)
TESTSCRIPTS += $(TESTSCRIPTS_HOST)
TESTPROD += $(TESTPROD_HOST)
@@ -53,6 +55,7 @@ LOADABLE_LIBRARY += $(LOADABLE_LIBRARY_IOC)
OBJS += $(OBJS_IOC)
PROD += $(PROD_IOC)
SCRIPTS += $(SCRIPTS_IOC)
+TARGETS += $(TARGETS_IOC)
TESTLIBRARY += $(TESTLIBRARY_IOC)
TESTSCRIPTS += $(TESTSCRIPTS_IOC)
TESTPROD += $(TESTPROD_IOC)
@@ -79,9 +82,9 @@ else
host:
endif
--include $(CONFIG)/RULES_FILE_TYPE
+include $(CONFIG)/RULES_FILE_TYPE
--include $(CONFIG)/RULES.Db
+include $(CONFIG)/RULES.Db
#---------------------------------------------------------------
# Do not build anything if current path matches SKIP_BUILD
@@ -202,7 +205,7 @@ endif
# RELEASE file consistency checking
checkRelease:
- $(CONVERTRELEASE) checkRelease
+ +$(CONVERTRELEASE) checkRelease
warnRelease:
-$(CONVERTRELEASE) checkRelease
noCheckRelease:
@@ -216,9 +219,9 @@ endif
$(TESTPRODNAME) $(PRODNAME): $(PRODUCT_OBJS) $(PROD_RESS) $(PROD_DEPLIBS)
-$(TESTPRODNAME) $(PRODNAME): %$(EXE):
+$(TESTPRODNAME) $(PRODNAME): %$(EXE): | $(INSTALL_LIB)
@$(RM) $@
- $(DEBUGCMD) $(LINK.cpp)
+ $(LINK.cpp)
$(MT_EXE_COMMAND)
%_ctdt$(OBJ) : %_ctdt.c
@@ -273,15 +276,13 @@ YACCOPT ?= $($*_YACCOPT)
$(MV) $*.tab.c $*.c
$(if $(findstring -d, $(YACCOPT)),$(MV) $*.tab.h $*.h,)
-# must be a seperate rule since when not using '-d' the
+# must be a separate rule since when not using '-d' the
# prefix for .h will be different then .c
%.h : %.c %.y
%.c: %.l
- @$(RM) $*.yy.c
- $(LEX) $(LEXOPT) -t $< > $*.yy.c
@$(RM) $@
- $(MV) $*.yy.c $@
+ $(LEX) $(LEXOPT) -o$@ $<
#---------------------------------------------------------------
# Libraries, shared/DLL and stubs
@@ -321,6 +322,10 @@ $(LOADABLE_SHRLIBNAME): $(LOADABLE_SHRLIB_PREFIX)%$(LOADABLE_SHRLIB_SUFFIX):
$(LINK.shrlib)
$(MT_DLL_COMMAND)
+$(LIBNAME) $(SHRLIBNAME) $(LOADABLE_SHRLIBNAME): | $(INSTALL_LIB)
+$(INSTALL_LIB):
+ @$(MKDIR) $@
+
#---------------------------------------------------------------
# C++ munching for VxWorks
@@ -378,14 +383,17 @@ ifneq ($(TAPFILES),)
ifdef RUNTESTS_ENABLED
prove --failures --ext .tap --exec "$(CAT)" --color $(TAPFILES)
endif
+
+CURRENT_TAPFILES := $(wildcard $(TAPFILES))
+CURRENT_JUNITFILES := $(wildcard $(JUNITFILES))
endif
clean-tests:
-ifneq ($(TAPFILES),)
- $(RM) $(TAPFILES)
+ifneq ($(CURRENT_TAPFILES),)
+ $(RM) $(CURRENT_TAPFILES)
endif
-ifneq ($(JUNITFILES),)
- $(RM) $(JUNITFILES)
+ifneq ($(CURRENT_JUNITFILES),)
+ $(RM) $(CURRENT_JUNITFILES)
endif
tapfiles: $(TESTSCRIPTS) $(TAPFILES)
@@ -409,7 +417,7 @@ endif
# Generate a perl program to exec the real test binary.
%.t: %$(EXE) $(TOOLS)/makeTestfile.pl
@$(RM) $@
- $(PERL) $(TOOLS)/makeTestfile.pl $@ $<
+ $(PERL) $(TOOLS)/makeTestfile.pl $(T_A) $(EPICS_HOST_ARCH) $@ $<
#---------------------------------------------------------------
# Generate header with version number from VCS
@@ -480,9 +488,11 @@ $(INSTALL_TCLLIB)/%: ../%
@$(INSTALL) -d -m $(BIN_PERMISSIONS) $< $(INSTALL_TCLLIB)
endif
+ifneq ($(TCLINDEX),)
$(INSTALL_TCLLIB)/$(TCLINDEX): $(INSTALL_TCLLIBS)
$(ECHO) "Updating $@"
$(ECHO) eval auto_mkindex $(INSTALL_TCLLIB) "$(TCLLIBNAME)" | tclsh
+endif
$(INSTALL_LOADABLE_SHRLIBS): $(INSTALL_SHRLIB)/%: %
$(ECHO) "Installing loadable shared library $@"
@@ -548,7 +558,7 @@ $(INSTALL_TEMPLATES_SUBDIR)/%: %
$(ECHO) "Installing $@"
@$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D)
--include $(CONFIG)/RULES_EXPAND
+include $(CONFIG)/RULES_EXPAND
.PRECIOUS: %.i %.o %.c %.nm %.cpp %.cc
.PRECIOUS: $(COMMON_INC)
@@ -557,5 +567,9 @@ $(INSTALL_TEMPLATES_SUBDIR)/%: %
.PHONY: runtests tapfiles clean-tests test-results junitfiles
.PHONY: checkRelease warnRelease noCheckRelease FORCE
+include $(CONFIG)/RULES_COMMON
+
+else
+ $(warning RULES_BUILD included more than once. \
+ Use 'make show-makefiles' to work out why.)
endif # BASE_RULES_BUILD
-# EOF RULES_BUILD
diff --git a/configure/RULES_COMMON b/configure/RULES_COMMON
new file mode 100644
index 000000000..fb8f211e4
--- /dev/null
+++ b/configure/RULES_COMMON
@@ -0,0 +1,35 @@
+#*************************************************************************
+# Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne
+# National Laboratory.
+# EPICS BASE is distributed subject to a Software License Agreement found
+# in the file LICENSE that is included with this distribution.
+#*************************************************************************
+
+# These rules show the set of Makefiles, config files and
+# rules files loaded by GNUmake.
+
+# Protect against filenames containing colons (Windows)
+SAFE_MAKEFILES = $(subst :,__colon__,$(MAKEFILE_LIST))
+SHOW_MAKEFILES = $(SAFE_MAKEFILES:%=show-makefile.%)
+show-makefiles: $(SHOW_MAKEFILES)
+
+# The sort prevents warnings about duplicate targets:
+$(sort $(SHOW_MAKEFILES)): show-makefile.%:
+ @echo " $(subst __colon__,:,$(@:show-makefile.%=%))"
+
+.PHONY: show-makefiles show-makefile.%
+
+# These rules support printing a Makefile variable values.
+# Many variables are only set inside an O. build directory.
+# make PRINT.T_A
+
+PRINT_Var = $(@:PRINT.%=%)
+PRINT.%:
+ @echo $(PRINT_Var) = '$($(PRINT_Var))'
+
+.PHONY: PRINT PRINT.%
+
+
+# User specific rules
+#
+-include $(HOME)/configure/RULES_USER
diff --git a/configure/RULES_DIRS b/configure/RULES_DIRS
index 37cea5e9a..ec156745d 100644
--- a/configure/RULES_DIRS
+++ b/configure/RULES_DIRS
@@ -92,7 +92,4 @@ $(ARCHS) $(ACTIONS) $(actionArchTargets) :%: \
.PHONY : $(dirActionArchTargets)
.PHONY : $(actionArchTargets)
-
-# User specific rules
-#
--include $(HOME)/configure/RULES_USER
+include $(CONFIG)/RULES_COMMON
diff --git a/configure/RULES_EXPAND b/configure/RULES_EXPAND
index 51657ebd8..f315a7e9e 100644
--- a/configure/RULES_EXPAND
+++ b/configure/RULES_EXPAND
@@ -1,4 +1,13 @@
-# /configure/RULES_EXPAND
+#*************************************************************************
+# Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne
+# National Laboratory.
+# Copyright (c) 2002 The Regents of the University of California, as
+# Operator of Los Alamos National Laboratory.
+# EPICS BASE is distributed subject to a Software License Agreement found
+# in the file LICENSE that is included with this distribution.
+#*************************************************************************
+
+# RULES_EXPAND
vpath %@ $(USR_VPATH) $(ALL_SRC_DIRS)
diff --git a/configure/RULES_FILE_TYPE b/configure/RULES_FILE_TYPE
index 28cc74bd4..5936fd5e0 100644
--- a/configure/RULES_FILE_TYPE
+++ b/configure/RULES_FILE_TYPE
@@ -7,9 +7,11 @@
# in the file LICENSE that is included with this distribution.
#*************************************************************************
-# Include /configure/RULES_BUILD from tops defined in RELEASE* files
+# Include /configure/RULES_BUILD from tops defined in RELEASE* files,
+# excluding EPICS_BASE
#
-RELEASE_RULES_BUILDS = $(foreach top, $(RELEASE_TOPS), \
+RELEASE_RULES_BUILDS = $(foreach top, \
+ $(filter-out EPICS_BASE, $(RELEASE_TOPS)), \
$(wildcard $($(top))/configure/RULES_BUILD))
ifneq ($(RELEASE_RULES_BUILDS),)
include $(RELEASE_RULES_BUILDS)
@@ -23,7 +25,7 @@ ifneq ($(RELEASE_CFG_RULES),)
include $(RELEASE_CFG_RULES)
endif
-# If this is not BASE then include /configure/RULES_BUILD
+# If this is not BASE then include /configure/RULES_BUILD
#
ifeq ($(wildcard $(TOP)/configure/CONFIG_BASE_VERSION),)
TOP_RULES_BUILDS = $(wildcard $(TOP)/configure/RULES_BUILD)
@@ -67,7 +69,3 @@ file_type_clean:
@$(RM) $(foreach type, $(FILE_TYPE), $($(type)))
.PHONY : file_type_clean
-
-# User specific rules
-#
--include $(HOME)/configure/RULES_USER
diff --git a/configure/RULES_OCTAVE b/configure/RULES_OCTAVE
index a06fb075a..1ec4cc7b2 100644
--- a/configure/RULES_OCTAVE
+++ b/configure/RULES_OCTAVE
@@ -1,11 +1,10 @@
#*************************************************************************
-# Copyright (c) 2002 The University of Chicago, as Operator of Argonne
+# Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne
# National Laboratory.
# Copyright (c) 2002 The Regents of the University of California, as
# Operator of Los Alamos National Laboratory.
-# EPICS BASE Versions 3.13.7
-# and higher are distributed subject to a Software License Agreement found
-# in file LICENSE that is included with this distribution.
+# EPICS BASE is distributed subject to a Software License Agreement found
+# in the file LICENSE that is included with this distribution.
#*************************************************************************
# Octave definitions and rules
diff --git a/configure/RULES_TARGET b/configure/RULES_TARGET
index 7a5dcba81..e96232876 100644
--- a/configure/RULES_TARGET
+++ b/configure/RULES_TARGET
@@ -1,18 +1,13 @@
#*************************************************************************
-# Copyright (c) 2002 The University of Chicago, as Operator of Argonne
+# Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne
# National Laboratory.
# Copyright (c) 2002 The Regents of the University of California, as
# Operator of Los Alamos National Laboratory.
-# EPICS BASE Versions 3.13.7
-# and higher are distributed subject to a Software License Agreement found
-# in file LICENSE that is included with this distribution.
+# EPICS BASE is distributed subject to a Software License Agreement found
+# in the file LICENSE that is included with this distribution.
#*************************************************************************
-#
-# RULES_TARGET
-#
-# This file is to be maintained by the community.
-#
-#-----------------------------------------------------------------------
+
+# RULES_TARGET
define TARGET_template
$(1)_$(2) += $$(if $$(strip $$($(1)_$(2)_$$(OS_CLASS))), \
diff --git a/configure/RULES_TOP b/configure/RULES_TOP
index 838994bd0..b18fbf026 100644
--- a/configure/RULES_TOP
+++ b/configure/RULES_TOP
@@ -15,6 +15,10 @@ CVSCLEAN = $(call FIND_TOOL,cvsclean.pl)
cvsclean:
$(PERL) $(CVSCLEAN)
+DEPCLEAN = $(call FIND_TOOL,depclean.pl)
+depclean:
+ $(PERL) $(DEPCLEAN)
+
realuninstall: uninstallDirs
$(RMDIR) $(INSTALL_LOCATION_BIN)
$(RMDIR) $(INSTALL_LOCATION_LIB)
@@ -73,12 +77,14 @@ help:
@echo " uninstall - Remove install directories created by this hostarch."
@echo " realuninstall - Removes ALL install dirs"
@echo " distclean - Same as realclean cvsclean realuninstall."
+ @echo " depclean - Removes all .d files from O. directories."
@echo " cvsclean - Removes cvs .#* files in all dirs of directory tree"
@echo " help - Prints this list of valid make targets "
@echo "Indiv. object targets are supported by O. level Makefile .e.g"
@echo " xxxRecord.o"
-.PHONY: cleandirs distclean cvsclean realuninstall archuninstall uninstallDirs
+.PHONY: cleandirs distclean cvsclean depclean
+.PHONY: realuninstall archuninstall uninstallDirs
.PHONY: uninstall help
# Include /cfg/TOP_RULES* files from tops defined in RELEASE* files
diff --git a/configure/os/CONFIG.Common.RTEMS b/configure/os/CONFIG.Common.RTEMS
index c22e37a66..8425e6ef9 100644
--- a/configure/os/CONFIG.Common.RTEMS
+++ b/configure/os/CONFIG.Common.RTEMS
@@ -27,9 +27,13 @@ ifneq ($(CONFIG),$(TOP)/configure)
-include $(TOP)/configure/CONFIG_SITE.Common.RTEMS
endif
+#--------------------------------------------------
+# Set RTEMS_BSP from T_A if not already done
+RTEMS_BSP ?= $(subst RTEMS-,,$(T_A))
+
#-------------------------------------------------------
# Pick up the RTEMS tool/path definitions from the RTEMS BSP directory.
-include $(RTEMS_BASE)/$(RTEMS_TARGET_CPU)-rtems$(RTEMS_VERSION)/$(subst RTEMS-,,$(T_A))/Makefile.inc
+include $(RTEMS_BASE)/$(RTEMS_TARGET_CPU)-rtems$(RTEMS_VERSION)/$(RTEMS_BSP)/Makefile.inc
include $(RTEMS_CUSTOM)
include $(CONFIG.CC)
@@ -72,7 +76,7 @@ CPPFLAGS += $($(BUILD_CLASS)_CPPFLAGS) $(POSIX_CPPFLAGS) $(OPT_CPPFLAGS)\
$(USR_CPPFLAGS) $(CMD_CPPFLAGS) $(ARCH_DEP_CPPFLAGS) $(OP_SYS_CPPFLAGS)\
$(OP_SYS_INCLUDE_CPPFLAGS) $(CODE_CPPFLAGS)
-ECHO = @$(if $(findstring s,$(patsubst T_A=%,,$(MAKEFLAGS))),$(NOP),echo)
+ECHO = @$(if $(findstring s,$(MFLAGS)),$(NOP),echo)
#--------------------------------------------------
# Although RTEMS uses gcc, it wants to use gcc its own way
diff --git a/configure/os/CONFIG.Common.RTEMS-at91rm9200ek b/configure/os/CONFIG.Common.RTEMS-at91rm9200ek
index 6f5f97732..735c07bb5 100644
--- a/configure/os/CONFIG.Common.RTEMS-at91rm9200ek
+++ b/configure/os/CONFIG.Common.RTEMS-at91rm9200ek
@@ -9,5 +9,6 @@
#
# All RTEMS targets use the same Makefile fragment
#
-RTEMS_TARGET_CPU=arm
+RTEMS_BSP = at91rm9200ek
+RTEMS_TARGET_CPU = arm
include $(CONFIG)/os/CONFIG.Common.RTEMS
diff --git a/configure/os/CONFIG.Common.RTEMS-beatnik b/configure/os/CONFIG.Common.RTEMS-beatnik
index aaf611638..aaa45e4c9 100644
--- a/configure/os/CONFIG.Common.RTEMS-beatnik
+++ b/configure/os/CONFIG.Common.RTEMS-beatnik
@@ -5,6 +5,7 @@
# All RTEMS targets use the same Makefile fragment
#
EXE = .elf
+RTEMS_BSP = beatnik
RTEMS_TARGET_CPU = powerpc
GNU_TARGET = powerpc-rtems
ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL
diff --git a/configure/os/CONFIG.Common.RTEMS-gen68360 b/configure/os/CONFIG.Common.RTEMS-gen68360
index a6663afc2..0a8a6e85e 100644
--- a/configure/os/CONFIG.Common.RTEMS-gen68360
+++ b/configure/os/CONFIG.Common.RTEMS-gen68360
@@ -5,5 +5,6 @@
#
# All RTEMS targets use the same Makefile fragment
#
-RTEMS_TARGET_CPU=m68k
+RTEMS_BSP = gen68360
+RTEMS_TARGET_CPU = m68k
include $(CONFIG)/os/CONFIG.Common.RTEMS
diff --git a/configure/os/CONFIG.Common.RTEMS-mcp750 b/configure/os/CONFIG.Common.RTEMS-mcp750
index d834ad9ec..035e6eb36 100644
--- a/configure/os/CONFIG.Common.RTEMS-mcp750
+++ b/configure/os/CONFIG.Common.RTEMS-mcp750
@@ -5,5 +5,6 @@
#
# All RTEMS targets use the same Makefile fragment
#
-RTEMS_TARGET_CPU=ppc
+RTEMS_BSP = mcp750
+RTEMS_TARGET_CPU = ppc
include $(CONFIG)/os/CONFIG.Common.RTEMS
diff --git a/configure/os/CONFIG.Common.RTEMS-mvme167 b/configure/os/CONFIG.Common.RTEMS-mvme167
index a6663afc2..3651f966f 100644
--- a/configure/os/CONFIG.Common.RTEMS-mvme167
+++ b/configure/os/CONFIG.Common.RTEMS-mvme167
@@ -5,5 +5,6 @@
#
# All RTEMS targets use the same Makefile fragment
#
-RTEMS_TARGET_CPU=m68k
+RTEMS_BSP = mvme167
+RTEMS_TARGET_CPU = m68k
include $(CONFIG)/os/CONFIG.Common.RTEMS
diff --git a/configure/os/CONFIG.Common.RTEMS-mvme2100 b/configure/os/CONFIG.Common.RTEMS-mvme2100
index 0ae64c791..a8e8bf76e 100644
--- a/configure/os/CONFIG.Common.RTEMS-mvme2100
+++ b/configure/os/CONFIG.Common.RTEMS-mvme2100
@@ -5,6 +5,7 @@
# All RTEMS targets use the same Makefile fragment
#
EXE = .elf
+RTEMS_BSP = mvme2100
RTEMS_TARGET_CPU = powerpc
GNU_TARGET = powerpc-rtems
ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL
diff --git a/configure/os/CONFIG.Common.RTEMS-mvme2700 b/configure/os/CONFIG.Common.RTEMS-mvme2700
index 899fab17f..a5ad7fbf5 100644
--- a/configure/os/CONFIG.Common.RTEMS-mvme2700
+++ b/configure/os/CONFIG.Common.RTEMS-mvme2700
@@ -1,6 +1,7 @@
#
# Author: Matt Rippa
#
+RTEMS_BSP = mvme2700
RTEMS_TARGET_CPU = powerpc
ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL
ARCH_DEP_CFLAGS += -DHAVE_PPCBUG
diff --git a/configure/os/CONFIG.Common.RTEMS-mvme3100 b/configure/os/CONFIG.Common.RTEMS-mvme3100
index e94d46211..283e7d680 100644
--- a/configure/os/CONFIG.Common.RTEMS-mvme3100
+++ b/configure/os/CONFIG.Common.RTEMS-mvme3100
@@ -5,6 +5,7 @@
# All RTEMS targets use the same Makefile fragment
#
EXE = .elf
+RTEMS_BSP = mvme3100
RTEMS_TARGET_CPU = powerpc
GNU_TARGET = powerpc-rtems
ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL
diff --git a/configure/os/CONFIG.Common.RTEMS-mvme5500 b/configure/os/CONFIG.Common.RTEMS-mvme5500
index 44ef7ea3e..0329185bb 100644
--- a/configure/os/CONFIG.Common.RTEMS-mvme5500
+++ b/configure/os/CONFIG.Common.RTEMS-mvme5500
@@ -5,6 +5,7 @@
# All RTEMS targets use the same Makefile fragment
#
EXE = .elf
+RTEMS_BSP = mvme5500
RTEMS_TARGET_CPU = powerpc
GNU_TARGET = powerpc-rtems
ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL
diff --git a/configure/os/CONFIG.Common.RTEMS-pc386 b/configure/os/CONFIG.Common.RTEMS-pc386
index 92ef4ac22..8dfa7d2ba 100644
--- a/configure/os/CONFIG.Common.RTEMS-pc386
+++ b/configure/os/CONFIG.Common.RTEMS-pc386
@@ -5,14 +5,15 @@
#
# All RTEMS targets use the same Makefile fragment
#
-RTEMS_TARGET_CPU=i386
+RTEMS_BSP = pc386
+RTEMS_TARGET_CPU = i386
MUNCH_SUFFIX = .boot
define MUNCH_CMD
- $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< temp.bin
+ $(RM) $*.bin
+ $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< $*.bin
$(BIN2BOOT) $@ 0x00097E00 \
- $(PROJECT_RELEASE)/lib/start16.bin 0x00097C00 0 temp.bin 0x00100000 0
- rm -f temp.bin
+ $(PROJECT_RELEASE)/lib/start16.bin 0x00097C00 0 $*.bin 0x00100000 0
endef
include $(CONFIG)/os/CONFIG.Common.RTEMS
diff --git a/configure/os/CONFIG.Common.RTEMS-pc386-qemu b/configure/os/CONFIG.Common.RTEMS-pc386-qemu
new file mode 100644
index 000000000..684f01a19
--- /dev/null
+++ b/configure/os/CONFIG.Common.RTEMS-pc386-qemu
@@ -0,0 +1,11 @@
+# CONFIG.Common.RTEMS-pc386-qemu
+#
+# Definitions for the RTEMS-pc386-qemu target
+# Site-specific overrides go in CONFIG_SITE.Common.RTEMS-pc386-qemu
+#
+#-------------------------------------------------------
+
+# Include definitions from RTEMS-pc386
+include $(CONFIG)/os/CONFIG.Common.RTEMS-pc386
+
+RTEMS_QEMU_FIXUPS = YES
diff --git a/configure/os/CONFIG.Common.RTEMS-psim b/configure/os/CONFIG.Common.RTEMS-psim
index 230d72e1f..58ad72852 100644
--- a/configure/os/CONFIG.Common.RTEMS-psim
+++ b/configure/os/CONFIG.Common.RTEMS-psim
@@ -5,5 +5,6 @@
#
# All RTEMS targets use the same Makefile fragment
#
-RTEMS_TARGET_CPU=ppc
+RTEMS_BSP = psim
+RTEMS_TARGET_CPU = ppc
include $(CONFIG)/os/CONFIG.Common.RTEMS
diff --git a/configure/os/CONFIG.Common.RTEMS-uC5282 b/configure/os/CONFIG.Common.RTEMS-uC5282
index 6b0903e07..2cb215aca 100644
--- a/configure/os/CONFIG.Common.RTEMS-uC5282
+++ b/configure/os/CONFIG.Common.RTEMS-uC5282
@@ -5,6 +5,7 @@
#
# All RTEMS targets use the same Makefile fragment
#
+RTEMS_BSP = uC5282
RTEMS_TARGET_CPU = m68k
ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL
diff --git a/configure/os/CONFIG.Common.vxWorksCommon b/configure/os/CONFIG.Common.vxWorksCommon
index 7fe56b874..66d0c2902 100644
--- a/configure/os/CONFIG.Common.vxWorksCommon
+++ b/configure/os/CONFIG.Common.vxWorksCommon
@@ -173,8 +173,10 @@ SHRLIB_CFLAGS =
SHRLIB_LDFLAGS =
#--------------------------------------------------
-# Earlier versions of gcc don't understand -MF
-HDEPENDS_COMPFLAGS = -MM > $@
+# Don't use gcc 2.x for dependency generation
+
+HDEPENDS_METHOD_2 = MKMF
+HDEPENDS_METHOD = $(firstword $(HDEPENDS_METHOD_$(VX_GNU_MAJOR_VERSION)) COMP)
#--------------------------------------------------
# osithead use default stack, YES or NO override
diff --git a/configure/os/CONFIG.darwinCommon.darwinCommon b/configure/os/CONFIG.darwinCommon.darwinCommon
index 56b539545..4a8f3ef8e 100644
--- a/configure/os/CONFIG.darwinCommon.darwinCommon
+++ b/configure/os/CONFIG.darwinCommon.darwinCommon
@@ -65,14 +65,14 @@ GNU = NO
#
# Darwin shared libraries
#
-SHRLIB_LDFLAGS = -dynamiclib -flat_namespace -undefined suppress \
+SHRLIB_LDFLAGS = -dynamiclib -undefined dynamic_lookup \
-install_name $(shell $(FULLPATHNAME) $(INSTALL_LIB))/$@ \
$(addprefix -compatibility_version , $(SHRLIB_VERSION)) \
$(addprefix -current_version , $(SHRLIB_VERSION))
SHRLIB_SUFFIX_BASE = .dylib
SHRLIB_SUFFIX = $(addprefix ., $(SHRLIB_VERSION))$(SHRLIB_SUFFIX_BASE)
-LOADABLE_SHRLIB_LDFLAGS = -bundle -flat_namespace -undefined suppress
+LOADABLE_SHRLIB_LDFLAGS = -bundle -undefined dynamic_lookup
#
# Position-independent code is the default on Darwin.
diff --git a/configure/os/CONFIG.win32-x86.Common b/configure/os/CONFIG.win32-x86.Common
index be328cc19..43cfc342f 100644
--- a/configure/os/CONFIG.win32-x86.Common
+++ b/configure/os/CONFIG.win32-x86.Common
@@ -26,3 +26,5 @@ endif
# Needed to find dlls for base installed build tools (antelope,eflex,...)
PATH := $(EPICS_BASE_BIN):$(PATH)
+# Silence the tr1 namespace deprecation warnings
+USR_CXXFLAGS_WIN32 += -D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING
diff --git a/configure/os/CONFIG_SITE.Common.RTEMS-pc386 b/configure/os/CONFIG_SITE.Common.RTEMS-pc386
deleted file mode 100644
index c772c44fc..000000000
--- a/configure/os/CONFIG_SITE.Common.RTEMS-pc386
+++ /dev/null
@@ -1,3 +0,0 @@
-#
-# Site-specific overrides for RTEMS-pc386 target
-#
diff --git a/configure/os/CONFIG_SITE.Common.RTEMS-pc386-qemu b/configure/os/CONFIG_SITE.Common.RTEMS-pc386-qemu
new file mode 100644
index 000000000..027dcf4ab
--- /dev/null
+++ b/configure/os/CONFIG_SITE.Common.RTEMS-pc386-qemu
@@ -0,0 +1,9 @@
+# CONFIG_SITE.Common.RTEMS-pc386-qemu
+#
+# Site-specific overrides for the RTEMS-pc386-qemu target
+#
+
+# If you're building this architecture you _probably_ want to
+# run the tests for it under QEMU, but if not you can turn
+# them off here by commenting out this line:
+CROSS_COMPILER_RUNTEST_ARCHS += RTEMS-pc386-qemu
diff --git a/configure/os/CONFIG_SITE.Common.linux-x86 b/configure/os/CONFIG_SITE.Common.linux-x86
index 07182820d..e4e7dd0fd 100644
--- a/configure/os/CONFIG_SITE.Common.linux-x86
+++ b/configure/os/CONFIG_SITE.Common.linux-x86
@@ -54,3 +54,7 @@ OPT_CXXFLAGS_YES += -g
# Tune GNU compiler output for a specific 32-bit cpu-type
# (e.g. generic, native, i386, i686, pentium2/3/4, prescott, k6, athlon etc.)
GNU_TUNE_CFLAGS = -mtune=generic
+
+
+# Developers using a suitable compiler may enable its address sanitizer:
+#ENABLE_ASAN = YES
diff --git a/configure/os/CONFIG_SITE.Common.linux-x86_64 b/configure/os/CONFIG_SITE.Common.linux-x86_64
index 18fb2974d..6f7a2dc7b 100644
--- a/configure/os/CONFIG_SITE.Common.linux-x86_64
+++ b/configure/os/CONFIG_SITE.Common.linux-x86_64
@@ -50,3 +50,7 @@ OPT_CXXFLAGS_YES += -g
# Tune GNU compiler output for a specific 64-bit cpu-type
# (e.g. generic, native, core2, nocona, k8, opteron, athlon64, barcelona etc.)
GNU_TUNE_CFLAGS = -mtune=generic
+
+
+# Developers using a suitable compiler may enable its address sanitizer:
+#ENABLE_ASAN = YES
diff --git a/documentation/KnownProblems.html b/documentation/KnownProblems.html
index 6ef523808..d38ad630c 100644
--- a/documentation/KnownProblems.html
+++ b/documentation/KnownProblems.html
@@ -4,17 +4,17 @@
- Known Problems in EPICS 7.0.1
+ Known Problems in EPICS 7.0.2
-
EPICS 7.0.1: Known Problems
+
EPICS 7.0.2: Known Problems
Any patch files linked below should be applied at the root of the
-base-7.0.1 tree. Download them, then use the GNU Patch program as
+base-7.0.2 tree. Download them, then use the GNU Patch program as
follows:
-
% cd /path/to/base-7.0.1
+
% cd /path/to/base-7.0.2
% patch -p1 < /path/to/file.patch
The following problems were known by the developers at the time of this
diff --git a/documentation/README.1st b/documentation/README.1st
index 042cede60..f0c560fb0 100644
--- a/documentation/README.1st
+++ b/documentation/README.1st
@@ -1,6 +1,6 @@
Installation Instructions
- EPICS Base Release 7.0.1.1
+ EPICS Base Release 7.0.2
--------------------------------------------------------------------------
@@ -164,12 +164,11 @@
base/startup directory - contains scripts to set environment and path
- EpicsHostArch C shell script to set EPICS_HOST_ARCH env variable
- EpicsHostArch.pl Perl script to set EPICS_HOST_ARCH env variable
- Site.profile bourne shell script to set path and env variables
- Site.cshrc c shell script to set path and env variables
- cygwin.bat WIN32 bat file to set cygwin path and env variables
- win32.bat WIN32 bat file to set path and env variables
+ EpicsHostArch Shell script to set EPICS_HOST_ARCH env variable
+ unix.csh C shell script to set path and env variables
+ unix.sh Bourne shell script to set path and env variables
+ win32.bat Bat file example to configure win32-x86 target
+ windows.bat Bat file example to configure windows-x64 target
base/configure directory - contains build definitions and rules
diff --git a/documentation/README.darwin.html b/documentation/README.darwin.html
index cbc290178..dd11ce857 100644
--- a/documentation/README.darwin.html
+++ b/documentation/README.darwin.html
@@ -21,7 +21,7 @@ of my Bash login script (~/.bash_login):
#
EPICS_BASE="${HOME}/src/EPICS/base"
EPICS_EXTENSIONS="${HOME}/src/EPICS/extensions"
-. "${EPICS_BASE}"/startup/Site.profile
+. "${EPICS_BASE}"/startup/unix.sh
base/startup directory - contains scripts to set environment and path
- EpicsHostArch C shell script to set EPICS_HOST_ARCH env variable
- EpicsHostArch.pl Perl script to set EPICS_HOST_ARCH env variable
- Site.profile bourne shell script to set path and env variables
- Site.cshrc c shell script to set path and env variables
- cygwin.bat WIN32 bat file to set cygwin path and env variables
- win32.bat WIN32 bat file to set path and env variables
+ EpicsHostArch Shell script to set EPICS_HOST_ARCH env variable
+ unix.csh C shell script to set path and env variables
+ unix.sh Bourne shell script to set path and env variables
+ win32.bat Bat file example to configure win32-x86 target
+ windows.bat Bat file example to configure windows-x64 target
base/configure directory - contains build definitions and rules
These release notes describe changes that have been made since the previous
+release of this series of EPICS Base. Note that changes which were merged up
+from commits to new releases in an older Base series are not described at the
+top of this file but have entries that appear lower down, under the series to
+which they were originally committed. Thus it is important to read more than
+just the first section to understand everything that has changed in each
+release.
+
+
The external PVA submodules each have their own separate set of release notes
+which should also be read to understand what has changed since an earlier
+release.
Status reporting for the callback and scanOnce task queues
+
+
Two new iocsh commands and some associated underlying APIs have been added to
+show the state of the queues that feed the three callback tasks and the scanOnce
+task, including a high-water mark which can optionally be reset. The new iocsh
+commands are callbackQueueShow and scanOnceQueueShow; both
+take an optional integer argument which must be non-zero to reset the
+high-water mark.
+
+
Support for event codes greater than or equal to NUM_TIME_EVENTS
+
+
Event numbers greater than or equal to NUM_TIME_EVENTS are now allowed if
+supported by the registered event time provider, which must provide its own
+advancing timestamp validation for such events.
+
+
Time events numbered 0 through (NUM_TIME_EVENTS-1) are still validated by
+code in epicsGeneralTime.c that checks for advancing timestamps and enforces
+that restriction.
+
+
Type-safe Device and Driver Support Tables
+
+
Type-safe versions of the device and driver support structures dset
+and drvet have been added to the devSup.h and drvSup.h headers
+respectively. The original structure definitions have not been changed so
+existing support modules will still build normally, but older modules can be
+modified and new code written to be compatible with both.
+
+
The old structure definitions will be replaced by the new ones if the macros
+USE_TYPED_DSET and/or USE_TYPED_DRVET are defined when the
+appropriate header is included. The best place to define these is in the
+Makefile, as with the USE_TYPED_RSET macro that was introduced in
+Base-3.16.1 and described below. See the comments in devSup.h for a brief usage
+example, or look at
+this commit to the ipac module to see a module conversion.
+
+
A helper function DBLINK* dbGetDevLink(dbCommon *prec) has also been
+added to devSup.h which fetches a pointer to the INP or OUT field of the
+record.
+
+
RTEMS build configuration update, running tests under QEMU
+
+
This release includes the ability to run the EPICS unit tests built for a
+special version of the RTEMS-pc386 target architecture on systems that have an
+appropriate QEMU emulator installed (qemu-system-i386). It is also now
+possible to create sub-architectures of RTEMS targets, whereas previously the
+EPICS target architecture name had to be RTEMS-$(RTEMS_BSP).
+
+
The new target RTEMS-pc386-qemu builds binaries that can be run in
+the qemu-system-i386 PC System emulator. This target is a derivative of
+the original RTEMS-pc386 target but with additional software to build
+an in-memory file-system, and some minor modifications to allow the unit tests
+to work properly under QEMU. When this target is enabled, building any of the
+make targets that cause the built-in self-tests to be run (such as
+make runtests) will also run the tests for RTEMS using QEMU.
+
+
To allow the new 3-component RTEMS target name, the EPICS build system for
+RTEMS was modified to allow a configure/os/CONFIG.Common.<arch>
+file to set the RTEMS_BSP variable to inform the build what RTEMS BSP
+to use. Previously this was inferred from the value of the T_A make
+variable, but that prevents having multiple EPICS targets that build against the
+same BSP. All the included RTEMS target configuration files have been updated;
+build configuration files for out-of-tree RTEMS targets will continue to work as
+the original rules are used to set RTEMS_BSP if it hasn't been set when
+needed.
+
+
Link type enhancements
+
+
This release adds three new link types: "state", "debug" and "trace". The
+"state" link type gets and puts boolean values from/to the dbState library that
+was added in the 3.15.1 release. The "debug" link type sets the
+jlink::debug flag in its child link, while the "trace" link type
+also causes the arguments and return values for all calls to the child link's
+jlif and lset routines to be printed on stdout. The debug flag can no longer be
+set using an info tag. The addition of the "trace" link type has allowed over
+200 lines of conditional diagnostic printf() calls to be removed from the other
+link types.
+
+
The "calc" link type can now be used for output links as well as input links.
+This allows modification of the output value and even combining it with values
+from other input links. See the separate JSON Link types document for
+details.
+
+
A new start_child() method was added to the end of the jlif
+interface table.
+
+
The lset methods have now been properly documented in the
+dbLink.h header file using Doxygen annotations, although we do not run Doxygen
+on the source tree yet to generate API documentation.
+
+
Link types that utilize child links must now indicate whether the child will
+be used for input, output or forward linking by the return value from its
+parse_start_map() method. The jlif_key_result enum now
+contains 3 values jlif_key_child_inlink,
+jlif_key_child_outlink and jlif_key_child_fwdlink
+instead of the single jlif_key_child_link that was previously used
+for this.
+
+
GNUmake targets for debugging
+
+
Some additional build rules have been added to help debug configuration
+problems with the build system. Run make show-makefiles to get a sorted
+list of all the files that the build system includes when building in the
+current directory.
+
+
A new pattern rule for PRINT.% can be used to show the value of any
+GNUmake variable for the current build directory (make sure you are in the right
+directory though, many variables are only set when inside the
+O.arch build directory). For example make PRINT.T_A
+will display the build target architecture name from inside a
+O.arch directory but the variable will be empty from an
+application top or src directory. make PRINT.EPICS_BASE will show the
+path to Base from any EPICS application directory though.
+
+
Propagate PUTF across Asynchronous record processing
+
+
The IOC contains a mechanism involving the PUTF and RPRO fields of each
+record to ensure that if a record is busy when it receives a put to one of its
+fields, the record will be processed again to ensure that the new field value
+has been correctly acted on. Until now that mechanism only worked if the put was
+to the asynchronous record itself, so puts that were chained from some other
+record via a DB link did not cause reprocessing.
+
+
In this release the mechanism has been extended to propagate the PUTF state
+across DB links until all downstream records have been reprocessed. Some
+additional information about the record state can be shown by setting the TPRO
+field of an upstream record, and even more trace data is displayed if the
+debugging variable dbAccessDebugPUTF is set in addition to TPRO.
Finding info fields
@@ -122,15 +306,18 @@ release also includes additional protection against buffer overflows while
printing long links in dbpr, and corrects the output of long strings
from the dbgf command.
-
Record types mbbiDirect and mbboDirect extended to 32 bit
+
Record types mbbiDirect and mbboDirect upgraded to 32 bit
-
The VAL fields of mbbiDirect and mbboDirect records have
-been extended from DBF_USHORT (16 bit) to DBF_LONG (32 bit).
-New bit fields B10...B1F have been added.
+
The VAL fields and related fields of these records are now DBF_LONG.
+(Not DBF_ULONG in order to prevent Channel Access from promoting them
+to DBF_DOUBLE.) Additional bit fields B10...B1F have
+been added.
-
Device support which accesses the bit fields can test if the macro
-mbbiDirectRecord1BF or mbboDirectRecord1BF is
-defined. Device support which only accesses RVAL needs no modification.
+
Device support that accesses VAL or the bit fields directly (most
+don't) and aims for compatibility with old and new versions of these records
+should use at least 32 bit integer types to avoid bit loss. The number of bit
+fields can be calculated using 8 * sizeof(prec->val)
+which is correct in both versions.
Restore use of ledlib for VxWorks command editing
@@ -772,11 +959,76 @@ the stdout stream, making it hard to parse.
callback.h header and removed the need for dbScan.c to reach into the internals
of its CALLBACK objects.
-
-
Changes from the 3.15 branch since 3.15.5
+
Changes from the 3.15 branch since 3.15.6
+
Routine epicsTempName() removed from libCom
+
+
This routine was a simple wrapper around the C89 function tmpnam()
+which is now seen as unsafe and causes warning messages to be generated by
+most modern compilers. The two internal uses of this function have been
+modified to call epicsTempFile() instead. We were unable to find any
+published code that used this function, so it was removed immediately instead
+of being deprecated.
+
+
DBD Parsing of Record Types
+
+
The Perl DBD file parser has been made slightly more liberal; the order in
+which DBD files must be parsed is now more flexible, so that a record type
+definition can now be parsed after a device support that referred to that
+record type. A warning message will be displayed when the device support is
+seen, but the subsequent loading of the record type will be accepted without
+triggering an error. See
+Launchpad bug
+#1801145.
+
+
menuScan and several record types documented with POD
+
+
The EPICS Wiki pages describing a number of standard record types has been
+converted into the Perl POD documentation format and added to the DBD files,
+so at build-time an HTML version of these documents is generated and installed
+into the htmls directory. Thanks to Tony Pietryla.
+
+
CA client tools learned -V option
+
+
This displays the version numbers of EPICS Base and the CA protocol.
+
+
+
Changes made between 3.15.5 and 3.15.6
+
+
Unsetting environment variables
+
+
The new command epicsEnvUnset varname can be used to
+unset an environment variable.
+
+
Warning indicators in msi (and macLib) output
+
+
The libCom macro expansion library has been modified so that when the
+SUPPRESS_WARNINGS flag is set it will no longer include any ,undefined
+or ,recursive indicators in its output when undefined or recursive
+macros are encountered. These indicators were harmless when the output was fed
+into an IOC along with a definition for the macro, but when the msi
+tool was used to generate other kinds of files they caused problems. If the
+msi -V flag is used the markers will still be present in the output
+whenever the appropriate condition is seen.
+
+
Improvements to msi
+
+
In addition to fixing its response to discovering parsing errors in its
+substitution input file (reported as Launchpad
+bug #1503661)
+so it now deletes the incomplete output file, the msi program has been cleaned
+up a little bit internally.
+
+
All array records now post monitors on their array-length fields
+
+
The waveform record has been posting monitors on its NORD field since Base
+3.15.0.1; we finally got around to doing the equivalent in all the other
+built-in record types, which even required modifying device support in some
+cases. This fixes
+Launchpad bug #1730727.
+
HOWTO: Converting Wiki Record Reference to POD
Some documentation has been added to the dbdToHtml.pl script
@@ -808,15 +1060,84 @@ of having go modify or replace the original. A new .gitignore pattern
tells git to ignore all configure/*.local files.
-
Changes from the 3.14 branch since 3.15.5
+
Changes from the 3.14 branch between 3.15.5 and 3.15.6
-
+
Fix broken EPICS_IOC_LOG_FILE_LIMIT=0 setting
+
+
The Application Developers' Guide says this is allowed and disables the
+limit on the log-file, but it hasn't actually worked for some time (if ever).
+Note that the iocLogServer will be removed from newer Base release sometime
+soon as its functionality can be implemented by other dedicated log servers
+such as logstash or syslog-ng.
The files in the startup directory have not been maintained in recent years
+and have grown crufty (technical term). This release includes the following
+updates to these files:
+
+
+
+
The Perl EpicsHostArch.pl script has been rewritten, and support
+for a few previously missing host architectures has been added to it.
+
+
The EpicsHostArch.pl script has also been moved into the standard
+src/tools directory, from where it will be installed into
+lib/perl. In this new location it is no longer executable, so it must
+be run by the perl executable.
+
+
The build system has been adjusted to look for EpicsHostArch.pl in
+both places if the EPICS_HOST_ARCH environment variable has not been
+set at build-time.
+
+
Sites that used the original Perl script to set EPICS_HOST_ARCH as
+part of their standard environment will need to adjust their scripts when they
+upgrade to this release.
+
+
The EpicsHostArch shell script has been replaced with a wrapper
+routine that calls the Perl EpicsHostArch.pl script. Sites that rely on
+this script to set EPICS_HOST_ARCH should consider switching to the
+Perl script instead.
+
+
The Site.cshrc and Site.profile files have been renamed to
+unix.csh and unix.sh, respectively.
+
+
The existing win32.bat file has been cleaned up and a new
+windows.bat file added for 64-bit targets. The contents of these files
+should be seen as examples, don't uncomment or install parts for software that
+you don't explicitly know that you need.
+
+
+
+
Recent Apple XCode Build Issues
+
+
The latest version of XCode will not compile calls to system() or
+clock_settime() for iOS targets. There were several places in Base
+where these were being compiled, although there were probably never called. The
+code has now been modified to permit iOS builds to complete again.
+
+
Prevent illegal alarm severities
+
+
A check has been added to recGblResetAlarms() that prevents records
+from getting an alarm severity higher than INVALID_ALARM. It is still possible
+for a field like HSV to get set to a value that is not a legal alarm severity,
+but the core IOC code should never copy such a value into a record's SEVR or
+ACKS fields. With this fix the record's alarm severity will be limited to
+INVALID_ALARM.
Edit and commit changes to the EPICS version number file
- configure/CONFIG_BASE_VERSION.
+
+
For each external submodule, check if the module's release version
+ number (and SHRLIB_VERSION setting) has been updated if appropriate,
+ and that its Release Notes have been updated to cover any changes.
+ Commit any fixes and tag the module if that hasn't already been
+ done.
+
Update all external submodules on the Base-7.0 branch and
+ commit.
Release Manager
-
Tag the module in Git:
+
Edit and commit changes to the EPICS Base version number file and
+ the embedded module version files:
+
- cd base-7.1
- git tag -m 'ANJ: Tagged for 7.1.1' R7.1.1
+ cd base-7.0
+ git tag -m 'ANJ: Tagged for 7.0.2' R7.0.2
-
+ Don't push the new tag to the Launchpad repository yet.
@@ -283,12 +302,12 @@ made.
script generates a gzipped tarfile directly from the tag, excluding the
files and directories that are only used for continuous integration:
- cd base-7.1
- .ci/make-tar.sh R7.1.1 base-7.1.1.tar.gz base-7.1.1/
+ cd base-7.0
+ ./.tools/make-tar.sh R7.0.2 base-7.0.2.tar.gz base-7.0.2/
Create a GPG signature file of the tarfile as follows:
Test the tar file by extracting its contents and building it on at
- least one supported platform
+ least one supported platform. When this succeeds the new git tag can be
+ pushed to the Launchpad repository:
+
+ git push --follow-tags upstream 7.0
+
+
@@ -316,8 +340,8 @@ made.
Website Manager
-
Copy the tar file and its .asc signature file to the Base
- download area of the website.
+
Upload the tar file and its .asc signature file to the
+ Launchpad milestone for this release version.
diff --git a/modules/Makefile b/modules/Makefile
index cd782fe80..214e914f1 100644
--- a/modules/Makefile
+++ b/modules/Makefile
@@ -55,4 +55,4 @@ $(RELEASE_LOCAL):
$(ECHO) Creating $@, EPICS_BASE = $(INSTALL_LOCATION_ABS)
@echo EPICS_BASE = $(INSTALL_LOCATION_ABS)> $@
realclean:
- $(RM) $(RELEASE_LOCAL)
+ $(RM) $(wildcard RELEASE.*.local)
diff --git a/modules/ca/configure/CONFIG_CA_VERSION b/modules/ca/configure/CONFIG_CA_VERSION
index c50549018..fb80868c0 100644
--- a/modules/ca/configure/CONFIG_CA_VERSION
+++ b/modules/ca/configure/CONFIG_CA_VERSION
@@ -1,4 +1,4 @@
EPICS_CA_MAJOR_VERSION = 4
EPICS_CA_MINOR_VERSION = 13
-EPICS_CA_MAINTENANCE_VERSION = 2
-EPICS_CA_DEVELOPMENT_FLAG = 1
+EPICS_CA_MAINTENANCE_VERSION = 3
+EPICS_CA_DEVELOPMENT_FLAG = 0
diff --git a/modules/ca/src/client/Makefile b/modules/ca/src/client/Makefile
index 8686a8bb0..d707d5395 100644
--- a/modules/ca/src/client/Makefile
+++ b/modules/ca/src/client/Makefile
@@ -126,7 +126,7 @@ EXPANDVARS += EPICS_CA_DEVELOPMENT_FLAG
EXPANDFLAGS += $(foreach var,$(EXPANDVARS),-D$(var)="$(strip $($(var)))")
# shared library ABI version.
-SHRLIB_VERSION = 4.13.1
+SHRLIB_VERSION = $(EPICS_CA_MAJOR_VERSION).$(EPICS_CA_MINOR_VERSION).$(EPICS_CA_MAINTENANCE_VERSION)
include $(TOP)/configure/RULES
diff --git a/modules/ca/src/client/udpiiu.cpp b/modules/ca/src/client/udpiiu.cpp
index 73d4ee4cb..1bcb03716 100644
--- a/modules/ca/src/client/udpiiu.cpp
+++ b/modules/ca/src/client/udpiiu.cpp
@@ -193,7 +193,7 @@ udpiiu::udpiiu (
#ifdef IP_MULTICAST_TTL
{
- int ttl;
+ osiSockOptMcastTTL_t ttl;
long val;
if(envGetLongConfigParam(&EPICS_CA_MCAST_TTL, &val))
val =1;
@@ -961,7 +961,7 @@ bool udpiiu::pushDatagramMsg ( epicsGuard < epicsMutex > & guard,
udpiiu :: SearchDestUDP :: SearchDestUDP (
const osiSockAddr & destAddr, udpiiu & udpiiuIn ) :
- _destAddr ( destAddr ), _udpiiu ( udpiiuIn )
+ _lastError (0u), _destAddr ( destAddr ), _udpiiu ( udpiiuIn )
{
}
@@ -976,6 +976,13 @@ void udpiiu :: SearchDestUDP :: searchRequest (
int status = sendto ( _udpiiu.sock, const_cast(pBuf), bufSizeAsInt, 0,
& _destAddr.sa, sizeof ( _destAddr.sa ) );
if ( status == bufSizeAsInt ) {
+ if ( _lastError ) {
+ char buf[64];
+ sockAddrToDottedIP ( &_destAddr.sa, buf, sizeof ( buf ) );
+ errlogPrintf (
+ "CAC: ok sending UDP msg to %s\n", buf);
+ }
+ _lastError = 0;
break;
}
if ( status >= 0 ) {
@@ -1002,7 +1009,9 @@ void udpiiu :: SearchDestUDP :: searchRequest (
else if ( localErrno == SOCK_EBADF ) {
break;
}
- else {
+ else if ( localErrno == _lastError) {
+ break;
+ } else {
char sockErrBuf[64];
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
@@ -1011,6 +1020,8 @@ void udpiiu :: SearchDestUDP :: searchRequest (
errlogPrintf (
"CAC: error = \"%s\" sending UDP msg to %s\n",
sockErrBuf, buf);
+
+ _lastError = localErrno;
break;
}
}
diff --git a/modules/ca/src/client/udpiiu.h b/modules/ca/src/client/udpiiu.h
index 6b41e38ed..fdf348296 100644
--- a/modules/ca/src/client/udpiiu.h
+++ b/modules/ca/src/client/udpiiu.h
@@ -123,6 +123,7 @@ private:
void show (
epicsGuard < epicsMutex > &, unsigned level ) const;
private:
+ int _lastError;
osiSockAddr _destAddr;
udpiiu & _udpiiu;
};
diff --git a/modules/ca/src/template/top/caPerlApp/caget.pl b/modules/ca/src/template/top/caPerlApp/caget.pl
index 5e7be9f79..0d9af37a1 100644
--- a/modules/ca/src/template/top/caPerlApp/caget.pl
+++ b/modules/ca/src/template/top/caPerlApp/caget.pl
@@ -143,6 +143,10 @@ sub display {
printf " Lo ctrl limit: %g\n", $data->{lower_ctrl_limit};
printf " Hi ctrl limit: %g\n", $data->{upper_ctrl_limit};
}
+ if (exists $data->{ackt}) {
+ printf " Ack transients: %s\n", $data->{ackt} ? 'YES' : 'NO';
+ printf " Ack severity: %s\n", $data->{acks};
+ }
} else {
my $value = format_number($data, $type);
if ($opt_t) {
diff --git a/modules/ca/src/tools/caget.c b/modules/ca/src/tools/caget.c
index 00056709e..75225d07f 100644
--- a/modules/ca/src/tools/caget.c
+++ b/modules/ca/src/tools/caget.c
@@ -34,6 +34,7 @@
#include
#include
#include
+#include "epicsVersion.h"
#include "tool_lib.h"
@@ -55,6 +56,7 @@ static void usage (void)
{
fprintf (stderr, "\nUsage: caget [options] ...\n\n"
" -h: Help: Print this message\n"
+ " -V: Version: Show EPICS and CA versions\n"
"Channel Access options:\n"
" -w : Wait time, specifies CA timeout, default is %f second(s)\n"
" -c: Asynchronous get (use ca_get_callback and wait for completion)\n"
@@ -389,11 +391,14 @@ int main (int argc, char *argv[])
LINE_BUFFER(stdout); /* Configure stdout buffering */
- while ((opt = getopt(argc, argv, ":taicnhsSe:f:g:l:#:d:0:w:p:F:")) != -1) {
+ while ((opt = getopt(argc, argv, ":taicnhsSVe:f:g:l:#:d:0:w:p:F:")) != -1) {
switch (opt) {
case 'h': /* Print usage */
usage();
return 0;
+ case 'V':
+ printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() );
+ return 0;
case 't': /* Terse output mode */
complainIfNotPlainAndSet(&format, terse);
break;
diff --git a/modules/ca/src/tools/cainfo.c b/modules/ca/src/tools/cainfo.c
index ad580f473..fc18ccd3f 100644
--- a/modules/ca/src/tools/cainfo.c
+++ b/modules/ca/src/tools/cainfo.c
@@ -23,6 +23,7 @@
#include
#include
+#include "epicsVersion.h"
#include
#include
@@ -36,12 +37,14 @@ void usage (void)
{
fprintf (stderr, "\nUsage: cainfo [options] ...\n\n"
" -h: Help: Print this message\n"
+ " -V: Version: Show EPICS and CA versions\n"
"Channel Access options:\n"
" -w : Wait time, specifies CA timeout, default is %f second(s)\n"
" -s : Call ca_client_status with the specified interest level\n"
" -p : CA priority (0-%u, default 0=lowest)\n"
"\nExample: cainfo my_channel another_channel\n\n"
, DEFAULT_TIMEOUT, CA_PRIORITY_MAX);
+ fprintf (stderr, "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() );
}
@@ -137,11 +140,14 @@ int main (int argc, char *argv[])
LINE_BUFFER(stdout); /* Configure stdout buffering */
- while ((opt = getopt(argc, argv, ":nhw:s:p:")) != -1) {
+ while ((opt = getopt(argc, argv, ":nhVw:s:p:")) != -1) {
switch (opt) {
case 'h': /* Print usage */
usage();
return 0;
+ case 'V':
+ printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() );
+ return 0;
case 'w': /* Set CA timeout value */
if(epicsScanDouble(optarg, &caTimeout) != 1)
{
diff --git a/modules/ca/src/tools/camonitor.c b/modules/ca/src/tools/camonitor.c
index 307dad8d6..a3fdecd55 100644
--- a/modules/ca/src/tools/camonitor.c
+++ b/modules/ca/src/tools/camonitor.c
@@ -26,6 +26,7 @@
#include
#include
#include
+#include "epicsVersion.h"
#include
#include
@@ -44,7 +45,8 @@ void usage (void)
{
fprintf (stderr, "\nUsage: camonitor [options] ...\n"
"\n"
- " -h: Help; Print this message\n"
+ " -h: Help: Print this message\n"
+ " -V: Version: Show EPICS and CA versions\n"
"Channel Access options:\n"
" -w : Wait time, specifies CA timeout, default is %f second(s)\n"
" -m : Specify CA event mask to use. is any combination of\n"
@@ -209,11 +211,14 @@ int main (int argc, char *argv[])
LINE_BUFFER(stdout); /* Configure stdout buffering */
- while ((opt = getopt(argc, argv, ":nhm:sSe:f:g:l:#:0:w:t:p:F:")) != -1) {
+ while ((opt = getopt(argc, argv, ":nhVm:sSe:f:g:l:#:0:w:t:p:F:")) != -1) {
switch (opt) {
case 'h': /* Print usage */
usage();
return 0;
+ case 'V':
+ printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() );
+ return 0;
case 'n': /* Print ENUM as index numbers */
enumAsNr=1;
break;
diff --git a/modules/ca/src/tools/caput.c b/modules/ca/src/tools/caput.c
index 5e4d10e23..79ffef8c3 100644
--- a/modules/ca/src/tools/caput.c
+++ b/modules/ca/src/tools/caput.c
@@ -37,6 +37,7 @@
#include
#include
#include
+#include "epicsVersion.h"
#include "tool_lib.h"
@@ -59,6 +60,7 @@ void usage (void)
fprintf (stderr, "\nUsage: caput [options] ...\n"
" caput -a [options] ...\n\n"
" -h: Help: Print this message\n"
+ " -V: Version: Show EPICS and CA versions\n"
"Channel Access options:\n"
" -w : Wait time, specifies CA timeout, default is %f second(s)\n"
" -c: Asynchronous put (use ca_put_callback and wait for completion)\n"
@@ -281,11 +283,14 @@ int main (int argc, char *argv[])
LINE_BUFFER(stdout); /* Configure stdout buffering */
putenv("POSIXLY_CORRECT="); /* Behave correct on GNU getopt systems */
- while ((opt = getopt(argc, argv, ":cnlhatsS#:w:p:F:")) != -1) {
+ while ((opt = getopt(argc, argv, ":cnlhatsVS#:w:p:F:")) != -1) {
switch (opt) {
case 'h': /* Print usage */
usage();
return 0;
+ case 'V':
+ printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() );
+ return 0;
case 'n': /* Force interpret ENUM as index number */
enumAsNr = 1;
enumAsString = 0;
diff --git a/modules/database/configure/CONFIG b/modules/database/configure/CONFIG
index 9ebdbeb20..c471407c4 100644
--- a/modules/database/configure/CONFIG
+++ b/modules/database/configure/CONFIG
@@ -25,7 +25,7 @@ INSTALL_LOCATION = $(TOP)
BASE_CPPFLAGS += -DUSE_TYPED_RSET
# Shared library ABI version.
-SHRLIB_VERSION = 3.17.0
+SHRLIB_VERSION = $(EPICS_DATABASE_MAJOR_VERSION).$(EPICS_DATABASE_MINOR_VERSION).$(EPICS_DATABASE_MAINTENANCE_VERSION)
# CONFIG_SITE files contain other build configuration settings
include $(TOP)/configure/CONFIG_SITE
diff --git a/modules/database/configure/CONFIG_DATABASE_VERSION b/modules/database/configure/CONFIG_DATABASE_VERSION
index a42eab01e..63ad9cd4c 100644
--- a/modules/database/configure/CONFIG_DATABASE_VERSION
+++ b/modules/database/configure/CONFIG_DATABASE_VERSION
@@ -1,4 +1,4 @@
EPICS_DATABASE_MAJOR_VERSION = 3
EPICS_DATABASE_MINOR_VERSION = 17
-EPICS_DATABASE_MAINTENANCE_VERSION = 1
-EPICS_DATABASE_DEVELOPMENT_FLAG = 1
+EPICS_DATABASE_MAINTENANCE_VERSION = 2
+EPICS_DATABASE_DEVELOPMENT_FLAG = 0
diff --git a/modules/database/src/ioc/Makefile b/modules/database/src/ioc/Makefile
index 00851867e..325872826 100644
--- a/modules/database/src/ioc/Makefile
+++ b/modules/database/src/ioc/Makefile
@@ -25,11 +25,6 @@ INC += databaseVersionNum.h
PROD_LIBS = Com
-EPICS_DATABASE_MAJOR_VERSION = 3
-EPICS_DATABASE_MINOR_VERSION = 17
-EPICS_DATABASE_MAINTENANCE_VERSION = 0
-EPICS_DATABASE_DEVELOPMENT_FLAG = 1
-
include $(IOCDIR)/as/Makefile
include $(IOCDIR)/bpt/Makefile
include $(IOCDIR)/db/Makefile
diff --git a/modules/database/src/ioc/db/Makefile b/modules/database/src/ioc/db/Makefile
index 1f4bfd181..7e9d56704 100644
--- a/modules/database/src/ioc/db/Makefile
+++ b/modules/database/src/ioc/db/Makefile
@@ -35,7 +35,6 @@ INC += dbTest.h
INC += dbCaTest.h
INC += db_test.h
INC += db_field_log.h
-INC += initHooks.h
INC += recGbl.h
INC += dbIocRegister.h
INC += chfPlugin.h
@@ -63,7 +62,7 @@ DBDINC += menuScan
DBDINC += dbCommon
dbMenusPod = $(notdir $(wildcard ../db/menu*.dbd.pod))
-HTMLS += $(patsubst %.dbd.pod,%.html,$(menusPod))
+HTMLS += $(patsubst %.dbd.pod,%.html,$(dbMenusPod))
dbCore_SRCS += dbLock.c
dbCore_SRCS += dbAccess.c
@@ -87,7 +86,6 @@ dbCore_SRCS += recGbl.c
dbCore_SRCS += callback.c
dbCore_SRCS += dbCa.c
dbCore_SRCS += dbCaTest.c
-dbCore_SRCS += initHooks.c
dbCore_SRCS += cvtBpt.c
dbCore_SRCS += dbContext.cpp
dbCore_SRCS += dbChannelIO.cpp
diff --git a/modules/database/src/ioc/db/callback.c b/modules/database/src/ioc/db/callback.c
index ae074141c..fe6db815b 100644
--- a/modules/database/src/ioc/db/callback.c
+++ b/modules/database/src/ioc/db/callback.c
@@ -54,6 +54,7 @@ typedef struct cbQueueSet {
epicsEventId semWakeUp;
epicsRingPointerId queue;
int queueOverflow;
+ int queueOverflows;
int shutdown;
int threadsConfigured;
int threadsRunning;
@@ -103,6 +104,51 @@ int callbackSetQueueSize(int size)
return 0;
}
+int callbackQueueStatus(const int reset, callbackQueueStats *result)
+{
+ int ret;
+ if (!callbackIsInit) return -1;
+ if (result) {
+ int prio;
+ result->size = callbackQueueSize;
+ for(prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
+ epicsRingPointerId qId = callbackQueue[prio].queue;
+ result->numUsed[prio] = epicsRingPointerGetUsed(qId);
+ result->maxUsed[prio] = epicsRingPointerGetHighWaterMark(qId);
+ result->numOverflow[prio] = epicsAtomicGetIntT(&callbackQueue[prio].queueOverflows);
+ }
+ ret = 0;
+ } else {
+ ret = -2;
+ }
+ if (reset) {
+ int prio;
+ for(prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
+ epicsRingPointerResetHighWaterMark(callbackQueue[prio].queue);
+ }
+ }
+ return ret;
+}
+
+void callbackQueueShow(const int reset)
+{
+ callbackQueueStats stats;
+ if (callbackQueueStatus(reset, &stats) == -1) {
+ fprintf(stderr, "Callback system not initialized, yet. Please run "
+ "iocInit before using this command.\n");
+ } else {
+ int prio;
+ printf("PRIORITY HIGH-WATER MARK ITEMS IN Q Q SIZE %% USED Q OVERFLOWS\n");
+ for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
+ double qusage = 100.0 * stats.numUsed[prio] / stats.size;
+ printf("%8s %15d %10d %6d %6.1f %11d\n",
+ threadNamePrefix[prio], stats.maxUsed[prio],
+ stats.numUsed[prio], stats.size, qusage,
+ stats.numOverflow[prio]);
+ }
+ }
+}
+
int callbackParallelThreads(int count, const char *prio)
{
if (callbackIsInit) {
@@ -290,6 +336,7 @@ int callbackRequest(CALLBACK *pcallback)
if (!pushOK) {
epicsInterruptContextMessage(fullMessage[priority]);
mySet->queueOverflow = TRUE;
+ epicsAtomicIncrIntT(&mySet->queueOverflows);
return S_db_bufFull;
}
epicsEventSignal(mySet->semWakeUp);
diff --git a/modules/database/src/ioc/db/callback.h b/modules/database/src/ioc/db/callback.h
index fa626d1d0..720cf337d 100644
--- a/modules/database/src/ioc/db/callback.h
+++ b/modules/database/src/ioc/db/callback.h
@@ -48,6 +48,13 @@ typedef epicsCallback CALLBACK;
typedef void (*CALLBACKFUNC)(struct callbackPvt*);
+typedef struct callbackQueueStats {
+ int size;
+ int numUsed[NUM_CALLBACK_PRIORITIES];
+ int maxUsed[NUM_CALLBACK_PRIORITIES];
+ int numOverflow[NUM_CALLBACK_PRIORITIES];
+} callbackQueueStats;
+
#define callbackSetCallback(PFUN, PCALLBACK) \
( (PCALLBACK)->callback = (PFUN) )
#define callbackSetPriority(PRIORITY, PCALLBACK) \
@@ -73,6 +80,8 @@ epicsShareFunc void callbackCancelDelayed(CALLBACK *pcallback);
epicsShareFunc void callbackRequestProcessCallbackDelayed(
CALLBACK *pCallback, int Priority, void *pRec, double seconds);
epicsShareFunc int callbackSetQueueSize(int size);
+epicsShareFunc int callbackQueueStatus(const int reset, callbackQueueStats *result);
+epicsShareFunc void callbackQueueShow(const int reset);
epicsShareFunc int callbackParallelThreads(int count, const char *prio);
#ifdef __cplusplus
diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c
index f52b8881f..af396c74e 100644
--- a/modules/database/src/ioc/db/dbAccess.c
+++ b/modules/database/src/ioc/db/dbAccess.c
@@ -34,7 +34,7 @@
#include "errlog.h"
#include "errMdef.h"
-#define epicsExportSharedSymbols
+#include "epicsExport.h" /* #define epicsExportSharedSymbols */
#include "caeventmask.h"
#include "callback.h"
#include "dbAccessDefs.h"
@@ -65,6 +65,9 @@
epicsShareDef struct dbBase *pdbbase = 0;
epicsShareDef volatile int interruptAccept=FALSE;
+epicsShareDef int dbAccessDebugPUTF = 0;
+epicsExportAddress(int, dbAccessDebugPUTF);
+
/* Hook Routines */
epicsShareDef DB_LOAD_RECORDS_HOOK_ROUTINE dbLoadRecordsHook = NULL;
@@ -446,22 +449,6 @@ int dbGetFieldIndex(const struct dbAddr *paddr)
return paddr->pfldDes->indRecordType;
}
-/*
- * Process a record if its scan field is passive.
- * Will notify if processing is complete by callback.
- * (only if you are interested in completion)
- */
-long dbScanPassive(dbCommon *pfrom, dbCommon *pto)
-{
- /* if not passive just return success */
- if (pto->scan != 0)
- return 0;
-
- if (pfrom && pfrom->ppn)
- dbNotifyAdd(pfrom,pto);
- return dbProcess(pto);
-}
-
/*
* Process the record.
* 1. Check for breakpoints.
@@ -527,7 +514,8 @@ long dbProcess(dbCommon *precord)
unsigned short monitor_mask;
if (*ptrace)
- printf("%s: Active %s\n", context, precord->name);
+ printf("%s: dbProcess of Active '%s' with RPRO=%d\n",
+ context, precord->name, precord->rpro);
/* raise scan alarm after MAX_LOCK times */
if ((precord->stat == SCAN_ALARM) ||
@@ -556,7 +544,8 @@ long dbProcess(dbCommon *precord)
/* if disabled check disable alarm severity and return success */
if (precord->disa == precord->disv) {
if (*ptrace)
- printf("%s: Disabled %s\n", context, precord->name);
+ printf("%s: dbProcess of Disabled '%s'\n",
+ context, precord->name);
/*take care of caching and notifyCompletion*/
precord->rpro = FALSE;
@@ -593,7 +582,7 @@ long dbProcess(dbCommon *precord)
}
if (*ptrace)
- printf("%s: Process %s\n", context, precord->name);
+ printf("%s: dbProcess of '%s'\n", context, precord->name);
/* process record */
status = prset->process(precord);
@@ -612,6 +601,31 @@ all_done:
return status;
}
+long dbEntryToAddr(const DBENTRY *pdbentry, DBADDR *paddr)
+{
+ dbFldDes *pflddes = pdbentry->pflddes;
+ short dbfType = pflddes->field_type;
+
+ paddr->precord = pdbentry->precnode->precord;
+ paddr->pfield = pdbentry->pfield;
+ paddr->pfldDes = pflddes;
+ paddr->no_elements = 1;
+ paddr->field_type = dbfType;
+ paddr->field_size = pflddes->size;
+ paddr->special = pflddes->special;
+ paddr->dbr_field_type = mapDBFToDBR[dbfType];
+
+ if (paddr->special == SPC_DBADDR) {
+ const rset *prset = dbGetRset(paddr);
+
+ /* Let record type modify paddr */
+ if (prset && prset->cvt_dbaddr) {
+ return prset->cvt_dbaddr(paddr);
+ }
+ }
+ return 0;
+}
+
/*
* Fill out a database structure (*paddr) for
* a record given by the name "pname."
@@ -622,9 +636,7 @@ all_done:
long dbNameToAddr(const char *pname, DBADDR *paddr)
{
DBENTRY dbEntry;
- dbFldDes *pflddes;
long status = 0;
- short dbfType;
if (!pname || !*pname || !pdbbase)
return S_db_notFound;
@@ -639,46 +651,28 @@ long dbNameToAddr(const char *pname, DBADDR *paddr)
status = dbGetAttributePart(&dbEntry, &pname);
if (status) goto finish;
- pflddes = dbEntry.pflddes;
- dbfType = pflddes->field_type;
-
- paddr->precord = dbEntry.precnode->precord;
- paddr->pfield = dbEntry.pfield;
- paddr->pfldDes = pflddes;
- paddr->no_elements = 1;
- paddr->field_type = dbfType;
- paddr->field_size = pflddes->size;
- paddr->special = pflddes->special;
- paddr->dbr_field_type = mapDBFToDBR[dbfType];
-
- if (paddr->special == SPC_DBADDR) {
- rset *prset = dbGetRset(paddr);
-
- /* Let record type modify paddr */
- if (prset && prset->cvt_dbaddr) {
- status = prset->cvt_dbaddr(paddr);
- if (status)
- goto finish;
- dbfType = paddr->field_type;
- }
- }
+ status = dbEntryToAddr(&dbEntry, paddr);
+ if (status) goto finish;
/* Handle field modifiers */
if (*pname++ == '$') {
+ short dbfType = paddr->field_type;
+
/* Some field types can be accessed as char arrays */
if (dbfType == DBF_STRING) {
paddr->no_elements = paddr->field_size;
paddr->field_type = DBF_CHAR;
paddr->field_size = 1;
paddr->dbr_field_type = DBR_CHAR;
- } else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
+ }
+ else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
/* Clients see a char array, but keep original dbfType */
paddr->no_elements = PVLINK_STRINGSZ;
paddr->field_size = 1;
paddr->dbr_field_type = DBR_CHAR;
- } else {
+ }
+ else {
status = S_dbLib_fieldNotFound;
- goto finish;
}
}
@@ -713,6 +707,18 @@ void dbInitEntryFromRecord(struct dbCommon *prec, DBENTRY *pdbentry)
pdbentry->precnode = ppvt->recnode;
}
+struct link* dbGetDevLink(struct dbCommon* prec)
+{
+ DBLINK *plink = 0;
+ DBENTRY entry;
+ dbInitEntryFromRecord(prec, &entry);
+ if(dbFindField(&entry, "INP")==0 || dbFindField(&entry, "OUT")==0) {
+ plink = (DBLINK*)entry.pfield;
+ }
+ dbFinishEntry(&entry);
+ return plink;
+}
+
long dbValueSize(short dbr_type)
{
/* sizes for value associated with each DBR request type */
@@ -1049,7 +1055,7 @@ static long dbPutFieldLink(DBADDR *paddr,
return S_db_badDbrtype;
}
- status = dbParseLink(pstring, pfldDes->field_type, &link_info, 0);
+ status = dbParseLink(pstring, pfldDes->field_type, &link_info);
if (status)
return status;
@@ -1222,8 +1228,8 @@ long dbPutField(DBADDR *paddr, short dbrType,
precord->scan == 0 &&
dbrType < DBR_PUT_ACKT)) {
if (precord->pact) {
- if (precord->tpro)
- printf("%s: Active %s\n",
+ if (dbAccessDebugPUTF && precord->tpro)
+ printf("%s: dbPutField to Active '%s', setting RPRO=1\n",
epicsThreadGetNameSelf(), precord->name);
precord->rpro = TRUE;
} else {
@@ -1356,4 +1362,3 @@ done:
paddr->pfield = pfieldsave;
return status;
}
-
diff --git a/modules/database/src/ioc/db/dbAccessDefs.h b/modules/database/src/ioc/db/dbAccessDefs.h
index cc45b17fe..52ac4016d 100644
--- a/modules/database/src/ioc/db/dbAccessDefs.h
+++ b/modules/database/src/ioc/db/dbAccessDefs.h
@@ -34,6 +34,7 @@ extern "C" {
epicsShareExtern struct dbBase *pdbbase;
epicsShareExtern volatile int interruptAccept;
+epicsShareExtern int dbAccessDebugPUTF;
/* The database field and request types are defined in dbFldTypes.h*/
/* Data Base Request Options */
@@ -203,6 +204,8 @@ struct dbr_alDouble {DBRalDouble};
#define S_db_notInit (M_dbAccess|67) /*Not initialized*/
#define S_db_bufFull (M_dbAccess|68) /*Buffer full*/
+struct dbEntry;
+
epicsShareFunc long dbPutSpecial(struct dbAddr *paddr,int pass);
epicsShareFunc rset * dbGetRset(const struct dbAddr *paddr);
epicsShareFunc long dbPutAttribute(
@@ -212,8 +215,29 @@ epicsShareFunc int dbGetFieldIndex(const struct dbAddr *paddr);
epicsShareFunc long dbScanPassive(
struct dbCommon *pfrom,struct dbCommon *pto);
epicsShareFunc long dbProcess(struct dbCommon *precord);
-epicsShareFunc long dbNameToAddr(
- const char *pname,struct dbAddr *);
+epicsShareFunc long dbNameToAddr(const char *pname, struct dbAddr *paddr);
+
+/** Initialize DBADDR from a dbEntry
+ * Also handles SPC_DBADDR processing. This is really an internal
+ * routine for use by dbNameToAddr() and dbChannelCreate().
+ */
+epicsShareFunc long dbEntryToAddr(const struct dbEntry *pdbentry,
+ struct dbAddr *paddr);
+
+/** Initialize DBENTRY from a valid dbAddr*
+ * Constant time equivalent of dbInitEntry() then dbFindRecord(),
+ * and finally dbFollowAlias().
+ */
+epicsShareFunc void dbInitEntryFromAddr(struct dbAddr *paddr,
+ struct dbEntry *pdbentry);
+
+/** Initialize DBENTRY from a valid record (dbCommon*)
+ * Constant time equivalent of dbInitEntry() then dbFindRecord(),
+ * and finally dbFollowAlias() when no field is specified.
+ */
+epicsShareFunc void dbInitEntryFromRecord(struct dbCommon *prec,
+ struct dbEntry *pdbentry);
+
epicsShareFunc devSup* dbDTYPtoDevSup(dbRecordType *prdes, int dtyp);
epicsShareFunc devSup* dbDSETtoDevSup(dbRecordType *prdes, struct dset *pdset);
epicsShareFunc long dbGetField(
diff --git a/modules/database/src/ioc/db/dbCa.c b/modules/database/src/ioc/db/dbCa.c
index e844ad826..843fbfc0c 100644
--- a/modules/database/src/ioc/db/dbCa.c
+++ b/modules/database/src/ioc/db/dbCa.c
@@ -703,7 +703,12 @@ static long doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv)
caLink *pca;
long status;
- pcaGetCheck
+ assert(plink);
+ if (plink->type != CA_LINK) return -1;
+ pca = (caLink *)plink->value.pv_link.pvt;
+ assert(pca);
+ epicsMutexMustLock(pca->lock);
+ assert(pca->plink);
status = rtn(plink, priv);
epicsMutexUnlock(pca->lock);
return status;
@@ -842,6 +847,7 @@ static void eventCallback(struct event_handler_args arg)
struct dbr_time_double *pdbr_time_double;
dbCaCallback monitor = 0;
void *userPvt = 0;
+ int doScan = 1;
assert(pca);
epicsMutexMustLock(pca->lock);
@@ -872,10 +878,13 @@ static void eventCallback(struct event_handler_args arg)
memcpy(pca->pgetString, dbr_value_ptr(arg.dbr, arg.type), size);
pca->gotInString = TRUE;
} else switch (arg.type){
+ case DBR_TIME_ENUM:
+ /* Disable the record scan if we also have a string monitor */
+ doScan = !(plink->value.pv_link.pvlMask & pvlOptInpString);
+ /* fall through */
case DBR_TIME_STRING:
case DBR_TIME_SHORT:
case DBR_TIME_FLOAT:
- case DBR_TIME_ENUM:
case DBR_TIME_CHAR:
case DBR_TIME_LONG:
case DBR_TIME_DOUBLE:
@@ -893,7 +902,7 @@ static void eventCallback(struct event_handler_args arg)
pca->sevr = pdbr_time_double->severity;
pca->stat = pdbr_time_double->status;
memcpy(&pca->timeStamp, &pdbr_time_double->stamp, sizeof(epicsTimeStamp));
- if (precord) {
+ if (doScan && precord) {
struct pv_link *ppv_link = &plink->value.pv_link;
if ((ppv_link->pvlMask & pvlOptCP) ||
diff --git a/modules/database/src/ioc/db/dbChannel.c b/modules/database/src/ioc/db/dbChannel.c
index 92be08c77..6db2982d8 100644
--- a/modules/database/src/ioc/db/dbChannel.c
+++ b/modules/database/src/ioc/db/dbChannel.c
@@ -20,6 +20,7 @@
#include "cantProceed.h"
#include "epicsAssert.h"
#include "epicsString.h"
+#include "epicsStdio.h"
#include "errlog.h"
#include "freeList.h"
#include "gpHash.h"
@@ -472,9 +473,7 @@ dbChannel * dbChannelCreate(const char *name)
dbChannel *chan = NULL;
char *cname;
dbAddr *paddr;
- dbFldDes *pflddes;
long status;
- short dbfType;
if (!name || !*name || !pdbbase)
return NULL;
@@ -497,32 +496,14 @@ dbChannel * dbChannelCreate(const char *name)
ellInit(&chan->post_chain);
paddr = &chan->addr;
- pflddes = dbEntry.pflddes;
- dbfType = pflddes->field_type;
-
- paddr->precord = dbEntry.precnode->precord;
- paddr->pfield = dbEntry.pfield;
- paddr->pfldDes = pflddes;
- paddr->no_elements = 1;
- paddr->field_type = dbfType;
- paddr->field_size = pflddes->size;
- paddr->special = pflddes->special;
- paddr->dbr_field_type = mapDBFToDBR[dbfType];
-
- if (paddr->special == SPC_DBADDR) {
- rset *prset = dbGetRset(paddr);
-
- /* Let record type modify paddr */
- if (prset && prset->cvt_dbaddr) {
- status = prset->cvt_dbaddr(paddr);
- if (status)
- goto finish;
- dbfType = paddr->field_type;
- }
- }
+ status = dbEntryToAddr(&dbEntry, paddr);
+ if (status)
+ goto finish;
/* Handle field modifiers */
if (*pname) {
+ short dbfType = paddr->field_type;
+
if (*pname == '$') {
/* Some field types can be accessed as char arrays */
if (dbfType == DBF_STRING) {
@@ -530,12 +511,14 @@ dbChannel * dbChannelCreate(const char *name)
paddr->field_type = DBF_CHAR;
paddr->field_size = 1;
paddr->dbr_field_type = DBR_CHAR;
- } else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
+ }
+ else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
/* Clients see a char array, but keep original dbfType */
paddr->no_elements = PVLINK_STRINGSZ;
paddr->field_size = 1;
paddr->dbr_field_type = DBR_CHAR;
- } else {
+ }
+ else {
status = S_dbLib_fieldNotFound;
goto finish;
}
diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c
index 105799876..ce0110a64 100644
--- a/modules/database/src/ioc/db/dbDbLink.c
+++ b/modules/database/src/ioc/db/dbDbLink.c
@@ -12,6 +12,29 @@
* Current Author: Andrew Johnson
*/
+/* The PUTF and RPRO fields in dbCommon are flags that indicate when a record
+ * is being processed as a result of an external put (i.e. some server process
+ * calling dbPutField()), ensuring that the record and its successors will
+ * eventually get processed even if they happen to be busy at the time of the
+ * put. From Base-3.16.2 and 7.0.2 the code ensures that all records downstream
+ * from the original are processed even if a busy asynchronous device appears
+ * in the processing chain (this breaks the chain in older versions).
+ *
+ * PUTF - This field is set in dbPutField() prior to it calling dbProcess().
+ * It is normally cleared at the end of processing in recGblFwdLink().
+ * It may also be cleared in dbProcess() if DISA==DISV (scan disabled),
+ * or by the processTarget() function below.
+ *
+ * If PUTF is TRUE before a call to dbProcess(prec), then after it returns
+ * either PACT is TRUE, or PUTF will be FALSE.
+ *
+ * RPRO - This field is set by dbPutField() or by the processTarget() function
+ * below when a record to be processed is found to be busy (PACT==1).
+ * It is normally cleared in recGblFwdLink() when the record is queued
+ * for re-processing, or in dbProcess() if DISA==DISV (scan disabled).
+ */
+
+
#include
#include
#include
@@ -43,17 +66,22 @@
#include "dbNotify.h"
#include "dbScan.h"
#include "dbStaticLib.h"
+#include "dbServer.h"
#include "devSup.h"
#include "link.h"
#include "recGbl.h"
#include "recSup.h"
#include "special.h"
+#include "dbDbLink.h"
+
/***************************** Database Links *****************************/
-/* Forward definition */
+/* Forward definitions */
static lset dbDb_lset;
+static long processTarget(dbCommon *psrc, dbCommon *pdst);
+
long dbDbInitLink(struct link *plink, short dbfType)
{
DBADDR dbaddr;
@@ -138,11 +166,7 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
/* scan passive records if link is process passive */
if (ppv_link->pvlMask & pvlOptPP) {
- unsigned char pact = precord->pact;
-
- precord->pact = TRUE;
status = dbScanPassive(precord, paddr->precord);
- precord->pact = pact;
if (status)
return status;
}
@@ -311,22 +335,10 @@ static long dbDbPutValue(struct link *plink, short dbrType,
return status;
if (paddr->pfield == (void *) &pdest->proc ||
- (ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) {
- /* if dbPutField caused asyn record to process */
- /* ask for reprocessing*/
- if (pdest->putf) {
- pdest->rpro = TRUE;
- } else { /* process dest record with source's PACT true */
- unsigned char pact;
-
- if (psrce && psrce->ppn)
- dbNotifyAdd(psrce, pdest);
- pact = psrce->pact;
- psrce->pact = TRUE;
- status = dbProcess(pdest);
- psrce->pact = pact;
- }
+ (ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) {
+ status = processTarget(psrce, pdest);
}
+
return status;
}
@@ -356,3 +368,75 @@ static lset dbDb_lset = {
dbDbPutValue, NULL,
dbDbScanFwdLink, doLocked
};
+
+
+/*
+ * Process a record if its scan field is passive.
+ */
+long dbScanPassive(dbCommon *pfrom, dbCommon *pto)
+{
+ /* if not passive we're done */
+ if (pto->scan != 0)
+ return 0;
+
+ return processTarget(pfrom, pto);
+}
+
+static long processTarget(dbCommon *psrc, dbCommon *pdst)
+{
+ char context[40] = "";
+ int trace = dbAccessDebugPUTF && *dbLockSetAddrTrace(psrc);
+ long status;
+ epicsUInt8 pact = psrc->pact;
+
+ psrc->pact = TRUE;
+
+ if (psrc && psrc->ppn)
+ dbNotifyAdd(psrc, pdst);
+
+ if (trace && dbServerClient(context, sizeof(context))) {
+ /* No client, use thread name */
+ strncpy(context, epicsThreadGetNameSelf(), sizeof(context));
+ context[sizeof(context) - 1] = 0;
+ }
+
+ if (!pdst->pact) {
+ /* Normal propagation of PUTF from src to dst */
+ if (trace)
+ printf("%s: '%s' -> '%s' with PUTF=%u\n",
+ context, psrc->name, pdst->name, psrc->putf);
+
+ if (pdst->putf)
+ errlogPrintf("Warning: '%s.PUTF' found true with PACT false\n",
+ pdst->name);
+
+ pdst->putf = psrc->putf;
+ }
+ else if (psrc->putf) {
+ /* The dst record is busy (awaiting async reprocessing) and
+ * we were originally triggered by a call to dbPutField(),
+ * so we mark the dst record for reprocessing once the async
+ * completion is over.
+ */
+ if (trace)
+ printf("%s: '%s' -> Active '%s', setting RPRO=1\n",
+ context, psrc->name, pdst->name);
+
+ pdst->putf = FALSE;
+ pdst->rpro = TRUE;
+ }
+ else {
+ /* The dst record is busy, but we weren't triggered by a call
+ * to dbPutField(). Do nothing.
+ */
+ if (trace)
+ printf("%s: '%s' -> Active '%s', done\n",
+ context, psrc->name, pdst->name);
+ }
+
+ status = dbProcess(pdst);
+
+ psrc->pact = pact;
+
+ return status;
+}
diff --git a/modules/database/src/ioc/db/dbIocRegister.c b/modules/database/src/ioc/db/dbIocRegister.c
index 4d0b88cd9..c40af92c1 100644
--- a/modules/database/src/ioc/db/dbIocRegister.c
+++ b/modules/database/src/ioc/db/dbIocRegister.c
@@ -296,6 +296,17 @@ static void scanOnceSetQueueSizeCallFunc(const iocshArgBuf *args)
scanOnceSetQueueSize(args[0].ival);
}
+/* scanOnceQueueShow */
+static const iocshArg scanOnceQueueShowArg0 = { "reset",iocshArgInt};
+static const iocshArg * const scanOnceQueueShowArgs[1] =
+ {&scanOnceQueueShowArg0};
+static const iocshFuncDef scanOnceQueueShowFuncDef =
+ {"scanOnceQueueShow",1,scanOnceQueueShowArgs};
+static void scanOnceQueueShowCallFunc(const iocshArgBuf *args)
+{
+ scanOnceQueueShow(args[0].ival);
+}
+
/* scanppl */
static const iocshArg scanpplArg0 = { "rate",iocshArgDouble};
static const iocshArg * const scanpplArgs[1] = {&scanpplArg0};
@@ -335,6 +346,17 @@ static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args)
callbackSetQueueSize(args[0].ival);
}
+/* callbackQueueShow */
+static const iocshArg callbackQueueShowArg0 = { "reset", iocshArgInt};
+static const iocshArg * const callbackQueueShowArgs[1] =
+ {&callbackQueueShowArg0};
+static const iocshFuncDef callbackQueueShowFuncDef =
+ {"callbackQueueShow",1,callbackQueueShowArgs};
+static void callbackQueueShowCallFunc(const iocshArgBuf *args)
+{
+ callbackQueueShow(args[0].ival);
+}
+
/* callbackParallelThreads */
static const iocshArg callbackParallelThreadsArg0 = { "no of threads", iocshArgInt};
static const iocshArg callbackParallelThreadsArg1 = { "priority", iocshArgString};
@@ -441,12 +463,14 @@ void dbIocRegister(void)
iocshRegister(&dbLockShowLockedFuncDef,dbLockShowLockedCallFunc);
iocshRegister(&scanOnceSetQueueSizeFuncDef,scanOnceSetQueueSizeCallFunc);
+ iocshRegister(&scanOnceQueueShowFuncDef,scanOnceQueueShowCallFunc);
iocshRegister(&scanpplFuncDef,scanpplCallFunc);
iocshRegister(&scanpelFuncDef,scanpelCallFunc);
iocshRegister(&postEventFuncDef,postEventCallFunc);
iocshRegister(&scanpiolFuncDef,scanpiolCallFunc);
iocshRegister(&callbackSetQueueSizeFuncDef,callbackSetQueueSizeCallFunc);
+ iocshRegister(&callbackQueueShowFuncDef,callbackQueueShowCallFunc);
iocshRegister(&callbackParallelThreadsFuncDef,callbackParallelThreadsCallFunc);
/* Needed before callback system is initialized */
diff --git a/modules/database/src/ioc/db/dbJLink.c b/modules/database/src/ioc/db/dbJLink.c
index ea054eee9..3acf75660 100644
--- a/modules/database/src/ioc/db/dbJLink.c
+++ b/modules/database/src/ioc/db/dbJLink.c
@@ -2,7 +2,7 @@
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
-* in file LICENSE that is included with this distribution.
+* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* dbJLink.c */
@@ -25,19 +25,33 @@
#include "dbLock.h"
#include "dbStaticLib.h"
#include "link.h"
+#include "epicsExport.h"
-#define IFDEBUG(n) if(parser->parse_debug)
+epicsShareDef int dbJLinkDebug = 0;
+epicsExportAddress(int, dbJLinkDebug);
+
+#define IFDEBUG(n) if (dbJLinkDebug >= (n))
typedef struct parseContext {
jlink *pjlink;
jlink *product;
short dbfType;
short jsonDepth;
- unsigned key_is_link:1;
- unsigned parse_debug:1;
- unsigned lset_debug:1;
} parseContext;
+epicsShareDef const char *jlif_result_name[2] = {
+ "jlif_stop",
+ "jlif_continue",
+};
+
+epicsShareDef const char *jlif_key_result_name[5] = {
+ "jlif_key_stop",
+ "jlif_key_continue",
+ "jlif_key_child_inlink",
+ "jlif_key_child_outlink",
+ "jlif_key_child_fwdlink"
+};
+
#define CALL_OR_STOP(routine) !(routine) ? jlif_stop : (routine)
static int dbjl_return(parseContext *parser, jlif_result result) {
@@ -45,8 +59,8 @@ static int dbjl_return(parseContext *parser, jlif_result result) {
IFDEBUG(10) {
printf("dbjl_return(%s@%p, %d)\t", pjlink ? pjlink->pif->name : "", pjlink, result);
- printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
- parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
+ printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
+ parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
}
if (result == jlif_stop && pjlink) {
@@ -59,6 +73,9 @@ static int dbjl_return(parseContext *parser, jlif_result result) {
pjlink->pif->free_jlink(pjlink);
}
+ IFDEBUG(10)
+ printf(" returning %d %s\n", result,
+ result == jlif_stop ? "*** STOP ***" : "Continue");
return result;
}
@@ -68,8 +85,8 @@ static int dbjl_value(parseContext *parser, jlif_result result) {
IFDEBUG(10) {
printf("dbjl_value(%s@%p, %d)\t", pjlink ? pjlink->pif->name : "", pjlink, result);
- printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
- parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
+ printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
+ parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
}
if (result == jlif_stop || pjlink->parseDepth > 0)
@@ -81,7 +98,6 @@ static int dbjl_value(parseContext *parser, jlif_result result) {
} else if (parent->pif->end_child) {
parent->pif->end_child(parent, pjlink);
}
- pjlink->debug = 0;
parser->pjlink = parent;
@@ -159,29 +175,46 @@ static int dbjl_start_map(void *ctx) {
if (!pjlink) {
IFDEBUG(10) {
printf("dbjl_start_map(NULL)\t");
- printf(" jsonDepth=%d, parseDepth=00, key_is_link=%d\n",
- parser->jsonDepth, parser->key_is_link);
+ printf(" jsonDepth=%d, parseDepth=00, dbfType=%d\n",
+ parser->jsonDepth, parser->dbfType);
}
assert(parser->jsonDepth == 0);
parser->jsonDepth++;
- parser->key_is_link = 1;
return jlif_continue; /* Opening '{' */
}
IFDEBUG(10) {
printf("dbjl_start_map(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink);
- printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
- parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
+ printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
+ parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
}
pjlink->parseDepth++;
parser->jsonDepth++;
result = CALL_OR_STOP(pjlink->pif->parse_start_map)(pjlink);
- if (result == jlif_key_child_link) {
- parser->key_is_link = 1;
+ switch (result) {
+ case jlif_key_child_inlink:
+ parser->dbfType = DBF_INLINK;
result = jlif_continue;
+ break;
+ case jlif_key_child_outlink:
+ parser->dbfType = DBF_OUTLINK;
+ result = jlif_continue;
+ break;
+ case jlif_key_child_fwdlink:
+ parser->dbfType = DBF_FWDLINK;
+ result = jlif_continue;
+ break;
+ case jlif_key_stop:
+ case jlif_key_continue:
+ break;
+ default:
+ errlogPrintf("dbJLinkInit: Bad return %d from '%s'::parse_start_map()\n",
+ result, pjlink->pif->name);
+ result = jlif_stop;
+ break;
}
IFDEBUG(10)
@@ -196,8 +229,9 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) {
char *link_name;
linkSup *linkSup;
jlif *pjlif;
+ jlink *child;
- if (!parser->key_is_link) {
+ if (parser->dbfType == 0) {
if (!pjlink) {
errlogPrintf("dbJLinkInit: Illegal second link key '%.*s'\n",
(int) len, key);
@@ -207,8 +241,8 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) {
IFDEBUG(10) {
printf("dbjl_map_key(%s@%p, \"%.*s\")\t",
pjlink->pif->name, pjlink, (int) len, key);
- printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
- parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
+ printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
+ parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
}
assert(pjlink->parseDepth > 0);
@@ -219,8 +253,8 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) {
IFDEBUG(10) {
printf("dbjl_map_key(NULL, \"%.*s\")\t", (int) len, key);
- printf(" jsonDepth=%d, parseDepth=00, key_is_link=%d\n",
- parser->jsonDepth, parser->key_is_link);
+ printf(" jsonDepth=%d, parseDepth=00, dbfType=%d\n",
+ parser->jsonDepth, parser->dbfType);
}
link_name = dbmfStrndup((const char *) key, len);
@@ -241,27 +275,35 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) {
return dbjl_return(parser, jlif_stop);
}
- dbmfFree(link_name);
-
- pjlink = pjlif->alloc_jlink(parser->dbfType);
- if (!pjlink) {
- errlogPrintf("dbJLinkInit: Out of memory\n");
+ child = pjlif->alloc_jlink(parser->dbfType);
+ if (!child) {
+ errlogPrintf("dbJLinkInit: Link type '%s' allocation failed. \n",
+ link_name);
+ dbmfFree(link_name);
return dbjl_return(parser, jlif_stop);
}
- pjlink->pif = pjlif;
- pjlink->parent = NULL;
- pjlink->parseDepth = 0;
- pjlink->debug = !!parser->lset_debug;
+
+ child->pif = pjlif;
+ child->parseDepth = 0;
+ child->debug = 0;
if (parser->pjlink) {
/* We're starting a child link, save its parent */
- pjlink->parent = parser->pjlink;
+ child->parent = pjlink;
+
+ if (pjlink->pif->start_child)
+ pjlink->pif->start_child(pjlink, child);
}
- parser->pjlink = pjlink;
- parser->key_is_link = 0;
+ else
+ child->parent = NULL;
+
+ parser->pjlink = child;
+ parser->dbfType = 0;
+
+ dbmfFree(link_name);
IFDEBUG(8)
- printf("dbjl_map_key: New %s@%p\n", pjlink ? pjlink->pif->name : "", pjlink);
+ printf("dbjl_map_key: New %s@%p\n", child ? child->pif->name : "", child);
return jlif_continue;
}
@@ -274,9 +316,9 @@ static int dbjl_end_map(void *ctx) {
IFDEBUG(10) {
printf("dbjl_end_map(%s@%p)\t",
pjlink ? pjlink->pif->name : "NULL", pjlink);
- printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
+ printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0,
- parser->key_is_link);
+ parser->dbfType);
}
parser->jsonDepth--;
@@ -298,8 +340,8 @@ static int dbjl_start_array(void *ctx) {
IFDEBUG(10) {
printf("dbjl_start_array(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink);
- printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
- parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
+ printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
+ parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
}
assert(pjlink);
@@ -316,8 +358,8 @@ static int dbjl_end_array(void *ctx) {
IFDEBUG(10) {
printf("dbjl_end_array(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink);
- printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
- parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
+ printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
+ parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
}
assert(pjlink);
@@ -335,7 +377,7 @@ static yajl_callbacks dbjl_callbacks = {
};
long dbJLinkParse(const char *json, size_t jlen, short dbfType,
- jlink **ppjlink, unsigned opts)
+ jlink **ppjlink)
{
parseContext context, *parser = &context;
yajl_alloc_funcs dbjl_allocs;
@@ -347,17 +389,14 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType,
parser->product = NULL;
parser->dbfType = dbfType;
parser->jsonDepth = 0;
- parser->key_is_link = 0;
- parser->parse_debug = !!(opts&LINK_DEBUG_JPARSE);
- parser->lset_debug = !!(opts&LINK_DEBUG_LSET);
IFDEBUG(10)
printf("dbJLinkInit(\"%.*s\", %d, %p)\n",
(int) jlen, json, dbfType, ppjlink);
IFDEBUG(10)
- printf("dbJLinkInit: jsonDepth=%d, key_is_link=%d\n",
- parser->jsonDepth, parser->key_is_link);
+ printf("dbJLinkInit: jsonDepth=%d, dbfType=%d\n",
+ parser->jsonDepth, parser->dbfType);
yajl_set_default_alloc_funcs(&dbjl_allocs);
yh = yajl_alloc(&dbjl_callbacks, &dbjl_allocs, parser);
@@ -365,8 +404,14 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType,
return S_db_noMemory;
ys = yajl_parse(yh, (const unsigned char *) json, jlen);
- if (ys == yajl_status_ok)
+ IFDEBUG(10)
+ printf("dbJLinkInit: yajl_parse() returned %d\n", ys);
+
+ if (ys == yajl_status_ok) {
ys = yajl_complete_parse(yh);
+ IFDEBUG(10)
+ printf("dbJLinkInit: yajl_complete_parse() returned %d\n", ys);
+ }
switch (ys) {
unsigned char *err;
@@ -378,6 +423,9 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType,
break;
case yajl_status_error:
+ IFDEBUG(10)
+ printf(" jsonDepth=%d, product=%p, pjlink=%p\n",
+ parser->jsonDepth, parser->product, parser->pjlink);
err = yajl_get_error(yh, 1, (const unsigned char *) json, jlen);
errlogPrintf("dbJLinkInit: %s\n", err);
yajl_free_error(yh, err);
@@ -389,18 +437,24 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType,
}
yajl_free(yh);
+
+ IFDEBUG(10)
+ printf("dbJLinkInit: returning status=0x%lx\n\n",
+ status);
+
return status;
}
long dbJLinkInit(struct link *plink)
{
- jlink *pjlink;
-
assert(plink);
- pjlink = plink->value.json.jlink;
- if (pjlink)
- plink->lset = pjlink->pif->get_lset(pjlink);
+ if (plink->type == JSON_LINK) {
+ jlink *pjlink = plink->value.json.jlink;
+
+ if (pjlink)
+ plink->lset = pjlink->pif->get_lset(pjlink);
+ }
dbLinkOpen(plink);
return 0;
diff --git a/modules/database/src/ioc/db/dbJLink.h b/modules/database/src/ioc/db/dbJLink.h
index 61b59670b..bd1a6c8a2 100644
--- a/modules/database/src/ioc/db/dbJLink.h
+++ b/modules/database/src/ioc/db/dbJLink.h
@@ -2,7 +2,7 @@
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
-* in file LICENSE that is included with this distribution.
+* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* dbJLink.h */
@@ -21,12 +21,16 @@ typedef enum {
jlif_continue = 1
} jlif_result;
+epicsShareExtern const char *jlif_result_name[2];
+
typedef enum {
jlif_key_stop = jlif_stop,
jlif_key_continue = jlif_continue,
- jlif_key_child_link
+ jlif_key_child_inlink, jlif_key_child_outlink, jlif_key_child_fwdlink
} jlif_key_result;
+epicsShareExtern const char *jlif_key_result_name[5];
+
struct link;
struct lset;
struct jlif;
@@ -35,7 +39,7 @@ typedef struct jlink {
struct jlif *pif; /* Link methods */
struct jlink *parent; /* NULL for top-level links */
int parseDepth; /* Used by parser, unused afterwards */
- unsigned debug:1; /* set by caller of jlif operations to request debug output to console */
+ unsigned debug:1; /* Set to request debug output to console */
/* Link types extend or embed this structure for private storage */
} jlink;
@@ -72,8 +76,9 @@ typedef struct jlif {
/* Optional, parser saw a string value */
jlif_key_result (*parse_start_map)(jlink *);
- /* Optional, parser saw an open-brace '{'. Return jlif_key_child_link
- * to expect a child link next (extra key/value pairs may follow).
+ /* Optional, parser saw an open-brace '{'. Return jlif_key_child_inlink,
+ * jlif_key_child_outlink, or jlif_key_child_fwdlink to expect a child
+ * link next (extra key/value pairs may follow)
*/
jlif_result (*parse_map_key)(jlink *, const char *key, size_t len);
@@ -90,7 +95,8 @@ typedef struct jlif {
void (*end_child)(jlink *parent, jlink *child);
/* Optional, called with pointer to the new child link after
- * parse_start_map() returned jlif_key_child_link */
+ * the child link has finished parsing successfully
+ */
struct lset* (*get_lset)(const jlink *);
/* Required, return lset for this link instance */
@@ -98,7 +104,7 @@ typedef struct jlif {
void (*report)(const jlink *, int level, int indent);
/* Optional, print status information about this link instance, then
* if (level > 0) print a link identifier (at indent+2) and call
- * dbJLinkReport(child, level-1, indent+4)
+ * dbJLinkReport(child, level-1, indent+4)
* for each child.
*/
@@ -107,13 +113,19 @@ typedef struct jlif {
* Stop immediately and return status if non-zero.
*/
+ void (*start_child)(jlink *parent, jlink *child);
+ /* Optional, called with pointer to the new child link after
+ * parse_start_map() returned a jlif_key_child_link value and
+ * the child link has been allocated (but not parsed yet)
+ */
+
/* Link types must NOT extend this table with their own routines,
* this space is reserved for extensions to the jlink interface.
*/
} jlif;
epicsShareFunc long dbJLinkParse(const char *json, size_t len, short dbfType,
- jlink **ppjlink, unsigned opts);
+ jlink **ppjlink);
epicsShareFunc long dbJLinkInit(struct link *plink);
epicsShareFunc void dbJLinkFree(jlink *);
@@ -130,4 +142,3 @@ epicsShareFunc long dbJLinkMapAll(char *recname, jlink_map_fn rtn, void *ctx);
#endif
#endif /* INC_dbJLink_H */
-
diff --git a/modules/database/src/ioc/db/dbLink.c b/modules/database/src/ioc/db/dbLink.c
index bcdbec473..7c37058b0 100644
--- a/modules/database/src/ioc/db/dbLink.c
+++ b/modules/database/src/ioc/db/dbLink.c
@@ -265,8 +265,19 @@ int dbIsLinkConnected(const struct link *plink)
{
lset *plset = plink->lset;
- if (!plset || !plset->isConnected)
+ if (!plset)
return FALSE;
+ if (!plset->isVolatile)
+ return TRUE;
+
+ if (!plset->isConnected) {
+ struct dbCommon *precord = plink->precord;
+
+ errlogPrintf("dbLink: Link type for '%s.%s' is volatile but has no"
+ " lset::isConnected() method\n",
+ precord->name, dbLinkFieldName(plink));
+ return FALSE;
+ }
return plset->isConnected(plink);
}
diff --git a/modules/database/src/ioc/db/dbLink.h b/modules/database/src/ioc/db/dbLink.h
index 16066e0d5..94e1f32a3 100644
--- a/modules/database/src/ioc/db/dbLink.h
+++ b/modules/database/src/ioc/db/dbLink.h
@@ -27,54 +27,337 @@ extern "C" {
struct dbLocker;
+/** @file dbLink.h
+ * @brief Link Support API
+ *
+ * Link support run-time API, all link types provide an lset which is used by
+ * the IOC database to control and operate the link. This file also declares the
+ * dbLink routines that IOC, record and device code can call to perform link
+ * operations.
+ */
+
+/** @brief callback routine for locked link operations
+ *
+ * Called by the lset::doLocked method to permit multiple link operations
+ * while the link instance is locked.
+ *
+ * @param plink the link
+ * @param priv context for the callback routine
+ */
typedef long (*dbLinkUserCallback)(struct link *plink, void *priv);
+/** @brief Link Support Entry Table
+ *
+ * This structure provides information about and methods for an individual link
+ * type. A pointer to this structure is included in every link's lset field, and
+ * is used to perform operations on the link. For JSON links the pointer is
+ * obtained by calling pjlink->pif->get_lset() at link initialization time,
+ * immediately before calling dbLinkOpen() to activate the link.
+ */
typedef struct lset {
/* Characteristics of the link type */
+
+ /** @brief link constancy
+ *
+ * 1 means this is a constant link type whose value doesn't change.
+ * The link's value will be obtained using one of the methods loadScalar,
+ * loadLS or loadArray.
+ */
const unsigned isConstant:1;
+
+ /** @brief link volatility
+ *
+ * 0 means the link is always connected.
+ */
const unsigned isVolatile:1;
- /* Activation */
+ /** @brief activate link
+ *
+ * Optional, called whenever a JSON link is initialized or added at runtime.
+ *
+ * @param plink the link
+ */
void (*openLink)(struct link *plink);
- /* Destructor */
+ /** @brief deactivate link
+ *
+ * Optional, called whenever a link address is changed at runtime, or the
+ * IOC is shutting down.
+ *
+ * @param locker
+ * @param plink the link
+ */
void (*removeLink)(struct dbLocker *locker, struct link *plink);
- /* Const init, data type hinting */
+ /* Constant link initialization and data type hinting */
+
+ /** @brief load constant scalar from link type
+ *
+ * Usually called during IOC initialization, constant link types must copy a
+ * scalar value of the indicated data type to the buffer provided and return
+ * 0. A non-constant link type can use this method call as an early hint
+ * that subsequent calls to dbGetLink() will request scalar data of the
+ * indicated type, although the type might change.
+ *
+ * @param plink the link
+ * @param dbrType data type code
+ * @param pbuffer where to put the value
+ * @returns 0 if a value was loaded, non-zero otherwise
+ */
long (*loadScalar)(struct link *plink, short dbrType, void *pbuffer);
+
+ /** @brief load constant long string from link type
+ *
+ * Usually called during IOC initialization, constant link types must copy a
+ * nil-terminated string up to size characters long to the buffer provided,
+ * and write the length of that string to the plen location. A non-constant
+ * link type can use this as an early hint that subsequent calls to
+ * dbGetLink() will request long string data, although this might change.
+ *
+ * @param plink the link
+ * @param pbuffer where to put the string
+ * @param size length of pbuffer in chars
+ * @param plen set to number of chars written
+ * @returns status value
+ */
long (*loadLS)(struct link *plink, char *pbuffer, epicsUInt32 size,
epicsUInt32 *plen);
+
+ /** @brief load constant array from link type
+ *
+ * Usually called during IOC initialization, constant link types must copy
+ * an array value of the indicated data type to the buffer provided, update
+ * the pnRequest location to indicate how many elements were loaded, and
+ * return 0. A non-constant link type can use this method call as an early
+ * hint that subsequent calls to dbGetLink() will request array data of the
+ * indicated type and max size, although the request might change.
+ *
+ * @param plink the link
+ * @param dbrType data type code
+ * @param pbuffer where to put the value
+ * @param pnRequest Max elements on entry, actual on exit
+ * @returns 0 if elements were loaded, non-zero otherwise
+ */
long (*loadArray)(struct link *plink, short dbrType, void *pbuffer,
long *pnRequest);
/* Metadata */
+
+ /** @brief return link connection status
+ *
+ * Return an indication whether this link is connected or not. This routine
+ * is polled by the calcout and some external record types. Not required for
+ * non-volatile link types, which are by definition always connected.
+ *
+ * @param plink the link
+ * @returns 1 if connected, 0 if disconnected
+ */
int (*isConnected)(const struct link *plink);
+
+ /** @brief get data type of link destination
+ *
+ * Called on both input and output links by long string support code to
+ * decide whether to use DBR_CHAR/DBR_UCHAR or DBR_STRING for a subsequent
+ * dbPutLink() or dbGetLink() call. Optional, but if not provided long
+ * strings cannot be transported over this link type, and no warning or
+ * error will appear to explain why. Not required for constant link types.
+ *
+ * @param plink the link
+ * @returns DBF_* type code, or -1 on error/disconnected link
+ */
int (*getDBFtype)(const struct link *plink);
- long (*getElements)(const struct link *plink, long *nelements);
/* Get data */
+
+ /** @brief get array size of an input link
+ *
+ * Called on input links by the compress record type for memory allocation
+ * purposes, before using the dbGetLink() routine to fetch the actual
+ * array data.
+ *
+ * @param plink the link
+ * @param pnElements where to put the answer
+ * @returns status value
+ */
+ long (*getElements)(const struct link *plink, long *pnElements);
+
+ /** @brief get value from an input link
+ *
+ * Called to fetch data from the link, which must be converted into the
+ * given data type and placed in the buffer indicated. The actual number of
+ * elements retrieved should be updated in the pnRequest location. If this
+ * method returns an error status value, the link's record will be placed
+ * into an Invalid severity / Link Alarm state by the dbGetLink() routine
+ * that calls this method.
+ *
+ * @param plink the link
+ * @param dbrType data type code
+ * @param pbuffer where to put the value
+ * @param pnRequest max elements on entry, actual on exit
+ * @returns status value
+ */
long (*getValue)(struct link *plink, short dbrType, void *pbuffer,
long *pnRequest);
+
+ /** @brief get the control range for an output link
+ *
+ * Called to fetch the control range for the link target, as a pair of
+ * double values for the lowest and highest values that the target will
+ * accept. This method is not used at all by the IOC or built-in record
+ * types, although external record types may require it.
+ *
+ * @param plink the link
+ * @param lo lowest accepted value
+ * @param hi highest accepted value
+ * @returns status value
+ */
long (*getControlLimits)(const struct link *plink, double *lo, double *hi);
+
+ /** @brief get the display range from an input link
+ *
+ * Called to fetch the display range for an input link target, as a pair of
+ * double values for the lowest and highest values that the PV expects to
+ * return. This method is used by several built-in record types to obtain
+ * the display range for their generic input links.
+ *
+ * @param plink the link
+ * @param lo lowest accepted value
+ * @param hi highest accepted value
+ * @returns status value
+ */
long (*getGraphicLimits)(const struct link *plink, double *lo, double *hi);
+
+ /** @brief get the alarm limits from an input link
+ *
+ * Called to fetch the alarm limits for an input link target, as four
+ * double values for the warning and alarm levels that the PV checks its
+ * value against. This method is used by several built-in record types to
+ * obtain the alarm limits for their generic input links.
+ *
+ * @param plink the link
+ * @param lolo low alarm value
+ * @param lo low warning value
+ * @param hi high warning value
+ * @param hihi high alarm value
+ * @returns status value
+ */
long (*getAlarmLimits)(const struct link *plink, double *lolo, double *lo,
double *hi, double *hihi);
+
+ /** @brief get the precision from an input link
+ *
+ * Called to fetch the precision for an input link target. This method is
+ * used by several built-in record types to obtain the precision for their
+ * generic input links.
+ *
+ * @param plink the link
+ * @param precision where to put the answer
+ * @returns status value
+ */
long (*getPrecision)(const struct link *plink, short *precision);
+
+ /** @brief get the units string from an input link
+ *
+ * Called to fetch the units string for an input link target. This method is
+ * used by several built-in record types to obtain the units string for
+ * their generic input links.
+ *
+ * @param plink the link
+ * @param units where to put the answer
+ * @param unitsSize buffer size for the answer
+ * @returns status value
+ */
long (*getUnits)(const struct link *plink, char *units, int unitsSize);
+
+ /** @brief get the alarm condition from an input link
+ *
+ * Called to fetch the alarm status and severity for an input link target.
+ * Either status or severity pointers may be NULL when that value is not
+ * needed by the calling code. This method is used by several built-in
+ * record types to obtain the alarm condition for their generic input links.
+ *
+ * @param plink the link
+ * @param status where to put the alarm status (or NULL)
+ * @param severity where to put the severity (or NULL)
+ * @returns status value
+ */
long (*getAlarm)(const struct link *plink, epicsEnum16 *status,
epicsEnum16 *severity);
+
+ /** @brief get the time-stamp from an input link
+ *
+ * Called to fetch the time-stamp for an input link target. This method is
+ * used by many built-in device supports to obtain the precision for their
+ * generic input links.
+ *
+ * @param plink the link
+ * @param pstamp where to put the answer
+ * @returns status value
+ */
long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp);
/* Put data */
+
+ /** @brief put a value to an output link
+ *
+ * Called to send nRequest elements of type dbrType found at pbuffer to an
+ * output link target.
+ *
+ * @param plink the link
+ * @param dbrType data type code
+ * @param pbuffer where to put the value
+ * @param nRequest number of elements to send
+ * @returns status value
+ */
long (*putValue)(struct link *plink, short dbrType,
const void *pbuffer, long nRequest);
+
+ /** @brief put a value to an output link with asynchronous completion
+ *
+ * Called to send nRequest elements of type dbrType found at pbuffer to an
+ * output link target. If the return status is zero, the link type will
+ * later indicate the put has completed by calling dbLinkAsyncComplete()
+ * from a background thread, which will be used to continue the record
+ * process operation from where it left off.
+ *
+ * @param plink the link
+ * @param dbrType data type code
+ * @param pbuffer where to put the value
+ * @param nRequest number of elements to send
+ * @returns status value
+ */
long (*putAsync)(struct link *plink, short dbrType,
const void *pbuffer, long nRequest);
/* Process */
+
+ /** @brief trigger processing of a forward link
+ *
+ * Called to trigger processing of the record pointed to by a forward link.
+ * This routine is optional, but if not provided no warning message will be
+ * shown when called by dbScanFwdLink(). JSON link types that do not support
+ * this operation should return NULL from their jlif::alloc_jlink() method
+ * if it gets called with a dbfType of DBF_FWDLINK.
+ *
+ * @param plink the link
+ */
void (*scanForward)(struct link *plink);
/* Atomicity */
+
+ /** @brief execute a callback routine with link locked
+ *
+ * Called on an input link when multiple link attributes need to be fetched
+ * in an atomic fashion. The link type must call the callback routine and
+ * prevent any background I/O from updating any cached link data until that
+ * routine returns. This method is used by most input device support to
+ * fetch the timestamp along with the value when the record's TSE field is
+ * set to epicsTimeEventDeviceTime.
+ *
+ * @param plink the link
+ * @param rtn routine to execute
+ * @returns status value
+ */
long (*doLocked)(struct link *plink, dbLinkUserCallback rtn, void *priv);
} lset;
@@ -99,7 +382,7 @@ epicsShareFunc long dbLoadLink(struct link *plink, short dbrType,
epicsShareFunc long dbLoadLinkArray(struct link *, short dbrType, void *pbuffer,
long *pnRequest);
-epicsShareFunc long dbGetNelements(const struct link *plink, long *nelements);
+epicsShareFunc long dbGetNelements(const struct link *plink, long *pnElements);
epicsShareFunc int dbIsLinkConnected(const struct link *plink); /* 0 or 1 */
epicsShareFunc int dbGetLinkDBFtype(const struct link *plink);
epicsShareFunc long dbTryGetLink(struct link *, short dbrType, void *pbuffer,
diff --git a/modules/database/src/ioc/db/dbLock.h b/modules/database/src/ioc/db/dbLock.h
index 1d6388ed3..e15ddd0e9 100644
--- a/modules/database/src/ioc/db/dbLock.h
+++ b/modules/database/src/ioc/db/dbLock.h
@@ -13,6 +13,8 @@
#ifndef INCdbLockh
#define INCdbLockh
+#include
+
#include "ellLib.h"
#include "shareLib.h"
diff --git a/modules/database/src/ioc/db/dbScan.c b/modules/database/src/ioc/db/dbScan.c
index e5c78fea1..9224ed504 100644
--- a/modules/database/src/ioc/db/dbScan.c
+++ b/modules/database/src/ioc/db/dbScan.c
@@ -24,6 +24,7 @@
#include "cantProceed.h"
#include "dbDefs.h"
#include "ellLib.h"
+#include "epicsAtomic.h"
#include "epicsEvent.h"
#include "epicsMutex.h"
#include "epicsPrint.h"
@@ -63,6 +64,7 @@ static volatile enum ctl scanCtl;
static int onceQueueSize = 1000;
static epicsEventId onceSem;
static epicsRingBytesId onceQ;
+static int onceQOverruns = 0;
static epicsThreadId onceTaskId;
static void *exitOnce;
@@ -676,6 +678,7 @@ int scanOnceCallback(struct dbCommon *precord, once_complete cb, void *usr)
if (!pushOK) {
if (newOverflow) errlogPrintf("scanOnce: Ring buffer overflow\n");
newOverflow = FALSE;
+ epicsAtomicIncrIntT(&onceQOverruns);
} else {
newOverflow = TRUE;
}
@@ -722,6 +725,40 @@ int scanOnceSetQueueSize(int size)
return 0;
}
+int scanOnceQueueStatus(const int reset, scanOnceQueueStats *result)
+{
+ int ret;
+ if (!onceQ) return -1;
+ if (result) {
+ result->size = epicsRingBytesSize(onceQ) / sizeof(onceEntry);
+ result->numUsed = epicsRingBytesUsedBytes(onceQ) / sizeof(onceEntry);
+ result->maxUsed = epicsRingBytesHighWaterMark(onceQ) / sizeof(onceEntry);
+ result->numOverflow = epicsAtomicGetIntT(&onceQOverruns);
+ ret = 0;
+ } else {
+ ret = -2;
+ }
+ if (reset) {
+ epicsRingBytesResetHighWaterMark(onceQ);
+ }
+ return ret;
+}
+
+void scanOnceQueueShow(const int reset)
+{
+ scanOnceQueueStats stats;
+ if (scanOnceQueueStatus(reset, &stats) == -1) {
+ fprintf(stderr, "scanOnce system not initialized, yet. Please run "
+ "iocInit before using this command.\n");
+ } else {
+ double qusage = 100.0 * stats.numUsed / stats.size;
+ printf("PRIORITY HIGH-WATER MARK ITEMS IN Q Q SIZE %% USED Q OVERFLOWS\n");
+ printf("%8s %15d %10d %6d %6.1f %11d\n", "scanOnce", stats.maxUsed,
+ stats.numUsed, stats.size, qusage,
+ epicsAtomicGetIntT(&onceQOverruns));
+ }
+}
+
static void initOnce(void)
{
if ((onceQ = epicsRingBytesLockedCreate(sizeof(onceEntry)*onceQueueSize)) == NULL) {
diff --git a/modules/database/src/ioc/db/dbScan.h b/modules/database/src/ioc/db/dbScan.h
index 028d09ec8..622103c3b 100644
--- a/modules/database/src/ioc/db/dbScan.h
+++ b/modules/database/src/ioc/db/dbScan.h
@@ -19,6 +19,7 @@
#include "menuScan.h"
#include "shareLib.h"
#include "compilerDependencies.h"
+#include "devSup.h"
#ifdef __cplusplus
extern "C" {
@@ -33,9 +34,7 @@ extern "C" {
#define MIN_PHASE SHRT_MIN
/*definitions for I/O Interrupt Scanning */
-struct ioscan_head;
-
-typedef struct ioscan_head *IOSCANPVT;
+/* IOSCANPVT now defined in devSup.h */
typedef struct event_list *EVENTPVT;
struct dbCommon;
@@ -43,6 +42,13 @@ struct dbCommon;
typedef void (*io_scan_complete)(void *usr, IOSCANPVT, int prio);
typedef void (*once_complete)(void *usr, struct dbCommon*);
+typedef struct scanOnceQueueStats {
+ int size;
+ int numUsed;
+ int maxUsed;
+ int numOverflow;
+} scanOnceQueueStats;
+
epicsShareFunc long scanInit(void);
epicsShareFunc void scanRun(void);
epicsShareFunc void scanPause(void);
@@ -58,6 +64,8 @@ epicsShareFunc double scanPeriod(int scan);
epicsShareFunc int scanOnce(struct dbCommon *);
epicsShareFunc int scanOnceCallback(struct dbCommon *, once_complete cb, void *usr);
epicsShareFunc int scanOnceSetQueueSize(int size);
+epicsShareFunc int scanOnceQueueStatus(const int reset, scanOnceQueueStats *result);
+epicsShareFunc void scanOnceQueueShow(const int reset);
/*print periodic lists*/
epicsShareFunc int scanppl(double rate);
diff --git a/modules/database/src/ioc/db/dbServer.c b/modules/database/src/ioc/db/dbServer.c
index 5374f02ad..ec89f4fec 100644
--- a/modules/database/src/ioc/db/dbServer.c
+++ b/modules/database/src/ioc/db/dbServer.c
@@ -12,6 +12,7 @@
#include
#include
+#include "dbDefs.h"
#include "ellLib.h"
#include "envDefs.h"
#include "epicsStdio.h"
@@ -29,6 +30,7 @@ static char *stateNames[] = {
int dbRegisterServer(dbServer *psrv)
{
const char * ignore = envGetConfigParamPtr(&EPICS_IOC_IGNORE_SERVERS);
+ ELLNODE *node;
if (!psrv || !psrv->name || state != registering)
return -1;
@@ -55,10 +57,14 @@ int dbRegisterServer(dbServer *psrv)
}
}
- if (ellNext(&psrv->node) || ellLast(&serverList) == &psrv->node) {
- fprintf(stderr, "dbRegisterServer: '%s' registered twice?\n",
- psrv->name);
- return -1;
+ for(node=ellFirst(&serverList); node; node = ellNext(node)) {
+ dbServer *cur = CONTAINER(node, dbServer, node);
+ if(cur==psrv) {
+ return 0; /* already registered identically. */
+ } else if(strcmp(cur->name, psrv->name)==0) {
+ fprintf(stderr, "dbRegisterServer: Can't redefine '%s'.\n", cur->name);
+ return -1;
+ }
}
ellAdd(&serverList, &psrv->node);
diff --git a/modules/database/src/ioc/db/dbState.h b/modules/database/src/ioc/db/dbState.h
index abd23259e..c7cd81c52 100644
--- a/modules/database/src/ioc/db/dbState.h
+++ b/modules/database/src/ioc/db/dbState.h
@@ -15,6 +15,10 @@
#include "shareLib.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/** @file dbState.h
* @brief Generic IOC state facility
*
@@ -89,4 +93,9 @@ epicsShareFunc void dbStateShow(dbStateId id, unsigned int level);
*/
epicsShareFunc void dbStateShowAll(unsigned int level);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif // INCdbStateH
diff --git a/modules/database/src/ioc/db/menuScan.dbd b/modules/database/src/ioc/db/menuScan.dbd.pod
similarity index 50%
rename from modules/database/src/ioc/db/menuScan.dbd
rename to modules/database/src/ioc/db/menuScan.dbd.pod
index 17fedd59b..e716e79da 100644
--- a/modules/database/src/ioc/db/menuScan.dbd
+++ b/modules/database/src/ioc/db/menuScan.dbd.pod
@@ -1,11 +1,48 @@
#*************************************************************************
-# Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
+# Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne
# National Laboratory.
# Copyright (c) 2002 The Regents of the University of California, as
# Operator of Los Alamos National Laboratory.
# EPICS BASE is distributed subject to a Software License Agreement found
# in file LICENSE that is included with this distribution.
#*************************************************************************
+
+=head1 Menu menuScan
+
+This menu is used for the C field of all record types.
+
+The set of periodic scan rates may be modified for an individual IOC by
+copying the F file from Base into the IOC's source
+directory and changing it to contain the desired scan rates.
+
+The scan periods are extracted from the choice strings at runtime, which
+must be expressed as a number with any of the following units appended:
+
+=over 4
+
+ second
+ seconds
+ minute
+ minutes
+ hour
+ hours
+ Hertz
+ Hz
+
+=back
+
+At IOC start-up a separate scan thread will be created for each period,
+with thread priority increasing further down the list, so faster periods
+should appear after slower ones.
+
+Scan rates that cannot be achieved will generate a warning message from
+the C command.
+
+
+=menu menuScan
+
+=cut
+
menu(menuScan) {
choice(menuScanPassive,"Passive")
choice(menuScanEvent,"Event")
diff --git a/modules/database/src/ioc/db/recGbl.c b/modules/database/src/ioc/db/recGbl.c
index 12c0a0e0f..5ff4730fe 100644
--- a/modules/database/src/ioc/db/recGbl.c
+++ b/modules/database/src/ioc/db/recGbl.c
@@ -19,6 +19,7 @@
#include "alarm.h"
#include "dbDefs.h"
+#include "alarm.h"
#include "epicsMath.h"
#include "epicsPrint.h"
#include "epicsStdlib.h"
@@ -180,6 +181,9 @@ unsigned short recGblResetAlarms(void *precord)
epicsEnum16 val_mask = 0;
epicsEnum16 stat_mask = 0;
+ if (new_sevr > INVALID_ALARM)
+ new_sevr = INVALID_ALARM;
+
pdbc->stat = new_stat;
pdbc->sevr = new_sevr;
pdbc->nsta = 0;
diff --git a/modules/database/src/ioc/dbStatic/dbStaticLib.c b/modules/database/src/ioc/dbStatic/dbStaticLib.c
index 1a3d9883f..7f5f1f885 100644
--- a/modules/database/src/ioc/dbStatic/dbStaticLib.c
+++ b/modules/database/src/ioc/dbStatic/dbStaticLib.c
@@ -79,16 +79,6 @@ epicsShareDef maplinkType pamaplinkType[LINK_NTYPES] = {
{"VXI_IO",VXI_IO}
};
-static int mapDBFtoDCT[DBF_NOACCESS+1] = {
- DCT_STRING,
- DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,
- DCT_REAL,DCT_REAL,
- DCT_INTEGER,
- DCT_MENU,
- DCT_MENUFORM,
- DCT_INLINK,DCT_OUTLINK,DCT_FWDLINK,
- DCT_NOACCESS};
-
/*forward references for private routines*/
static void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...)
EPICS_PRINTF_STYLE(2,3);
@@ -166,38 +156,6 @@ void dbFreePath(DBBASE *pdbbase)
}
-static void entryErrMessage(DBENTRY *pdbentry,long status,char *mess)
-{
- char message[200];
- char *pmessage=&message[0];
- dbRecordNode *precnode = pdbentry->precnode;
- dbFldDes *pflddes = pdbentry->pflddes;
- char *pname = NULL;
-
- *pmessage=0;
- if(pdbentry->precordType) pname = pdbentry->precordType->name;
- if(pname) {
- strcat(pmessage,"RecordType:");
- strcat(pmessage,pname);
- }
- if(precnode){
- if (dbIsAlias(pdbentry))
- strcat(pmessage," Record Alias:");
- else
- strcat(pmessage," Record:");
- strcat(pmessage,(char *)precnode->precord);
- }
- if(pflddes) {
- char *pstr=pflddes->name;
-
- strcat(pmessage," Field:");
- strcat(pmessage,pstr);
- }
- strcat(pmessage,"\n");
- strcat(pmessage,mess);
- errMessage(status,pmessage);
-}
-
static void zeroDbentry(DBENTRY *pdbentry)
{
/*NOTE that pdbbase and message MUST NOT be set to NULL*/
@@ -1398,19 +1356,6 @@ long dbNextField(DBENTRY *pdbentry,int dctonly)
}
}
-int dbGetFieldType(DBENTRY *pdbentry)
-{
- dbFldDes *pflddes = pdbentry->pflddes;
- long status;
-
- if(!pflddes){
- status = S_dbLib_flddesNotFound;
- entryErrMessage(pdbentry,status,"dbGetFieldType");
- return(status);
- }
- return(mapDBFtoDCT[pflddes->field_type]);
-}
-
int dbGetNFields(DBENTRY *pdbentry,int dctonly)
{
dbRecordType *precordType = pdbentry->precordType;
@@ -2241,7 +2186,7 @@ long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec)
if(!plink->text)
continue;
- if(dbParseLink(plink->text, pflddes->field_type, &link_info, 0)!=0) {
+ if(dbParseLink(plink->text, pflddes->field_type, &link_info)!=0) {
/* This was already parsed once when ->text was set.
* Any syntax error messages were printed at that time.
*/
@@ -2270,7 +2215,7 @@ void dbFreeLinkInfo(dbLinkInfo *pinfo)
pinfo->target = NULL;
}
-long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts)
+long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo)
{
char *pstr;
size_t len;
@@ -2306,7 +2251,7 @@ long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts)
/* Check for braces => JSON */
if (*str == '{' && str[len-1] == '}') {
- if (dbJLinkParse(str, len, ftype, &pinfo->jlink, opts))
+ if (dbJLinkParse(str, len, ftype, &pinfo->jlink))
goto fail;
pinfo->ltype = JSON_LINK;
@@ -2353,9 +2298,13 @@ long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts)
else if (strcmp(pinfo->hwid, "VS")==0) pinfo->ltype = VXI_IO;
else goto fail;
- if (parm && pinfo->ltype != RF_IO) {
- /* move parm string to beginning of buffer */
- memmove(pinfo->target, parm, len + 1);
+ if (pinfo->ltype != RF_IO) {
+ if (!parm) {
+ pinfo->target[0] = '\0';
+ } else {
+ /* move parm string to beginning of buffer */
+ memmove(pinfo->target, parm, len + 1);
+ }
} else if (!parm && pinfo->ltype == RF_IO) {
/* RF_IO, the string isn't needed at all */
free(pinfo->target);
@@ -2641,21 +2590,8 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring)
case DBF_FWDLINK: {
dbLinkInfo link_info;
DBLINK *plink = (DBLINK *)pfield;
- DBENTRY infoentry;
- unsigned opts = 0;
- if(pdbentry->precnode && ellCount(&pdbentry->precnode->infoList)) {
- dbCopyEntryContents(pdbentry, &infoentry);
-
- if(dbFindInfo(&infoentry, "base:lsetDebug")==0 && epicsStrCaseCmp(dbGetInfoString(&infoentry), "YES")==0)
- opts |= LINK_DEBUG_LSET;
- if(dbFindInfo(&infoentry, "base:jlinkDebug")==0 && epicsStrCaseCmp(dbGetInfoString(&infoentry), "YES")==0)
- opts |= LINK_DEBUG_JPARSE;
-
- dbFinishEntry(&infoentry);
- }
-
- status = dbParseLink(pstring, pflddes->field_type, &link_info, opts);
+ status = dbParseLink(pstring, pflddes->field_type, &link_info);
if (status) break;
if (plink->type==CONSTANT && plink->value.constantStr==NULL) {
@@ -3225,48 +3161,21 @@ int dbGetNLinks(DBENTRY *pdbentry)
return((int)precordType->no_links);
}
-long dbGetLinkField(DBENTRY *pdbentry,int index)
+long dbGetLinkField(DBENTRY *pdbentry, int index)
{
- dbRecordType *precordType = pdbentry->precordType;
- dbFldDes *pflddes;
+ dbRecordType *precordType = pdbentry->precordType;
+ dbFldDes *pflddes;
+
+ if (!precordType)
+ return S_dbLib_recordTypeNotFound;
+
+ if (index < 0 || index >= precordType->no_links)
+ return S_dbLib_badLink;
- if(!precordType) return(S_dbLib_recordTypeNotFound);
- if(index<0 || index>=precordType->no_links) return(S_dbLib_badLink);
pdbentry->indfield = precordType->link_ind[index];
pdbentry->pflddes = pflddes = precordType->papFldDes[pdbentry->indfield];
dbGetFieldAddress(pdbentry);
- return(0);
-}
-
-int dbGetLinkType(DBENTRY *pdbentry)
-{
- dbFldDes *pflddes;
- DBLINK *plink;
- int field_type;
-
- dbGetFieldAddress(pdbentry);
- pflddes = pdbentry->pflddes;
- if(!pflddes) return(-1);
- plink = (DBLINK *)pdbentry->pfield;
- if(!plink) return(-1);
- field_type = pflddes->field_type;
- switch (field_type) {
- case DBF_INLINK:
- case DBF_OUTLINK:
- case DBF_FWDLINK:
- switch(plink->type) {
- case CONSTANT:
- return(DCT_LINK_CONSTANT);
- case PV_LINK:
- case PN_LINK:
- case DB_LINK:
- case CA_LINK:
- return(DCT_LINK_PV);
- default:
- return(DCT_LINK_FORM);
- }
- }
- return(-1);
+ return 0;
}
void dbDumpPath(DBBASE *pdbbase)
@@ -3565,68 +3474,110 @@ void dbDumpBreaktable(DBBASE *pdbbase,const char *name)
return;
}
-static char *bus[VXI_IO+1] = {"","","VME","CAMAC","AB",
- "GPIB","BITBUS","","","","","","INST","BBGPIB","VXI"};
-void dbReportDeviceConfig(dbBase *pdbbase,FILE *report)
+static char *bus[LINK_NTYPES] = {
+ "", /* CONSTANT */
+ NULL, /* PV_LINK */
+ "VME",
+ "CAMAC",
+ "AB",
+ "GPIB",
+ "BITBUS",
+ NULL, /* MACRO_LINK */
+ NULL, /* JSON_LINK */
+ NULL, /* PN_LINK */
+ NULL, /* DB_LINK */
+ NULL, /* CA_LINK */
+ "INST",
+ "BBGPIB",
+ "VXI"
+};
+void dbReportDeviceConfig(dbBase *pdbbase, FILE *report)
{
- DBENTRY dbentry;
- DBENTRY *pdbentry=&dbentry;
- long status;
- char linkValue[messagesize];
- char dtypValue[50];
- char cvtValue[40];
- int ilink,nlinks;
- struct link *plink;
- int linkType;
- FILE *stream = (report==0) ? stdout : report;
+ DBENTRY dbentry, *pdbentry = &dbentry;
+ long status;
+ FILE *stream = report ? report : stdout;
- if(!pdbbase) {
- fprintf(stderr,"pdbbase not specified\n");
- return;
+ if (!pdbbase) {
+ fprintf(stderr, "dbReportDeviceConfig: pdbbase not specified\n");
+ return;
}
+
dbInitEntry(pdbbase,pdbentry);
status = dbFirstRecordType(pdbentry);
- while(!status) {
- status = dbFirstRecord(pdbentry);
- while(!status) {
- nlinks = dbGetNLinks(pdbentry);
- for(ilink=0; ilinkpfield;
- linkType = plink->type;
- if(bus[linkType][0]==0) continue;
- strncpy(linkValue, dbGetString(pdbentry), NELEMENTS(linkValue)-1);
- linkValue[NELEMENTS(linkValue)-1] = '\0';
- status = dbFindField(pdbentry,"DTYP");
- if(status) break;
- strcpy(dtypValue,dbGetString(pdbentry));
- status = dbFindField(pdbentry,"LINR");
- if(status) {
- cvtValue[0] = 0;
- } else {
- if(strcmp(dbGetString(pdbentry),"LINEAR")!=0) {
- cvtValue[0] = 0;
- } else {
- strcpy(cvtValue,"cvt(");
- status = dbFindField(pdbentry,"EGUL");
- if(!status) strcat(cvtValue,dbGetString(pdbentry));
- status = dbFindField(pdbentry,"EGUF");
- if(!status) {
- strcat(cvtValue,",");
- strcat(cvtValue,dbGetString(pdbentry));
- }
- strcat(cvtValue,")");
- }
- }
- fprintf(stream,"%-8s %-20s %-20s %-20s %-s\n",
- bus[linkType],linkValue,dtypValue,
- dbGetRecordName(pdbentry),cvtValue);
- break;
- }
- status = dbNextRecord(pdbentry);
- }
- status = dbNextRecordType(pdbentry);
+ while (!status) {
+ const int nlinks = dbGetNLinks(pdbentry);
+
+ status = dbFirstRecord(pdbentry);
+ while (!status) {
+ int ilink;
+
+ for (ilink=0; ilinkpfield;
+ linkType = plink->type;
+ if (plink->text) { /* Not yet parsed */
+ dbLinkInfo linfo;
+
+ if (dbParseLink(plink->text, pdbentry->pflddes->field_type, &linfo))
+ continue;
+
+ linkType = linfo.ltype;
+ if (linkType && bus[linkType])
+ strncpy(linkValue, plink->text, messagesize-1);
+
+ dbFreeLinkInfo(&linfo);
+ }
+ else {
+ strncpy(linkValue, dbGetString(pdbentry), messagesize-1);
+ }
+
+ if (!linkType || !bus[linkType])
+ continue;
+ linkValue[messagesize-1] = '\0';
+
+ status = dbFindField(pdbentry, "DTYP");
+ if (status)
+ break; /* Next record type */
+
+ strcpy(dtypValue, dbGetString(pdbentry));
+ status = dbFindField(pdbentry, "LINR");
+ if (status) {
+ cvtValue[0] = 0;
+ }
+ else {
+ if (strcmp(dbGetString(pdbentry), "LINEAR") != 0) {
+ cvtValue[0] = 0;
+ }
+ else {
+ strcpy(cvtValue,"cvt(");
+ status = dbFindField(pdbentry, "EGUL");
+ if (!status)
+ strcat(cvtValue, dbGetString(pdbentry));
+ status = dbFindField(pdbentry, "EGUF");
+ if (!status) {
+ strcat(cvtValue, ",");
+ strcat(cvtValue, dbGetString(pdbentry));
+ }
+ strcat(cvtValue, ")");
+ }
+ }
+ fprintf(stream,"%-8s %-20s %-20s %-20s %-s\n",
+ bus[linkType], linkValue, dtypValue,
+ dbGetRecordName(pdbentry), cvtValue);
+ break;
+ }
+ status = dbNextRecord(pdbentry);
+ }
+ status = dbNextRecordType(pdbentry);
}
dbFinishEntry(pdbentry);
finishOutstream(stream);
diff --git a/modules/database/src/ioc/dbStatic/dbStaticLib.h b/modules/database/src/ioc/dbStatic/dbStaticLib.h
index 4082600be..e0a2ddc6b 100644
--- a/modules/database/src/ioc/dbStatic/dbStaticLib.h
+++ b/modules/database/src/ioc/dbStatic/dbStaticLib.h
@@ -28,25 +28,9 @@
extern "C" {
#endif
-/*Field types as seen by static database access clients*/
-#define DCT_STRING 0
-#define DCT_INTEGER 1
-#define DCT_REAL 2
-#define DCT_MENU 3
-#define DCT_MENUFORM 4
-#define DCT_INLINK 5
-#define DCT_OUTLINK 6
-#define DCT_FWDLINK 7
-#define DCT_NOACCESS 8
-
-/*Link types as seen by static database access clients*/
-#define DCT_LINK_CONSTANT 0
-#define DCT_LINK_FORM 1
-#define DCT_LINK_PV 2
-
typedef dbBase DBBASE;
-typedef struct{
+typedef struct dbEntry {
DBBASE *pdbbase;
dbRecordType *precordType;
dbFldDes *pflddes;
@@ -57,9 +41,6 @@ typedef struct{
short indfield;
} DBENTRY;
-struct dbAddr;
-struct dbCommon;
-
/* Static database access routines*/
epicsShareFunc DBBASE * dbAllocBase(void);
epicsShareFunc void dbFreeBase(DBBASE *pdbbase);
@@ -68,18 +49,6 @@ epicsShareFunc void dbFreeEntry(DBENTRY *pdbentry);
epicsShareFunc void dbInitEntry(DBBASE *pdbbase,
DBENTRY *pdbentry);
-/** Initialize DBENTRY from a valid dbAddr*.
- * Constant time equivalent of dbInitEntry() then dbFindRecord(),
- * and finally dbFollowAlias()
- */
-epicsShareFunc void dbInitEntryFromAddr(struct dbAddr *paddr, DBENTRY *pdbentry);
-
-/** Initialize DBENTRY from a valid record (dbCommon*).
- * Constant time equivalent of dbInitEntry() then dbFindRecord(),
- * and finally dbFollowAlias() when no field is specified.
- */
-epicsShareFunc void dbInitEntryFromRecord(struct dbCommon *prec, DBENTRY *pdbentry);
-
epicsShareFunc void dbFinishEntry(DBENTRY *pdbentry);
epicsShareFunc DBENTRY * dbCopyEntry(DBENTRY *pdbentry);
epicsShareFunc void dbCopyEntryContents(DBENTRY *pfrom,
@@ -139,7 +108,6 @@ epicsShareFunc long dbGetAttributePart(DBENTRY *pdbentry,
epicsShareFunc long dbFirstField(DBENTRY *pdbentry, int dctonly);
epicsShareFunc long dbNextField(DBENTRY *pdbentry, int dctonly);
-epicsShareFunc int dbGetFieldType(DBENTRY *pdbentry);
epicsShareFunc int dbGetNFields(DBENTRY *pdbentry, int dctonly);
epicsShareFunc char * dbGetFieldName(DBENTRY *pdbentry);
epicsShareFunc char * dbGetDefault(DBENTRY *pdbentry);
@@ -231,7 +199,6 @@ epicsShareFunc linkSup * dbFindLinkSup(dbBase *pdbbase,
epicsShareFunc int dbGetNLinks(DBENTRY *pdbentry);
epicsShareFunc long dbGetLinkField(DBENTRY *pdbentry, int index);
-epicsShareFunc int dbGetLinkType(DBENTRY *pdbentry);
/* Dump routines */
epicsShareFunc void dbDumpPath(DBBASE *pdbbase);
diff --git a/modules/database/src/ioc/dbStatic/dbStaticPvt.h b/modules/database/src/ioc/dbStatic/dbStaticPvt.h
index 58d32c28c..85fc02217 100644
--- a/modules/database/src/ioc/dbStatic/dbStaticPvt.h
+++ b/modules/database/src/ioc/dbStatic/dbStaticPvt.h
@@ -5,7 +5,7 @@
* Operator of Los Alamos National Laboratory.
* EPICS BASE Versions 3.13.7
* and higher are distributed subject to a Software License Agreement found
-* in file LICENSE that is included with this distribution.
+* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* dbStaticPvt.h */
/*
@@ -59,13 +59,10 @@ typedef struct dbLinkInfo {
long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec);
-#define LINK_DEBUG_LSET 1
-#define LINK_DEBUG_JPARSE 2
-
/* Parse link string. no record locks needed.
* on success caller must free pinfo->target
*/
-epicsShareFunc long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts);
+epicsShareFunc long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo);
/* Check if link type allow the parsed link value pinfo
* to be assigned to the given link.
* Record containing plink must be locked.
diff --git a/modules/database/src/ioc/dbStatic/devSup.h b/modules/database/src/ioc/dbStatic/devSup.h
index bd900cae4..73dba198c 100644
--- a/modules/database/src/ioc/dbStatic/devSup.h
+++ b/modules/database/src/ioc/dbStatic/devSup.h
@@ -6,7 +6,11 @@
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
-/* devSup.h Device Support */
+/** @file devSup.h
+ *
+ * @brief Device support routines
+ */
+
/*
* Author: Marty Kraimer
* Date: 6-1-90
@@ -21,6 +25,111 @@
/* structures defined elsewhere */
struct dbCommon;
struct devSup;
+typedef struct ioscan_head *IOSCANPVT;
+struct link; /* aka DBLINK */
+
+/** Type safe version of 'struct dset'
+ *
+ * Recommended usage:
+ *
+ * In Makefile:
+ @code
+ USR_CFLAGS += -DUSE_TYPED_RSET -DUSE_TYPED_DSET
+ @endcode
+ *
+ * In C source file:
+ @code
+ #include
+ #include // For IOCSCANPVT
+ ...
+ #include // defines epicsExportSharedSymbols
+ ...
+ static long init_record(dbCommon *prec);
+ static long get_iointr_info(int detach, dbCommon *prec, IOCSCANPVT* pscan);
+ static long longin_read(longinRecord *prec);
+
+ const struct {
+ dset common;
+ long (*read)(longinRecord *prec);
+ } devLiDevName = {
+ {
+ 5, // 4 from dset + 1 from longinRecord
+ NULL,
+ NULL,
+ &init_record,
+ &get_iointr_info
+ },
+ &longin_read
+ };
+ epicsExportAddress(dset, devLiDevName);
+ @endcode
+ */
+typedef struct typed_dset {
+ /** Number of function pointers which follow.
+ * The value depends on the recordtype, but must be >=4 */
+ long number;
+ /** Called from dbior() */
+ long (*report)(int lvl);
+ /** Called twice during iocInit().
+ * First with @a after = 0 before init_record() or array field allocation.
+ * Again with @a after = 1 after init_record() has finished.
+ */
+ long (*init)(int after);
+ /** Called once per record instance */
+ long (*init_record)(struct dbCommon *prec);
+ /** Called when SCAN="I/O Intr" on startup, or after SCAN is changed.
+ *
+ * Caller must assign the third arguement (IOCSCANPVT*). eg.
+ @code
+ struct mpvt {
+ IOSCANPVT drvlist;
+ };
+ ...
+ // init_record() routine calls
+ scanIoInit(&pvt->drvlist);
+ ...
+ static long get_ioint_info(int detach, struct dbCommon *prec, IOCSCANPVT* pscan) {
+ if(prec->dpvt)
+ *pscan = &((mypvt*)prec->dpvt)->drvlist;
+ @endcode
+ *
+ * When a particular record instance can/will only used a single scan list,
+ * the @a detach argument can be ignored.
+ *
+ * If this is not the case, then the following should be noted.
+ * + get_ioint_info() is called with @a detach = 0 to fetch the scan list to
+ * which this record will be added.
+ * + get_ioint_info() is called later with @a detach = 1 to fetch the scan
+ * list from which this record should be removed.
+ * + Calls will be balanced, so a call with @a detach = 0 will be followed
+ * by one with @a detach = 1.
+ *
+ * @note get_ioint_info() will be called during IOC shutdown if the
+ * dsxt::del_record() extended callback is defined. (from 3.15.0.1)
+ */
+ long (*get_ioint_info)(int detach, struct dbCommon *prec, IOSCANPVT* pscan);
+ /* Any further functions are specified by the record type. */
+} typed_dset;
+
+/** Device support extension table.
+ *
+ * Optional routines to allow run-time address modifications to be communicated
+ * to device support, which must register a struct dsxt by calling devExtend()
+ * from its init() routine.
+ */
+typedef struct dsxt {
+ /** Optional, called to offer device support a new record to control.
+ *
+ * Routine may return a non-zero error code to refuse record.
+ */
+ long (*add_record)(struct dbCommon *precord);
+ /** Optional, called to remove record from device support control.
+ *
+ * Routine return a non-zero error code to refuse record removal.
+ */
+ long (*del_record)(struct dbCommon *precord);
+ /* Only future Base releases may extend this table. */
+} dsxt;
#ifdef __cplusplus
extern "C" {
@@ -29,6 +138,8 @@ extern "C" {
typedef long (*DEVSUPFUN)(); /* ptr to device support function*/
#endif
+#ifndef USE_TYPED_DSET
+
typedef struct dset { /* device support entry table */
long number; /*number of support routines*/
DEVSUPFUN report; /*print report*/
@@ -38,11 +149,15 @@ typedef struct dset { /* device support entry table */
/*other functions are record dependent*/
} dset;
-typedef struct dsxt { /* device support extension table */
- long (*add_record)(struct dbCommon *precord);
- long (*del_record)(struct dbCommon *precord);
- /* Recordtypes are *not* allowed to extend this table */
-} dsxt;
+#else
+typedef typed_dset dset;
+#endif /* USE_TYPED_DSET */
+
+/** Fetch INP or OUT link (or NULL if record type has neither).
+ *
+ * Recommended for use in device support init_record()
+ */
+epicsShareFunc struct link* dbGetDevLink(struct dbCommon* prec);
epicsShareExtern dsxt devSoft_DSXT; /* Allow anything table */
diff --git a/modules/database/src/ioc/dbStatic/drvSup.h b/modules/database/src/ioc/dbStatic/drvSup.h
index 5778038e7..193d57482 100644
--- a/modules/database/src/ioc/dbStatic/drvSup.h
+++ b/modules/database/src/ioc/dbStatic/drvSup.h
@@ -6,7 +6,10 @@
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
-/* drvSup.h Driver Support */
+/** @file drvSup.h
+ *
+ * @brief Driver support routines.
+ */
/*
* Author: Marty Kraimer
@@ -18,16 +21,38 @@
#include "errMdef.h"
-typedef long (*DRVSUPFUN) (); /* ptr to driver support function*/
+/** Driver entry table */
+typedef struct typed_drvet {
+ /** Number of function pointers which follow. Must be >=2 */
+ long number;
+ /** Called from dbior() */
+ long (*report)(int lvl);
+ /** Called during iocInit() */
+ long (*init)(void);
+ /* Any further functions are driver-specific */
+} typed_drvet;
+
+#ifdef USE_TYPED_DRVET
+
+typedef typed_drvet drvet;
+
+#else
+
+/* These interfaces may eventually get deprecated */
+
+typedef long (*DRVSUPFUN) (); /* ptr to driver support function */
+
+typedef struct drvet { /* driver entry table */
+ long number; /* number of support routines */
+ DRVSUPFUN report; /* print report */
+ DRVSUPFUN init; /* init support */
+ /* Any further functions are driver-specific */
+} drvet;
-typedef struct drvet { /* driver entry table */
- long number; /*number of support routines*/
- DRVSUPFUN report; /*print report*/
- DRVSUPFUN init; /*init support*/
- /*other functions are device dependent*/
-}drvet;
#define DRVETNUMBER ( (sizeof(struct drvet) -sizeof(long))/sizeof(DRVSUPFUN) )
+#endif /* USE_TYPED_DRVET */
+
#define S_drv_noDrvSup (M_drvSup| 1) /*SDR_DRVSUP: Driver support missing*/
#define S_drv_noDrvet (M_drvSup| 3) /*Missing driver support entry table*/
diff --git a/modules/database/src/ioc/dbtemplate/msi.c b/modules/database/src/ioc/dbtemplate/msi.c
index 5a5023163..69680e17e 100644
--- a/modules/database/src/ioc/dbtemplate/msi.c
+++ b/modules/database/src/ioc/dbtemplate/msi.c
@@ -4,7 +4,7 @@
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS Base is distributed subject to a Software License Agreement found
-* in the file LICENSE that is included with this distribution.
+* in the file LICENSE that is included with this distribution.
\*************************************************************************/
/* msi - macro substitutions and include */
@@ -22,10 +22,36 @@
#include
#include
#include
+#include
#define MAX_BUFFER_SIZE 4096
#define MAX_DEPS 1024
+#if 0
+/* Debug Tracing */
+int din = 0;
+#define ENTER fprintf(stderr, "%*sEntering %s\n", 2*din++, "", __FUNCTION__)
+
+#define STEP(s) fprintf(stderr, "%*s%s: %s\n", 2*din, "", __FUNCTION__, s)
+#define STEPS(s, v) fprintf(stderr, "%*s%s: %s '%s'\n", 2*din, "", __FUNCTION__, s, v)
+#define STEPD(s, v) fprintf(stderr, "%*s%s: %s %d\n", 2*din, "", __FUNCTION__, s, v)
+
+#define EXIT fprintf(stderr, "%*s%s: Returning\n", 2*din--, "", __FUNCTION__)
+#define EXITD(r) fprintf(stderr, "%*s%s: Returning %d\n", 2*din--, "", __FUNCTION__, r)
+#define EXITS(r) fprintf(stderr, "%*s%s: Returning '%s'\n", 2*din--, "", __FUNCTION__, r)
+#else
+#define ENTER
+
+#define STEP(s)
+#define STEPS(s, v)
+#define STEPD(s, v)
+
+#define EXIT
+#define EXITD(r)
+#define EXITS(r)
+#endif
+
+
/* Module to read the template files */
typedef struct inputData inputData;
@@ -49,6 +75,7 @@ static char *substituteGetGlobalReplacements(subInfo *pvt);
/* Forward references to local routines */
static void usageExit(int status);
+static void abortExit(int status);
static void addMacroReplacements(MAC_HANDLE *macPvt, char *pval);
static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *templateName);
@@ -66,48 +93,62 @@ int main(int argc,char **argv)
MAC_HANDLE *macPvt;
char *pval;
int narg;
- char *substitutionName=0;
- char *templateName=0;
+ char *substitutionName = 0;
+ char *templateName = 0;
int i;
int localScope = 1;
inputConstruct(&inputPvt);
- macCreateHandle(&macPvt,0);
- while((argc>1) && (argv[1][0] == '-')) {
- narg = (strlen(argv[1])==2) ? 2 : 1;
- pval = (narg==1) ? (argv[1]+2) : argv[2];
- if(strncmp(argv[1],"-I",2)==0) {
- inputAddPath(inputPvt,pval);
- } else if (strcmp(argv[1], "-D") == 0) {
+ macCreateHandle(&macPvt, 0);
+ while ((argc > 1) && (argv[1][0] == '-')) {
+ narg = (strlen(argv[1]) == 2) ? 2 : 1;
+ pval = (narg == 1) ? (argv[1] + 2) : argv[2];
+
+ if (strncmp(argv[1], "-I", 2) == 0) {
+ inputAddPath(inputPvt, pval);
+ }
+ else if (strcmp(argv[1], "-D") == 0) {
opt_D = 1;
narg = 1; /* no argument for this option */
- } else if(strncmp(argv[1],"-o",2)==0) {
+ }
+ else if(strncmp(argv[1], "-o", 2) == 0) {
outFile = epicsStrDup(pval);
- } else if(strncmp(argv[1],"-M",2)==0) {
- addMacroReplacements(macPvt,pval);
- } else if(strncmp(argv[1],"-S",2)==0) {
+ }
+ else if(strncmp(argv[1], "-M", 2) == 0) {
+ addMacroReplacements(macPvt, pval);
+ }
+ else if(strncmp(argv[1], "-S", 2) == 0) {
substitutionName = epicsStrDup(pval);
- } else if (strcmp(argv[1], "-V") == 0) {
+ }
+ else if (strcmp(argv[1], "-V") == 0) {
opt_V = 1;
narg = 1; /* no argument for this option */
- } else if (strcmp(argv[1], "-g") == 0) {
+ }
+ else if (strcmp(argv[1], "-g") == 0) {
localScope = 0;
narg = 1; /* no argument for this option */
- } else if (strcmp(argv[1], "-h") == 0) {
+ }
+ else if (strcmp(argv[1], "-h") == 0) {
usageExit(0);
- } else {
+ }
+ else {
fprintf(stderr, "msi: Bad argument \"%s\"\n", argv[1]);
usageExit(1);
}
+
argc -= narg;
- for(i=1; i2) {
- fprintf(stderr,"msi: Too many arguments\n");
+ macSuppressWarning(macPvt, 1);
+
+ if (argc > 2) {
+ fprintf(stderr, "msi: Too many arguments\n");
usageExit(1);
}
+
if (opt_D) {
if (!outFile) {
fprintf(stderr, "msi: Option -D requires -o for Makefile target\n");
@@ -120,34 +161,47 @@ int main(int argc,char **argv)
outFile, strerror(errno));
exit(1);
}
- if(argc==2) {
+
+ if (argc == 2)
templateName = epicsStrDup(argv[1]);
+
+ if (!substitutionName) {
+ STEP("Single template+substitutions file");
+ makeSubstitutions(inputPvt, macPvt, templateName);
}
- if(!substitutionName) {
- makeSubstitutions(inputPvt,macPvt,templateName);
- } else {
+ else {
subInfo *substitutePvt;
char *filename = 0;
int isGlobal, isFile;
- substituteOpen(&substitutePvt,substitutionName);
+ STEPS("Substitutions from file", substitutionName);
+ substituteOpen(&substitutePvt, substitutionName);
do {
- if ((isGlobal = substituteGetGlobalSet(substitutePvt))) {
+ isGlobal = substituteGetGlobalSet(substitutePvt);
+ if (isGlobal) {
+ STEP("Handling global macros");
pval = substituteGetGlobalReplacements(substitutePvt);
- if(pval) {
- addMacroReplacements(macPvt,pval);
- }
- } else if ((isFile = substituteGetNextSet(substitutePvt,&filename))) {
- if(templateName) filename = templateName;
- if(!filename) {
- fprintf(stderr,"msi: No template file\n");
+ if (pval)
+ addMacroReplacements(macPvt, pval);
+ }
+ else if ((isFile = substituteGetNextSet(substitutePvt, &filename))) {
+ if (templateName)
+ filename = templateName;
+ if (!filename) {
+ fprintf(stderr, "msi: No template file\n");
usageExit(1);
}
- while((pval = substituteGetReplacements(substitutePvt))){
- if (localScope) macPushScope(macPvt);
- addMacroReplacements(macPvt,pval);
- makeSubstitutions(inputPvt,macPvt,filename);
- if (localScope) macPopScope(macPvt);
+
+ STEPS("Handling template file", filename);
+ while ((pval = substituteGetReplacements(substitutePvt))) {
+ if (localScope)
+ macPushScope(macPvt);
+
+ addMacroReplacements(macPvt, pval);
+ makeSubstitutions(inputPvt, macPvt, filename);
+
+ if (localScope)
+ macPopScope(macPvt);
}
}
} while (isGlobal || isFile);
@@ -182,20 +236,29 @@ void usageExit(int status)
exit(status);
}
-static void addMacroReplacements(MAC_HANDLE *macPvt,char *pval)
+void abortExit(int status)
+{
+ if (outFile) {
+ fclose(stdout);
+ unlink(outFile);
+ }
+ exit(status);
+}
+
+static void addMacroReplacements(MAC_HANDLE *macPvt, char *pval)
{
char **pairs;
long status;
- status = macParseDefns(macPvt,pval,&pairs);
- if(status==-1) {
- fprintf(stderr,"msi: Error from macParseDefns\n");
+ status = macParseDefns(macPvt, pval, &pairs);
+ if (status == -1) {
+ fprintf(stderr, "msi: Error from macParseDefns\n");
usageExit(1);
}
- if(status) {
- status = macInstallMacros(macPvt,pairs);
- if(!status) {
- fprintf(stderr,"Error from macInstallMacros\n");
+ if (status) {
+ status = macInstallMacros(macPvt, pairs);
+ if (!status) {
+ fprintf(stderr, "Error from macInstallMacros\n");
usageExit(1);
}
free(pairs);
@@ -211,86 +274,98 @@ static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *tem
static char buffer[MAX_BUFFER_SIZE];
int n;
- inputBegin(inputPvt,templateName);
- while((input = inputNextLine(inputPvt))) {
+ ENTER;
+ inputBegin(inputPvt, templateName);
+ while ((input = inputNextLine(inputPvt))) {
int expand=1;
char *p;
char *command = 0;
- p = input;
+ p = input;
/*skip whitespace at beginning of line*/
- while(*p && (isspace((int) *p))) ++p;
+ while (*p && (isspace((int) *p))) ++p;
+
/*Look for i or s */
- if(*p && (*p=='i' || *p=='s')) command = p;
- if(command) {
+ if (*p && (*p=='i' || *p=='s'))
+ command = p;
+
+ if (command) {
char *pstart;
char *pend;
char *copy;
int cmdind=-1;
int i;
-
- for(i=0; i< NELEMENTS(cmdNames); i++) {
- if(strstr(command,cmdNames[i])) {
+
+ for (i = 0; i < NELEMENTS(cmdNames); i++) {
+ if (strstr(command, cmdNames[i])) {
cmdind = i;
}
}
- if(cmdind<0) goto endif;
+ if (cmdind < 0) goto endcmd;
p = command + strlen(cmdNames[cmdind]);
/*skip whitespace after command*/
- while(*p && (isspace((int) *p))) ++p;
+ while (*p && (isspace((int) *p))) ++p;
/*Next character must be quote*/
- if((*p==0) || (*p!='"')) goto endif;
+ if ((*p == 0) || (*p != '"')) goto endcmd;
pstart = ++p;
/*Look for end quote*/
- while(*p && (*p!='"')) {
- /*allow escape for imbeded quote*/
- if((*p=='\\') && *(p+1)=='"') {
- p += 2; continue;
- } else {
- if(*p=='"') break;
+ while (*p && (*p != '"')) {
+ /*allow escape for embeded quote*/
+ if ((p[0] == '\\') && p[1] == '"') {
+ p += 2;
+ continue;
+ }
+ else {
+ if (*p == '"') break;
}
++p;
}
pend = p;
- if(*p==0) goto endif;
+ if (*p == 0) goto endcmd;
/*skip quote and any trailing blanks*/
- while(*++p==' ') ;
- if(*p != '\n' && *p !=0) goto endif;
- copy = calloc(pend-pstart+1,sizeof(char));
- strncpy(copy,pstart,pend-pstart);
+ while (*++p == ' ') ;
+ if (*p != '\n' && *p != 0) goto endcmd;
+ copy = calloc(pend-pstart + 1, sizeof(char));
+ strncpy(copy, pstart, pend-pstart);
+
switch(cmdind) {
case cmdInclude:
inputNewIncludeFile(inputPvt,copy);
break;
+
case cmdSubstitute:
addMacroReplacements(macPvt,copy);
break;
+
default:
- fprintf(stderr,"msi: Logic error in makeSubstitutions\n");
+ fprintf(stderr, "msi: Logic error in makeSubstitutions\n");
inputErrPrint(inputPvt);
- exit(1);
+ abortExit(1);
}
free(copy);
expand = 0;
}
-endif:
+
+endcmd:
if (expand && !opt_D) {
- n = macExpandString(macPvt,input,buffer,MAX_BUFFER_SIZE-1);
- fputs(buffer,stdout);
+ STEP("Expanding to output stream");
+ n = macExpandString(macPvt, input, buffer, MAX_BUFFER_SIZE - 1);
+ fputs(buffer, stdout);
if (opt_V == 1 && n < 0) {
- fprintf(stderr,"msi: Error - undefined macros present\n");
+ fprintf(stderr, "msi: Error - undefined macros present\n");
opt_V++;
}
}
}
+ EXIT;
}
-typedef struct inputFile{
+typedef struct inputFile {
ELLNODE node;
char *filename;
FILE *fp;
int lineNum;
-}inputFile;
+} inputFile;
typedef struct pathNode {
ELLNODE node;
@@ -303,15 +378,15 @@ struct inputData {
char inputBuffer[MAX_BUFFER_SIZE];
};
-static void inputOpenFile(inputData *pinputData,char *filename);
+static void inputOpenFile(inputData *pinputData, char *filename);
static void inputCloseFile(inputData *pinputData);
static void inputCloseAllFiles(inputData *pinputData);
static void inputConstruct(inputData **ppvt)
{
- inputData *pinputData;
+ inputData *pinputData;
- pinputData = calloc(1,sizeof(inputData));
+ pinputData = calloc(1, sizeof(inputData));
ellInit(&pinputData->inputFileList);
ellInit(&pinputData->pathList);
*ppvt = pinputData;
@@ -319,11 +394,11 @@ static void inputConstruct(inputData **ppvt)
static void inputDestruct(inputData *pinputData)
{
- pathNode *ppathNode;
+ pathNode *ppathNode;
inputCloseAllFiles(pinputData);
- while((ppathNode = (pathNode *)ellFirst(&pinputData->pathList))) {
- ellDelete(&pinputData->pathList,&ppathNode->node);
+ while ((ppathNode = (pathNode *) ellFirst(&pinputData->pathList))) {
+ ellDelete(&pinputData->pathList, &ppathNode->node);
free(ppathNode->directory);
free(ppathNode);
}
@@ -340,38 +415,45 @@ static void inputAddPath(inputData *pinputData, char *path)
int emptyName;
const char sep = *OSI_PATH_LIST_SEPARATOR;
+ ENTER;
pdir = path;
/*an empty name at beginning, middle, or end means current directory*/
- while(pdir && *pdir) {
+ while (pdir && *pdir) {
emptyName = ((*pdir == sep) ? 1 : 0);
- if(emptyName) ++pdir;
- ppathNode = (pathNode *)calloc(1,sizeof(pathNode));
- ellAdd(ppathList,&ppathNode->node);
- if(!emptyName) {
- pcolon = strchr(pdir,sep);
+ if (emptyName) ++pdir;
+
+ ppathNode = (pathNode *) calloc(1, sizeof(pathNode));
+ ellAdd(ppathList, &ppathNode->node);
+
+ if (!emptyName) {
+ pcolon = strchr(pdir, sep);
len = (pcolon ? (pcolon - pdir) : strlen(pdir));
- if(len>0) {
- ppathNode->directory = (char *)calloc(len+1,sizeof(char));
- strncpy(ppathNode->directory,pdir,len);
+ if (len > 0) {
+ ppathNode->directory = (char *) calloc(len + 1, sizeof(char));
+ strncpy(ppathNode->directory, pdir, len);
pdir = pcolon;
/*unless at end skip past first colon*/
- if(pdir && *(pdir+1)!=0) ++pdir;
- } else { /*must have been trailing : */
- emptyName=1;
+ if (pdir && *(pdir + 1) != 0) ++pdir;
+ }
+ else { /*must have been trailing : */
+ emptyName = 1;
}
}
- if(emptyName) {
- ppathNode->directory = (char *)calloc(2,sizeof(char));
- strcpy(ppathNode->directory,".");
+
+ if (emptyName) {
+ ppathNode->directory = (char *) calloc(2, sizeof(char));
+ strcpy(ppathNode->directory, ".");
}
}
- return;
+ EXIT;
}
static void inputBegin(inputData *pinputData, char *fileName)
{
+ ENTER;
inputCloseAllFiles(pinputData);
- inputOpenFile(pinputData,fileName);
+ inputOpenFile(pinputData, fileName);
+ EXIT;
}
static char *inputNextLine(inputData *pinputData)
@@ -379,43 +461,54 @@ static char *inputNextLine(inputData *pinputData)
inputFile *pinputFile;
char *pline;
- while((pinputFile = (inputFile *)ellFirst(&pinputData->inputFileList))) {
- pline = fgets(pinputData->inputBuffer,MAX_BUFFER_SIZE,pinputFile->fp);
- if(pline) {
+ ENTER;
+ while ((pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList))) {
+ pline = fgets(pinputData->inputBuffer, MAX_BUFFER_SIZE, pinputFile->fp);
+ if (pline) {
++pinputFile->lineNum;
- return(pline);
+ EXITS(pline);
+ return pline;
}
inputCloseFile(pinputData);
}
- return(0);
+ EXITD(0);
+ return 0;
}
static void inputNewIncludeFile(inputData *pinputData, char *name)
{
+ ENTER;
inputOpenFile(pinputData,name);
+ EXIT;
}
static void inputErrPrint(inputData *pinputData)
{
inputFile *pinputFile;
- fprintf(stderr,"input: '%s' at ",pinputData->inputBuffer);
- pinputFile = (inputFile *)ellFirst(&pinputData->inputFileList);
- while(pinputFile) {
- fprintf(stderr,"line %d of ",pinputFile->lineNum);
- if(pinputFile->filename) {
- fprintf(stderr," file %s\n",pinputFile->filename);
- } else {
- fprintf(stderr,"stdin:\n");
+ ENTER;
+ fprintf(stderr, "input: '%s' at ", pinputData->inputBuffer);
+ pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList);
+ while (pinputFile) {
+ fprintf(stderr, "line %d of ", pinputFile->lineNum);
+
+ if (pinputFile->filename) {
+ fprintf(stderr, " file %s\n", pinputFile->filename);
}
- pinputFile = (inputFile *)ellNext(&pinputFile->node);
- if(pinputFile) {
- fprintf(stderr," included from ");
- } else {
+ else {
+ fprintf(stderr, "stdin:\n");
+ }
+
+ pinputFile = (inputFile *) ellNext(&pinputFile->node);
+ if (pinputFile) {
+ fprintf(stderr, " included from ");
+ }
+ else {
fprintf(stderr,"\n");
}
}
fprintf(stderr,"\n");
+ EXIT;
}
static void inputOpenFile(inputData *pinputData,char *filename)
@@ -426,35 +519,48 @@ static void inputOpenFile(inputData *pinputData,char *filename)
char *fullname = 0;
FILE *fp = 0;
- if(!filename) {
+ ENTER;
+ if (!filename) {
+ STEP("Using stdin");
fp = stdin;
- } else if((ellCount(ppathList)==0) || strchr(filename,'/')){
- fp = fopen(filename,"r");
- } else {
- ppathNode = (pathNode *)ellFirst(ppathList);
- while(ppathNode) {
- fullname = calloc(strlen(filename)+strlen(ppathNode->directory) +2,
+ }
+ else if ((ellCount(ppathList) == 0) || strchr(filename, '/')){
+ STEPS("Opening ", filename);
+ fp = fopen(filename, "r");
+ }
+ else {
+ ppathNode = (pathNode *) ellFirst(ppathList);
+ while (ppathNode) {
+ fullname = calloc(strlen(filename) + strlen(ppathNode->directory) + 2,
sizeof(char));
- strcpy(fullname,ppathNode->directory);
- strcat(fullname,"/");
- strcat(fullname,filename);
- fp = fopen(fullname,"r");
- if(fp) break;
+ strcpy(fullname, ppathNode->directory);
+ strcat(fullname, "/");
+ strcat(fullname, filename);
+ STEPS("Trying", filename);
+ fp = fopen(fullname, "r");
+ if (fp)
+ break;
free(fullname);
- ppathNode = (pathNode *)ellNext(&ppathNode->node);
+ ppathNode = (pathNode *) ellNext(&ppathNode->node);
}
}
- if(!fp) {
- fprintf(stderr,"msi: Can't open file '%s'\n",filename);
+
+ if (!fp) {
+ fprintf(stderr, "msi: Can't open file '%s'\n", filename);
inputErrPrint(pinputData);
- exit(1);
+ abortExit(1);
}
- pinputFile = calloc(1,sizeof(inputFile));
- if(ppathNode) {
+
+ STEP("File opened");
+ pinputFile = calloc(1, sizeof(inputFile));
+
+ if (ppathNode) {
pinputFile->filename = fullname;
- } else if(filename) {
+ }
+ else if (filename) {
pinputFile->filename = epicsStrDup(filename);
- } else {
+ }
+ else {
pinputFile->filename = epicsStrDup("stdin");
}
@@ -484,35 +590,41 @@ static void inputOpenFile(inputData *pinputData,char *filename)
}
pinputFile->fp = fp;
- ellInsert(&pinputData->inputFileList,0,&pinputFile->node);
+ ellInsert(&pinputData->inputFileList, 0, &pinputFile->node);
+ EXIT;
}
static void inputCloseFile(inputData *pinputData)
{
inputFile *pinputFile;
- pinputFile = (inputFile *)ellFirst(&pinputData->inputFileList);
- if(!pinputFile) return;
- ellDelete(&pinputData->inputFileList,&pinputFile->node);
- if(fclose(pinputFile->fp))
- fprintf(stderr,"msi: Can't close input file '%s'\n",pinputFile->filename);
- free(pinputFile->filename);
- free(pinputFile);
+ ENTER;
+ pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList);
+ if (pinputFile) {
+ ellDelete(&pinputData->inputFileList, &pinputFile->node);
+ if (fclose(pinputFile->fp))
+ fprintf(stderr, "msi: Can't close input file '%s'\n", pinputFile->filename);
+ free(pinputFile->filename);
+ free(pinputFile);
+ }
+ EXIT;
}
static void inputCloseAllFiles(inputData *pinputData)
{
inputFile *pinputFile;
- while((pinputFile=(inputFile *)ellFirst(&pinputData->inputFileList))){
+ ENTER;
+ while ((pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList))) {
inputCloseFile(pinputData);
}
+ EXIT;
}
/*start of code that handles substitution file*/
typedef enum {
- tokenLBrace,tokenRBrace,tokenSeparater,tokenString,tokenEOF
-}tokenType;
+ tokenLBrace, tokenRBrace, tokenSeparator, tokenString, tokenEOF
+} tokenType;
typedef struct subFile {
char *substitutionName;
@@ -549,70 +661,87 @@ static void catMacroReplacements(subInfo *psubInfo,const char *value);
void freeSubFile(subInfo *psubInfo)
{
- subFile *psubFile = psubInfo->psubFile;
- if(psubFile->fp) {
- if(fclose(psubFile->fp))
- fprintf(stderr,"msi: Can't close substitution file\n");
+ subFile *psubFile = psubInfo->psubFile;
+
+ ENTER;
+ if (psubFile->fp) {
+ if (fclose(psubFile->fp))
+ fprintf(stderr, "msi: Can't close substitution file\n");
}
free(psubFile);
free(psubInfo->filename);
psubInfo->psubFile = 0;
+ EXIT;
}
void freePattern(subInfo *psubInfo)
{
patternNode *ppatternNode;
- while((ppatternNode = (patternNode *)ellFirst(&psubInfo->patternList))) {
- ellDelete(&psubInfo->patternList,&ppatternNode->node);
+
+ ENTER;
+ while ((ppatternNode = (patternNode *) ellFirst(&psubInfo->patternList))) {
+ ellDelete(&psubInfo->patternList, &ppatternNode->node);
free(ppatternNode->var);
free(ppatternNode);
}
psubInfo->isPattern = 0;
+ EXIT;
}
static void substituteDestruct(subInfo *psubInfo)
{
+ ENTER;
freeSubFile(psubInfo);
freePattern(psubInfo);
free(psubInfo);
- return;
+ EXIT;
}
-static void substituteOpen(subInfo **ppvt,char *substitutionName)
+static void substituteOpen(subInfo **ppvt, char *substitutionName)
{
subInfo *psubInfo;
subFile *psubFile;
FILE *fp;
- psubInfo = calloc(1,sizeof(subInfo));
+ ENTER;
+ psubInfo = calloc(1, sizeof(subInfo));
*ppvt = psubInfo;
- psubFile = calloc(1,sizeof(subFile));
+ psubFile = calloc(1, sizeof(subFile));
psubInfo->psubFile = psubFile;
ellInit(&psubInfo->patternList);
- fp = fopen(substitutionName,"r");
- if(!fp) {
- fprintf(stderr,"msi: Can't open file '%s'\n",substitutionName);
- exit(1);
+
+ fp = fopen(substitutionName, "r");
+ if (!fp) {
+ fprintf(stderr, "msi: Can't open file '%s'\n", substitutionName);
+ abortExit(1);
}
+
psubFile->substitutionName = substitutionName;
psubFile->fp = fp;
psubFile->lineNum = 1;
psubFile->inputBuffer[0] = 0;
psubFile->pnextChar = &psubFile->inputBuffer[0];
subGetNextToken(psubFile);
- return;
+ EXIT;
}
static int substituteGetGlobalSet(subInfo *psubInfo)
{
- subFile *psubFile = psubInfo->psubFile;
+ subFile *psubFile = psubInfo->psubFile;
- while(psubFile->token==tokenSeparater) subGetNextToken(psubFile);
- if(psubFile->token==tokenString && strcmp(psubFile->string,"global")==0) {
+ ENTER;
+ while (psubFile->token == tokenSeparator)
subGetNextToken(psubFile);
- return(1);
+
+ if (psubFile->token == tokenString &&
+ strcmp(psubFile->string, "global") == 0) {
+ subGetNextToken(psubFile);
+ EXITD(1);
+ return 1;
}
- return(0);
+
+ EXITD(0);
+ return 0;
}
static int substituteGetNextSet(subInfo *psubInfo,char **filename)
@@ -620,96 +749,155 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
subFile *psubFile = psubInfo->psubFile;
patternNode *ppatternNode;
+ ENTER;
*filename = 0;
- while(psubFile->token==tokenSeparater) subGetNextToken(psubFile);
- if(psubFile->token==tokenEOF) return(0);
- if(psubFile->token==tokenString && strcmp(psubFile->string,"file")==0) {
+ while (psubFile->token == tokenSeparator)
+ subGetNextToken(psubFile);
+
+ if (psubFile->token == tokenEOF) {
+ EXITD(0);
+ return 0;
+ }
+
+ if (psubFile->token == tokenString &&
+ strcmp(psubFile->string, "file") == 0) {
+ size_t len;
+
+ STEP("Parsed 'file'");
psubInfo->isFile = 1;
- if(subGetNextToken(psubFile)!=tokenString) {
- subFileErrPrint(psubFile,"Parse error, expecting filename");
- exit(1);
+ if (subGetNextToken(psubFile) != tokenString) {
+ subFileErrPrint(psubFile, "Parse error, expecting a filename");
+ abortExit(1);
}
+
freePattern(psubInfo);
free(psubInfo->filename);
- if(psubFile->string[0]=='"'&&psubFile->string[strlen(psubFile->string)-1]=='"') {
- psubFile->string[strlen(psubFile->string)-1]='\0';
- psubInfo->filename = macEnvExpand(psubFile->string+1);
+
+ len = strlen(psubFile->string);
+ if (psubFile->string[0] == '"' &&
+ psubFile->string[len - 1] == '"') {
+ psubFile->string[len - 1] = '\0';
+ psubInfo->filename = macEnvExpand(psubFile->string + 1);
}
- else {
+ else
psubInfo->filename = macEnvExpand(psubFile->string);
+ STEPS("Parsed filename", psubInfo->filename);
+
+ while (subGetNextToken(psubFile) == tokenSeparator);
+
+ if (psubFile->token != tokenLBrace) {
+ subFileErrPrint(psubFile, "Parse error, expecting '{'");
+ abortExit(1);
}
- while(subGetNextToken(psubFile)==tokenSeparater);
- if(psubFile->token!=tokenLBrace) {
- subFileErrPrint(psubFile,"Parse error, expecting {");
- exit(1);
- }
+ STEP("Parsed '{'");
subGetNextToken(psubFile);
}
*filename = psubInfo->filename;
- while(psubFile->token==tokenSeparater) subGetNextToken(psubFile);
- if(psubFile->token==tokenLBrace) return(1);
- if(psubFile->token==tokenRBrace) return(1);
- if(psubFile->token!=tokenString
- || strcmp(psubFile->string,"pattern")!=0) {
- subFileErrPrint(psubFile,"Parse error, expecting pattern");
- exit(1);
+
+ while (psubFile->token == tokenSeparator)
+ subGetNextToken(psubFile);
+
+ if (psubFile->token == tokenLBrace) {
+ EXITD(1);
+ return 1;
}
+
+ if (psubFile->token == tokenRBrace) {
+ subFileErrPrint(psubFile, "Parse error, unexpected '}'");
+ abortExit(1);
+ }
+
+ if (psubFile->token != tokenString ||
+ strcmp(psubFile->string, "pattern") != 0) {
+ subFileErrPrint(psubFile, "Parse error, expecting 'pattern'");
+ abortExit(1);
+ }
+
+ STEP("Parsed 'pattern'");
freePattern(psubInfo);
psubInfo->isPattern = 1;
- while(subGetNextToken(psubFile)==tokenSeparater);
- if(psubFile->token!=tokenLBrace) {
- subFileErrPrint(psubFile,"Parse error, expecting {");
- exit(1);
+
+ while (subGetNextToken(psubFile) == tokenSeparator);
+
+ if (psubFile->token != tokenLBrace) {
+ subFileErrPrint(psubFile, "Parse error, expecting '{'");
+ abortExit(1);
}
- while(1) {
- while(subGetNextToken(psubFile)==tokenSeparater);
- if(psubFile->token!=tokenString) break;
- ppatternNode = calloc(1,sizeof(patternNode));
- ellAdd(&psubInfo->patternList,&ppatternNode->node);
+ STEP("Parsed '{'");
+
+ while (1) {
+ while (subGetNextToken(psubFile) == tokenSeparator);
+
+ if (psubFile->token != tokenString)
+ break;
+
+ ppatternNode = calloc(1, sizeof(patternNode));
+ ellAdd(&psubInfo->patternList, &ppatternNode->node);
ppatternNode->var = epicsStrDup(psubFile->string);
}
- if(psubFile->token!=tokenRBrace) {
- subFileErrPrint(psubFile,"Parse error, expecting }");
- exit(1);
+
+ if (psubFile->token != tokenRBrace) {
+ subFileErrPrint(psubFile, "Parse error, expecting '}'");
+ abortExit(1);
}
+
subGetNextToken(psubFile);
- return(1);
+ EXITD(1);
+ return 1;
}
static char *substituteGetGlobalReplacements(subInfo *psubInfo)
{
subFile *psubFile = psubInfo->psubFile;
- if(psubInfo->macroReplacements) psubInfo->macroReplacements[0] = 0;
+ ENTER;
+ if (psubInfo->macroReplacements)
+ psubInfo->macroReplacements[0] = 0;
psubInfo->curLength = 0;
- while(psubFile->token==tokenSeparater) subGetNextToken(psubFile);
- if(psubFile->token==tokenRBrace && psubInfo->isFile) {
+
+ while (psubFile->token == tokenSeparator)
+ subGetNextToken(psubFile);
+
+ if (psubFile->token == tokenRBrace && psubInfo->isFile) {
psubInfo->isFile = 0;
free(psubInfo->filename);
psubInfo->filename = 0;
freePattern(psubInfo);
subGetNextToken(psubFile);
- return(0);
+ EXITD(0);
+ return 0;
}
- if(psubFile->token==tokenEOF) return(0);
- if(psubFile->token!=tokenLBrace) return(0);
- while(1) {
+
+ if (psubFile->token == tokenEOF) {
+ EXITD(0);
+ return 0;
+ }
+ if (psubFile->token != tokenLBrace) {
+ EXITD(0);
+ return 0;
+ }
+
+ while (1) {
switch(subGetNextToken(psubFile)) {
case tokenRBrace:
subGetNextToken(psubFile);
- if (!psubInfo->macroReplacements) {
- catMacroReplacements(psubInfo,"");
- }
- return(psubInfo->macroReplacements);
- case tokenSeparater:
- catMacroReplacements(psubInfo,",");
+ EXITS(psubInfo->macroReplacements);
+ return psubInfo->macroReplacements;
+
+ case tokenSeparator:
+ catMacroReplacements(psubInfo, ",");
break;
+
case tokenString:
- catMacroReplacements(psubInfo,psubFile->string);
+ catMacroReplacements(psubInfo, psubFile->string);
break;
- default:
- subFileErrPrint(psubFile,"Parse error, illegal token");
- exit(1);
+
+ case tokenLBrace:
+ subFileErrPrint(psubFile, "Parse error, unexpected '{'");
+ abortExit(1);
+ case tokenEOF:
+ subFileErrPrint(psubFile, "Parse error, incomplete file?");
+ abortExit(1);
}
}
}
@@ -719,62 +907,89 @@ static char *substituteGetReplacements(subInfo *psubInfo)
subFile *psubFile = psubInfo->psubFile;
patternNode *ppatternNode;
- if(psubInfo->macroReplacements) psubInfo->macroReplacements[0] = 0;
+ ENTER;
+ if (psubInfo->macroReplacements)
+ psubInfo->macroReplacements[0] = 0;
psubInfo->curLength = 0;
- while(psubFile->token==tokenSeparater) subGetNextToken(psubFile);
- if(psubFile->token==tokenRBrace && psubInfo->isFile) {
+
+ while (psubFile->token == tokenSeparator)
+ subGetNextToken(psubFile);
+
+ if (psubFile->token==tokenRBrace && psubInfo->isFile) {
psubInfo->isFile = 0;
free(psubInfo->filename);
psubInfo->filename = 0;
freePattern(psubInfo);
subGetNextToken(psubFile);
- return(0);
+ EXITD(0);
+ return 0;
}
- if(psubFile->token==tokenEOF) return(0);
- if(psubFile->token!=tokenLBrace) return(0);
- if(psubInfo->isPattern) {
+
+ if (psubFile->token == tokenEOF) {
+ EXITD(0);
+ return 0;
+ }
+
+ if (psubFile->token != tokenLBrace) {
+ EXITD(0);
+ return 0;
+ }
+
+ if (psubInfo->isPattern) {
int gotFirstPattern = 0;
- while(subGetNextToken(psubFile)==tokenSeparater);
- ppatternNode = (patternNode *)ellFirst(&psubInfo->patternList);
- while(1) {
- if(psubFile->token==tokenRBrace) {
+ while (subGetNextToken(psubFile) == tokenSeparator);
+ ppatternNode = (patternNode *) ellFirst(&psubInfo->patternList);
+ while (1) {
+ if (psubFile->token == tokenRBrace) {
subGetNextToken(psubFile);
- return(psubInfo->macroReplacements);
+ EXITS(psubInfo->macroReplacements);
+ return psubInfo->macroReplacements;
}
- if(psubFile->token!=tokenString) {
- subFileErrPrint(psubFile,"Parse error, illegal token");
- exit(-1);
+
+ if (psubFile->token != tokenString) {
+ subFileErrPrint(psubFile,"Parse error, expecting macro value");
+ abortExit(1);
}
- if(gotFirstPattern) catMacroReplacements(psubInfo,",");
+
+ if (gotFirstPattern)
+ catMacroReplacements(psubInfo, ",");
gotFirstPattern = 1;
- if(ppatternNode) {
- catMacroReplacements(psubInfo,ppatternNode->var);
- catMacroReplacements(psubInfo,"=");
- catMacroReplacements(psubInfo,psubFile->string);
- ppatternNode = (patternNode *)ellNext(&ppatternNode->node);
- } else {
- subFileErrPrint(psubFile,"Warning, too many values given");
+
+ if (ppatternNode) {
+ catMacroReplacements(psubInfo, ppatternNode->var);
+ catMacroReplacements(psubInfo, "=");
+ catMacroReplacements(psubInfo, psubFile->string);
+ ppatternNode = (patternNode *) ellNext(&ppatternNode->node);
}
- while(subGetNextToken(psubFile)==tokenSeparater);
+ else {
+ subFileErrPrint(psubFile, "Warning, too many values given");
+ }
+
+ while (subGetNextToken(psubFile) == tokenSeparator);
}
- } else while(1) {
+ }
+ else while(1) {
switch(subGetNextToken(psubFile)) {
case tokenRBrace:
subGetNextToken(psubFile);
- if (!psubInfo->macroReplacements) {
- catMacroReplacements(psubInfo,"");
- }
- return(psubInfo->macroReplacements);
- case tokenSeparater:
- catMacroReplacements(psubInfo,",");
+ EXITS(psubInfo->macroReplacements);
+ return psubInfo->macroReplacements;
+
+ case tokenSeparator:
+ catMacroReplacements(psubInfo, ",");
break;
+
case tokenString:
- catMacroReplacements(psubInfo,psubFile->string);
+ catMacroReplacements(psubInfo, psubFile->string);
break;
- default:
- subFileErrPrint(psubFile,"Parse error, illegal token");
- exit(1);
+
+ case tokenLBrace:
+ subFileErrPrint(psubFile, "Parse error, unexpected '{'");
+ abortExit(1);
+ case tokenEOF:
+ subFileErrPrint(psubFile, "Parse error, incomplete file?");
+ abortExit(1);
}
}
}
@@ -783,69 +998,84 @@ static char *subGetNextLine(subFile *psubFile)
{
char *pline;
+ ENTER;
do {
- pline = fgets(psubFile->inputBuffer,MAX_BUFFER_SIZE,psubFile->fp);
+ pline = fgets(psubFile->inputBuffer, MAX_BUFFER_SIZE, psubFile->fp);
++psubFile->lineNum;
- } while(pline && psubFile->inputBuffer[0]=='#');
- if(!pline) {
+ } while (pline && psubFile->inputBuffer[0] == '#');
+
+ if (!pline) {
psubFile->token = tokenEOF;
psubFile->inputBuffer[0] = 0;
psubFile->pnextChar = 0;
- return(0);
+ EXITD(0);
+ return 0;
}
+
psubFile->pnextChar = &psubFile->inputBuffer[0];
- return(&psubFile->inputBuffer[0]);
+ EXITS(&psubFile->inputBuffer[0]);
+ return &psubFile->inputBuffer[0];
}
static void subFileErrPrint(subFile *psubFile,char * message)
{
- fprintf(stderr,"msi: %s\n",message);
- fprintf(stderr," in substitution file '%s' at line %d:\n %s",
- psubFile->substitutionName,
- psubFile->lineNum,psubFile->inputBuffer);
+ fprintf(stderr, "msi: %s\n",message);
+ fprintf(stderr, " in substitution file '%s' at line %d:\n %s",
+ psubFile->substitutionName, psubFile->lineNum, psubFile->inputBuffer);
}
static tokenType subGetNextToken(subFile *psubFile)
{
- char *p;
- char *pto;
+ char *p, *pto;
+ ENTER;
p = psubFile->pnextChar;
- if(!p) { psubFile->token = tokenEOF; return(tokenEOF);}
- if(*p==0 || *p=='\n' || *p=='#') {
- p = subGetNextLine(psubFile);
- if(!p) { psubFile->token = tokenEOF; return(tokenEOF);}
- else { psubFile->token = tokenSeparater; return(tokenSeparater);}
+ if (!p) {
+ STEP("Got EOF");
+ psubFile->token = tokenEOF;
+ goto done;
}
- while(isspace((int) *p)) p++;
- if(*p=='{') {
+
+ if (*p == 0 || *p == '\n' || *p == '#') {
+ STEP("Got newline/comment");
+ p = subGetNextLine(psubFile);
+ psubFile->token = p ? tokenSeparator : tokenEOF;
+ goto done;
+ }
+
+ while (isspace((int) *p)) p++;
+ if (*p == '{') {
+ STEP("Got '{'");
psubFile->token = tokenLBrace;
psubFile->pnextChar = ++p;
- return(tokenLBrace);
+ goto done;
}
- if(*p=='}') {
+ if (*p == '}') {
+ STEP("Got '}'");
psubFile->token = tokenRBrace;
psubFile->pnextChar = ++p;
- return(tokenRBrace);
+ goto done;
}
- if(*p==0 || isspace((int) *p) || *p==',') {
- while (isspace((int) *p) || *p==',') p++;
- psubFile->token = tokenSeparater;
+ if (*p == 0 || isspace((int) *p) || *p == ',') {
+ STEP("Got space/comma");
+ while (isspace((int) *p) || *p == ',') p++;
+ psubFile->token = tokenSeparator;
psubFile->pnextChar = p;
- return(tokenSeparater);
+ goto done;
}
/*now handle quoted strings*/
- if(*p=='"') {
+ if (*p == '"') {
+ STEP("Got '\"'");
pto = &psubFile->string[0];
*pto++ = *p++;
- while(*p!='"') {
- if(*p==0 || *p=='\n') {
- subFileErrPrint(psubFile,"Strings must be on single line\n");
- exit(1);
+ while (*p != '"') {
+ if (*p == 0 || *p == '\n') {
+ subFileErrPrint(psubFile, "Strings must be on single line\n");
+ abortExit(1);
}
- /*allow escape for imbeded quote*/
- if((*p=='\\') && *(p+1)=='"') {
+ /*allow escape for embeded quote*/
+ if ((p[0] == '\\') && p[1] == '"') {
*pto++ = *p++;
*pto++ = *p++;
continue;
@@ -856,40 +1086,53 @@ static tokenType subGetNextToken(subFile *psubFile)
psubFile->pnextChar = p;
*pto = 0;
psubFile->token = tokenString;
- return(tokenString);
+ goto done;
}
+
/*Now take anything up to next non String token and not space*/
pto = &psubFile->string[0];
- while(!isspace((int) *p) && (strspn(p,"\",{}")==0)) *pto++ = *p++;
+
+ while (!isspace((int) *p) && (strspn(p, "\",{}") == 0))
+ *pto++ = *p++;
*pto = 0;
+ STEPS("Got bareword", psubFile->string);
+
psubFile->pnextChar = p;
psubFile->token = tokenString;
- return(tokenString);
+
+done:
+ EXITD(psubFile->token);
+ return psubFile->token;
}
-static void catMacroReplacements(subInfo *psubInfo,const char *value)
+static void catMacroReplacements(subInfo *psubInfo, const char *value)
{
- size_t len = strlen(value);
+ size_t len = strlen(value);
- if(psubInfo->size <= (psubInfo->curLength + len)) {
+ ENTER;
+ if (psubInfo->size <= (psubInfo->curLength + len)) {
size_t newsize = psubInfo->size + MAX_BUFFER_SIZE;
char *newbuf;
- if(newsize <= psubInfo->curLength + len)
+ STEP("Enlarging buffer");
+ if (newsize <= psubInfo->curLength + len)
newsize = psubInfo->curLength + len + 1;
- newbuf = calloc(1,newsize);
- if(!newbuf) {
- fprintf(stderr,"calloc failed for size %lu\n",
- (unsigned long) newsize);
- exit(1);
+ newbuf = calloc(1, newsize);
+ if (!newbuf) {
+ fprintf(stderr, "calloc failed for size %lu\n",
+ (unsigned long) newsize);
+ abortExit(1);
}
- if(psubInfo->macroReplacements) {
- memcpy(newbuf,psubInfo->macroReplacements,psubInfo->curLength);
+ if (psubInfo->macroReplacements) {
+ memcpy(newbuf, psubInfo->macroReplacements, psubInfo->curLength);
free(psubInfo->macroReplacements);
}
psubInfo->size = newsize;
psubInfo->macroReplacements = newbuf;
}
- strcat(psubInfo->macroReplacements,value);
+
+ STEPS("Appending", value);
+ strcat(psubInfo->macroReplacements, value);
psubInfo->curLength += len;
+ EXIT;
}
diff --git a/modules/database/src/ioc/dbtemplate/msi.html b/modules/database/src/ioc/dbtemplate/msi.html
index ff4341ead..b5929fc79 100644
--- a/modules/database/src/ioc/dbtemplate/msi.html
+++ b/modules/database/src/ioc/dbtemplate/msi.html
@@ -34,10 +34,10 @@ be written to stdout unless the -o option is given.
-V
-
Verbose warnings; if this parameter is specified then any undefined
- macro discovered in the template file which does not have an associated
- default value is considered an error. An error message is generated, and
- when msi terminates it will do so with an exit status of 2.
+
Verbose warnings; if this parameter is specified then any undefined or
+ recursive macros discovered in the template will be considered an error and
+ will be marked in the output file. An error message will be shown, and when
+ msi terminates it will do so with an exit status of 2.