diff --git a/.gitattributes b/.gitattributes index 5221674ea..8162ae0f2 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,9 +1,11 @@ -.ci/ export-ignore -.tools/ export-ignore -.github/ export-ignore -.appveyor/ export-ignore -.appveyor.yml export-ignore -README export-subst +.appveyor.yml export-ignore +.appveyor/ export-ignore +.ci/ export-ignore +.github/ export-ignore +.gitmodules export-ignore +.readthedocs.yml export-ignore +.tools/ export-ignore +README export-subst #Which files need CRLF handling # default to automatic diff --git a/.gitignore b/.gitignore index 46b32dc1a..046630981 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /lib/ /db/ /dbd/ +/doc/ /html/ /include/ /templates/ diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 000000000..49b5b0d42 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,36 @@ +# .readthedocs.yml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +build: + os: ubuntu-22.04 + tools: + python: "3.9" + apt_packages: + - graphviz + - rsync + jobs: + pre_build: + - make inc + - make -C documentation rtd + +# Build documentation in the documentation/O.Common directory with Sphinx +sphinx: + configuration: documentation/O.Common/rtd-src/conf.py + +# Build documentation with MkDocs +#mkdocs: +# configuration: mkdocs.yml + +# Optionally build your docs in additional formats such as PDF and ePub +#formats: [] + +submodules: + exclude: all + +python: + install: + - requirements: documentation/requirements.txt diff --git a/README b/README index 05d441f5b..b3b8167f5 100644 --- a/README +++ b/README @@ -22,6 +22,6 @@ training materials, additional components, links to other websites etc. is available on the EPICS home page at https://epics.anl.gov/ -Thu, 27 Jun 2024 20:55:03 -0500 -57c930fbee53b3ca8ab4e8b5461c54f1518f9676 +Fri, 21 Feb 2025 17:31:33 -0600 +86154953f57b1796e7cb81bbc807eae120b9e840 https://code.launchpad.net/epics-base diff --git a/configure/CONFIG.gnuCommon b/configure/CONFIG.gnuCommon index f721b065e..a6305990b 100644 --- a/configure/CONFIG.gnuCommon +++ b/configure/CONFIG.gnuCommon @@ -27,13 +27,17 @@ RANLIB = $(GNU_BIN)/$(CMPLR_PREFIX)ranlib$(CMPLR_SUFFIX) ASAN_FLAGS_YES = -fsanitize=address ASAN_LDFLAGS_YES = $(ASAN_FLAGS_YES) +# It makes sense to include debugging symbols even in optimized builds +# in case you want to attach gdb to the process or examine a core-dump. +# This does cost disk space, but not memory as debug symbols are not +# loaded into RAM when the binary is loaded. 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 -Werror-implicit-function-declaration WARN_CFLAGS_NO = -w -OPT_CFLAGS_YES = -O3 +OPT_CFLAGS_YES = -O3 -g OPT_CFLAGS_NO = -g PROF_CXXFLAGS_YES = -p @@ -42,7 +46,7 @@ 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_YES = -O3 -g OPT_CXXFLAGS_NO = -g CODE_LDFLAGS = $(PROF_CXXFLAGS_$(PROFILE)) $(GPROF_CXXFLAGS_$(GPROF)) diff --git a/configure/CONFIG_BASE b/configure/CONFIG_BASE index a86076520..a737b37fc 100644 --- a/configure/CONFIG_BASE +++ b/configure/CONFIG_BASE @@ -45,10 +45,13 @@ FIND_PM = $(wildcard $(EPICS_BASE)/lib/perl/$(1)) #--------------------------------------------------------------- # EPICS Base build tools and tool flags +PODTOMD_pl = $(TOOLS)/podToMD.pl +PODTOMD = $(PERL) $(PODTOMD_pl) PODTOHTML_pl = $(TOOLS)/podToHtml.pl PODTOHTML_dep = $(PODTOHTML_pl) $(call FIND_PM,EPICS/PodHtml.pm) PODTOHTML = $(PERL) $(PODTOHTML_pl) CONVERTRELEASE = $(PERL) $(call FIND_TOOL,convertRelease.pl) +FILTERMAKEFLAGS = $(PERL) $(call FIND_TOOL,filterMakeflags.pl) FULLPATHNAME = $(PERL) $(TOOLS)/fullPathName.pl GENVERSIONHEADER = $(PERL) $(TOOLS)/genVersionHeader.pl $(QUIET_FLAG) $(QUESTION_FLAG) @@ -65,6 +68,24 @@ INSTALL_LIBRARY = $(INSTALL) MKMF = $(PERL) $(TOOLS)/mkmf.pl REPLACEVAR = $(PERL) $(TOOLS)/replaceVAR.pl +#--------------------------------------------------------------- +# How to portably check the flags to make +# GNUmake versions before 4.0 gave different values +makeflags := $(shell $(FILTERMAKEFLAGS) $(MAKEFLAGS)) +define checkflags + make-$1 := $(findstring $1,$(makeflags)) +endef +# This is extensible to most single letter flags: +$(foreach flag,s q, $(eval $(call checkflags,$(flag)))) + +# Silent builds - suppress messages during 'make -s' +NOP = : +ECHO = @$(if $(make-s),$(NOP),echo) +QUIET_FLAG := $(if $(make-s),-q,) + +# Convert 'make -q' flag into '-i' for genVersionHeader.pl +QUESTION_FLAG := $(if $(make-q),-i,) + #--------------------------------------------------------------- # tools for cleaning out unwanted files CVSCLEAN = $(call FIND_TOOL,cvsclean.pl) diff --git a/configure/CONFIG_BASE_VERSION b/configure/CONFIG_BASE_VERSION index 78e75be79..86319c032 100644 --- a/configure/CONFIG_BASE_VERSION +++ b/configure/CONFIG_BASE_VERSION @@ -48,11 +48,11 @@ EPICS_VERSION = 7 EPICS_REVISION = 0 # EPICS_MODIFICATION must be a number >=0 and <256 -EPICS_MODIFICATION = 8 +EPICS_MODIFICATION = 9 # EPICS_PATCH_LEVEL must be a number (win32 resource file requirement) # Not included in the official EPICS version number if zero -EPICS_PATCH_LEVEL = 1 +EPICS_PATCH_LEVEL = 0 # Immediately after an official release the EPICS_PATCH_LEVEL is incremented # and the -DEV suffix is added (similar to the Maven -SNAPSHOT versions) diff --git a/configure/CONFIG_CA_VERSION b/configure/CONFIG_CA_VERSION index 54d24a22b..905f0d590 100644 --- a/configure/CONFIG_CA_VERSION +++ b/configure/CONFIG_CA_VERSION @@ -2,7 +2,7 @@ EPICS_CA_MAJOR_VERSION = 4 EPICS_CA_MINOR_VERSION = 14 -EPICS_CA_MAINTENANCE_VERSION = 4 +EPICS_CA_MAINTENANCE_VERSION = 5 # Development flag, set to zero for release versions diff --git a/configure/CONFIG_COMMON b/configure/CONFIG_COMMON index 742e54ae4..9165ae294 100644 --- a/configure/CONFIG_COMMON +++ b/configure/CONFIG_COMMON @@ -81,23 +81,6 @@ FINAL_LOCATION = $(INSTALL_ABSOLUTE) # IOC's view of install path IOCS_APPL_TOP = $(INSTALL_ABSOLUTE) -#------------------------------------------------------- -# How to portably check the flags to make -makeflags := $(firstword $(filter-out -,$(filter-out --%,$(MAKEFLAGS)))) -define checkflags - make-$1 := $(findstring $1,$(makeflags)) -endef -# This is extensible to most single letter flags: -$(foreach flag,s q, $(eval $(call checkflags,$(flag)))) - -# Silent builds - suppress messages during 'make -s' -NOP = : -ECHO = @$(if $(make-s),$(NOP),echo) -QUIET_FLAG := $(if $(make-s),-q,) - -# Convert 'make -q' flag into '-i' for genVersionHeader.pl -QUESTION_FLAG := $(if $(make-q),-i,) - #------------------------------------------------------- ifdef T_A @@ -422,9 +405,9 @@ INSTALL_LIB_INSTALLS = $(addprefix $(INSTALL_LIB)/,$(notdir $(LIB_INSTALLS))) #--------------------------------------------------------------- # Installed file permissions -BIN_PERMISSIONS = 555 -LIB_PERMISSIONS = 444 -SHRLIB_PERMISSIONS = 555 +BIN_PERMISSIONS = 755 +LIB_PERMISSIONS = 644 +SHRLIB_PERMISSIONS = 755 INSTALL_PERMISSIONS = 444 #--------------------------------------------------------------- @@ -486,4 +469,9 @@ COMMON_INC += $(filter $(COMMON_DIR)/%, \ $(foreach dir, $(ALL_SRC_DIRS), \ $(addsuffix /$(file), $(dir)))) $(COMMON_DIR)/$(file)))) +COMMON_DOCS += $(filter $(COMMON_DIR)/%, \ + $(foreach file, $(DOCS), \ + $(firstword $(wildcard $(file) \ + $(foreach dir, $(ALL_SRC_DIRS), \ + $(addsuffix /$(file), $(dir)))) $(COMMON_DIR)/$(file)))) endif diff --git a/configure/CONFIG_DATABASE_MODULE b/configure/CONFIG_DATABASE_MODULE index 14c219c2c..22f2bce6e 100644 --- a/configure/CONFIG_DATABASE_MODULE +++ b/configure/CONFIG_DATABASE_MODULE @@ -12,6 +12,7 @@ DBDTORECTYPEH_pl = $(EPICS_BASE_HOST_BIN)/dbdToRecordtypeH.pl DBDTORECTYPEH_dep = $(DBDTORECTYPEH_pl) $(call FIND_PM,DBD/Rec*.pm) DBDTOMENUH_pl = $(EPICS_BASE_HOST_BIN)/dbdToMenuH.pl DBDTOMENUH_dep = $(DBDTOMENUH_pl) $(call FIND_PM,DBD/Menu.pm) +DBDTOMD_pl = $(EPICS_BASE_HOST_BIN)/dbdToMD.pl DBDTOHTML_pl = $(EPICS_BASE_HOST_BIN)/dbdToHtml.pl DBDTOHTML_dep = $(DBDTOHTML_pl) $(call FIND_PM,EPICS/Pod*Html.pm) REGRECDEVDRV_pl = $(EPICS_BASE_HOST_BIN)/registerRecordDeviceDriver.pl @@ -21,6 +22,7 @@ REGRECDEVDRV_dep = $(REGRECDEVDRV_pl) DBEXPAND = $(PERL) $(DBDEXPAND_pl) DBTORECORDTYPEH = $(PERL) $(DBDTORECTYPEH_pl) DBTOMENUH = $(PERL) $(DBDTOMENUH_pl) +DBDTOMD = $(PERL) $(DBDTOMD_pl) DBDTOHTML = $(PERL) $(DBDTOHTML_pl) REGISTERRECORDDEVICEDRIVER = $(PERL) $(REGRECDEVDRV_pl) diff --git a/configure/CONFIG_DATABASE_VERSION b/configure/CONFIG_DATABASE_VERSION index 8ee2abff6..546c63110 100644 --- a/configure/CONFIG_DATABASE_VERSION +++ b/configure/CONFIG_DATABASE_VERSION @@ -1,8 +1,8 @@ # Version number for the database APIs and shared library EPICS_DATABASE_MAJOR_VERSION = 3 -EPICS_DATABASE_MINOR_VERSION = 23 -EPICS_DATABASE_MAINTENANCE_VERSION = 1 +EPICS_DATABASE_MINOR_VERSION = 24 +EPICS_DATABASE_MAINTENANCE_VERSION = 0 # Development flag, set to zero for release versions diff --git a/configure/CONFIG_LIBCOM_VERSION b/configure/CONFIG_LIBCOM_VERSION index 8be90d9c8..d1546a965 100644 --- a/configure/CONFIG_LIBCOM_VERSION +++ b/configure/CONFIG_LIBCOM_VERSION @@ -1,8 +1,8 @@ # Version number for the libcom APIs and shared library EPICS_LIBCOM_MAJOR_VERSION = 3 -EPICS_LIBCOM_MINOR_VERSION = 23 -EPICS_LIBCOM_MAINTENANCE_VERSION = 1 +EPICS_LIBCOM_MINOR_VERSION = 24 +EPICS_LIBCOM_MAINTENANCE_VERSION = 0 # Development flag, set to zero for release versions diff --git a/configure/CONFIG_SITE_ENV b/configure/CONFIG_SITE_ENV index bacbc14a5..bcc3bdf7a 100644 --- a/configure/CONFIG_SITE_ENV +++ b/configure/CONFIG_SITE_ENV @@ -92,3 +92,6 @@ EPICS_IOC_LOG_FILE_NAME= EPICS_IOC_LOG_FILE_COMMAND= EPICS_IOC_LOG_FILE_LIMIT=1000000 +# Set to 'YES' to call abort() rather than suspend the current thread +# when an assert() fails +EPICS_ABORT_ON_ASSERT=NO diff --git a/configure/RULES.Db b/configure/RULES.Db index f08af423d..4a984f8c3 100644 --- a/configure/RULES.Db +++ b/configure/RULES.Db @@ -197,7 +197,7 @@ endif # build dependancies, clean rule inc: $(COMMON_INC) $(INSTALL_INC) $(COMMON_DBDS) $(COMMON_DBDCATS) \ - $(INSTALL_DBDS) $(INSTALL_DBD_INSTALLS) + $(INSTALL_DBDS) $(INSTALL_DBD_INSTALLS) $(COMMON_DOCS) build: $(COMMON_DBS) $(INSTALL_DBS) \ $(DBDDEPENDS_FILES) $(TARGETS) \ @@ -427,6 +427,26 @@ $(foreach file, $(DBD_INSTALLS), $(eval $(call DBD_INSTALLS_template, $(file)))) .PRECIOUS: $(COMMON_DBDS) $(COMMON_DIR)/%.dbd +#--------------------------------------------------------------- +# Markdown files + +$(COMMON_DIR)/%.md: %.dbd.pod $(DBDTOMD_pl) + @$(RM) $(notdir $@) + $(DBDTOMD) $(DBDFLAGS) -o $(notdir $@) $< + @$(MV) $(notdir $@) $@ + +$(COMMON_DIR)/%.md: %.pod $(PODTOMD_pl) + @$(RM) $(notdir $@) + $(PODTOMD) -o $(notdir $@) $< + @$(MV) $(notdir $@) $@ + +$(COMMON_DIR)/%.md: ../%.pl $(PODTOMD_pl) + @$(RM) $(notdir $@) + $(PODTOMD) -o $(notdir $@) $< + @$(MV) $(notdir $@) $@ + +.PRECIOUS: $(COMMON_DIR)/%.md + #--------------------------------------------------------------- # HTML files diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index cbfaae99d..e38bbcf08 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -22,7 +22,8 @@ vpath %.cpp $(USR_VPATH) $(ALL_SRC_DIRS) vpath %.rc $(USR_VPATH) $(ALL_SRC_DIRS) vpath %.h $(USR_VPATH) $(ALL_SRC_DIRS) vpath %.hpp $(USR_VPATH) $(ALL_SRC_DIRS) -vpath %.html $(USR_VPATH) $(ALL_SRC_DIRS) +vpath %.html $(USR_VPATH) $(ALL_SRC_DIRS) $(COMMON_DIR) +vpath %.md $(USR_VPATH) $(ALL_SRC_DIRS) $(COMMON_DIR) vpath %.skel.static $(USR_VPATH) $(ALL_SRC_DIRS) vpath %.y $(USR_VPATH) $(ALL_SRC_DIRS) vpath %.l $(USR_VPATH) $(ALL_SRC_DIRS) @@ -111,7 +112,7 @@ endif #--------------------------------------------------------------- # Read dependency files -ifneq (inc,$(strip $(MAKECMDGOALS))) +ifneq ($(filter-out inc,$(strip $(MAKECMDGOALS))),) ifneq (,$(strip $(HDEPENDS_FILES))) $(filter-out $(wildcard *$(DEP)), $(HDEPENDS_FILES)): | $(COMMON_INC) -include $(HDEPENDS_FILES) @@ -165,12 +166,12 @@ build: $(OBJSNAME) $(LIBTARGETS) $(PRODTARGETS) $(TESTPRODTARGETS) \ $(TARGETS) $(TESTSCRIPTS) $(INSTALL_LIB_INSTALLS) inc: $(COMMON_INC) $(INSTALL_INC) $(INSTALL_CONFIGS) $(INSTALLS_CFG) \ - $(INSTALL_HTMLS) $(INSTALLS_PERL_MODULES) $(INSTALL_SCRIPTS) + $(INSTALL_HTMLS) $(INSTALLS_PERL_MODULES) $(INSTALL_SCRIPTS) \ + $(INSTALL_DOCS) buildInstall: \ $(INSTALL_PROD) $(INSTALL_MUNCHS) \ $(INSTALL_TCLLIBS) $(INSTALL_TCLINDEX) \ - $(INSTALL_DOCS) \ $(INSTALL_OBJS) \ $(INSTALL_TEMPLATE) \ $(INSTALL_BIN_INSTALLS) @@ -555,6 +556,10 @@ $(INSTALL_INCLUDE)/compiler/$(CMPLR_CLASS)/%: % $(ECHO) "Installing compiler dependent include file $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) +$(INSTALL_DOC)/%: $(COMMON_DIR)/% + $(ECHO) "Installing generated doc $@" + @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) + $(INSTALL_DOC)/%: % $(ECHO) "Installing doc $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(INSTALL_DOC) diff --git a/configure/os/CONFIG.Common.RTEMS b/configure/os/CONFIG.Common.RTEMS index a736addaa..c58ac7bdc 100644 --- a/configure/os/CONFIG.Common.RTEMS +++ b/configure/os/CONFIG.Common.RTEMS @@ -65,7 +65,7 @@ CXXFLAGS = $($(BUILD_CLASS)_CXXFLAGS) $(POSIX_CXXFLAGS) $(OPT_CXXFLAGS)\ $(USR_CXXFLAGS) $(CMD_CXXFLAGS) $(ARCH_DEP_CXXFLAGS) $(CODE_CXXFLAGS)\ $(STATIC_CXXFLAGS) $(OP_SYS_CXXFLAGS) $(LIBRARY_SRC_CFLAGS) -LDFLAGS = $(OPT_LDFLAGS) $(TARGET_LDFLAGS) $(USR_LDFLAGS) $(CMD_LDFLAGS)\ +LDFLAGS += $(OPT_LDFLAGS) $(TARGET_LDFLAGS) $(USR_LDFLAGS) $(CMD_LDFLAGS)\ $(POSIX_LDFLAGS) $(ARCH_DEP_LDFLAGS) $(DEBUG_LDFLAGS) $(OP_SYS_LDFLAGS)\ $($(BUILD_CLASS)_LDFLAGS) $(RUNTIME_LDFLAGS) $(CODE_LDFLAGS) @@ -102,15 +102,47 @@ OS_CLASS = RTEMS # Operating system compile & link flags OP_SYS_CFLAGS += -D__LINUX_ERRNO_EXTENSIONS__ -OP_SYS_CFLAGS_NET_yes = -DRTEMS_LEGACY_STACK -OP_SYS_CFLAGS += $(OP_SYS_CFLAGS_NET_$(RTEMS_HAS_NETWORKING)) +# Has RTEMS been built with the internal legacy stack? +ifeq ($(RTEMS_LEGACY_NETWORKING_INTERNAL),yes) +RTEMS_HAS_NETWORKING = yes +RTEMS_NETWORKING = legacy_internal +endif + +# Has RTEMS been built with the legacy stack as a separate package? +ifeq ($(RTEMS_LEGACY_NETWORKING),yes) +RTEMS_HAS_NETWORKING = yes +RTEMS_NETWORKING = legacy +endif -ifeq ($(RTEMS_HAS_POSIX_API),yes) -POSIX_CPPFLAGS = -D_GNU_SOURCE -D_DEFAULT_SOURCE +# Has RTEMS been built with the libbsd stack as a separate package? +ifeq ($(RTEMS_BSD_NETWORKING),yes) +RTEMS_HAS_NETWORKING = yes +RTEMS_NETWORKING = bsd endif -OP_SYS_LDLIBS_posix_NET_yes = -ltftpfs -lnfs -lz -ltelnetd -OP_SYS_LDLIBS_posix_NET_no = -ltftpfs -lbsd -lz +RTEMS_LEGACY_NET_LIB_no= + +# Legacy network with RTEMS 5 and earlier +RTEMS_NET_LIB_legacy_internal=-lnfs +OP_SYS_CFLAGS_NET_legacy_internal = -DRTEMS_LEGACY_STACK + +# Legacy network with RTEMS 6 is a separate package and library +RTEMS_NET_LIB_legacy=-lnfs -lnetworking -lnfs +OP_SYS_CFLAGS_NET_legacy = -DRTEMS_LEGACY_STACK + +# LibBSD network with RTEMS 5 and 6 is a separate package and library +RTEMS_NET_LIB_bsd=-lbsd +OP_SYS_CFLAGS_NET_bsd = -DRTEMS_LIBBSD_STACK + +# Set the networking flags +OP_SYS_CFLAGS += $(OP_SYS_CFLAGS_NET_$(RTEMS_NETWORKING)) + +POSIX_CPPFLAGS_posix = -D_GNU_SOURCE -D_DEFAULT_SOURCE +POSIX_CPPFLAGS = $(POSIX_CPPFLAGS_$(OS_API)) + +OP_SYS_LDLIBS_posix_NET_yes = -ltftpfs -lz -ltelnetd +OP_SYS_LDLIBS_posix_NET_yes += $(RTEMS_NET_LIB_$(RTEMS_NETWORKING)) +OP_SYS_LDLIBS_posix_NET_no = -ltftpfs -lz OP_SYS_LDLIBS_score_NET_yes = -lnfs OP_SYS_LDLIBS_score_NET_no = -lnfs OP_SYS_LDLIBS += -lrtemsCom -lCom diff --git a/configure/os/CONFIG.Common.RTEMS-mvme2700 b/configure/os/CONFIG.Common.RTEMS-mvme2700 index 82715277e..3b43ae45b 100644 --- a/configure/os/CONFIG.Common.RTEMS-mvme2700 +++ b/configure/os/CONFIG.Common.RTEMS-mvme2700 @@ -1,7 +1,6 @@ # # Author: Matt Rippa # -RTEMS_BSP = mvme2307 RTEMS_TARGET_CPU = powerpc ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL ARCH_DEP_CFLAGS += -DHAVE_PPCBUG @@ -23,3 +22,9 @@ define MUNCH_CMD endef include $(CONFIG)/os/CONFIG.Common.RTEMS + +ifeq ($(shell test $(RTEMS_VERSION) -ge 5; echo $$?),0) +RTEMS_BSP = mvme2700 +else +RTEMS_BSP = mvme2307 +endif diff --git a/configure/os/CONFIG.Common.RTEMS-pc386 b/configure/os/CONFIG.Common.RTEMS-pc386 index 9fc518d6b..77867f004 100644 --- a/configure/os/CONFIG.Common.RTEMS-pc386 +++ b/configure/os/CONFIG.Common.RTEMS-pc386 @@ -25,7 +25,7 @@ include $(CONFIG)/os/CONFIG.Common.RTEMS OP_SYS_LDFLAGS += -Wl,-Ttext,0x100000 # This check must appear after the above include -ifeq ($(RTEMS_VERSION),5) +ifeq ($(shell test $(RTEMS_VERSION) -ge 5; echo $$?),0) $(info *** This target is not compatible with the configured RTEMS version.) $(info *** Build the RTEMS-pc686 (-qemu) target for RTEMS 5.x) $(error Can't continue) diff --git a/configure/os/CONFIG.Common.RTEMS-pc686 b/configure/os/CONFIG.Common.RTEMS-pc686 index 882a2f3cb..bfe4c26e4 100644 --- a/configure/os/CONFIG.Common.RTEMS-pc686 +++ b/configure/os/CONFIG.Common.RTEMS-pc686 @@ -32,7 +32,7 @@ OP_SYS_LDFLAGS += -Wl,-Ttext,0x100000 # This check must appear after the above include -ifneq ($(RTEMS_VERSION),5) +ifeq ($(shell test $(RTEMS_VERSION) -lt 5; echo $$?),0) $(info *** This target is not compatible with the configured RTEMS version.) $(info *** Build the RTEMS-pc386 (-qemu) target for RTEMS 4.x) $(error Can't continue) diff --git a/configure/os/CONFIG.Common.iosCommon b/configure/os/CONFIG.Common.iosCommon index 5c248f38a..03d3cab8e 100644 --- a/configure/os/CONFIG.Common.iosCommon +++ b/configure/os/CONFIG.Common.iosCommon @@ -42,12 +42,6 @@ ARCH_DEP_LDFLAGS += $(ARCH_DEP_FLAGS) OP_SYS_CFLAGS += -isysroot $(SDK_DIR) OP_SYS_LDFLAGS += -isysroot $(SDK_DIR) -#-------------------------------------------------- -# Always compile in debugging symbol table information -# -OPT_CFLAGS_YES += -g -OPT_CXXFLAGS_YES += -g - #------------------------------------------------------- # Compiler definitions: diff --git a/configure/os/CONFIG.darwin-aarch64-debug.Common b/configure/os/CONFIG.darwin-aarch64-debug.Common new file mode 100644 index 000000000..49b58c879 --- /dev/null +++ b/configure/os/CONFIG.darwin-aarch64-debug.Common @@ -0,0 +1,11 @@ +# CONFIG.darwin-aarch64-debug.Common +# +# Definitions for darwin-aarch64-debug host builds - darwin-aarch64 target build with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.darwin-aarch64-debug.Common +#------------------------------------------------------- + +include $(CONFIG)/os/CONFIG.darwin-aarch64.Common + +# Removes -O optimization and adds -g compile option +HOST_OPT=NO + diff --git a/configure/os/CONFIG.darwin-aarch64.darwin-aarch64-debug b/configure/os/CONFIG.darwin-aarch64.darwin-aarch64-debug new file mode 100644 index 000000000..25d27b996 --- /dev/null +++ b/configure/os/CONFIG.darwin-aarch64.darwin-aarch64-debug @@ -0,0 +1,14 @@ +# CONFIG.darwin-aarch64.darwin-aarch64-debug +# +# Definitions for darwin-aarch64 host - darwin-aarch64-debug target build with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.darwin-aarch64.darwin-aarch64-debug +#------------------------------------------------------- + +-include $(CONFIG)/os/CONFIG.Common.darwin-aarch64 +-include $(CONFIG)/os/CONFIG.darwin-aarch64.darwin-aarch64 +-include $(CONFIG)/os/CONFIG_SITE.Common.darwin-aarch64 +-include $(CONFIG)/os/CONFIG_SITE.darwin-aarch64.darwin-aarch64 + + +BUILD_CLASS=HOST +HOST_OPT = NO diff --git a/configure/os/CONFIG.darwin-x86-debug.Common b/configure/os/CONFIG.darwin-x86-debug.Common new file mode 100644 index 000000000..4e9aca82b --- /dev/null +++ b/configure/os/CONFIG.darwin-x86-debug.Common @@ -0,0 +1,11 @@ +# CONFIG.darwin-x86-debug.Common +# +# Definitions for darwin-x86-debug host builds - darwin-x86 target build with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.darwin-x86-debug.Common +#------------------------------------------------------- + +include $(CONFIG)/os/CONFIG.darwin-x86.Common + +# Removes -O optimization and adds -g compile option +HOST_OPT=NO + diff --git a/configure/os/CONFIG.darwinCommon.darwinCommon b/configure/os/CONFIG.darwinCommon.darwinCommon index c0a69f46c..2c12b2fbb 100644 --- a/configure/os/CONFIG.darwinCommon.darwinCommon +++ b/configure/os/CONFIG.darwinCommon.darwinCommon @@ -39,12 +39,6 @@ OP_SYS_CFLAGS += -fno-common # OP_SYS_CPPFLAGS += -Ddarwin -# -# Always compile in debugging symbol table information -# -OPT_CFLAGS_YES += -g -OPT_CXXFLAGS_YES += -g - # # Libraries for command-line editing. # diff --git a/configure/os/CONFIG.freebsd-x86_64.freebsd-x86_64 b/configure/os/CONFIG.freebsd-x86_64.freebsd-x86_64 index 0ad2bfcf8..5e8b691d8 100644 --- a/configure/os/CONFIG.freebsd-x86_64.freebsd-x86_64 +++ b/configure/os/CONFIG.freebsd-x86_64.freebsd-x86_64 @@ -2,13 +2,23 @@ # Definitions for freebsd-x86_64 host - freebsd-x86_64 target builds # Sites may override these definitions in CONFIG_SITE.freebsd-x86_64.freebsd-x86_64 #------------------------------------------------------- -GNU_DIR=/usr/local +GNU_DIR=/usr # Include common gnu compiler definitions include $(CONFIG)/CONFIG.gnuCommon +GNU_BIN = $(GNU_DIR)/bin +GNU_LIB = $(GNU_DIR)/lib + +CMPLR_CLASS = clang + +CC = $(GNU_BIN)/$(CMPLR_PREFIX)cc$(CMPLR_SUFFIX) +CCC = $(GNU_BIN)/$(CMPLR_PREFIX)c++$(CMPLR_SUFFIX) +CPP = $(CC) -x c -E + +GNU_LDLIBS_YES = + STATIC_LDFLAGS_YES= -Wl,-Bstatic STATIC_LDFLAGS_NO= STATIC_LDLIBS_YES= -Wl,-Bdynamic STATIC_LDLIBS_NO= - diff --git a/configure/os/CONFIG.win32-x86.win32-x86 b/configure/os/CONFIG.win32-x86.win32-x86 index 767f4c7fa..d45a48041 100644 --- a/configure/os/CONFIG.win32-x86.win32-x86 +++ b/configure/os/CONFIG.win32-x86.win32-x86 @@ -16,7 +16,7 @@ OPT_WHOLE_PROGRAM = YES WINLINK = link -RCCMD = rc -l 0x409 $(INCLUDES) -fo $@ $< +RCCMD = rc -nologo -l 0x409 $(INCLUDES) -fo $@ $< ARCMD = lib -nologo -verbose -out:$@ $(LIB_OPT_LDFLAGS) $(LIBRARY_LD_OBJS) diff --git a/configure/os/CONFIG_SITE.cygwin-x86.cygwin-x86 b/configure/os/CONFIG_SITE.cygwin-x86.cygwin-x86 index 46a1eccea..3125c7a64 100644 --- a/configure/os/CONFIG_SITE.cygwin-x86.cygwin-x86 +++ b/configure/os/CONFIG_SITE.cygwin-x86.cygwin-x86 @@ -6,11 +6,3 @@ # GNU_DIR used when COMMANDLINE_LIBRARY is READLINE #GNU_DIR=C:/cygwin - -# It makes sense to include debugging symbols even in optimized builds -# in case you want to attach gdb to the process or examine a core-dump. -# This does cost disk space, but not memory as debug symbols are not -# loaded into RAM when the binary is loaded. -OPT_CFLAGS_YES += -g -OPT_CXXFLAGS_YES += -g - diff --git a/configure/os/CONFIG_SITE.linux-aarch64.linux-aarch64 b/configure/os/CONFIG_SITE.linux-aarch64.linux-aarch64 index e196135b7..f569951f1 100644 --- a/configure/os/CONFIG_SITE.linux-aarch64.linux-aarch64 +++ b/configure/os/CONFIG_SITE.linux-aarch64.linux-aarch64 @@ -3,10 +3,3 @@ # Site specific definitions for native linux-aarch64 builds #------------------------------------------------------- -# It makes sense to include debugging symbols even in optimized builds -# in case you want to attach gdb to the process or examine a core-dump. -# This does cost disk space, but not memory as debug symbols are not -# loaded into RAM when the binary is loaded. -OPT_CFLAGS_YES += -g -OPT_CXXFLAGS_YES += -g - diff --git a/configure/os/CONFIG_SITE.linux-x86.linux-x86 b/configure/os/CONFIG_SITE.linux-x86.linux-x86 index 5b2fa7c65..64810a835 100644 --- a/configure/os/CONFIG_SITE.linux-x86.linux-x86 +++ b/configure/os/CONFIG_SITE.linux-x86.linux-x86 @@ -3,13 +3,6 @@ # Site specific definitions for linux-x86 host - linux-x86 target builds #------------------------------------------------------- -# It makes sense to include debugging symbols even in optimized builds -# in case you want to attach gdb to the process or examine a core-dump. -# This does cost disk space, but not memory as debug symbols are not -# loaded into RAM when the binary is loaded. -OPT_CFLAGS_YES += -g -OPT_CXXFLAGS_YES += -g - # Uncomment the followings lines to build with CLANG instead of GCC. # #GNU = NO diff --git a/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 b/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 index 9178afd43..2f538151a 100644 --- a/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 +++ b/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 @@ -3,13 +3,6 @@ # Site specific definitions for linux-x86_64 host - linux-x86_64 target builds #------------------------------------------------------- -# It makes sense to include debugging symbols even in optimized builds -# in case you want to attach gdb to the process or examine a core-dump. -# This does cost disk space, but not memory as debug symbols are not -# loaded into RAM when the binary is loaded. -OPT_CFLAGS_YES += -g -OPT_CXXFLAGS_YES += -g - # Uncomment the followings lines to build with CLANG instead of GCC. # #GNU = NO diff --git a/configure/toolchain.c b/configure/toolchain.c index 79333198c..0a5276045 100644 --- a/configure/toolchain.c +++ b/configure/toolchain.c @@ -30,9 +30,26 @@ MSVC_VER = _MSC_VER #ifdef __rtems__ #include # if __RTEMS_MAJOR__>=5 -OS_API = posix + OS_API = posix # else -OS_API = score + OS_API = score +# endif +# if defined(RTEMS_NETWORKING) + /* legacy stack circa RTEMS <= 5 and networking internal to RTEMS */ + RTEMS_LEGACY_NETWORKING_INTERNAL = yes +# else +# if !defined(__has_include) + /* assume old GCC implies RTEMS < 5 with mis-configured BSP */ +# error rebuild BSP with --enable-network +# elif __has_include() + /* legacy stack circa RTEMS > 5 */ + RTEMS_LEGACY_NETWORKING = yes +# elif __has_include() + /* libbsd stack */ + RTEMS_BSD_NETWORKING = yes +# else +# error Cannot determine RTEMS network configuration +# endif # endif #endif @@ -47,8 +64,3 @@ COMMANDLINE_LIBRARY ?= EPICS #else COMMANDLINE_LIBRARY ?= $(strip $(if $(wildcard $(if $(GNU_DIR),$(GNU_DIR)/include/readline/readline.h)), READLINE, EPICS)) #endif - -#if defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE>2 -OP_SYS_CPPFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -#endif - diff --git a/debian/changelog b/debian/changelog index 2e19178aa..673de7d45 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,22 @@ -epics-base (7.0.8.1-1) UNRELEASED; urgency=medium +epics-base (7.0.9-3) unstable; urgency=medium + + * Remove epics-perl files already included in -dev + + -- Evan Daykin Mon, 23 Jun 2025 19:44:51 +0000 + +epics-base (7.0.9-2) unstable; urgency=medium + + * Skip channelMonitor test if no loopback iface available + + -- Evan Daykin Mon, 23 Jun 2025 15:48:12 +0000 + +epics-base (7.0.9-1) unstable; urgency=medium + + * New upstream version 7.0.9 + + -- Evan Daykin Thu, 22 May 2025 15:23:20 -0400 + +epics-base (7.0.8.1-1) unstable; urgency=medium * Upstream 7.0.8.1 diff --git a/debian/compat b/debian/compat deleted file mode 100644 index f599e28b8..000000000 --- a/debian/compat +++ /dev/null @@ -1 +0,0 @@ -10 diff --git a/debian/control b/debian/control index 7856fa981..9bcaa770e 100644 --- a/debian/control +++ b/debian/control @@ -3,15 +3,15 @@ Section: devel Priority: optional Maintainer: Evan Daykin Uploaders: Anton Derbenev -Build-Depends: debhelper (>= 10), epics-debhelper (>= 8.18), - libreadline6-dev | libreadline-dev, libncurses5-dev, perl, - mingw32 | g++-mingw-w64, python3 -Standards-Version: 4.3.0 +Build-Depends: debhelper (>= 13), epics-debhelper (>= 12.0), + libreadline6-dev | libreadline-dev, libncurses-dev, perl, + mingw32 | g++-mingw-w64, python3, debhelper-compat (= 13), +Standards-Version: 4.6.2 Vcs-Git: https://github.com/epicsdeb/epics-base.git Vcs-Browser: https://github.com/epicsdeb/epics-base Homepage: http://www.aps.anl.gov/epics/ -Package: libepics7.0.8.1 +Package: libepics7.0.9 Section: libs Architecture: any Multi-Arch: same @@ -29,7 +29,7 @@ Description: EPICS Base libraries for clients and servers Package: ioclogserver Section: admin Architecture: any -Depends: libepics7.0.8.1 (= ${binary:Version}), +Depends: libepics7.0.9 (= ${binary:Version}), epics-dev (= ${binary:Version}), sysvinit-utils (>= 3), ${misc:Depends}, ${shlibs:Depends}, adduser, systemd Description: Log recording daemon for EPICS IOCS @@ -43,7 +43,7 @@ Description: Log recording daemon for EPICS IOCS Package: epics-dev Architecture: any -Depends: python3, libepics7.0.8.1 (= ${binary:Version}), epics-catools (= ${binary:Version}), +Depends: python3, libepics7.0.9 (= ${binary:Version}), epics-catools (= ${binary:Version}), epics-pvatools (= ${binary:Version}), perl (>= 5.10.0~), libreadline6-dev, @@ -87,7 +87,7 @@ Description: EPICS Macro Substitution and Include tool Package: epics-catools Section: admin Architecture: any -Depends: libepics7.0.8.1 (= ${binary:Version}), +Depends: libepics7.0.9 (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends} Description: Command line utilities for EPICS The Experimental Physics and Industrial Control System is a collection of @@ -99,7 +99,7 @@ Description: Command line utilities for EPICS Package: epics-pvatools Section: admin Architecture: any -Depends: libepics7.0.8.1 (= ${binary:Version}), +Depends: libepics7.0.9 (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends} Description: Command line utilities for EPICS The Experimental Physics and Industrial Control System is a collection of @@ -110,7 +110,7 @@ Description: Command line utilities for EPICS Package: epics-perl Architecture: any -Depends: libepics7.0.8.1 (= ${binary:Version}), +Depends: libepics7.0.9 (= ${binary:Version}), ${shlibs:Depends}, ${perl:Depends}, ${misc:Depends} Conflicts: epics-libs (<= 3.14.10-7), epics-dev (<= 3.14.12.3-2), Description: Perl bindings for EPICS diff --git a/debian/copyright b/debian/copyright index 606c95655..69f1b3956 100644 --- a/debian/copyright +++ b/debian/copyright @@ -21,7 +21,7 @@ Copyright: 2002, The Regents of the University of California. SURA CEBAF 1993. License: EPICS -Files: src/libCom/yajl/* +Files: modules/libcom/src/yajl/* Copyright: 2010, Lloyd Hilaiel. License: BSDx diff --git a/debian/epics-catools.manpages b/debian/epics-catools.manpages index 74e14e278..b1f227300 100644 --- a/debian/epics-catools.manpages +++ b/debian/epics-catools.manpages @@ -5,3 +5,4 @@ debian/man/caput.1 debian/man/caRepeater.8 debian/man/makeBaseApp.1 debian/man/makeBaseExt.1 +debian/man/casw.1 diff --git a/debian/epics-dev.lintian-overrides b/debian/epics-dev.lintian-overrides index 4ce7bec0b..54246bfb2 100644 --- a/debian/epics-dev.lintian-overrides +++ b/debian/epics-dev.lintian-overrides @@ -1,9 +1,5 @@ epics-dev: unstripped-binary-or-object # dh_strip doesn't seem to want to touch usr/lib/epics/lib/*-debug/*.so -# not sure why? -epics-dev: hardening-no-fortify-functions usr/lib/epics/bin/linux-x86_64-debug/* -epics-dev: hardening-no-fortify-functions usr/lib/epics/lib/linux-x86_64-debug/* -# -debug targets are built with -O0. Fortify is disabled in this situation epics-dev: breakout-link # EPICS config files live in /etc/epics/configure. Shared libs live in the # standard /usr/lib, but accessed in most cases from $EPICS_BASE=/usr/lib/epics \ No newline at end of file diff --git a/debian/epics-dev.manpages b/debian/epics-dev.manpages index aa933a3df..36e8730e7 100644 --- a/debian/epics-dev.manpages +++ b/debian/epics-dev.manpages @@ -2,3 +2,7 @@ debian/man/softIoc.1 debian/man/softioc.1 debian/man/softIocPVA.1 debian/man/softiocpva.1 +debian/man/ascheck.1 +debian/man/iocLogServer.1 +debian/man/msi.1 + diff --git a/debian/epics-doc.install b/debian/epics-doc.install index 98e1215b4..8f56c347d 100644 --- a/debian/epics-doc.install +++ b/debian/epics-doc.install @@ -1 +1,2 @@ usr/lib/epics/html +usr/lib/epics/doc diff --git a/debian/epics-perl.install b/debian/epics-perl.install new file mode 100644 index 000000000..9271245aa --- /dev/null +++ b/debian/epics-perl.install @@ -0,0 +1 @@ +usr/lib/epics/lib/perl/ diff --git a/debian/ioclogserver.lintian-overrides b/debian/ioclogserver.lintian-overrides index 987945817..a5dacfe1f 100644 --- a/debian/ioclogserver.lintian-overrides +++ b/debian/ioclogserver.lintian-overrides @@ -1,4 +1,4 @@ -ioclogserver: systemd-service-file-refers-to-unusual-wantedby-target lib/systemd/system/ioclogserver@.service ioclogserver.target +ioclogserver: systemd-service-file-refers-to-unusual-wantedby-target ioclogserver.target [usr/lib/systemd/system/ioclogserver@.service] # All instances of ioclogserver@.service are wanted by ioclogserver.target # which in turn is wanted by multi-user.target. This allows all IOC log servers # to be started/stopped together. diff --git a/debian/libepics7.0.8.1.install b/debian/libepics7.0.9.install similarity index 100% rename from debian/libepics7.0.8.1.install rename to debian/libepics7.0.9.install diff --git a/debian/libepics7.0.8.1.lintian-overrides b/debian/libepics7.0.9.lintian-overrides similarity index 66% rename from debian/libepics7.0.8.1.lintian-overrides rename to debian/libepics7.0.9.lintian-overrides index 9f0fe4446..526b60b69 100644 --- a/debian/libepics7.0.8.1.lintian-overrides +++ b/debian/libepics7.0.9.lintian-overrides @@ -1,6 +1,6 @@ -libepics7.0.8.1: package-name-doesnt-match-sonames +libepics7.0.9: package-name-doesnt-match-sonames # Don't want to package the many Base libraries seperately # All have the same soname -libepics7.0.8.1: dev-pkg-without-shlib-symlink +libepics7.0.9: dev-pkg-without-shlib-symlink # Our policy is not to place the .so symlinks in the default search path # but rather in /usr/lib/epics/lib/*/ diff --git a/debian/man/ascheck.1 b/debian/man/ascheck.1 new file mode 100644 index 000000000..9ed92e78d --- /dev/null +++ b/debian/man/ascheck.1 @@ -0,0 +1,39 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" First parameter, NAME, should be all caps +.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection +.\" other parameters are allowed: see man(7), man(1) +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.TH "ASCHECK" "1" "June 3, 2025" "" "" +.SH "NAME" +ascheck \- check integrity of EPICS access security files +.SH "SYNOPSIS" +.B ascheck +[-S\ "mac=sub,..."] [< filename] + +.SH "DESCRIPTION" +.B ascheck +displays errors on stdout +when a file is redirected to it. +If no errors are detected it prints nothing. +Only syntax errors, not logic errors, are detected. +Thus it is still possible to get yourself in trouble. +The flag -S means a set of macro substitutions may appear. +This is just like the macro substitutions for dbLoadDatabase. + +.SH "OPTIONS" +.TP +.B -S +This option allows macro substitutions to be used in the file. +It is a single string of the form +.B "mac1=sub1,mac2=sub2,..." diff --git a/debian/man/casw.1 b/debian/man/casw.1 new file mode 100644 index 000000000..246eaa318 --- /dev/null +++ b/debian/man/casw.1 @@ -0,0 +1,47 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" First parameter, NAME, should be all caps +.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection +.\" other parameters are allowed: see man(7), man(1) +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.TH "CASW" "1" "June 3, 2025" "" "" +.SH "NAME" +casw \- Channel Access Server "beacon anomaly" logging +.SH "SYNOPSIS" +.B casw +.RI [-i\ ] + +.SH "DESCRIPTION" +.B casw +detects CA server beacon anomalies, which occur when a +new server joins the network, a server is rebooted, +network connectivity to a server is reestablished, +or if a server’s CPU exits a CPU load saturated state. +.sp 1 +CA clients with unresolved channels reset their search request scheduling +timers whenever they see a beacon anomaly. +.sp 1 +This program can be used to detect situations where there are too many +beacon anomalies. IP routing configuration problems may result in false +beacon anomalies that might cause CA clients to use unnecessary additional +network bandwidth and server CPU load when searching for unresolved channels. +.sp 1 +If there are no new CA servers appearing on the network, and network +connectivity remains constant, then casw should print no messages at all. +At higher interest levels the program prints a message for every +beacon that is received, and anomalous entries are flagged with a star. +.SH "OPTIONS" +.TP +.B -i +Set the verbosity of the output. Higher levels produce more output. +" diff --git a/debian/man/iocLogServer.1 b/debian/man/iocLogServer.1 new file mode 100644 index 000000000..348843b79 --- /dev/null +++ b/debian/man/iocLogServer.1 @@ -0,0 +1,63 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" First parameter, NAME, should be all caps +.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection +.\" other parameters are allowed: see man(7), man(1) +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.TH "IOCLOGSERVER" "1" "June 3, 2025" "" "" +.SH "NAME" +iocLogServer \- receives messages for all +.B iocLogClient +s in the local area network +.SH "SYNOPSIS" +.B iocLogServer + +.SH "DESCRIPTION" +.B iocLogServer +is +.B DEPRECATED +and will be removed in future releases. Consider using other logging solutions +such as +.B logstash or +.B graylog +instead. +.sp 1 +.B iocLogServer +runs on a host. It receives messages for all enabled iocLogClients in the local +area network. The messages are written to a file. +.sp 1 +To start a log server on a UNIX or PC workstation you must first set the +following environment variables and then run the executable iocLogServer +on your PC or UNIX workstation. +.sp 1 +.TP +.B EPICS_IOC_LOG_FILE_NAME +.sp 1 +The name and path to the log file. +.sp 1 +.TP +.B EPICS_IOC_LOG_FILE_LIMIT +.sp 1 +The maximum size in characters for the log file. If the file grows larger than +this limit the server will seek back to the beginning of the file and write +new messages over the old messages starting from the beginning. +If the value is zero then there is no limit on the size of the log file. +.sp 1 +.TP +.B EPICS_IOC_LOG_FILE_COMMAND +.sp 1 +A shell command string used to obtain the log file path name during +initialization and in response to SIGHUP. The new path name will +replace any path name supplied in +.B EPICS_IOC_LOG_FILE_NAME +. \ No newline at end of file diff --git a/debian/man/msi.1 b/debian/man/msi.1 new file mode 100644 index 000000000..104912672 --- /dev/null +++ b/debian/man/msi.1 @@ -0,0 +1,175 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" First parameter, NAME, should be all caps +.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection +.\" other parameters are allowed: see man(7), man(1) +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.TH "MSI" "1" "June 3, 2025" "" "" +.SH "NAME" +msi \- EPICS Macro Substitution and Include Tool +.SH "SYNOPSIS" +.B msi [\-V] [\-g] [\-o output_file] [\-I dir] [\-M subs] [\-S subfile] [template_file] +.SH "DESCRIPTION" +.B msi +is a general purpose macro substitution/include tool. It accepts as input an +ascii template file. It looks for lines containing two reserved command names: +.RI include +and +.RI substitute. +It also looks for and performs substitutions on macros of the form +.RI $(var) +and +.RI ${var}. +It uses the macLib routines from EPICS Base to perform the substitutions, +so it also accepts the default value and value definition syntax that +macLib implements. +.sp 1 +msi also allows substitutions to be specified via a separate substitution file. +This substitution file allows the same format as the substitution files accepted +by the EPICS IOC’s dbLoadTemplate command. +.SH "OPTIONS" +All parameters are optional. The +.B \-o, \-I, \-M, +and +.B \-S +switches may be separated from their associated value string by spaces if desired. +Output will be written to stdout unless the +.B \-o option is given. +Switches have the following meanings: +.TP +.B \-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. +.TP +.B \-g +When this flag is given, all macros defined in a substitution file will +have global scope and thus their values will persist until a new value is given +for this macro. This flag is provided for backwards compatibility as this was +the behavior of previous versions of msi, but it does not follow common scoping +rules and is discouraged. +.TP +.B \-o output_file +Output will be written to the specifed rather than to the standard output. +.TP +.B \-I +This parameter, which may be repeated or contain a colon-separated (or +semi-colon separated on Windows) list of directory paths, specifies a search +path for include commands. For example: +.sp 1 +.nf +msi \-I /home/mrk/examples:. \-I.. template +.fi +.sp 1 +specifies that all named files should be searched for in the following +locations, in the order given: +.sp 1 +.nf +\/home/mrk/examples +\&. (the current directory) +\&.. (the parent of the current directory) +.fi +.sp 1 +Note that relative path searching is handled as +.sp 1 +.nf +$ cat foo.substitutions +file rel/path/bar.template { + # contents +} +$ msi -I . -I /some/path foo.substitutions +.fi +.sp 1 +which will try to find +.RI bar.template +at the path +.RI ./rel/path/ +followed by +.RI /some/path/rel/path. +.TP +.B \-M +This parameter specifies macro values for the template instance. Multiple macro +values can be specified in one substitution parameter, or in multiple +\-M parameters. For example: +.sp 1 +.nf +msi \-M "a=aval,b=bval" \-Mc=cval template +.fi +.sp 1 +specifies that in the template file, each occurrence of: +.sp 1 +.RS +.br +.ti -2 +\[bu] +.RI $(a) +or +.RI ${a} +is replaced by +.RI aval +.ti -2 +\[bu] +.RI $(b) +or +.RI ${b} +is replaced by +.RI bval +.ti -2 +\[bu] +.RI $(c) +or +.RI ${c} +is replaced by +.RI cval +.TP +.B \-S +The substitution file. see +.br +.nf +https://docs.epics-controls.org/projects/base/en/latest/msi.html#substitution-file-format +.fi +.br +for format details. +.TP +.B template_file +The input file. If no file is specified then input is taken from stdin, i.e. +msi can be used as a filter. See below for a description of commands that +can be embedded in the template file. + +It is not possible to display usage by just typing msi since executing the +command with no arguments is a valid command. To show usage specify an illegal +switch, e.g. +.sp 1 +.nf +msi \-h +.fi +.sp 1 + +.SH "EXIT STATUS" +.RS +.br +.ti -2 +\[bu] +.B 0 +Success. +.br +.ti -2 +\[bu] +.B 1 +Can’t open/create file, or other I/O error. +.br +.ti -2 +\[bu] +.B 2 +Undefined macros encountered with the -V option specified. \ No newline at end of file diff --git a/debian/patches/0001-cap5-hardcode-default-base-path-in-perl-scripts.patch b/debian/patches/0001-cap5-hardcode-default-base-path-in-perl-scripts.patch index 140a381ba..966e9ea29 100644 --- a/debian/patches/0001-cap5-hardcode-default-base-path-in-perl-scripts.patch +++ b/debian/patches/0001-cap5-hardcode-default-base-path-in-perl-scripts.patch @@ -13,7 +13,7 @@ Index: epics-base/src/template/base/makeBaseApp.pl =================================================================== --- epics-base.orig/src/template/base/makeBaseApp.pl +++ epics-base/src/template/base/makeBaseApp.pl -@@ -65,7 +65,7 @@ sub ReplaceFilename { # (filename) +@@ -69,7 +69,7 @@ sub ReplaceFilename { # (filename) } elsif (-r "$base\@Common") { # Default version exists $base = '' unless ($filearch eq "Common" && -s $file); } else { # No default version @@ -22,7 +22,7 @@ Index: epics-base/src/template/base/makeBaseApp.pl } $file = $base; # Strip the @... part from the target name } -@@ -177,8 +177,7 @@ sub get_commandline_opts { #no args +@@ -181,8 +181,7 @@ sub get_commandline_opts { #no args } elsif ($ENV{EPICS_MBA_BASE}) { # third choice is env var EPICS_MBA_BASE $epics_base = UnixPath($ENV{EPICS_MBA_BASE}); } elsif ($command =~ m|/bin/|) { # assume script was run with full path to base @@ -36,7 +36,7 @@ Index: epics-base/src/template/ext/makeBaseExt.pl =================================================================== --- epics-base.orig/src/template/ext/makeBaseExt.pl +++ epics-base/src/template/ext/makeBaseExt.pl -@@ -126,8 +126,7 @@ sub get_commandline_opts { #no args +@@ -130,8 +130,7 @@ sub get_commandline_opts { #no args } elsif ($eBASE) { # third choice is env var EPICS_MBE_BASE $epics_base = UnixPath($eBASE); } elsif ($command =~ m|/bin/|) { # assume script was called with full path to base diff --git a/debian/patches/0003-allow-more-flexible-makefile-config.patch b/debian/patches/0003-allow-more-flexible-makefile-config.patch index 0b0e8c55f..40503c693 100644 --- a/debian/patches/0003-allow-more-flexible-makefile-config.patch +++ b/debian/patches/0003-allow-more-flexible-makefile-config.patch @@ -11,7 +11,7 @@ Index: epics-base/configure/CONFIG =================================================================== --- epics-base.orig/configure/CONFIG +++ epics-base/configure/CONFIG -@@ -117,6 +117,13 @@ ifneq ($(TOP_CFG_CONFIGS),) +@@ -122,6 +122,13 @@ ifneq ($(TOP_CFG_CONFIGS),) include $(TOP_CFG_CONFIGS) endif @@ -29,7 +29,7 @@ Index: epics-base/configure/RULES_BUILD =================================================================== --- epics-base.orig/configure/RULES_BUILD +++ epics-base/configure/RULES_BUILD -@@ -583,6 +583,11 @@ $(INSTALL_TEMPLATES_SUBDIR)/%: % +@@ -590,6 +590,11 @@ $(INSTALL_TEMPLATES_SUBDIR)/%: % include $(CONFIG)/RULES_EXPAND diff --git a/debian/patches/0005-rtems-use-tecla-for-better-shell-prompt.patch b/debian/patches/0005-rtems-use-tecla-for-better-shell-prompt.patch index 4b496dcab..26596200f 100644 --- a/debian/patches/0005-rtems-use-tecla-for-better-shell-prompt.patch +++ b/debian/patches/0005-rtems-use-tecla-for-better-shell-prompt.patch @@ -7,11 +7,11 @@ Subject: rtems: use tecla for better shell prompt configure/os/CONFIG_SITE.Common.RTEMS | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) -diff --git a/configure/os/CONFIG.Common.RTEMS b/configure/os/CONFIG.Common.RTEMS -index 03f0723..ee2c990 100644 ---- a/configure/os/CONFIG.Common.RTEMS -+++ b/configure/os/CONFIG.Common.RTEMS -@@ -156,7 +156,7 @@ ARFLAGS = rc +Index: epics-base/configure/os/CONFIG.Common.RTEMS +=================================================================== +--- epics-base.orig/configure/os/CONFIG.Common.RTEMS ++++ epics-base/configure/os/CONFIG.Common.RTEMS +@@ -215,7 +215,7 @@ ARFLAGS = rc #-------------------------------------------------- # Command-line input support @@ -20,11 +20,11 @@ index 03f0723..ee2c990 100644 LDLIBS_READLINE = -lreadline -lncurses #-------------------------------------------------- -diff --git a/configure/os/CONFIG_SITE.Common.RTEMS b/configure/os/CONFIG_SITE.Common.RTEMS -index 0b5d227..adb2530 100644 ---- a/configure/os/CONFIG_SITE.Common.RTEMS -+++ b/configure/os/CONFIG_SITE.Common.RTEMS -@@ -28,6 +28,6 @@ USE_GESYS = NO +Index: epics-base/configure/os/CONFIG_SITE.Common.RTEMS +=================================================================== +--- epics-base.orig/configure/os/CONFIG_SITE.Common.RTEMS ++++ epics-base/configure/os/CONFIG_SITE.Common.RTEMS +@@ -43,6 +43,6 @@ USE_GESYS = NO # Select the command-line-input library to use # diff --git a/debian/patches/0022-Change-shebang-lines-of-Perl-scripts-to-usr-bin-perl.patch b/debian/patches/0022-Change-shebang-lines-of-Perl-scripts-to-usr-bin-perl.patch index 80c343ac4..db36e0d2a 100644 --- a/debian/patches/0022-Change-shebang-lines-of-Perl-scripts-to-usr-bin-perl.patch +++ b/debian/patches/0022-Change-shebang-lines-of-Perl-scripts-to-usr-bin-perl.patch @@ -39,8 +39,8 @@ Index: epics-base/src/template/base/makeBaseApp.pl -#!/usr/bin/env perl +#!/usr/bin/perl - # Authors: Ralph Lange, Marty Kraimer, Andrew Johnson and Janet Anderson - + # SPDX-FileCopyrightText: 1997 Argonne National Laboratory + # Index: epics-base/src/template/ext/makeBaseExt.pl =================================================================== --- epics-base.orig/src/template/ext/makeBaseExt.pl @@ -49,8 +49,8 @@ Index: epics-base/src/template/ext/makeBaseExt.pl -#!/usr/bin/env perl +#!/usr/bin/perl - # Authors: Ralph Lange, Marty Kraimer, Andrew Johnson and Janet Anderson - + # SPDX-FileCopyrightText: 2000 Argonne National Laboratory + # Index: epics-base/src/tools/EpicsHostArch.pl =================================================================== --- epics-base.orig/src/tools/EpicsHostArch.pl @@ -277,8 +277,8 @@ Index: epics-base/modules/ca/src/template/top/caPerlApp/caget.pl -#!/usr/bin/env perl +#!/usr/bin/perl - use strict; - + # SPDX-FileCopyrightText: 2008 Argonne National Laboratory + # Index: epics-base/modules/ca/src/template/top/caPerlApp/cainfo.pl =================================================================== --- epics-base.orig/modules/ca/src/template/top/caPerlApp/cainfo.pl @@ -287,8 +287,8 @@ Index: epics-base/modules/ca/src/template/top/caPerlApp/cainfo.pl -#!/usr/bin/env perl +#!/usr/bin/perl - use strict; - + # SPDX-FileCopyrightText: 2008 Argonne National Laboratory + # Index: epics-base/modules/ca/src/template/top/caPerlApp/camonitor.pl =================================================================== --- epics-base.orig/modules/ca/src/template/top/caPerlApp/camonitor.pl @@ -297,8 +297,8 @@ Index: epics-base/modules/ca/src/template/top/caPerlApp/camonitor.pl -#!/usr/bin/env perl +#!/usr/bin/perl - use strict; - + # SPDX-FileCopyrightText: 2008 Argonne National Laboratory + # Index: epics-base/modules/ca/src/template/top/caPerlApp/caput.pl =================================================================== --- epics-base.orig/modules/ca/src/template/top/caPerlApp/caput.pl @@ -307,8 +307,8 @@ Index: epics-base/modules/ca/src/template/top/caPerlApp/caput.pl -#!/usr/bin/env perl +#!/usr/bin/perl - use strict; - + # SPDX-FileCopyrightText: 2008 Argonne National Laboratory + # Index: epics-base/modules/database/src/std/softIoc/makeInstallDir.pl =================================================================== --- epics-base.orig/modules/database/src/std/softIoc/makeInstallDir.pl @@ -589,3 +589,13 @@ Index: epics-base/modules/database/src/tools/dbdReport.pl #************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +Index: epics-base/src/tools/filterMakeflags.pl +=================================================================== +--- epics-base.orig/src/tools/filterMakeflags.pl ++++ epics-base/src/tools/filterMakeflags.pl +@@ -1,4 +1,4 @@ +-#!/bin/env perl ++#!/usr/bin/perl + # + # Filter all versions of GNU Make's MAKEFLAGS variable to return + # only the single-letter flags. The content differed slightly diff --git a/debian/patches/0048-skip-non-loopback-iface-tests.patch b/debian/patches/0048-skip-non-loopback-iface-tests.patch index 2be8940eb..201f2b344 100644 --- a/debian/patches/0048-skip-non-loopback-iface-tests.patch +++ b/debian/patches/0048-skip-non-loopback-iface-tests.patch @@ -2,7 +2,7 @@ Index: epics-base/modules/database/test/std/rec/Makefile =================================================================== --- epics-base.orig/modules/database/test/std/rec/Makefile +++ epics-base/modules/database/test/std/rec/Makefile -@@ -176,10 +176,12 @@ TARGET_SRCS += dbHeaderTestxx.cpp +@@ -230,10 +230,12 @@ TARGET_SRCS += dbHeaderTestxx.cpp ifeq ($(T_A),$(EPICS_HOST_ARCH)) # Host-only tests of softIoc/softIocPVA, caget and pvget (if present) # Unfortunately hangs too often on CI systems: @@ -80,15 +80,27 @@ Index: epics-base/modules/pvDatabase/test/src/Makefile =================================================================== --- epics-base.orig/modules/pvDatabase/test/src/Makefile +++ epics-base/modules/pvDatabase/test/src/Makefile -@@ -29,7 +29,10 @@ testLocalProvider_SRCS += testLocalProvi +@@ -29,12 +29,15 @@ testLocalProvider_SRCS += testLocalProvi testHarness_SRCS += testLocalProvider.cpp TESTS += testLocalProvider +-TESTPROD_HOST += testPVAServer +-testPVAServer_SRCS += testPVAServer.cpp +-testHarness_SRCS += testPVAServer.cpp +-TESTS += testPVAServer +# If there's an available interface to do this on +ifneq ($(shell ip addr | grep MULTICAST | wc -l),0) - TESTPROD_HOST += testPVAServer - testPVAServer_SRCS += testPVAServer.cpp - testHarness_SRCS += testPVAServer.cpp - TESTS += testPVAServer ++ TESTPROD_HOST += testPVAServer ++ testPVAServer_SRCS += testPVAServer.cpp ++ testHarness_SRCS += testPVAServer.cpp ++ TESTS += testPVAServer + +-TESTPROD_HOST += testChannelMonitor +-testChannelMonitor_SRCS += testChannelMonitor.cpp +-testHarness_SRCS += testChannelMonitor.cpp +-TESTS += testChannelMonitor ++ TESTPROD_HOST += testChannelMonitor ++ testChannelMonitor_SRCS += testChannelMonitor.cpp ++ testHarness_SRCS += testChannelMonitor.cpp ++ TESTS += testChannelMonitor +endif -\ No newline at end of file diff --git a/debian/rules b/debian/rules index 37a4e04dd..b89da11e8 100755 --- a/debian/rules +++ b/debian/rules @@ -47,7 +47,7 @@ export CROSS_COMPILER_TARGET_ARCHS=$(TARGETS) export SHRLIB_VERSION=$(SOV) %: - dh $@ --with epics -Sepicsmake --with systemd + dh $@ --with epics -Sepicsmake override_dh_auto_clean: dh_auto_clean @@ -132,18 +132,22 @@ override_dh_install: # Don't need other copies rm debian/tmp/usr/lib/epics/lib/linux-*/libCap5.so + # get rid of vcs files if they snuck into the build somehow + rm -f debian/tmp/usr/lib/epics/templates/makeBaseApp/top/.gitignore + rm -f debian/tmp/usr/lib/epics/templates/makeBaseExt/top/.gitignore + # This path is used by CA.pm ln -s ${EPICS_HOST_ARCH} debian/tmp/usr/lib/epics/lib/host # Install systemd unit files into the right location - install -d debian/epics-catools/lib/systemd/system + install -d debian/epics-catools/usr/lib/systemd/system for arch in $(EPICS_HOST_ARCH); do \ - mv debian/tmp/usr/lib/epics/bin/$$arch/caRepeater.service debian/epics-catools/lib/systemd/system/; \ + mv debian/tmp/usr/lib/epics/bin/$$arch/caRepeater.service debian/epics-catools/usr/lib/systemd/system/; \ rm debian/tmp/usr/lib/epics/bin/$$arch-debug/caRepeater.service; \ done # Unit files shouldn't be marked executable - chmod -x debian/epics-catools/lib/systemd/system/*.service + chmod -x debian/epics-catools/usr/lib/systemd/system/*.service # Remove legacy init scripts rm -f debian/tmp/usr/lib/epics/bin/*/S99caRepeater @@ -154,6 +158,16 @@ override_dh_install: install -d debian/epics-perl/usr/lib/epics/bin/$(EPICS_HOST_ARCH) install -d debian/epics-perl/usr/lib/epics/bin/$(EPICS_HOST_ARCH)-debug + # make perl scripts executable + for name in $(PERLBINS); do \ + chmod -f +x debian/epics-dev/usr/lib/epics/templates/makeBaseApp/top/caPerlApp/$$name || : ; \ + done + + # remove DBD perl scripts from epics-perl, already installed by -dev + rm -f debian/epics-perl/usr/lib/epics/lib/perl/DBD.pm + rm -f debian/epics-perl/usr/lib/epics/lib/perl/EpicsHostArch.pl + rm -rf debian/epics-perl/usr/lib/epics/lib/perl/DBD/ + for arch in $(EPICS_HOST_ARCH); do for name in $(PERLBINS); do \ mv debian/epics-dev/usr/lib/epics/bin/$$arch/$$name debian/epics-perl/usr/lib/epics/bin/$$arch/ \ ;done ;done diff --git a/documentation/.gitignore b/documentation/.gitignore deleted file mode 100644 index 52230970a..000000000 --- a/documentation/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.db -epics-base.tag -html/ diff --git a/documentation/Doxyfile@ b/documentation/Doxyfile@ index a657db19d..7462156d1 100644 --- a/documentation/Doxyfile@ +++ b/documentation/Doxyfile@ @@ -759,10 +759,10 @@ WARN_LOGFILE = # spaces. # Note: If this tag is empty the current directory is searched. -INPUT = ../mainpage.dox \ - ../RELEASE_NOTES.md \ +INPUT = ../RELEASE_NOTES.md \ ../README.md \ - @TOP@/include + @TOP@/include \ + @TOP@/doc # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -1833,7 +1833,7 @@ MAN_LINKS = NO # captures the structure of the code including all documentation. # The default value is: NO. -GENERATE_XML = NO +GENERATE_XML = YES # The XML_OUTPUT tag is used to specify where the XML pages will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of @@ -1841,7 +1841,7 @@ GENERATE_XML = NO # The default directory is: xml. # This tag requires that the tag GENERATE_XML is set to YES. -XML_OUTPUT = xml +XML_OUTPUT = @RTD_SRC@/xml # If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program # listings (including syntax highlighting and cross-referencing information) to @@ -1946,7 +1946,7 @@ ENABLE_PREPROCESSING = YES # The default value is: NO. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -MACRO_EXPANSION = NO +MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then # the macro expansion is limited to the macros specified with the PREDEFINED and @@ -1986,7 +1986,17 @@ INCLUDE_FILE_PATTERNS = # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -PREDEFINED = __cplusplus +PREDEFINED = __cplusplus \ + LIBCOM_API \ + DBCORE_API \ + LIBCA_API \ + DBRECSTD_API \ + EPICS_ALWAYS_INLINE \ + epicsShareExtern \ + epicsShareClass \ + epicsShareFunc \ + epicsShareAPI \ + epicsStdCall # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The diff --git a/documentation/HEADER_h.md b/documentation/HEADER_h.md new file mode 100644 index 000000000..8fd0ba69c --- /dev/null +++ b/documentation/HEADER_h.md @@ -0,0 +1,4 @@ +# @HEADER@.h + +```{doxygenfile} @HEADER@.h +``` diff --git a/documentation/KnownProblems.html b/documentation/KnownProblems.html deleted file mode 100644 index 5c29cc2a7..000000000 --- a/documentation/KnownProblems.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - Known Problems in EPICS 7.0.3.1 - - - -

EPICS 7.0.3.1: Known Problems

- -

Any patch files linked below should be applied at the root of the -base-7.0.3.1 tree. Download them, then use the GNU Patch program as -follows:

- -
% cd /path/to/base-7.0.3.1
-% patch -p1 < /path/to/file.patch
- -

The following problems were known by the developers at the time of this -release:

- -
    - - - -
- - - diff --git a/documentation/Makefile b/documentation/Makefile index f2a75dab1..8f509757c 100644 --- a/documentation/Makefile +++ b/documentation/Makefile @@ -7,25 +7,168 @@ EXPAND_ME += EPICS_REVISION EXPAND_ME += EPICS_MODIFICATION EXPAND_ME += EPICS_PATCH_LEVEL EXPAND_ME += OS_CLASS CMPLR_CLASS +EXPAND_ME += RTD_SRC -HTMLS += ComponentReference.html +DOXYGEN ?= doxygen -TARGETS += doxygen +libcom_HEADERS += adjustment +libcom_HEADERS += alarm +libcom_HEADERS += alarmString +libcom_HEADERS += asTrapWrite +libcom_HEADERS += bucketLib +libcom_HEADERS += cantProceed +libcom_HEADERS += compilerDependencies +libcom_HEADERS += cvtFast +libcom_HEADERS += dbmf +libcom_HEADERS += devLibVME +libcom_HEADERS += devLibVMEImpl +libcom_HEADERS += ellLib +libcom_HEADERS += envDefs +libcom_HEADERS += epicsAlgorithm +libcom_HEADERS += epicsAssert +libcom_HEADERS += epicsAtomic +libcom_HEADERS += epicsEvent +libcom_HEADERS += epicsExit +libcom_HEADERS += epicsExport +libcom_HEADERS += epicsGeneralTime +libcom_HEADERS += epicsGuard +libcom_HEADERS += epicsMessageQueue +libcom_HEADERS += epicsMutex +libcom_HEADERS += epicsReadline +libcom_HEADERS += epicsRingBytes +libcom_HEADERS += epicsRingPointer +libcom_HEADERS += epicsSignal +libcom_HEADERS += epicsSpin +libcom_HEADERS += epicsStackTrace +libcom_HEADERS += epicsStdio +libcom_HEADERS += epicsStdlib +libcom_HEADERS += epicsString +libcom_HEADERS += epicsTempFile +libcom_HEADERS += epicsThread +libcom_HEADERS += epicsTime +libcom_HEADERS += epicsTypes +libcom_HEADERS += epicsUnitTest +libcom_HEADERS += errlog +libcom_HEADERS += freeList +libcom_HEADERS += iocsh +libcom_HEADERS += ipAddrToAsciiAsynchronous +libcom_HEADERS += logClient +libcom_HEADERS += macLib +libcom_HEADERS += osiPoolStatus +libcom_HEADERS += osiProcess +libcom_HEADERS += osiSock +libcom_HEADERS += postfix +libcom_HEADERS += shareLib +libcom_HEADERS += testMain +libcom_HEADERS += yajl_alloc +libcom_HEADERS += yajl_common +libcom_HEADERS += yajl_gen +libcom_HEADERS += yajl_parse -DOXYGEN = doxygen -ME = documentation/O.$(T_A)/html -GH_FILES = $(ME)/ $(ME)/.nojekyll $(ME)/*.* $(ME)/*/*.* +database_HEADERS += chfPlugin +database_HEADERS += dbChannel +database_HEADERS += dbCommon +database_HEADERS += dbDefs +database_HEADERS += dbExtractArray +database_HEADERS += dbLink +database_HEADERS += dbServer +database_HEADERS += dbState +database_HEADERS += dbStaticLib +database_HEADERS += dbUnitTest +database_HEADERS += devLib +database_HEADERS += devSup +database_HEADERS += drvSup +database_HEADERS += initHooks + +record_HEADERS += aaiRecord +record_HEADERS += aaoRecord +record_HEADERS += aiRecord +record_HEADERS += aoRecord +record_HEADERS += aSubRecord +record_HEADERS += biRecord +record_HEADERS += boRecord +record_HEADERS += calcRecord +record_HEADERS += calcoutRecord +record_HEADERS += compressRecord +record_HEADERS += dfanoutRecord +record_HEADERS += eventRecord +record_HEADERS += fanoutRecord +record_HEADERS += histogramRecord +record_HEADERS += int64inRecord +record_HEADERS += int64outRecord +record_HEADERS += longinRecord +record_HEADERS += longoutRecord +record_HEADERS += lsiRecord +record_HEADERS += lsoRecord +record_HEADERS += mbbiRecord +record_HEADERS += mbbiDirectRecord +record_HEADERS += mbboRecord +record_HEADERS += mbboDirectRecord +record_HEADERS += permissiveRecord +record_HEADERS += printfRecord +record_HEADERS += selRecord +record_HEADERS += seqRecord +record_HEADERS += stateRecord +record_HEADERS += stringinRecord +record_HEADERS += stringoutRecord +record_HEADERS += subRecord +record_HEADERS += subArrayRecord +record_HEADERS += waveformRecord + +menu_HEADERS += menuAlarmSevr +menu_HEADERS += menuAlarmStat +menu_HEADERS += menuConvert +menu_HEADERS += menuFtype +menu_HEADERS += menuIvoa +menu_HEADERS += menuOmsl +menu_HEADERS += menuPini +menu_HEADERS += menuPost +menu_HEADERS += menuPriority +menu_HEADERS += menuScan +menu_HEADERS += menuSimm +menu_HEADERS += menuYesNo + +HEADER_TYPES = libcom database record menu + +HEADER_MD_FILES = $(foreach t, $(HEADER_TYPES), \ + $(addsuffix _h.md, $($t_HEADERS))) +API_RST_FILES = $(addsuffix -api.rst, $(HEADER_TYPES)) + +RTD_SRC = $(COMMON_DIR)/rtd-src + +DOCS += README.md +DOCS += RELEASE_NOTES.md include $(TOP)/configure/RULES -doxygen: Doxyfile ../mainpage.dox $(INSTALL_HTMLS) +$(HEADER_MD_FILES): %_h.md: ../HEADER_h.md + $(EXPAND_TOOL) -t $(INSTALL_LOCATION) -DHEADER=$* $< $@ + +$(API_RST_FILES): %-api.rst: ../%-API.rst + @$(RM) $@ + @$(ECHO) Creating $@ + @$(CP) $< $@ + @$(foreach h, $($*_HEADERS), \ + echo " $h_h.rst" >> $@;) + +ifndef T_A +doxygen rtd sphinx: inc + $(MAKE) -C O.$(EPICS_HOST_ARCH) $@ +else + +doxygen: Doxyfile + @$(MKDIR) $(RTD_SRC) $(DOXYGEN) - rsync -av $(TOP)/html/ html/ -.PHONY: doxygen +rtd: doxygen $(API_RST_FILES) $(HEADER_MD_FILES) + rsync -av $(INSTALL_DOC)/ $(RTD_SRC)/ + rsync -av $(HEADER_MD_FILES) $(RTD_SRC)/ + rsync -av $(API_RST_FILES) $(RTD_SRC)/ + rsync -av ../index.rst ../conf.py $(RTD_SRC)/ -commit: doxygen - $(TOUCH) html/.nojekyll - (cd $(TOP) && $(CURDIR)/../commit-gh.sh $(GH_FILES)) +sphinx: rtd + cd $(COMMON_DIR); $(PYTHON) -m sphinx rtd-src readthedocs + rsync -av $(COMMON_DIR)/readthedocs $(INSTALL_HTML)/ +endif -.PHONY: commit +.PHONY: doxygen rtd sphinx diff --git a/documentation/README.md b/documentation/README.md index 85913891a..8856cba9d 100644 --- a/documentation/README.md +++ b/documentation/README.md @@ -1,27 +1,7 @@ -# Installation Instructions {#install} +# Installation instructions ## EPICS Base Release 7.0.x ------ - -### Table of Contents - - - [What is EPICS base?](#what-is-epics-base?) - - [What is new in this release?](#what-is-new-in-this-release?) - - [Copyright](#copyright) - - [Supported platforms](#supported-platforms) - - [Supported compilers](#supported-compilers) - - [Software requirements](#software-requirements) - - [Host system storage requirements](#host-system-storage-requirements) - - [Documentation](#documentation) - - [Directory Structure](#directory-structure) - - [Site-specific build configuration](#site-specific-build-configuration) - - [Building EPICS base](#building-epics-base) - - [Example application and extension](#example-application-and-extension) - - [Multiple host platforms](#multiple-host-platforms) - ------ - ### What is EPICS base? The Experimental Physics and Industrial Control Systems (EPICS) is an @@ -36,8 +16,8 @@ interfaces) of various types. ### What is new in this release? -Please check the `documentation/RELEASE_NOTES.md` file for -description of changes and release migration details. +Please check the [release notes](RELEASE_NOTES.md) +for description of changes and release migration details. ### Copyright @@ -80,8 +60,7 @@ executable must be found through your normal search path. #### Unzip and tar (Winzip on WIN32 systems) -You must have tools available to unzip and untar the EPICS base -distribution file. +You may need tools to unzip and untar the EPICS base distribution file. #### Target systems @@ -91,7 +70,7 @@ running as processes on the host platform. #### vxWorks -You must have vxWorks 6.8 or later installed if any of your target +You must have vxWorks 6.8 or 6.9 installed if any of your target systems are vxWorks systems; the C++ compiler from older versions cannot compile recently developed code. The vxWorks installation provides the cross-compiler and header files needed to build for these targets. The @@ -120,16 +99,10 @@ readline is available and will then use it. RTEMS targets may be configured to use `LIBTECLA` if available, and on vxWorks the OS's ledLib line-editing library is normally used. -### Host system storage requirements - -The compressed tar file is approximately 3 MB in size. The -distribution source tree takes up approximately 21 MB. A 64-bit host -architecture may need around 610 MB to compile, while cross-compiled -targets are somewhat smaller. - ### Documentation -EPICS documentation is available through the [EPICS website](https://epics.anl.gov/) at Argonne. +EPICS documentation is available from the +[EPICS Documentation Website](https://docs.epics-controls.org/). Release specific documentation can also be found in the `base/documentation` directory of the distribution. @@ -143,8 +116,10 @@ Release specific documentation can also be found in the base/configure Build rules and OS-independent config files base/configure/os OS-dependent build config files base/documentation Distribution documentation - base/src Source code in various subdirectories + base/src Source code for templates and build tools + base/modules Source code for distribution submodules base/startup Scripts for setting up path and environment + base/test Unit tests for build tools ``` #### Directories created by the build @@ -158,7 +133,8 @@ configuration variable if that has been set. cfg Installed build configuration files db Installed database files dbd Installed database definition files - html Installed html documentation + doc Installed Markdown documentation + html Installed HTML documentation include Installed header files include/os Installed OS-specific header files in subdirs include/compiler Installed compiler-specific header files @@ -169,12 +145,11 @@ configuration variable if that has been set. #### `base/documentation` Directory -This contains documents on how to setup, build, and install EPICS. +This contains EPICS reference documentation. ``` README.md This file RELEASE_NOTES.md Notes on release changes - KnownProblems.html List of known problems and workarounds ``` #### `base/startup` Directory @@ -272,8 +247,7 @@ need C and C++ compilers in your search path. When building base you must have `echo` in your search path. For Unix host builds you will also need `cp`, `rm`, `mv`, and `mkdir` in your search path. Some Unix systems may also need `ar` and `ranlib`, and the C/C++ compilers may -require `as` and `ld` in your path. On Solaris systems you need -`uname` in your path. +require `as` and `ld` in your path. * **`LD_LIBRARY_PATH`** EPICS shared libraries and executables normally contain the full path @@ -290,7 +264,6 @@ Shared libraries are now built by default on all Unix type hosts. To configure EPICS, you may want to modify some values set in the following files: ->>>>>>> mirror/3.15 ``` configure/CONFIG_SITE Build settings. Specify target archs. diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index 8cad1a544..e201d79e6 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -1,4 +1,4 @@ -# EPICS 7.0 Release Notes {#releasenotes} +# EPICS 7.0 Release Notes 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 @@ -8,15 +8,133 @@ under the 3.15 release 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 PVA submodules each have their own individual sets of release notes which -should also be read to understand what has changed since earlier releases: +The external PVA submodules each have their own individual release notes files. +However the entries describing changes included in those submodules since EPICS +7.0.5 have now been copied into the appropriate place of this file. -- [normativeTypes](https://github.com/epics-base/normativeTypesCPP/blob/master/documentation/RELEASE_NOTES.md) -- [pvAccess](http://epics-base.github.io/pvAccessCPP/pvarelease_notes.html) -- [pvData](http://epics-base.github.io/pvDataCPP/release_notes.html) -- [pvDatabase](https://github.com/epics-base/pvDatabaseCPP/blob/master/documentation/RELEASE_NOTES.md) -- [pva2pva](https://epics-base.github.io/pva2pva/release_notes.html) -- [pvaClient](https://github.com/epics-base/pvaClientCPP/blob/master/documentation/RELEASE_NOTES.md) +## EPICS Release 7.0.9 + +### Core documentation published at ReadTheDocs + +The `documentation` directory's `Makefile` can now run various publication scripts including Sphinx and Doxygen to generate formatted documentation that is now being published +[at docs.epics-controls.org](https://docs.epics-controls.org/projects/base/en/latest/index.html) +and integrated into the main [EPICS Documentation website](https://docs.epics-controls.org/en/latest/index.html). +The best place to find out more about these mechanisms is the +[Contribution Guide](https://docs.epics-controls.org/en/latest/CONTRIBUTING.html) +although it doesn't currently cover the new processes added to epics-base. + +Much of the documentation generated from .dbd.pod files at build time is now +also being converted into MarkDown (.md) files and installed into the top-level +`doc` directory. Some users might find it quicker to look up information about a +record type by opening these files in a text editor intead of opening a browser +and loading the HTML versions or finding and opening the files from the EPICS +Documentation site. + +### Post monitors from compress record when it's reset + +Writing into a compress record's `RES` field now posts a monitor event instead +of only changing `VAL`. Monitor clients will therefore receive an empty array. + +### The AMSG error message propagates through MSS links + +A database link with the MSS attribute will now propagate not only SEVR and +STAT, but also AMSG. This field contains additional information that complements +STAT. Links with MS or MSI attributes do not propagate STAT, and therefore do +not propagate AMSG, either. + +Channel Access links do not propagate AMSG, regardless of the MSS attribute, +because the message is not available as Channel metadata. + +### Reloading record aliases + +Aliases can now be defined more than once as long as they still refer to the +same record, unless the global variable `dbRecordsOnceOnly` is non-zero. +This allows database files to be loaded multiple times, even if they contain +alias definitions. + +### `DBE_PROPERTY` event rate changed + +Updating property fields now only posts `DBE_PROPERTY` events if the +field actually changed. + +### Changes to msi related to include paths + +There are two changes to `msi` included here. + +`msi` now treats files included by .template or .substutiions files in a more +consistent way: for relative paths, it will always look relative to the current +working directory if no `-I` flags are passed, and if they are passed then it +will search for the _relative_ path from each of those flags. That is, the +following will now find the file `bar.template` located at +`/some/path/rel/path/bar.template` +``` +$ cat foo.substitutions +file rel/path/bar.template { + # contents +} +$ msi -I /some/path foo.substitutions +``` + +Note that this does provide one change from previous behaviour: when opening a +file from the command line, `msi` will not use the `-I`-specified paths to +search for the file, but will only work relative to the current working +directory, consistent with most commandline utilities. + +### Allow users to delete previously created records from the database + +From this release, record instances and aliases that have already been loaded +by an IOC can be removed from the database again before the call to iocInit +by loading a second instance of the named records but using `"#"` in place of +the record type. Values for the fields are not required or advised, just use +an empty record body `{}`. This is useful when a template defines records that +are not wanted in some IOCs, without having to split or duplicate the original +template. + +For example this will remove the record named "unwanted": + +``` +record("#", "unwanted") {} +``` + +### Only keep readline history for interactive sessions + +Previously, all IOCsh commands were persisited in the libreadline history +(when readline support is included). +Going forward, only interactive commands are saved. + +### Type change to asTrap serverSpecific data + +Change `void*` to `dbChannel*` in `asTrapWriteBeforeWithData()` and +`asTrapWriteMessage::serverSpecific` to reflect the reality since +the `dbAddr*` to `dbChannel*` migration. +External code wishing to support both before and after 3.15 should +already be conditionally casting to/from the appropriate type. + +### Fix issues with `_FORTIFY_SOURCE=3` + +This release fixes the false positives failures whhen building with `_FORTIFY_SOURCE` level 3. +The override introduced in 7.0.8.1 has been removed. + +### Other + +- genVersionHeader: work with git submodules and worktrees. +- avoid UB with self `pthread_join()` +- freebsd: Add support for x86 and amd64 builds +- Clear AMSG when SEVR becomes zero. +- `seqRecord` fix support for link `DLY0` +- Add `ABORT_ON_ASSERT` flag to `CONFIG_SITE_ENV` +- rationalize osdMutex + +### Submodule updates + +The pvDatabase module was updated to version 4.7.2: + +* Resolved issue with changed field set in the case where the top level (master) +field ("_") is not requested by the client, but the master field callback causes +all fields to be marked as updated, rather than only those fields that have +actually been modified. + +----- ## EPICS Release 7.0.8.1 @@ -255,6 +373,50 @@ The floating point modulo function `FMOD(NUM,DEN)` has been added to the CALC expression engine and is available to all software using that (calc and calcout record types, access security library and some extensions). +### Submodule updates + +The pvData module was updated to version 8.0.6: + +- Compatible changes + - Actually enable JSON-5 output in PVStructure::Formatter::JSON when available. + - Fix unaligned access issues for some ARM/Linux targets. + +The pvAccess module was updated to version 7.1.7: + +- Changes + - Registering the PVA server with the IOC now sets the `PVAS_SERVER_PORT` + variable in the environment. + +The pva2pva module was updated to version 1.4.1: + +- Bug Fixes + - `dbLoadGroup` was fixed +- Additions + - Support for "meta" member at top of array of structs + +The pvDatabase module was updated to version 4.7.1: + +* Added data distributor plugin which can be used for distributing data between + a group of clients. The plugin is triggered by the request string of the + form: + + `_[distributor=group:;set:;trigger:;updates:;mode:]` + + The plugin parameters are optional and are described bellow: + + - group: this parameter indicates a group that client application belongs to (default value: "default"); groups of clients are completely independent of each other + + - set: this parameter designates a client set that application belongs to within its group (default value: "default") + + - trigger: this is the PV structure field that distinguishes different channel updates (default value: "timeStamp"); for example, for area detector images one could use the "uniqueId" field of the NTND structure + + - updates: this parameter configures how many sequential updates a client (or a set of clients) will receive before the data distributor starts updating the next one (default value: "1") + + - mode: this parameter configures how channel updates are to be distributed between clients in a set: + - one: update goes to one client per set + - all: update goes to all clients in a set + - default is "one" if client set id is not specified, and "all" if set id is specified + ----- ## EPICS Release 7.0.7 @@ -333,10 +495,10 @@ changed to `(p)->dtor`. The order over operations when processing a waveformRecord is adjusted so that updates to NORD is posted with the correct timestamp. -### Automatic COMMANDLINE_LIBRARY w/ newer compilers +### Automatic `COMMANDLINE_LIBRARY` with newer compilers When built with a compiler supporting `__has_include<>`, the presence -of the `` will be used to automatically determine +of a `readline/readline.h` header will be used to automatically determine a default value for `COMMANDLINE_LIBRARY`. Mingw builds with readline support now link `-ltermcap` instead of `-lcurses`. @@ -505,6 +667,44 @@ or if unsupported (`$TERM` not set, or Windows < 10). The `dbnd` server side filter now passes through alarm and property change events, even when not exceeding the deadband. +### Submodule updates + +The pvData module was updated to version 8.0.5: + +- Compatible changes + - Internal changes to use the YAJL API for generating JSON and JSON-5 output. + +The pvAccess module was updated to version 7.1.6: + +- Changes to caProvider + - Bug fix related to enum values. + - More internal changes to improve performance when connecting tens of + thousands of CA channels. +- Several minor internal improvements. + +The pva2pva module was updated to version 1.4.0: + +- Bug Fixes + - Apply ACF when writing to atomic group +- Additions + - Add new "structure" to @ref qsrv_group_map_types +- Changes + - Add Access Security hooks for single and group writes. + - Enable "Async Soft Channel" for output links + - When built against Base 7.0.6.1, set timeStamp.userTag from UTAG field. + - Add DTYP="QSRV Set UTag" for longin, which sets UTAG=VAL. + +The pvDatabase module was updated to version 4.7.0: + +* Added support for the whole structure (master field) server side plugins. + The whole structure is identified as the `_` string, and a pvRequest string + that applies a plugin to it takes the form: + + `field(_[XYZ=A:3;B:uniqueId])` + + where `XYZ` is the name of a specific filter plugin that takes parameters + `A` and `B` with values `3` and `uniqueId` respectively. + ----- ## EPICS Release 7.0.6.1 @@ -571,6 +771,15 @@ This was done to simplify the code and may have improved performance slightly fo Many of the built-in record types have had improvements to their documentation with additional fields added to the tables, rewrites of descriptions and links to other documents added or fixed. +### Submodule updates + +The pvAccess module was updated to version 7.1.4: + +- Changes to caProvider + - Resolve issues with pv structures that don't have a value field + - Add NULL checks for handling unusual structures + - Speed up channel creation when using large numbers of channels + ----- ## EPICS Release 7.0.6 @@ -581,7 +790,7 @@ These target architectures have been removed: + darwin-ppc, darwin-ppcx86 + linux-386, linux-486, linux-586, linux-686, linux-athlon (cross-build) -+ linux-cris, linux-cris_v10, linux-cris_v32 (cross-build) ++ linux-cris, linux-cris\_v10, linux-cris\_v32 (cross-build) + RTEMS-at91rm9200ek, RTEMS-gen68360, RTEMS-mcp750, RTEMS-mvme167, RTEMS-psim (cross-build) @@ -599,9 +808,9 @@ running on RTEMS 5: - RTEMS-beagleboneblack - RTEMS-pc686 -- RTEMS-qoriq_e500 (MVME2500) -- RTEMS-xilinx_zynq_a9_qemu -- RTEMS-xilinx_zynq_zedboard +- RTEMS-qoriq\_e500 (MVME2500) +- RTEMS-xilinx\_zynq\_a9\_qemu +- RTEMS-xilinx\_zynq\_zedboard The EPICS support for RTEMS 4 has always relied on RTEMS-specific kernel APIs which cannot be used on an SMP system, so a new port was @@ -613,7 +822,7 @@ to run `make distclean` if switching a single source tree from one to the other (both header files and dependency files are different between the two and must be cleaned out). -The configuration variable RTEMS_VERSION in the EPICS config file +The configuration variable `RTEMS_VERSION` in the EPICS config file `configure/os/CONFIG_SITE.Common.RTEMS` must be set to the full 3- part version number for RTEMS 4 releases, e.g. `4.9.1`, `4.10.2` but for RTEMS 5.1 and later it must only contain the major version @@ -811,6 +1020,39 @@ Test programs written directly in Perl as a `.plt` script should implement a similar timeout for themselves. The "netget" test in Base does this in a way that works on Windows as well as Unix-like hosts. +### Submodule updates + +The pvAccess module was updated to version 7.1.4: + +- Changes + - Adjust argument parsing with pvput (Jesus Vasquez). + +The pva2pva module was updated to version 1.3.1: + +- Bug Fixes + - Correct handling for server side filters. +- Changes + - Syncing softMain.cpp with epics-base + +The pvDatabase module was updated to version 4.6.0: + +* Access Security is now supported. +* special has been revised and extended. +* addRecord, removeRecord, processRecord, and traceRecord are replaced by pvdbcr versions. +* support is DEPRECATED + +The pvaClient module was updated to version 4.8.0: + +* `PvaClientNTMultiData::getChannelChangeFlags` is a new method. It fixes + issue #66. +* Fix for issue #68. Both `PvaClientArray` and `PvaClientField` are not longer + present. Neither was previously implemented. +* Several public methods are now protected. They were never meant to be called + by clients. +* Issue #70 has been fixed. +* Changes was made to increase the performance of `pvaMultiChannel`. +* doxygen changes were made. + ----- ## EPICS Release 7.0.5 @@ -848,7 +1090,7 @@ compile device supports as loadable modules. ### Priority inversion safe Posix mutexes On Posix systems, epicsMutex now support priority inheritance if available. -The IOC needs to run with SCHED_FIFO engaged to use these. +The IOC needs to run with `SCHED_FIFO` engaged to use these. Support for Posix implementations before POSIX.1-2001 (`_XOPEN_SOURCE < 500`, glibc version < 2.3.3) has been dropped. @@ -981,14 +1223,14 @@ properly handle zero-length arrays. The `caget`, `caput` and `camonitor` client programs are known to work with empty arrays as long as they were built with this or a later version of EPICS. -#### Change to the db_access.h `dbr_size_n(TYPE, COUNT)` macro +#### Change to the db\_access.h `dbr_size_n(TYPE, COUNT)` macro When called with COUNT=0 this macro no longer returns the number of bytes required for a scalar (1 element) but for an empty array (0 elements). Make sure code that uses this doesn't call it with COUNT=0 when it really means COUNT=1. -Note that the db_access.h header file is included by cadef.h so the change +Note that the db\_access.h header file is included by cadef.h so the change can impact Channel Access client programs that use this macro. #### Channel Access support for zero-length arrays @@ -1098,6 +1340,35 @@ GNUmake added the directive `undefine` in version 3.82 to allow variables to be undefined. Support for this has been added to the EPICS Release file parser, so `undefine` can now be used in configure/RELEASE files to unset variables. + +### Submodule updates + +The pvData module was updated to version 8.0.4: + +- Incompatible changes + - Remove `ByteBuffer::align()` +- Compatible changes + - Deprecate `SerializableControl::alignBuffer()` and + `DeserializableControl::alignData()` + - `shared_vector_convert<>()` fix convert of empty, untyped, array + +The pvAccess module was updated to version 7.1.3: + +- Bug fixes + - Increase default TCP timeout to 40 seconds. + Applies a 4/3 multiplier on `$EPICS_PVA_CONN_TMO` for compatibility. + - CA Provider implementation restructured to simplify, reduce duplication + and fix issues #163 and #165. +- Changes + - Enable building of pvtools to all except vxWorks, RTEMS and iOS. + +The pva2pva module was updated to version 1.3.0: + +- Changes + - Add `dbLoadGroup()` iocsh function to read group JSON definitions + from a file. Mappings in files must refer to full record names + instead of fields. eg. 'recname.VAL' instead of 'VAL'. + ----- ## EPICS Release 7.0.4.1 @@ -1163,7 +1434,7 @@ The following launchpad bugs have fixes included in this release: operators on aarch64 - [lp: 1853148](https://bugs.launchpad.net/bugs/1853148), mingw compiler problem with printf/scanf formats -- [lp: 1852653](https://bugs.launchpad.net/bugs/1852653), USE_TYPED_DSET +- [lp: 1852653](https://bugs.launchpad.net/bugs/1852653), `USE_TYPED_DSET` incompatible with C++ - [lp: 1862328](https://bugs.launchpad.net/bugs/1862328), Race condition on IOC start leaves rsrv unresponsive @@ -1173,7 +1444,7 @@ The following launchpad bugs have fixes included in this release: - [lp: 1868680](https://bugs.launchpad.net/bugs/1868680), Access Security file reload (asInit) fails -### \*_API macros in EPICS headers +### `*_API` macros in EPICS headers Internally, the Com and ca libraries now express dllimport/export (Windows) and symbol visibility (GCC) using library-specific macros (eg. `LIBCOM_API`) @@ -1407,7 +1678,7 @@ The API functions `epicsGetExecDir()` and `epicsGetExecName()` are also added to `osiFileName.h` to provide runtime access to the directory or filename of the executable with which the process was started. -### Decouple LINKER_USE_RPATH and STATIC_BUILD +### Decouple `LINKER_USE_RPATH` and `STATIC_BUILD` Previously, setting `STATIC_BUILD=NO` implied `LINKER_USE_RPATH=NO`. This is no longer the case. Setting `LINKER_USE_RPATH=YES` will @@ -1986,7 +2257,7 @@ number instead, like this: Channel Access does not (and probably never will) directly support 64-bit integer types, so the new field types are presented to the CA server as `DBF_DOUBLE` values. This means that field values larger than 2^52 -(0x10_0000_0000_0000 = 4503599627370496) cannot be transported over Channel +(0x10\_0000\_0000\_0000 = 4503599627370496) cannot be transported over Channel Access without their least significant bits being truncated. The EPICS V4 pvAccess network protocol _can_ transport 64-bit data types however, and a future release of the pvaSrv module will connect this ability to the fields of diff --git a/documentation/ReleaseChecklist.html b/documentation/ReleaseChecklist.html index 77fe6c7a9..40f6d5808 100644 --- a/documentation/ReleaseChecklist.html +++ b/documentation/ReleaseChecklist.html @@ -179,14 +179,10 @@

Roles

  • git grep UNRELEASED and insert the module version to any doxygen annotations that have a @since UNRELEASED comment. Commit (don't push yet).
  • +
  • Check that the module's Release Notes have been updated to cover all changes; add items as necessary, and set the module version - number and release date if appropriate. Convert to HTML and view in - a browser to check the formatting: -
    - cd base-7.0/modules/<module>/documentation
    - pandoc -f gfm -t html -o RELEASE_NOTES.html RELEASE_NOTES.md -
    + number and release date if appropriate. Commit these changes (don't push).
  • Edit the module's release version file @@ -194,14 +190,37 @@

    Roles

    Doxyfiles in the top-level and/or documentation directories. In these, set DEVELOPMENT_FLAG to 0 and remove -dev from the PROJECT_NUMBER string. Commit these - changes (don't push).
  • + changes (don't push): +
    + git ci -m 'Final commit for <module-version>' +
    +
  • Tag the module:
    - git tag -m 'ANJ: Tag for EPICS 7.0.8.1' <module-version> + git tag -m 'ANJ: Tag for EPICS 7.0.8.2' <module-version>
  • +
  • Generate documentation or Release Notes using one of these: +
      +
    • For older modules with a RELEASE_NOTES.md file convert it to + HTML and view in a browser to check the formatting as follows: +
      + cd base-7.0/modules/<module>/documentation
      + pandoc -f gfm -t html -o RELEASE_NOTES.html RELEASE_NOTES.md +
      + +
    • For newer modules with release_notes.dox file, generate the + new github-pages website as follows: +
      + cd base-7.0/modules/<module>/documentation
      + make commit + git push --force upstream gh-pages +
      + Q: Delay this git push until later?
    • +
  • +
  • Update the git submodule on the Base-7.0 branch to the newly-tagged version, check the module's status matches the tag:
    @@ -269,7 +288,7 @@

    Roles

    Tag the epics-base module in Git:
    cd base-7.0
    - git tag -m 'ANJ: Tagged for release' R7.0.8.1 + git tag -m 'ANJ: Tagged for release' R7.0.8.2

    Don't push to GitHub yet.

    @@ -303,12 +322,12 @@

    Roles

    files and directories that are only used for continuous integration:
    cd base-7.0
    - ./.tools/make-tar.sh R7.0.8.1 ../base-7.0.8.1.tar.gz base-7.0.8.1/ + ./.tools/make-tar.sh R7.0.8.2 ../base-7.0.8.2.tar.gz base-7.0.8.2/
    Create a GPG signature file of the tarfile as follows:
    cd ..
    - gpg --armor --sign --detach-sig base-7.0.8.1.tar.gz + gpg --armor --sign --detach-sig base-7.0.8.2.tar.gz
    @@ -374,7 +393,7 @@

    Roles

    Upload the tar file and its .asc signature file to the epics-controls web-server.
    - scp base-7.0.8.1.tar.gz base-7.0.8.1.tar.gz.asc epics-controls:download/base
    + scp base-7.0.8.2.tar.gz base-7.0.8.2.tar.gz.asc epics-controls:download/base
    @@ -397,8 +416,8 @@

    Roles

    Release Manager Go to the GitHub - - Create release from tag R7.0.8.1 page. + + Create release from tag R7.0.8.2 page. Upload the tar file and its .asc signature file to the new GitHub release page. diff --git a/documentation/commit-gh.sh b/documentation/commit-gh.sh deleted file mode 100644 index d630165d2..000000000 --- a/documentation/commit-gh.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/sh -set -e -x -# Usage: commit-gh -# -# Creates a commit containing only the files in the sub-directory provided as an argument -# -# Does not disturb the working copy or index - -prefix="$1" -shift - -# Commit to this branch -BRANCH=refs/heads/gh-pages - -# Use the main branch description as the gh-pages commit message -MSG=`git describe --tags --always` - -# Scratch space -TDIR=`mktemp -d -p $PWD` - -# Automatic cleanup of scratch space -trap 'rm -rf $TDIR' INT TERM QUIT EXIT - -export GIT_INDEX_FILE="$TDIR/index" - -# Add listed files to a new (empty) index -git update-index --add "$@" - -# Write the index into the repo, get tree hash -TREE=`git write-tree --prefix="$prefix"` - -echo "TREE $TREE" -git cat-file -p $TREE - -# Create a commit with our new tree -# Reference current branch head as parent (if any) -CMT=`git commit-tree -m "$MSG" $TREE` - -echo "COMMIT $CMT" -git cat-file -p $CMT - -# Update the branch with the new commit tree hash -git update-ref $BRANCH $CMT - -echo "Done" diff --git a/documentation/conf.py b/documentation/conf.py new file mode 100644 index 000000000..bce2ff85e --- /dev/null +++ b/documentation/conf.py @@ -0,0 +1,130 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys + +sys.path.insert(0, os.path.abspath(".")) + +# -- Project information ----------------------------------------------------- + +project = "EPICS Base Documentation" +copyright = "EPICS Controls" +author = "The EPICS Collaboration" + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. + +extensions = [ + "hoverxref.extension", + "breathe", + "sphinx.ext.mathjax", + "sphinx.ext.ifconfig", + "sphinx.ext.graphviz", + "sphinx_copybutton", + "sphinx.ext.intersphinx", + 'myst_parser', +] + +# Setup the breathe extension +breathe_projects = {"epics-base": "xml"} + +breathe_default_project = "epics-base" + +# Tell sphinx what the primary language being documented is. +primary_domain = "cpp" + +# Tell sphinx what the pygments highlight language should be. +highlight_language = "cpp" + +# Add any paths that contain templates here, relative to this directory. +# templates_path = () + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "O.*", "venv"] + +# Intersphinx links to subprojects +intersphinx_mapping = { + "epics": ("https://docs.epics-controls.org/en/latest/", None), +} +intersphinx_disabled_reftypes = ["*"] +hoverxref_role_types = { + "hoverxref": "tooltip", + "ref": "modal", + "confval": "tooltip", + "mod": "modal", + "class": "modal", + "obj": "tooltip", +} + +hoverxref_intersphinx_types = { + "readthedocs": "modal", + "sphinx": "tooltip", +} + +hoverxref_domains = [ + "py", +] + +# Enabled Markdown extensions. +# See here for what they do: +# https://myst-parser.readthedocs.io/en/latest/syntax/optional.html +myst_enable_extensions = [ + "amsmath", + "colon_fence", + "deflist", + "dollarmath", + "fieldlist", + "html_image", + "replacements", + "smartquotes", + "strikethrough", + "tasklist", +] + +# Allows auto-generated header anchors: +# https://myst-parser.readthedocs.io/en/latest/syntax/optional.html#auto-generated-header-anchors +myst_heading_anchors = 4 + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "sphinx_rtd_theme" + + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +# html_static_path = ['_static'] + +# html_css_files = [ +# 'css/custom.css', +# ] + +master_doc = "index" + +# html_theme_options = { +# 'logo_only': True, +# } +# html_logo = "images/EPICS_white_logo_v02.png" + +# html_extra_path = ['../html'] + +# Breathe directives diff --git a/documentation/database-API.rst b/documentation/database-API.rst new file mode 100644 index 000000000..a7e230d53 --- /dev/null +++ b/documentation/database-API.rst @@ -0,0 +1,7 @@ +IOC Database C/C++ APIs +======================= + +.. toctree:: + :maxdepth: 1 + :caption: IOC Header Files + diff --git a/documentation/index.rst b/documentation/index.rst new file mode 100644 index 000000000..9f7ebfa35 --- /dev/null +++ b/documentation/index.rst @@ -0,0 +1,30 @@ +EPICS Base Documentation +======================== + + +.. toctree:: + :hidden: + + EPICS Documentation Index + +.. toctree:: + :maxdepth: 1 + :caption: General Information + + README + RELEASE_NOTES + +.. toctree:: + :maxdepth: 2 + :caption: IOC Component Reference + + ComponentReference + +.. toctree:: + :maxdepth: 1 + :caption: C/C++ Headers + + libcom-api + database-api + record-api + menu-api diff --git a/documentation/libcom-API.rst b/documentation/libcom-API.rst new file mode 100644 index 000000000..5d1305f41 --- /dev/null +++ b/documentation/libcom-API.rst @@ -0,0 +1,7 @@ +Common Library C/C++ APIs +========================= + +.. toctree:: + :maxdepth: 1 + :caption: LibCom Header Files + diff --git a/documentation/mainpage.dox b/documentation/mainpage.dox deleted file mode 100644 index ed3d688ef..000000000 --- a/documentation/mainpage.dox +++ /dev/null @@ -1,15 +0,0 @@ -/** -@mainpage EPICS Base - -Documentation index - -@li @ref releasenotes -@li @ref install -@li EPICS Component Reference Manual -@li Field Modifiers and Channel Filters -@li Extensible IOC Database Links -@li Channel Access Reference Manual -@li msi: Macro Substitution and Include Tool -@li Perl 5 Interface to Channel Access - -*/ diff --git a/documentation/menu-API.rst b/documentation/menu-API.rst new file mode 100644 index 000000000..e5a3b56b5 --- /dev/null +++ b/documentation/menu-API.rst @@ -0,0 +1,7 @@ +C/C++ Headers from Menus +======================== + +.. toctree:: + :maxdepth: 1 + :caption: Generated Menu Header Files + diff --git a/documentation/record-API.rst b/documentation/record-API.rst new file mode 100644 index 000000000..880fa94aa --- /dev/null +++ b/documentation/record-API.rst @@ -0,0 +1,7 @@ +C/C++ Headers from Record Types +=============================== + +.. toctree:: + :maxdepth: 1 + :caption: Generated Record Header Files + diff --git a/documentation/requirements.txt b/documentation/requirements.txt new file mode 100644 index 000000000..f1d6f0bdb --- /dev/null +++ b/documentation/requirements.txt @@ -0,0 +1,6 @@ +sphinx==7.2.6 +myst-parser==2.0.0 +breathe==4.35.0 +sphinx_copybutton==0.5.2 +sphinx-hoverxref==1.3.0 +sphinx-rtd-theme==2.0.0 diff --git a/modules/ca/src/client/Makefile b/modules/ca/src/client/Makefile index 08394ea54..e80e0adfc 100644 --- a/modules/ca/src/client/Makefile +++ b/modules/ca/src/client/Makefile @@ -118,7 +118,6 @@ EXPAND_VARS = INSTALL_BIN=$(FINAL_LOCATION)/bin/$(T_A) SRC_DIRS += $(CURDIR)/test PROD_HOST += ca_test ca_test_SRCS = ca_test_main.c ca_test.c -ca_test_LIBS = ca Com ca_test_SYS_LIBS_WIN32 = ws2_32 advapi32 user32 OBJS_vxWorks += ca_test diff --git a/modules/ca/src/client/db_access.h b/modules/ca/src/client/db_access.h index 3ab92fbc0..cca17a512 100644 --- a/modules/ca/src/client/db_access.h +++ b/modules/ca/src/client/db_access.h @@ -676,7 +676,7 @@ union db_access_val{ (type)%(LAST_TYPE+1) == DBR_DOUBLE) #define dbf_type_to_text(type) \ - ( ((type) >= -1 && (type) < dbf_text_dim-2) ? \ + ( ((type+1) >= 0 && (type) < dbf_text_dim-2) ? \ dbf_text[type+1] : dbf_text_invalid ) #define dbf_text_to_type(text, type) \ diff --git a/modules/ca/src/perl/Makefile b/modules/ca/src/perl/Makefile index 05500ad05..98eaedc93 100644 --- a/modules/ca/src/perl/Makefile +++ b/modules/ca/src/perl/Makefile @@ -38,7 +38,7 @@ ifeq ($(wildcard $(PERL_h)),) endif endif -ifneq (inc,$(strip $(MAKECMDGOALS))) +ifneq ($(filter-out inc,$(strip $(MAKECMDGOALS))),) ifeq ($(T_A),$(EPICS_HOST_ARCH)) # No cross-builds (wrong Perl!) ifeq ($(strip $(XSUBPP)),) $(warning Perl's xsubpp program was not found.) diff --git a/modules/ca/src/template/top/caClientApp/Makefile b/modules/ca/src/template/top/caClientApp/Makefile index 47011375c..98c52fd03 100644 --- a/modules/ca/src/template/top/caClientApp/Makefile +++ b/modules/ca/src/template/top/caClientApp/Makefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2003 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP=.. include $(TOP)/configure/CONFIG diff --git a/modules/ca/src/template/top/caClientApp/caExample.c b/modules/ca/src/template/top/caClientApp/caExample.c index 5ab230827..63afbe880 100644 --- a/modules/ca/src/template/top/caClientApp/caExample.c +++ b/modules/ca/src/template/top/caClientApp/caExample.c @@ -1,3 +1,7 @@ +/* SPDX-FileCopyrightText: 1998 Argonne National Laboratory */ + +/* SPDX-License-Identifier: EPICS */ + /*caExample.c*/ #include #include diff --git a/modules/ca/src/template/top/caClientApp/caMonitor.c b/modules/ca/src/template/top/caClientApp/caMonitor.c index 5adc90e7f..8e19b99cb 100644 --- a/modules/ca/src/template/top/caClientApp/caMonitor.c +++ b/modules/ca/src/template/top/caClientApp/caMonitor.c @@ -1,3 +1,7 @@ +/* SPDX-FileCopyrightText: 2000 Argonne National Laboratory */ + +/* SPDX-License-Identifier: EPICS */ + /*caMonitor.c*/ /* This example accepts the name of a file containing a list of pvs to monitor. diff --git a/modules/ca/src/template/top/caPerlApp/Makefile b/modules/ca/src/template/top/caPerlApp/Makefile index 34439cbfd..5932f000e 100644 --- a/modules/ca/src/template/top/caPerlApp/Makefile +++ b/modules/ca/src/template/top/caPerlApp/Makefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2018 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP=.. include $(TOP)/configure/CONFIG diff --git a/modules/ca/src/template/top/caPerlApp/caget.pl b/modules/ca/src/template/top/caPerlApp/caget.pl index 0d9af37a1..2538628fb 100644 --- a/modules/ca/src/template/top/caPerlApp/caget.pl +++ b/modules/ca/src/template/top/caPerlApp/caget.pl @@ -1,5 +1,9 @@ #!/usr/bin/env perl +# SPDX-FileCopyrightText: 2008 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + use strict; # This construct sets @INC to search lib/perl of all RELEASE entries diff --git a/modules/ca/src/template/top/caPerlApp/cainfo.pl b/modules/ca/src/template/top/caPerlApp/cainfo.pl index 3e38e8baf..71550c364 100644 --- a/modules/ca/src/template/top/caPerlApp/cainfo.pl +++ b/modules/ca/src/template/top/caPerlApp/cainfo.pl @@ -1,5 +1,9 @@ #!/usr/bin/env perl +# SPDX-FileCopyrightText: 2008 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + use strict; # This construct sets @INC to search lib/perl of all RELEASE entries diff --git a/modules/ca/src/template/top/caPerlApp/camonitor.pl b/modules/ca/src/template/top/caPerlApp/camonitor.pl index 3a61f2d6c..483be29a2 100644 --- a/modules/ca/src/template/top/caPerlApp/camonitor.pl +++ b/modules/ca/src/template/top/caPerlApp/camonitor.pl @@ -1,5 +1,9 @@ #!/usr/bin/env perl +# SPDX-FileCopyrightText: 2008 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + use strict; # This construct sets @INC to search lib/perl of all RELEASE entries diff --git a/modules/ca/src/template/top/caPerlApp/caput.pl b/modules/ca/src/template/top/caPerlApp/caput.pl index 64ff9cbda..4df1c6de2 100644 --- a/modules/ca/src/template/top/caPerlApp/caput.pl +++ b/modules/ca/src/template/top/caPerlApp/caput.pl @@ -1,5 +1,9 @@ #!/usr/bin/env perl +# SPDX-FileCopyrightText: 2008 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + use strict; # This construct sets @INC to search lib/perl of all RELEASE entries diff --git a/modules/ca/src/tools/caget.c b/modules/ca/src/tools/caget.c index d24fcd066..0472cddad 100644 --- a/modules/ca/src/tools/caget.c +++ b/modules/ca/src/tools/caget.c @@ -343,6 +343,7 @@ static int caget (pv *pvs, int nPvs, RequestT request, OutputT format, default : break; } + free(pvs[n].value); } return 0; } @@ -388,7 +389,7 @@ int main (int argc, char *argv[]) int digits = 0; /* getopt() no. of float digits */ int nPvs; /* Number of PVs */ - pv* pvs; /* Array of PV structures */ + pv* pvs = NULL; /* Array of PV structures */ LINE_BUFFER(stdout); /* Configure stdout buffering */ @@ -556,6 +557,7 @@ int main (int argc, char *argv[]) result = caget(pvs, nPvs, request, format, type, count); /* Shut down Channel Access */ + free(pvs); ca_context_destroy(); return result; diff --git a/modules/database/src/ioc/Makefile b/modules/database/src/ioc/Makefile index cf3345355..15894cd0d 100644 --- a/modules/database/src/ioc/Makefile +++ b/modules/database/src/ioc/Makefile @@ -16,7 +16,7 @@ USR_CPPFLAGS += -DUSE_TYPED_RSET -DUSE_TYPED_DSET # Shared library ABI version. SHRLIB_VERSION = $(EPICS_DATABASE_MAJOR_VERSION).$(EPICS_DATABASE_MINOR_VERSION).$(EPICS_DATABASE_MAINTENANCE_VERSION) -API_HEADER = dbCoreAPI.h +# dbCoreAPI.h generated earlier during libcom/ dbCore_API = dbCore LIBRARY_IOC += dbCore diff --git a/modules/database/src/ioc/as/Makefile b/modules/database/src/ioc/as/Makefile index bef8637e5..25237c26c 100644 --- a/modules/database/src/ioc/as/Makefile +++ b/modules/database/src/ioc/as/Makefile @@ -23,4 +23,4 @@ dbCore_SRCS += asIocRegister.c PROD_HOST += ascheck ascheck_SRCS = ascheck.c -ascheck_LIBS = dbCore ca Com +ascheck_LIBS = dbCore ca diff --git a/modules/database/src/ioc/bpt/Makefile b/modules/database/src/ioc/bpt/Makefile index a383d0864..78886a3b9 100644 --- a/modules/database/src/ioc/bpt/Makefile +++ b/modules/database/src/ioc/bpt/Makefile @@ -20,7 +20,7 @@ BPT_DBD += bptTypeJdegF.dbd BPT_DBD += bptTypeKdegC.dbd BPT_DBD += bptTypeKdegF.dbd -ifneq (inc,$(strip $(MAKECMDGOALS))) +ifneq ($(filter-out inc,$(strip $(MAKECMDGOALS))),) DBD += $(BPT_DBD) endif @@ -28,5 +28,5 @@ PROD_HOST += makeBpt makeBpt_SRCS = makeBpt +DOCS += menuConvert.md HTMLS += menuConvert.html - diff --git a/modules/database/src/ioc/bpt/makeBpt.c b/modules/database/src/ioc/bpt/makeBpt.c index 51821ebd9..1038598bd 100644 --- a/modules/database/src/ioc/bpt/makeBpt.c +++ b/modules/database/src/ioc/bpt/makeBpt.c @@ -224,6 +224,8 @@ int main(int argc, char **argv) fprintf(outFile,"}\n"); fclose(inFile); fclose(outFile); + free(outFilename); + free(pname); return(0); } diff --git a/modules/database/src/ioc/db/Makefile b/modules/database/src/ioc/db/Makefile index ebb78e229..a17e45f18 100644 --- a/modules/database/src/ioc/db/Makefile +++ b/modules/database/src/ioc/db/Makefile @@ -61,6 +61,12 @@ DBDINC += menuScan DBDINC += dbCommon dbMenusPod = $(notdir $(wildcard ../db/menu*.dbd.pod)) + +DOCS += $(patsubst %.dbd.pod,%.md,$(dbMenusPod)) +DOCS += dbCommonRecord.md +DOCS += dbCommonInput.md +DOCS += dbCommonOutput.md + HTMLS += $(patsubst %.dbd.pod,%.html,$(dbMenusPod)) HTMLS += dbCommonRecord.html HTMLS += dbCommonInput.html diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c index 4128cbe4e..a6c040d45 100644 --- a/modules/database/src/ioc/db/dbAccess.c +++ b/modules/database/src/ioc/db/dbAccess.c @@ -112,7 +112,7 @@ void dbSpcAsRegisterCallback(SPC_ASCALLBACK func) long dbPutSpecial(DBADDR *paddr,int pass) { - long int (*pspecial)()=NULL; + long int (*pspecial)(struct dbAddr *, int)=NULL; rset *prset; dbCommon *precord = paddr->precord; long status=0; @@ -145,63 +145,66 @@ long dbPutSpecial(DBADDR *paddr,int pass) } static void get_enum_strs(DBADDR *paddr, char **ppbuffer, - rset *prset,long *options) + rset *prset, long *options) { - short field_type=paddr->field_type; - dbFldDes *pdbFldDes = paddr->pfldDes; - dbMenu *pdbMenu; - dbDeviceMenu *pdbDeviceMenu; - char **papChoice; - unsigned long no_str; - char *ptemp; - struct dbr_enumStrs *pdbr_enumStrs=(struct dbr_enumStrs*)(*ppbuffer); - unsigned int i; - - memset(pdbr_enumStrs,'\0',dbr_enumStrs_size); - switch(field_type) { - case DBF_ENUM: - if( prset && prset->get_enum_strs ) { - (*prset->get_enum_strs)(paddr,pdbr_enumStrs); - } else { - *options = (*options)^DBR_ENUM_STRS;/*Turn off option*/ - } - break; - case DBF_MENU: - pdbMenu = (dbMenu *)pdbFldDes->ftPvt; - no_str = pdbMenu->nChoice; - papChoice= pdbMenu->papChoiceValue; - goto choice_common; - case DBF_DEVICE: - pdbDeviceMenu = (dbDeviceMenu *)pdbFldDes->ftPvt; - if(!pdbDeviceMenu) { - *options = (*options)^DBR_ENUM_STRS;/*Turn off option*/ - break; - } - no_str = pdbDeviceMenu->nChoice; - papChoice = pdbDeviceMenu->papChoice; - goto choice_common; -choice_common: - i = sizeof(pdbr_enumStrs->strs)/ - sizeof(pdbr_enumStrs->strs[0]); - if(ino_str = no_str; - ptemp = &(pdbr_enumStrs->strs[0][0]); - for (i=0; istrs[0])); - *(ptemp+sizeof(pdbr_enumStrs->strs[0])-1) = 0; - } - ptemp += sizeof(pdbr_enumStrs->strs[0]); - } - break; - default: - *options = (*options)^DBR_ENUM_STRS;/*Turn off option*/ - break; + struct dbr_enumStrs *penum=(struct dbr_enumStrs*)(*ppbuffer); + /* advance output buffer on success or failure for next option */ + *ppbuffer = dbr_enumStrs_size + (char*)penum; + + memset(penum, 0, dbr_enumStrs_size); + + /* from this point + * on success, return early + * on failure, jump or fall through to clear *options bit. + */ + + if(paddr->field_type == DBF_ENUM) { + if( prset && prset->get_enum_strs ) { + (*prset->get_enum_strs)(paddr,penum); + return; + } + + } else if(paddr->field_type == DBF_MENU || paddr->field_type == DBF_DEVICE) { + char **ppchoices; + epicsUInt32 i, nchoices = 0; + + if(paddr->field_type == DBF_MENU) { + dbMenu *pmenu = paddr->pfldDes->ftPvt; + nchoices = pmenu->nChoice; + ppchoices= pmenu->papChoiceValue; + + } else if(paddr->field_type == DBF_DEVICE) { + dbDeviceMenu *pdevs = paddr->pfldDes->ftPvt; + if(!pdevs) + goto nostrs; + + nchoices = pdevs->nChoice; + ppchoices = pdevs->papChoice; + } + + if(nchoices > NELEMENTS(penum->strs)) + nchoices = NELEMENTS(penum->strs); /* availible > capacity, truncated list */ + + penum->no_str = nchoices; + + for(i=0; istrs[i], ppchoices[i], + sizeof(penum->strs[i])); + /* strs[i][] allowed to omit trailing nil */ + } else { + penum->strs[i][0] = '\0'; + } } - *ppbuffer = ((char *)*ppbuffer) + dbr_enumStrs_size; return; + + } else { + /* other DBF_* fall through to error */ + } + +nostrs: + /* indicate option data not available. distinct from no_str==0 */ + *options = (*options)^DBR_ENUM_STRS; } static void get_graphics(DBADDR *paddr, char **ppbuffer, @@ -1322,6 +1325,7 @@ long dbPut(DBADDR *paddr, short dbrType, long status = 0; dbFldDes *pfldDes; int isValueField; + int propertyUpdate = paddr->pfldDes->prop && precord->mlis.count; if (special == SPC_ATTRIBUTE) return S_db_noMod; @@ -1366,12 +1370,31 @@ long dbPut(DBADDR *paddr, short dbrType, if (nRequest < 1) { recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM); } else { - status = dbFastPutConvertRoutine[dbrType][field_type](pbuffer, - paddr->pfield, paddr); + if (propertyUpdate && paddr->field_size <= MAX_STRING_SIZE) { + char propBuffer[MAX_STRING_SIZE]; + status = dbFastPutConvertRoutine[dbrType][field_type](pbuffer, + &propBuffer, paddr); + if (!status) { + if (memcmp(paddr->pfield, &propBuffer, paddr->field_size) != 0) { + memcpy(paddr->pfield, &propBuffer, paddr->field_size); + } else { + /* suppress DBE_PROPERTY event if property did not change */ + propertyUpdate = 0; + } + } + } else { + status = dbFastPutConvertRoutine[dbrType][field_type](pbuffer, + paddr->pfield, paddr); + } nRequest = 1; } } + /* Post property updates before second dbPutSpecial */ + /* which may post DBE_VALUE and/or DBE_LOG events */ + if (propertyUpdate && !status) + db_post_events(precord, NULL, DBE_PROPERTY); + /* Always do special processing if needed */ if (special) { long status2 = dbPutSpecial(paddr, 1); @@ -1388,12 +1411,6 @@ long dbPut(DBADDR *paddr, short dbrType, if (precord->mlis.count && !(isValueField && pfldDes->process_passive)) db_post_events(precord, pfieldsave, DBE_VALUE | DBE_LOG); - /* If this field is a property (metadata) field, - * then post a property change event (even if the field - * didn't change). - */ - if (precord->mlis.count && pfldDes->prop) - db_post_events(precord, NULL, DBE_PROPERTY); done: paddr->pfield = pfieldsave; return status; diff --git a/modules/database/src/ioc/db/dbCa.c b/modules/database/src/ioc/db/dbCa.c index 57cca404f..42fe374a8 100644 --- a/modules/database/src/ioc/db/dbCa.c +++ b/modules/database/src/ioc/db/dbCa.c @@ -225,6 +225,7 @@ void testdbCaWaitForEvent(DBLINK *plink, unsigned long cnt, enum testEvent event assert(plink->type==CA_LINK); pca = (caLink *)plink->value.pv_link.pvt; + caLinkInc(pca); epicsMutexMustLock(pca->lock); assert(!pca->monitor && !pca->connect && !pca->userPvt); @@ -239,6 +240,8 @@ void testdbCaWaitForEvent(DBLINK *plink, unsigned long cnt, enum testEvent event dbScanUnlock(plink->precord); epicsEventMustWait(evt); + /* ensure worker has finished executing */ + dbCaSync(); dbScanLock(plink->precord); epicsMutexMustLock(pca->lock); @@ -250,6 +253,7 @@ void testdbCaWaitForEvent(DBLINK *plink, unsigned long cnt, enum testEvent event epicsEventDestroy(evt); epicsMutexUnlock(pca->lock); + caLinkDec(pca); dbScanUnlock(plink->precord); } @@ -287,16 +291,15 @@ void dbCaSync(void) epicsEventMustWait(wake); /* Worker holds workListLock when calling epicsEventMustTrigger() - * we cycle through workListLock to ensure worker call to + * we hold workListLock to ensure worker call to * epicsEventMustTrigger() returns before we destroy the event. */ epicsMutexMustLock(workListLock); - epicsMutexUnlock(workListLock); - assert(templink.refcount==1); epicsMutexDestroy(templink.lock); epicsEventDestroy(wake); + epicsMutexUnlock(workListLock); } void dbCaCallbackProcess(void *userPvt) diff --git a/modules/database/src/ioc/db/dbChannelIO.cpp b/modules/database/src/ioc/db/dbChannelIO.cpp index 449717e73..87a0044ee 100644 --- a/modules/database/src/ioc/db/dbChannelIO.cpp +++ b/modules/database/src/ioc/db/dbChannelIO.cpp @@ -14,6 +14,8 @@ * 505 665 1831 */ +#define USE_TYPED_DBEVENT + #include #include @@ -165,7 +167,7 @@ unsigned long dbChannelIO::nativeElementCount ( { guard.assertIdenticalMutex ( this->mutex ); long elements = dbChannelElements ( this->dbch ); - if ( elements >= 0u ) { + if ( elements >= 0 ) { return static_cast < unsigned long > ( elements ); } return 0u; diff --git a/modules/database/src/ioc/db/dbCommon.dbd.pod b/modules/database/src/ioc/db/dbCommon.dbd.pod index 5366d477a..8d7c1a0ca 100644 --- a/modules/database/src/ioc/db/dbCommon.dbd.pod +++ b/modules/database/src/ioc/db/dbCommon.dbd.pod @@ -285,7 +285,11 @@ Inf (Infinite) value. UDF defaults to TRUE but can be set in a database file. Record and device support routines which write to the VAL field are generally responsible for setting and clearing UDF. -=fields STAT, SEVR, AMSG, NSTA, NSEV, NAMSG, ACKS, ACKT, UDF +The B field specifies the alarm severity that the record will be set to +whenever its value is undefined (i.e., the UDF field is 1). This includes the +initial severity of the record being undefined after the IOC boots. + +=fields STAT, SEVR, AMSG, NSTA, NSEV, NAMSG, ACKS, ACKT, UDF, UDFS =cut diff --git a/modules/database/src/ioc/db/dbCommonPvt.h b/modules/database/src/ioc/db/dbCommonPvt.h index eda8b18a7..4d41902a2 100644 --- a/modules/database/src/ioc/db/dbCommonPvt.h +++ b/modules/database/src/ioc/db/dbCommonPvt.h @@ -15,13 +15,21 @@ typedef struct dbCommonPvt { /* Thread which is currently processing this record */ struct epicsThreadOSD* procThread; - struct dbCommon common; + /* actually followed by: + * struct dbCommon common; + */ } dbCommonPvt; static EPICS_ALWAYS_INLINE dbCommonPvt* dbRec2Pvt(struct dbCommon *prec) { - return CONTAINER(prec, dbCommonPvt, common); + return (dbCommonPvt*)((char*)prec - sizeof(dbCommonPvt)); +} + +static EPICS_ALWAYS_INLINE +dbCommon* dbPvt2Rec(struct dbCommonPvt *pvt) +{ + return (dbCommon*)&pvt[1]; } #endif // DBCOMMONPVT_H diff --git a/modules/database/src/ioc/db/dbConstLink.c b/modules/database/src/ioc/db/dbConstLink.c index 46ffd92b2..2bc305762 100644 --- a/modules/database/src/ioc/db/dbConstLink.c +++ b/modules/database/src/ioc/db/dbConstLink.c @@ -94,7 +94,7 @@ static long cvt_st_UInt32(const char *from, void *pfield, const dbAddr *paddr) status = epicsParseFloat64(from, &dval, &end); if (!status && dval >=0 && - dval <= ULONG_MAX) + dval <= UINT_MAX) *to = dval; } return status; diff --git a/modules/database/src/ioc/db/dbContext.cpp b/modules/database/src/ioc/db/dbContext.cpp index dc3087602..c38ca663c 100644 --- a/modules/database/src/ioc/db/dbContext.cpp +++ b/modules/database/src/ioc/db/dbContext.cpp @@ -13,6 +13,8 @@ * 505 665 1831 */ +#define USE_TYPED_DBEVENT + #include #include "epicsMutex.h" diff --git a/modules/database/src/ioc/db/dbContextReadNotifyCache.cpp b/modules/database/src/ioc/db/dbContextReadNotifyCache.cpp index 1c5e0d0f9..c11ca3ab4 100644 --- a/modules/database/src/ioc/db/dbContextReadNotifyCache.cpp +++ b/modules/database/src/ioc/db/dbContextReadNotifyCache.cpp @@ -12,6 +12,8 @@ * Author Jeff Hill */ +#define USE_TYPED_DBEVENT + #include #include "epicsMutex.h" diff --git a/modules/database/src/ioc/db/dbConvert.c b/modules/database/src/ioc/db/dbConvert.c index 84f36ed28..ee1b5cbeb 100644 --- a/modules/database/src/ioc/db/dbConvert.c +++ b/modules/database/src/ioc/db/dbConvert.c @@ -302,7 +302,7 @@ static long getStringUlong(const dbAddr *paddr, epicsFloat64 dval; status = epicsParseFloat64(psrc, &dval, &end); - if (!status && 0 <= dval && dval <= ULONG_MAX) + if (!status && 0 <= dval && dval <= UINT_MAX) *pdst = dval; } if (status) @@ -1052,7 +1052,7 @@ static long putStringUlong(dbAddr *paddr, epicsFloat64 dval; status = epicsParseFloat64(psrc, &dval, &end); - if (!status && 0 <= dval && dval <= ULONG_MAX) + if (!status && 0 <= dval && dval <= UINT_MAX) *pdst = dval; } if (status) diff --git a/modules/database/src/ioc/db/dbConvertFast.h b/modules/database/src/ioc/db/dbConvertFast.h index 7ab630b91..470cedbdd 100644 --- a/modules/database/src/ioc/db/dbConvertFast.h +++ b/modules/database/src/ioc/db/dbConvertFast.h @@ -7,20 +7,38 @@ * EPICS Base is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ -/* dbConvertFast.h */ +/** @file dbConvertFast.h + * @brief Data conversion for scalar values + * + * The typedef FASTCONVERTFUNC is defined in link.h as: + * @code + * long convert(const void *from, void *to, const struct dbAddr *paddr); + * @endcode + * + * The arrays declared here provide pointers to the fast conversion + * routine where the first array index is the data type for the first + * "from" pointer arg, and the second array index is the data type for + * the second "to" pointer arg. The array index values are a subset of + * the DBF_ enum values defined in dbFldTypes.h + */ #ifndef INCdbConvertFasth #define INCdbConvertFasth #include "dbFldTypes.h" #include "dbCoreAPI.h" +#include "link.h" #ifdef __cplusplus extern "C" { #endif -DBCORE_API extern long (*dbFastGetConvertRoutine[DBF_DEVICE+1][DBR_ENUM+1])(); -DBCORE_API extern long (*dbFastPutConvertRoutine[DBR_ENUM+1][DBF_DEVICE+1])(); +/** Function pointers for get conversions */ +DBCORE_API extern const FASTCONVERTFUNC + dbFastGetConvertRoutine[DBF_DEVICE+1][DBR_ENUM+1]; +/** Function pointers for put conversions */ +DBCORE_API extern const FASTCONVERTFUNC + dbFastPutConvertRoutine[DBR_ENUM+1][DBF_DEVICE+1]; #ifdef __cplusplus } diff --git a/modules/database/src/ioc/db/dbConvertJSON.c b/modules/database/src/ioc/db/dbConvertJSON.c index 95f944e2b..ce3f5bbed 100644 --- a/modules/database/src/ioc/db/dbConvertJSON.c +++ b/modules/database/src/ioc/db/dbConvertJSON.c @@ -19,8 +19,6 @@ #include "dbConvertFast.h" #include "dbConvertJSON.h" -typedef long (*FASTCONVERT)(); - typedef struct parseContext { int depth; short dbrType; @@ -42,7 +40,7 @@ static int dbcj_boolean(void *ctx, int val) { static int dbcj_integer(void *ctx, long long num) { parseContext *parser = (parseContext *) ctx; epicsInt64 val64 = num; - FASTCONVERT conv = dbFastPutConvertRoutine[DBF_INT64][parser->dbrType]; + FASTCONVERTFUNC conv = dbFastPutConvertRoutine[DBF_INT64][parser->dbrType]; if (parser->elems > 0) { conv(&val64, parser->pdest, NULL); @@ -54,7 +52,7 @@ static int dbcj_integer(void *ctx, long long num) { static int dbcj_double(void *ctx, double num) { parseContext *parser = (parseContext *) ctx; - FASTCONVERT conv = dbFastPutConvertRoutine[DBF_DOUBLE][parser->dbrType]; + FASTCONVERTFUNC conv = dbFastPutConvertRoutine[DBF_DOUBLE][parser->dbrType]; if (parser->elems > 0) { conv(&num, parser->pdest, NULL); diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c index 77d926630..16cc33279 100644 --- a/modules/database/src/ioc/db/dbDbLink.c +++ b/modules/database/src/ioc/db/dbDbLink.c @@ -226,25 +226,48 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, } if (!status && precord != dbChannelRecord(chan)) - recGblInheritSevr(plink->value.pv_link.pvlMask & pvlOptMsMode, + recGblInheritSevrMsg(plink->value.pv_link.pvlMask & pvlOptMsMode, plink->precord, - dbChannelRecord(chan)->stat, dbChannelRecord(chan)->sevr); + dbChannelRecord(chan)->stat, dbChannelRecord(chan)->sevr, + dbChannelRecord(chan)->amsg); return status; } -static long dbDbGetControlLimits(const struct link *plink, double *low, - double *high) +/* Some records get options (precsision, units, ...) for some fields + * from an input link. We need to catch the case that this link + * points back to the same field or we will end in an infinite recursion. +*/ +static long dbDbGetOptionLoopSafe(const struct link *plink, short dbrType, + void *pbuffer, long option) { + /* We need to cast away const to set the flags. + That's ok because we know that plink is never actually readonly. + And we reset everything to its original state. + */ + struct link *mutable_plink = (struct link *)plink; + long status = S_dbLib_badLink; dbChannel *chan = linkChannel(plink); DBADDR *paddr = &chan->addr; + long number_elements = 0; + + dbScanLock(paddr->precord); + if (!(mutable_plink->flags & DBLINK_FLAG_VISITED)) { + mutable_plink->flags |= DBLINK_FLAG_VISITED; + status = dbGet(paddr, dbrType, pbuffer, &option, &number_elements, NULL); + mutable_plink->flags &= ~DBLINK_FLAG_VISITED; + } + dbScanUnlock(paddr->precord); + return status; +} + +static long dbDbGetControlLimits(const struct link *plink, double *low, + double *high) +{ struct buffer { DBRctrlDouble double value; } buffer; - long options = DBR_CTRL_DOUBLE; - long number_elements = 0; - long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, - NULL); + long status = dbDbGetOptionLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_CTRL_DOUBLE); if (status) return status; @@ -257,16 +280,11 @@ static long dbDbGetControlLimits(const struct link *plink, double *low, static long dbDbGetGraphicLimits(const struct link *plink, double *low, double *high) { - dbChannel *chan = linkChannel(plink); - DBADDR *paddr = &chan->addr; struct buffer { DBRgrDouble double value; } buffer; - long options = DBR_GR_DOUBLE; - long number_elements = 0; - long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, - NULL); + long status = dbDbGetOptionLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_GR_DOUBLE); if (status) return status; @@ -279,16 +297,11 @@ static long dbDbGetGraphicLimits(const struct link *plink, double *low, static long dbDbGetAlarmLimits(const struct link *plink, double *lolo, double *low, double *high, double *hihi) { - dbChannel *chan = linkChannel(plink); - DBADDR *paddr = &chan->addr; struct buffer { DBRalDouble double value; } buffer; - long options = DBR_AL_DOUBLE; - long number_elements = 0; - long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, - 0); + long status = dbDbGetOptionLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_AL_DOUBLE); if (status) return status; @@ -302,16 +315,11 @@ static long dbDbGetAlarmLimits(const struct link *plink, double *lolo, static long dbDbGetPrecision(const struct link *plink, short *precision) { - dbChannel *chan = linkChannel(plink); - DBADDR *paddr = &chan->addr; struct buffer { DBRprecision double value; } buffer; - long options = DBR_PRECISION; - long number_elements = 0; - long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, - 0); + long status = dbDbGetOptionLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_PRECISION); if (status) return status; @@ -322,16 +330,11 @@ static long dbDbGetPrecision(const struct link *plink, short *precision) static long dbDbGetUnits(const struct link *plink, char *units, int unitsSize) { - dbChannel *chan = linkChannel(plink); - DBADDR *paddr = &chan->addr; struct buffer { DBRunits double value; } buffer; - long options = DBR_UNITS; - long number_elements = 0; - long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, - 0); + long status = dbDbGetOptionLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_UNITS); if (status) return status; @@ -376,8 +379,8 @@ static long dbDbPutValue(struct link *plink, short dbrType, dbCommon *pdest = dbChannelRecord(chan); long status = dbPut(paddr, dbrType, pbuffer, nRequest); - recGblInheritSevr(ppv_link->pvlMask & pvlOptMsMode, pdest, psrce->nsta, - psrce->nsev); + recGblInheritSevrMsg(ppv_link->pvlMask & pvlOptMsMode, pdest, psrce->nsta, + psrce->nsev, psrce->namsg); if (status) return status; diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c index e7d0427fc..c910d4692 100644 --- a/modules/database/src/ioc/db/dbEvent.c +++ b/modules/database/src/ioc/db/dbEvent.c @@ -19,6 +19,8 @@ */ #define EPICS_PRIVATE_API +#define USE_TYPED_DBEVENT + #include #include #include @@ -98,8 +100,8 @@ struct event_user { unsigned char extra_labor; /* if set call extra labor func */ unsigned char flowCtrlMode; /* replace existing monitor */ unsigned char extraLaborBusy; - void (*init_func)(); - epicsThreadId init_func_arg; + void (*init_func)(void *); + void *init_func_arg; }; typedef struct { diff --git a/modules/database/src/ioc/db/dbEvent.h b/modules/database/src/ioc/db/dbEvent.h index 277d6c07e..32e54265b 100644 --- a/modules/database/src/ioc/db/dbEvent.h +++ b/modules/database/src/ioc/db/dbEvent.h @@ -32,6 +32,15 @@ struct dbChannel; struct db_field_log; struct evSubscrip; +#ifdef USE_TYPED_DBEVENT +struct dbEventContext; // use dbEventCtx +typedef struct evSubscrip* dbEventSubscription; +typedef struct dbEventContext* dbEventCtx; +#else +typedef void * dbEventSubscription; +typedef void * dbEventCtx; +#endif + DBCORE_API int db_event_list ( const char *name, unsigned level); DBCORE_API int dbel ( @@ -39,8 +48,6 @@ DBCORE_API int dbel ( DBCORE_API int db_post_events ( void *pRecord, void *pField, unsigned caEventMask ); -typedef void * dbEventCtx; - typedef void EXTRALABORFUNC (void *extralabor_arg); DBCORE_API dbEventCtx db_init_events (void); DBCORE_API int db_start_events ( @@ -63,7 +70,6 @@ DBCORE_API void db_init_event_freelists (void); typedef void EVENTFUNC (void *user_arg, struct dbChannel *chan, int eventsRemaining, struct db_field_log *pfl); -typedef void * dbEventSubscription; DBCORE_API dbEventSubscription db_add_event ( dbEventCtx ctx, struct dbChannel *chan, EVENTFUNC *user_sub, void *user_arg, unsigned select); diff --git a/modules/database/src/ioc/db/dbFastLinkConv.c b/modules/database/src/ioc/db/dbFastLinkConv.c index 39cddce5f..48339f549 100644 --- a/modules/database/src/ioc/db/dbFastLinkConv.c +++ b/modules/database/src/ioc/db/dbFastLinkConv.c @@ -71,11 +71,10 @@ */ /* Convert String to String */ -static long cvt_st_st( - char *from, - char *to, - const dbAddr *paddr) +static long cvt_st_st(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + char *to = (char *) t; size_t size; if (paddr && paddr->field_size < MAX_STRING_SIZE) { @@ -89,11 +88,10 @@ static long cvt_st_st( } /* Convert String to Char */ -static long cvt_st_c( - char *from, - epicsInt8 *to, - const dbAddr *paddr) +static long cvt_st_c(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsInt8 *to = (epicsInt8 *) t; char *end; if (*from == 0) { @@ -104,11 +102,10 @@ static long cvt_st_c( } /* Convert String to Unsigned Char */ -static long cvt_st_uc( - char *from, - epicsUInt8 *to, - const dbAddr *paddr) +static long cvt_st_uc(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsUInt8 *to = (epicsUInt8 *) t; char *end; if (*from == 0) { @@ -119,11 +116,10 @@ static long cvt_st_uc( } /* Convert String to Short */ -static long cvt_st_s( - char *from, - epicsInt16 *to, - const dbAddr *paddr) +static long cvt_st_s(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsInt16 *to = (epicsInt16 *) t; char *end; if (*from == 0) { @@ -134,11 +130,10 @@ static long cvt_st_s( } /* Convert String to Unsigned Short */ -static long cvt_st_us( - char *from, - epicsUInt16 *to, - const dbAddr *paddr) +static long cvt_st_us(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsUInt16 *to = (epicsUInt16 *) t; char *end; if (*from == 0) { @@ -149,11 +144,10 @@ static long cvt_st_us( } /* Convert String to Long */ -static long cvt_st_l( - char *from, - epicsInt32 *to, - const dbAddr *paddr) +static long cvt_st_l(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsInt32 *to = (epicsInt32 *) t; char *end; if (*from == 0) { @@ -164,11 +158,10 @@ static long cvt_st_l( } /* Convert String to Unsigned Long */ -static long cvt_st_ul( - char *from, - epicsUInt32 *to, - const dbAddr *paddr) +static long cvt_st_ul(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsUInt32 *to = (epicsUInt32 *) t; char *end; long status; @@ -188,18 +181,17 @@ static long cvt_st_ul( status = epicsParseFloat64(from, &dval, &end); if (!status && dval >=0 && - dval <= ULONG_MAX) + dval <= UINT_MAX) *to = dval; } return status; } /* Convert String to Int64 */ -static long cvt_st_q( - char *from, - epicsInt64 *to, - const dbAddr *paddr) +static long cvt_st_q(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsInt64 *to = (epicsInt64 *) t; char *end; if (*from == 0) { @@ -210,11 +202,10 @@ static long cvt_st_q( } /* Convert String to UInt64 */ -static long cvt_st_uq( - char *from, - epicsUInt64 *to, - const dbAddr *paddr) +static long cvt_st_uq(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsUInt64 *to = (epicsUInt64 *) t; char *end; if (*from == 0) { @@ -225,41 +216,38 @@ static long cvt_st_uq( } /* Convert String to Float */ -static long cvt_st_f( - char *from, - epicsFloat32 *to, - const dbAddr *paddr) +static long cvt_st_f(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsFloat32 *to = (epicsFloat32 *) t; char *end; - if (*from == 0) { - *to = 0; - return 0; - } - return epicsParseFloat32(from, to, &end); + if (*from == 0) { + *to = 0; + return 0; + } + return epicsParseFloat32(from, to, &end); } /* Convert String to Double */ -static long cvt_st_d( - char *from, - epicsFloat64 *to, - const dbAddr *paddr) +static long cvt_st_d(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsFloat64 *to = (epicsFloat64 *) t; char *end; - if (*from == 0) { - *to = 0.0; - return 0; - } - return epicsParseFloat64(from, to, &end); + if (*from == 0) { + *to = 0.0; + return 0; + } + return epicsParseFloat64(from, to, &end); } /* Convert String to Enumerated */ -static long cvt_st_e( - char *from, - epicsEnum16 *to, - const dbAddr *paddr) +static long cvt_st_e(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsEnum16 *to = (epicsEnum16 *) t; rset *prset = dbGetRset(paddr); long status = S_db_noRSET; struct dbr_enumStrs enumStrs; @@ -294,11 +282,10 @@ static long cvt_st_e( } /* Convert String to Menu */ -static long cvt_st_menu( - char *from, - epicsEnum16 *to, - const dbAddr *paddr) +static long cvt_st_menu(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsEnum16 *to = (epicsEnum16 *) t; dbFldDes *pdbFldDes = paddr->pfldDes; dbMenu *pdbMenu; char **pchoices; @@ -325,15 +312,14 @@ static long cvt_st_menu( } } recGblDbaddrError(S_db_badChoice, paddr, "dbFastLinkConv(cvt_st_menu)"); - return(S_db_badChoice); + return S_db_badChoice; } /* Convert String to Device */ -static long cvt_st_device( - char *from, - epicsEnum16 *to, - const dbAddr *paddr) +static long cvt_st_device(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsEnum16 *to = (epicsEnum16 *) t; dbFldDes *pdbFldDes = paddr->pfldDes; dbDeviceMenu *pdbDeviceMenu = pdbFldDes->ftPvt; char **pchoices, *pchoice; @@ -363,979 +349,1234 @@ static long cvt_st_device( } /* Convert Char to String */ -static long cvt_c_st( - epicsInt8 *from, - char *to, - const dbAddr *paddr) -{ cvtCharToString(*from, to); return(0); } +static long cvt_c_st(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + char *to = (char *) t; + cvtCharToString(*from, to); + return 0; +} /* Convert Char to Char */ -static long cvt_c_c( - epicsInt8 *from, - epicsInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_c_c(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + epicsInt8 *to = (epicsInt8 *) t; + *to=*from; + return 0; +} /* Convert Char to Unsigned Char */ -static long cvt_c_uc( - epicsInt8 *from, - epicsUInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_c_uc(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + epicsUInt8 *to = (epicsUInt8 *) t; + *to=*from; + return 0; +} /* Convert Char to Short */ -static long cvt_c_s( - epicsInt8 *from, - epicsInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_c_s(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + epicsInt16 *to = (epicsInt16 *) t; + *to=*from; + return 0; +} /* Convert Char to Unsigned Short */ -static long cvt_c_us( - epicsInt8 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_c_us(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=*from; + return 0; +} /* Convert Char to Long */ -static long cvt_c_l( - epicsInt8 *from, - epicsInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_c_l(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + epicsInt32 *to = (epicsInt32 *) t; + *to=*from; + return 0; +} /* Convert Char to Unsigned Long */ -static long cvt_c_ul( - epicsInt8 *from, - epicsUInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_c_ul(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + epicsUInt32 *to = (epicsUInt32 *) t; + *to=*from; + return 0; +} /* Convert Char to Int64 */ -static long cvt_c_q( - epicsInt8 *from, - epicsInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_c_q(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + epicsInt64 *to = (epicsInt64 *) t; + *to=*from; + return 0; +} /* Convert Char to UInt64 */ -static long cvt_c_uq( - epicsInt8 *from, - epicsUInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_c_uq(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + epicsUInt64 *to = (epicsUInt64 *) t; + *to=*from; + return 0; +} /* Convert Char to Float */ -static long cvt_c_f( - epicsInt8 *from, - epicsFloat32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_c_f(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + epicsFloat32 *to = (epicsFloat32 *) t; + *to=*from; + return 0; +} /* Convert Char to Double */ -static long cvt_c_d( - epicsInt8 *from, - epicsFloat64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_c_d(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + epicsFloat64 *to = (epicsFloat64 *) t; + *to=*from; + return 0; +} /* Convert Char to Enumerated */ -static long cvt_c_e( - epicsInt8 *from, - epicsEnum16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_c_e(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + epicsEnum16 *to = (epicsEnum16 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Char to String */ -static long cvt_uc_st( - epicsUInt8 *from, - char *to, - const dbAddr *paddr) -{ cvtUcharToString(*from, to); return(0); } +static long cvt_uc_st(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + char *to = (char *) t; + cvtUcharToString(*from, to); + return 0; +} /* Convert Unsigned Char to Char */ -static long cvt_uc_c( - epicsUInt8 *from, - epicsInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uc_c(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + epicsInt8 *to = (epicsInt8 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Char to Unsigned Char */ -static long cvt_uc_uc( - epicsUInt8 *from, - epicsUInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uc_uc(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + epicsUInt8 *to = (epicsUInt8 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Char to Short */ -static long cvt_uc_s( - epicsUInt8 *from, - epicsInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uc_s(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + epicsInt16 *to = (epicsInt16 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Char to Unsigned Short */ -static long cvt_uc_us( - epicsUInt8 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uc_us(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Char to Long */ -static long cvt_uc_l( - epicsUInt8 *from, - epicsInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uc_l(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + epicsInt32 *to = (epicsInt32 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Char to Unsigned Long */ -static long cvt_uc_ul( - epicsUInt8 *from, - epicsUInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uc_ul(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + epicsUInt32 *to = (epicsUInt32 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Char to Int64 */ -static long cvt_uc_q( - epicsUInt8 *from, - epicsInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uc_q(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + epicsInt64 *to = (epicsInt64 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Char to UInt64 */ -static long cvt_uc_uq( - epicsUInt8 *from, - epicsUInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uc_uq(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + epicsUInt64 *to = (epicsUInt64 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Char to Float */ -static long cvt_uc_f( - epicsUInt8 *from, - epicsFloat32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uc_f(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + epicsFloat32 *to = (epicsFloat32 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Char to Double */ -static long cvt_uc_d( - epicsUInt8 *from, - epicsFloat64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uc_d(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + epicsFloat64 *to = (epicsFloat64 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Char to Enumerated */ -static long cvt_uc_e( - epicsUInt8 *from, - epicsEnum16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uc_e(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + epicsEnum16 *to = (epicsEnum16 *) t; + *to=*from; + return 0; +} /* Convert Short to String */ -static long cvt_s_st( - epicsInt16 *from, - char *to, - const dbAddr *paddr) -{ cvtShortToString(*from, to); return(0); } +static long cvt_s_st(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + char *to = (char *) t; + cvtShortToString(*from, to); + return 0; +} /* Convert Short to Char */ -static long cvt_s_c( - epicsInt16 *from, - epicsInt8 *to, - const dbAddr *paddr) - { *to=(epicsInt8)*from; return(0); } +static long cvt_s_c(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + epicsInt8 *to = (epicsInt8 *) t; + *to=(epicsInt8)*from; + return 0; +} /* Convert Short to Unsigned Char */ -static long cvt_s_uc( - epicsInt16 *from, - epicsUInt8 *to, - const dbAddr *paddr) - { *to=(epicsUInt8)*from; return(0); } +static long cvt_s_uc(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + epicsUInt8 *to = (epicsUInt8 *) t; + *to=(epicsUInt8)*from; + return 0; +} /* Convert Short to Short */ -static long cvt_s_s( - epicsInt16 *from, - epicsInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_s_s(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + epicsInt16 *to = (epicsInt16 *) t; + *to=*from; + return 0; +} /* Convert Short to Unsigned Short */ -static long cvt_s_us( - epicsInt16 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_s_us(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=*from; + return 0; +} /* Convert Short to Long */ -static long cvt_s_l( - epicsInt16 *from, - epicsInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_s_l(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + epicsInt32 *to = (epicsInt32 *) t; + *to=*from; + return 0; +} /* Convert Short to Unsigned Long */ -static long cvt_s_ul( - epicsInt16 *from, - epicsUInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_s_ul(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + epicsUInt32 *to = (epicsUInt32 *) t; + *to=*from; + return 0; +} /* Convert Short to Int64 */ -static long cvt_s_q( - epicsInt16 *from, - epicsInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_s_q(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + epicsInt64 *to = (epicsInt64 *) t; + *to=*from; + return 0; +} /* Convert Short to UInt64 */ -static long cvt_s_uq( - epicsInt16 *from, - epicsUInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_s_uq(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + epicsUInt64 *to = (epicsUInt64 *) t; + *to=*from; + return 0; +} /* Convert Short to Float */ -static long cvt_s_f( - epicsInt16 *from, - epicsFloat32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_s_f(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + epicsFloat32 *to = (epicsFloat32 *) t; + *to=*from; + return 0; +} /* Convert Short to Double */ -static long cvt_s_d( - epicsInt16 *from, - epicsFloat64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_s_d(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + epicsFloat64 *to = (epicsFloat64 *) t; + *to=*from; + return 0; +} /* Convert Short to Enumerated */ -static long cvt_s_e( - epicsInt16 *from, - epicsEnum16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_s_e(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + epicsEnum16 *to = (epicsEnum16 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Short to String */ -static long cvt_us_st( - epicsUInt16 *from, - char *to, - const dbAddr *paddr) -{ cvtUshortToString(*from, to); return(0); } +static long cvt_us_st(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + char *to = (char *) t; + cvtUshortToString(*from, to); + return 0; +} /* Convert Unsigned Short to Char */ -static long cvt_us_c( - epicsUInt16 *from, - epicsInt8 *to, - const dbAddr *paddr) - { *to=(epicsInt8)*from; return(0); } +static long cvt_us_c(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + epicsInt8 *to = (epicsInt8 *) t; + *to=(epicsInt8)*from; + return 0; +} /* Convert Unsigned Short to Unsigned Char */ -static long cvt_us_uc( - epicsUInt16 *from, - epicsUInt8 *to, - const dbAddr *paddr) - { *to=(epicsUInt8)*from; return(0); } +static long cvt_us_uc(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + epicsUInt8 *to = (epicsUInt8 *) t; + *to=(epicsUInt8)*from; + return 0; +} /* Convert Unsigned Short to Short */ -static long cvt_us_s( - epicsUInt16 *from, - epicsInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_us_s(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + epicsInt16 *to = (epicsInt16 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Short to Unsigned Short */ -static long cvt_us_us( - epicsUInt16 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_us_us(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Short to Long */ -static long cvt_us_l( - epicsUInt16 *from, - epicsInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_us_l(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + epicsInt32 *to = (epicsInt32 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Short to Unsigned Long */ -static long cvt_us_ul( - epicsUInt16 *from, - epicsUInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_us_ul(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + epicsUInt32 *to = (epicsUInt32 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Short to Int64 */ -static long cvt_us_q( - epicsUInt16 *from, - epicsInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_us_q(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + epicsInt64 *to = (epicsInt64 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Short to UInt64 */ -static long cvt_us_uq( - epicsUInt16 *from, - epicsUInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_us_uq(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + epicsUInt64 *to = (epicsUInt64 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Short to Float */ -static long cvt_us_f( - epicsUInt16 *from, - epicsFloat32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_us_f(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + epicsFloat32 *to = (epicsFloat32 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Short to Double */ -static long cvt_us_d( - epicsUInt16 *from, - epicsFloat64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_us_d(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + epicsFloat64 *to = (epicsFloat64 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Short to Enumerated */ -static long cvt_us_e( - epicsUInt16 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_us_e(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=*from; + return 0; +} /* Convert Long to String */ -static long cvt_l_st( - epicsInt32 *from, - char *to, - const dbAddr *paddr) -{ cvtLongToString(*from, to); return(0); } +static long cvt_l_st(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + char *to = (char *) t; + cvtLongToString(*from, to); + return 0; +} /* Convert Long to Char */ -static long cvt_l_c( - epicsInt32 *from, - epicsInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_l_c(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + epicsInt8 *to = (epicsInt8 *) t; + *to=*from; + return 0; +} /* Convert Long to Unsigned Char */ -static long cvt_l_uc( - epicsInt32 *from, - epicsUInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_l_uc(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + epicsUInt8 *to = (epicsUInt8 *) t; + *to=*from; + return 0; +} /* Convert Long to Short */ -static long cvt_l_s( - epicsInt32 *from, - epicsInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_l_s(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + epicsInt16 *to = (epicsInt16 *) t; + *to=*from; + return 0; +} /* Convert Long to Unsigned Short */ -static long cvt_l_us( - epicsInt32 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_l_us(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=*from; + return 0; +} /* Convert Long to Long */ -static long cvt_l_l( - epicsInt32 *from, - epicsInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_l_l(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + epicsInt32 *to = (epicsInt32 *) t; + *to=*from; + return 0; +} /* Convert Long to Unsigned Long */ -static long cvt_l_ul( - epicsInt32 *from, - epicsUInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_l_ul(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + epicsUInt32 *to = (epicsUInt32 *) t; + *to=*from; + return 0; +} /* Convert Long to Int64 */ -static long cvt_l_q( - epicsInt32 *from, - epicsInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_l_q(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + epicsInt64 *to = (epicsInt64 *) t; + *to=*from; + return 0; +} /* Convert Long to UInt64 */ -static long cvt_l_uq( - epicsInt32 *from, - epicsUInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_l_uq(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + epicsUInt64 *to = (epicsUInt64 *) t; + *to=*from; + return 0; +} /* Convert Long to Float */ -static long cvt_l_f( - epicsInt32 *from, - epicsFloat32 *to, - const dbAddr *paddr) - { *to=(epicsFloat32)*from; return(0); } +static long cvt_l_f(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + epicsFloat32 *to = (epicsFloat32 *) t; + *to=(epicsFloat32)*from; + return 0; +} /* Convert Long to Double */ -static long cvt_l_d( - epicsInt32 *from, - epicsFloat64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_l_d(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + epicsFloat64 *to = (epicsFloat64 *) t; + *to=*from; + return 0; +} /* Convert Long to Enumerated */ -static long cvt_l_e( - epicsInt32 *from, - epicsEnum16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_l_e(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + epicsEnum16 *to = (epicsEnum16 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Long to String */ -static long cvt_ul_st( - epicsUInt32 *from, - char *to, - const dbAddr *paddr) -{ cvtUlongToString(*from, to); return(0); } +static long cvt_ul_st(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + char *to = (char *) t; + cvtUlongToString(*from, to); + return 0; +} /* Convert Unsigned Long to Char */ -static long cvt_ul_c( - epicsUInt32 *from, - epicsInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_ul_c(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + epicsInt8 *to = (epicsInt8 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Long to Unsigned Char */ -static long cvt_ul_uc( - epicsUInt32 *from, - epicsUInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_ul_uc(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + epicsUInt8 *to = (epicsUInt8 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Long to Short */ -static long cvt_ul_s( - epicsUInt32 *from, - epicsInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_ul_s(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + epicsInt16 *to = (epicsInt16 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Long to Unsigned Short */ -static long cvt_ul_us( - epicsUInt32 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_ul_us(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Long to Long */ -static long cvt_ul_l( - epicsUInt32 *from, - epicsInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_ul_l(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + epicsInt32 *to = (epicsInt32 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Long to Unsigned Long */ -static long cvt_ul_ul( - epicsUInt32 *from, - epicsUInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_ul_ul(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + epicsUInt32 *to = (epicsUInt32 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Long to Int64 */ -static long cvt_ul_q( - epicsUInt32 *from, - epicsInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_ul_q(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + epicsInt64 *to = (epicsInt64 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Long to UInt64 */ -static long cvt_ul_uq( - epicsUInt32 *from, - epicsUInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_ul_uq(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + epicsUInt64 *to = (epicsUInt64 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Long to Float */ -static long cvt_ul_f( - epicsUInt32 *from, - epicsFloat32 *to, - const dbAddr *paddr) - { *to=(epicsFloat32)*from; return(0); } +static long cvt_ul_f(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + epicsFloat32 *to = (epicsFloat32 *) t; + *to=(epicsFloat32)*from; + return 0; +} /* Convert Unsigned Long to Double */ -static long cvt_ul_d( - epicsUInt32 *from, - epicsFloat64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_ul_d(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + epicsFloat64 *to = (epicsFloat64 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Long to Enumerated */ -static long cvt_ul_e( - epicsUInt32 *from, - epicsEnum16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_ul_e(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + epicsEnum16 *to = (epicsEnum16 *) t; + *to=*from; + return 0; +} /* Convert Int64 to String */ -static long cvt_q_st( - epicsInt64 *from, - char *to, - const dbAddr *paddr) -{ cvtInt64ToString(*from, to); return(0); } +static long cvt_q_st(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + char *to = (char *) t; + cvtInt64ToString(*from, to); + return 0; +} /* Convert Int64 to Char */ -static long cvt_q_c( - epicsInt64 *from, - epicsInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_q_c(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + epicsInt8 *to = (epicsInt8 *) t; + *to=*from; + return 0; +} /* Convert Int64 to Unsigned Char */ -static long cvt_q_uc( - epicsInt64 *from, - epicsUInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_q_uc(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + epicsUInt8 *to = (epicsUInt8 *) t; + *to=*from; + return 0; +} /* Convert Int64 to Short */ -static long cvt_q_s( - epicsInt64 *from, - epicsInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_q_s(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + epicsInt16 *to = (epicsInt16 *) t; + *to=*from; + return 0; +} /* Convert Int64 to Unsigned Short */ -static long cvt_q_us( - epicsInt64 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_q_us(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=*from; + return 0; +} /* Convert Int64 to Long */ -static long cvt_q_l( - epicsInt64 *from, - epicsInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_q_l(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + epicsInt32 *to = (epicsInt32 *) t; + *to=*from; + return 0; +} /* Convert Int64 to Unsigned Long */ -static long cvt_q_ul( - epicsInt64 *from, - epicsUInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_q_ul(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + epicsUInt32 *to = (epicsUInt32 *) t; + *to=*from; + return 0; +} /* Convert Int64 to Int64 */ -static long cvt_q_q( - epicsInt64 *from, - epicsInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_q_q(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + epicsInt64 *to = (epicsInt64 *) t; + *to=*from; + return 0; +} /* Convert Int64 to UInt64 */ -static long cvt_q_uq( - epicsInt64 *from, - epicsUInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_q_uq(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + epicsUInt64 *to = (epicsUInt64 *) t; + *to=*from; + return 0; +} /* Convert Int64 to Float */ -static long cvt_q_f( - epicsInt64 *from, - epicsFloat32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_q_f(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + epicsFloat32 *to = (epicsFloat32 *) t; + *to=*from; + return 0; +} /* Convert Int64 to Double */ -static long cvt_q_d( - epicsInt64 *from, - epicsFloat64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_q_d(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + epicsFloat64 *to = (epicsFloat64 *) t; + *to=*from; + return 0; +} /* Convert Int64 to Enumerated */ -static long cvt_q_e( - epicsInt64 *from, - epicsEnum16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_q_e(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + epicsEnum16 *to = (epicsEnum16 *) t; + *to=*from; + return 0; +} /* Convert UInt64 to String */ -static long cvt_uq_st( - epicsUInt64 *from, - char *to, - const dbAddr *paddr) -{ cvtUInt64ToString(*from, to); return(0); } +static long cvt_uq_st(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + char *to = (char *) t; + cvtUInt64ToString(*from, to); + return 0; +} /* Convert UInt64 to Char */ -static long cvt_uq_c( - epicsUInt64 *from, - epicsInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uq_c(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + epicsInt8 *to = (epicsInt8 *) t; + *to=*from; + return 0; +} /* Convert UInt64 to Unsigned Char */ -static long cvt_uq_uc( - epicsUInt64 *from, - epicsUInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uq_uc(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + epicsUInt8 *to = (epicsUInt8 *) t; + *to=*from; + return 0; +} /* Convert UInt64 to Short */ -static long cvt_uq_s( - epicsUInt64 *from, - epicsInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uq_s(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + epicsInt16 *to = (epicsInt16 *) t; + *to=*from; + return 0; +} /* Convert UInt64 to Unsigned Short */ -static long cvt_uq_us( - epicsUInt64 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uq_us(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=*from; + return 0; +} /* Convert UInt64 to Long */ -static long cvt_uq_l( - epicsUInt64 *from, - epicsInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uq_l(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + epicsInt32 *to = (epicsInt32 *) t; + *to=*from; + return 0; +} /* Convert UInt64 to Unsigned Long */ -static long cvt_uq_ul( - epicsUInt64 *from, - epicsUInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uq_ul(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + epicsUInt32 *to = (epicsUInt32 *) t; + *to=*from; + return 0; +} /* Convert UInt64 to Int64 */ -static long cvt_uq_q( - epicsUInt64 *from, - epicsInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uq_q(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + epicsInt64 *to = (epicsInt64 *) t; + *to=*from; + return 0; +} /* Convert UInt64 to UInt64 */ -static long cvt_uq_uq( - epicsUInt64 *from, - epicsUInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uq_uq(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + epicsUInt64 *to = (epicsUInt64 *) t; + *to=*from; + return 0; +} /* Convert UInt64 to Float */ -static long cvt_uq_f( - epicsUInt64 *from, - epicsFloat32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uq_f(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + epicsFloat32 *to = (epicsFloat32 *) t; + *to=*from; + return 0; +} /* Convert UInt64 to Double */ -static long cvt_uq_d( - epicsUInt64 *from, - epicsFloat64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uq_d(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + epicsFloat64 *to = (epicsFloat64 *) t; + *to=*from; + return 0; +} /* Convert UInt64 to Enumerated */ -static long cvt_uq_e( - epicsUInt64 *from, - epicsEnum16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uq_e(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + epicsEnum16 *to = (epicsEnum16 *) t; + *to=*from; + return 0; +} /* Convert Float to String */ -static long cvt_f_st( - epicsFloat32 *from, - char *to, - const dbAddr *paddr) - { - rset *prset = 0; - long status = 0; - long precision = 6; - - if(paddr) prset = dbGetRset(paddr); - - if (prset && prset->get_precision) - status = (*prset->get_precision)(paddr, &precision); - cvtFloatToString(*from, to, (unsigned short)precision); - return(status); +static long cvt_f_st(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + char *to = (char *) t; + + rset *prset = 0; + long status = 0; + long precision = 6; + + if (paddr) prset = dbGetRset(paddr); + + if (prset && prset->get_precision) + status = (*prset->get_precision)(paddr, &precision); + cvtFloatToString(*from, to, (unsigned short)precision); + return status; } /* Convert Float to Char */ -static long cvt_f_c( - epicsFloat32 *from, - epicsInt8 *to, - const dbAddr *paddr) - { *to=(epicsInt8)*from; return(0); } +static long cvt_f_c(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + epicsInt8 *to = (epicsInt8 *) t; + *to=(epicsInt8)*from; + return 0; +} /* Convert Float to Unsigned Char */ -static long cvt_f_uc( - epicsFloat32 *from, - epicsUInt8 *to, - const dbAddr *paddr) - { *to=(epicsUInt8)*from; return(0); } +static long cvt_f_uc(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + epicsUInt8 *to = (epicsUInt8 *) t; + *to=(epicsUInt8)*from; + return 0; +} /* Convert Float to Short */ -static long cvt_f_s( - epicsFloat32 *from, - epicsInt16 *to, - const dbAddr *paddr) - { *to=(epicsInt16)*from; return(0); } +static long cvt_f_s(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + epicsInt16 *to = (epicsInt16 *) t; + *to=(epicsInt16)*from; + return 0; +} /* Convert Float to Unsigned Short */ -static long cvt_f_us( - epicsFloat32 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=(epicsUInt16)*from; return(0); } +static long cvt_f_us(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=(epicsUInt16)*from; + return 0; +} /* Convert Float to Long */ -static long cvt_f_l( - epicsFloat32 *from, - epicsInt32 *to, - const dbAddr *paddr) - { *to=(epicsInt32)*from; return(0); } +static long cvt_f_l(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + epicsInt32 *to = (epicsInt32 *) t; + *to=(epicsInt32)*from; + return 0; +} /* Convert Float to Unsigned Long */ -static long cvt_f_ul( - epicsFloat32 *from, - epicsUInt32 *to, - const dbAddr *paddr) - { *to=(epicsUInt32)*from; return(0); } +static long cvt_f_ul(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + epicsUInt32 *to = (epicsUInt32 *) t; + *to=(epicsUInt32)*from; + return 0; +} /* Convert Float to Int64 */ -static long cvt_f_q( - epicsFloat32 *from, - epicsInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_f_q(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + epicsInt64 *to = (epicsInt64 *) t; + *to=*from; + return 0; +} /* Convert Float to UInt64 */ -static long cvt_f_uq( - epicsFloat32 *from, - epicsUInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_f_uq(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + epicsUInt64 *to = (epicsUInt64 *) t; + *to=*from; + return 0; +} /* Convert Float to Float */ -static long cvt_f_f( - epicsFloat32 *from, - epicsFloat32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_f_f(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + epicsFloat32 *to = (epicsFloat32 *) t; + *to=*from; + return 0; +} /* Convert Float to Double */ -static long cvt_f_d( - epicsFloat32 *from, - epicsFloat64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_f_d(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + epicsFloat64 *to = (epicsFloat64 *) t; + *to=*from; + return 0; +} /* Convert Float to Enumerated */ -static long cvt_f_e( - epicsFloat32 *from, - epicsEnum16 *to, - const dbAddr *paddr) - { *to=(epicsEnum16)*from; return(0); } +static long cvt_f_e(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + epicsEnum16 *to = (epicsEnum16 *) t; + *to=(epicsEnum16)*from; + return 0; +} /* Convert Double to String */ -static long cvt_d_st( - epicsFloat64 *from, - char *to, - const dbAddr *paddr) - { - rset *prset = 0; - long status = 0; - long precision = 6; - - if(paddr) prset = dbGetRset(paddr); - - if (prset && prset->get_precision) - status = (*prset->get_precision)(paddr, &precision); - cvtDoubleToString(*from, to, (unsigned short)precision); - return(status); +static long cvt_d_st(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + char *to = (char *) t; + rset *prset = 0; + long status = 0; + long precision = 6; + + if (paddr) prset = dbGetRset(paddr); + + if (prset && prset->get_precision) + status = (*prset->get_precision)(paddr, &precision); + cvtDoubleToString(*from, to, (unsigned short)precision); + return status; } /* Convert Double to Char */ -static long cvt_d_c( - epicsFloat64 *from, - epicsInt8 *to, - const dbAddr *paddr) - { *to=(epicsInt8)*from; return(0); } +static long cvt_d_c(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + epicsInt8 *to = (epicsInt8 *) t; + *to=(epicsInt8)*from; + return 0; +} /* Convert Double to Unsigned Char */ -static long cvt_d_uc( - epicsFloat64 *from, - epicsUInt8 *to, - const dbAddr *paddr) - { *to=(epicsUInt8)*from; return(0); } +static long cvt_d_uc(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + epicsUInt8 *to = (epicsUInt8 *) t; + *to=(epicsUInt8)*from; + return 0; +} /* Convert Double to Short */ -static long cvt_d_s( - epicsFloat64 *from, - epicsInt16 *to, - const dbAddr *paddr) - { *to=(epicsInt16)*from; return(0); } +static long cvt_d_s(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + epicsInt16 *to = (epicsInt16 *) t; + *to=(epicsInt16)*from; + return 0; +} /* Convert Double to Unsigned Short */ -static long cvt_d_us( - epicsFloat64 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=(epicsUInt16)*from; return(0); } +static long cvt_d_us(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=(epicsUInt16)*from; + return 0; +} /* Convert Double to Long */ -static long cvt_d_l( - epicsFloat64 *from, - epicsInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_d_l(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + epicsInt32 *to = (epicsInt32 *) t; + *to=*from; + return 0; +} /* Convert Double to Unsigned Long */ -static long cvt_d_ul( - epicsFloat64 *from, - epicsUInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_d_ul(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + epicsUInt32 *to = (epicsUInt32 *) t; + *to=*from; + return 0; +} /* Convert Double to Int64 */ -static long cvt_d_q( - epicsFloat64 *from, - epicsInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_d_q(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + epicsInt64 *to = (epicsInt64 *) t; + *to=*from; + return 0; +} /* Convert Double to UInt64 */ -static long cvt_d_uq( - epicsFloat64 *from, - epicsUInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_d_uq(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + epicsUInt64 *to = (epicsUInt64 *) t; + *to=*from; + return 0; +} /* Convert Double to Float */ -static long cvt_d_f( - epicsFloat64 *from, - epicsFloat32 *to, - const dbAddr *paddr) -{ *to = epicsConvertDoubleToFloat(*from); return 0;} +static long cvt_d_f(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + epicsFloat32 *to = (epicsFloat32 *) t; + *to = epicsConvertDoubleToFloat(*from); return 0;} /* Convert Double to Double */ -static long cvt_d_d( - epicsFloat64 *from, - epicsFloat64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_d_d(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + epicsFloat64 *to = (epicsFloat64 *) t; + *to=*from; + return 0; +} /* Convert Double to Enumerated */ -static long cvt_d_e( - epicsFloat64 *from, - epicsEnum16 *to, - const dbAddr *paddr) - { *to=(epicsEnum16)*from; return(0); } +static long cvt_d_e(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + epicsEnum16 *to = (epicsEnum16 *) t; + *to=(epicsEnum16)*from; + return 0; +} /* Convert Enumerated to Char */ -static long cvt_e_c( - epicsEnum16 *from, - epicsInt8 *to, - const dbAddr *paddr) - { *to=(epicsInt8)*from; return(0); } +static long cvt_e_c(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + epicsInt8 *to = (epicsInt8 *) t; + *to=(epicsInt8)*from; + return 0; +} /* Convert Enumerated to Unsigned Char */ -static long cvt_e_uc( - epicsEnum16 *from, - epicsUInt8 *to, - const dbAddr *paddr) - { *to=(epicsUInt8)*from; return(0); } +static long cvt_e_uc(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + epicsUInt8 *to = (epicsUInt8 *) t; + *to=(epicsUInt8)*from; + return 0; +} /* Convert Enumerated to Short */ -static long cvt_e_s( - epicsEnum16 *from, - epicsInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_e_s(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + epicsInt16 *to = (epicsInt16 *) t; + *to=*from; + return 0; +} /* Convert Enumerated to Unsigned Short */ -static long cvt_e_us( - epicsEnum16 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_e_us(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=*from; + return 0; +} /* Convert Enumerated to Long */ -static long cvt_e_l( - epicsEnum16 *from, - epicsInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_e_l(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + epicsInt32 *to = (epicsInt32 *) t; + *to=*from; + return 0; +} /* Convert Enumerated to Unsigned Long */ -static long cvt_e_ul( - epicsEnum16 *from, - epicsUInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_e_ul(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + epicsUInt32 *to = (epicsUInt32 *) t; + *to=*from; + return 0; +} /* Convert Enumerated to Int64 */ -static long cvt_e_q( - epicsEnum16 *from, - epicsInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_e_q(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + epicsInt64 *to = (epicsInt64 *) t; + *to=*from; + return 0; +} /* Convert Enumerated to UInt64 */ -static long cvt_e_uq( - epicsEnum16 *from, - epicsUInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_e_uq(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + epicsUInt64 *to = (epicsUInt64 *) t; + *to=*from; + return 0; +} /* Convert Enumerated to Float */ -static long cvt_e_f( - epicsEnum16 *from, - epicsFloat32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_e_f(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + epicsFloat32 *to = (epicsFloat32 *) t; + *to=*from; + return 0; +} /* Convert Enumerated to Double */ -static long cvt_e_d( - epicsEnum16 *from, - epicsFloat64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_e_d(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + epicsFloat64 *to = (epicsFloat64 *) t; + *to=*from; + return 0; +} /* Convert Enumerated to Enumerated */ -static long cvt_e_e( - epicsEnum16 *from, - epicsEnum16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_e_e(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + epicsEnum16 *to = (epicsEnum16 *) t; + *to=*from; + return 0; +} /* Convert Choices And Enumerated Types To String ... */ /* Get Enumerated to String */ -static long cvt_e_st_get( - epicsEnum16 *from, - char *to, - const dbAddr *paddr) - { - rset *prset = 0; - long status; +static long cvt_e_st_get(const void *f, void *t, const dbAddr *paddr) +{ + char *to = (char *) t; + rset *prset = 0; + long status; - if(paddr) prset = dbGetRset(paddr); + if(paddr) prset = dbGetRset(paddr); - if (prset && prset->get_enum_str) - return (*prset->get_enum_str)(paddr, to); + if (prset && prset->get_enum_str) + return (*prset->get_enum_str)(paddr, to); - status = S_db_noRSET; - recGblRecSupError(status, paddr, "dbGetField", "get_enum_str"); + status = S_db_noRSET; + recGblRecSupError(status, paddr, "dbGetField", "get_enum_str"); - return(S_db_badDbrtype); - } + return S_db_badDbrtype; +} /* Put Enumerated to String */ -static long cvt_e_st_put( - epicsEnum16 *from, - char *to, - const dbAddr *paddr) - { cvtUshortToString(*from, to); return(0); } +static long cvt_e_st_put(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + char *to = (char *) t; + + cvtUshortToString(*from, to); + return 0; +} /* Get Menu to String */ -static long cvt_menu_st( - epicsEnum16 *from, - char *to, - const dbAddr *paddr) +static long cvt_menu_st(const void *f, void *t, const dbAddr *paddr) { + const epicsEnum16 *from = (const epicsEnum16 *) f; + char *to = (char *) t; dbFldDes *pdbFldDes; dbMenu *pdbMenu; @@ -1358,15 +1599,14 @@ static long cvt_menu_st( /* Get Device to String */ -static long cvt_device_st( - epicsEnum16 *from, - char *to, - const dbAddr *paddr) - { - dbFldDes *pdbFldDes; - dbDeviceMenu *pdbDeviceMenu; - char **papChoice; - char *pchoice; +static long cvt_device_st(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + char *to = (char *) t; + dbFldDes *pdbFldDes; + dbDeviceMenu *pdbDeviceMenu; + char **papChoice; + char *pchoice; if (!paddr || !(pdbFldDes = paddr->pfldDes)) { @@ -1382,11 +1622,11 @@ static long cvt_device_st( || !(papChoice= pdbDeviceMenu->papChoice) || !(pchoice=papChoice[*from])) { recGblDbaddrError(S_db_badChoice,paddr,"dbFastLinkConv(cvt_device_st)"); - return(S_db_badChoice); + return S_db_badChoice; } strncpy(to,pchoice,MAX_STRING_SIZE); - return(0); - } + return 0; +} /* * Get conversion routine lookup table @@ -1399,7 +1639,7 @@ static long cvt_device_st( * NULL implies the conversion is not supported. */ -long (*dbFastGetConvertRoutine[DBF_DEVICE+1][DBR_ENUM+1])() = { +FASTCONVERTFUNC const dbFastGetConvertRoutine[DBF_DEVICE+1][DBR_ENUM+1] = { /* Convert DBF_STRING to ... */ { cvt_st_st, cvt_st_c, cvt_st_uc, cvt_st_s, cvt_st_us, cvt_st_l, cvt_st_ul, cvt_st_q, cvt_st_uq, cvt_st_f, cvt_st_d, cvt_st_e }, @@ -1455,7 +1695,7 @@ long (*dbFastGetConvertRoutine[DBF_DEVICE+1][DBR_ENUM+1])() = { * NULL implies the conversion is not supported. */ -long (*dbFastPutConvertRoutine[DBR_ENUM+1][DBF_DEVICE+1])() = { +FASTCONVERTFUNC const dbFastPutConvertRoutine[DBR_ENUM+1][DBF_DEVICE+1] = { /* Convert DBR_STRING to ... */ { cvt_st_st, cvt_st_c, cvt_st_uc, cvt_st_s, cvt_st_us, cvt_st_l, cvt_st_ul, cvt_st_q, cvt_st_uq, cvt_st_f, cvt_st_d, cvt_st_e, cvt_st_menu, cvt_st_device}, diff --git a/modules/database/src/ioc/db/dbIocRegister.c b/modules/database/src/ioc/db/dbIocRegister.c index 83a2c9114..9b5085ea0 100644 --- a/modules/database/src/ioc/db/dbIocRegister.c +++ b/modules/database/src/ioc/db/dbIocRegister.c @@ -75,21 +75,21 @@ static const iocshFuncDef dbbFuncDef = {"dbb",1,dbbArgs, "Set Breakpoint on a record\n" "This command spawns one breakpoint continuation task per lockset," " in which further record execution is run\n"}; -static void dbbCallFunc(const iocshArgBuf *args) { dbb(args[0].sval);} +static void dbbCallFunc(const iocshArgBuf *args) { iocshSetError(dbb(args[0].sval));} /* dbd */ static const iocshArg dbdArg0 = { "record name",iocshArgStringRecord}; static const iocshArg * const dbdArgs[1] = {&dbdArg0}; static const iocshFuncDef dbdFuncDef = {"dbd",1,dbdArgs, "Remove breakpoint from a record.\n"}; -static void dbdCallFunc(const iocshArgBuf *args) { dbd(args[0].sval);} +static void dbdCallFunc(const iocshArgBuf *args) { iocshSetError(dbd(args[0].sval));} /* dbc */ static const iocshArg dbcArg0 = { "record name",iocshArgStringRecord}; static const iocshArg * const dbcArgs[1] = {&dbcArg0}; static const iocshFuncDef dbcFuncDef = {"dbc",1,dbcArgs, "Continue processing in a lockset until next breakpoint is found.\n"}; -static void dbcCallFunc(const iocshArgBuf *args) { dbc(args[0].sval);} +static void dbcCallFunc(const iocshArgBuf *args) { iocshSetError(dbc(args[0].sval));} /* dbs */ static const iocshArg dbsArg0 = { "record name",iocshArgStringRecord}; @@ -97,12 +97,12 @@ static const iocshArg * const dbsArgs[1] = {&dbsArg0}; static const iocshFuncDef dbsFuncDef = {"dbs",1,dbsArgs, "Step through record processing within a lockset.\n" "If called without an argument, automatically steps with the last breakpoint.\n"}; -static void dbsCallFunc(const iocshArgBuf *args) { dbs(args[0].sval);} +static void dbsCallFunc(const iocshArgBuf *args) { iocshSetError(dbs(args[0].sval));} /* dbstat */ static const iocshFuncDef dbstatFuncDef = {"dbstat",0,0, "Print list of suspended records, and breakpoints set in locksets.\n"}; -static void dbstatCallFunc(const iocshArgBuf *args) { dbstat();} +static void dbstatCallFunc(const iocshArgBuf *args) { iocshSetError(dbstat());} /* dbp */ static const iocshArg dbpArg0 = { "record name",iocshArgStringRecord}; @@ -119,7 +119,9 @@ static const iocshFuncDef dbpFuncDef = { " 3 - Fields of minor interest to a System developer.\n" " 4 - Internal record fields.\n"}; static void dbpCallFunc(const iocshArgBuf *args) -{ dbp(args[0].sval,args[1].ival);} +{ + iocshSetError(dbp(args[0].sval,args[1].ival)); +} /* dbap */ static const iocshArg dbapArg0 = { "record name",iocshArgStringRecord}; @@ -127,7 +129,7 @@ static const iocshArg * const dbapArgs[1] = {&dbapArg0}; static const iocshFuncDef dbapFuncDef = {"dbap",1,dbapArgs, "Auto Print.\n" "Toggle automatic printing after processing a record that has a breakpoint.\n"}; -static void dbapCallFunc(const iocshArgBuf *args) { dbap(args[0].sval);} +static void dbapCallFunc(const iocshArgBuf *args) { iocshSetError(dbap(args[0].sval));} /* dbsr */ static const iocshArg dbsrArg0 = { "interest level",iocshArgInt}; @@ -150,7 +152,7 @@ static const iocshFuncDef dbcarFuncDef = {"dbcar",2,dbcarArgs, " 2 - Shows info. for all links.\n"}; static void dbcarCallFunc(const iocshArgBuf *args) { - dbcar(args[0].sval,args[1].ival); + iocshSetError(dbcar(args[0].sval,args[1].ival)); } /* dbjlr */ @@ -162,7 +164,7 @@ static const iocshFuncDef dbjlrFuncDef = {"dbjlr",2,dbjlrArgs, "List all JSON links in a record. If no record is specified, print for all\n"}; static void dbjlrCallFunc(const iocshArgBuf *args) { - dbjlr(args[0].sval,args[1].ival); + iocshSetError(dbjlr(args[0].sval,args[1].ival)); } /* dbel */ @@ -176,7 +178,7 @@ static const iocshFuncDef dbelFuncDef = {"dbel",2,dbelArgs, "Example: dbel aitest 2\n"}; static void dbelCallFunc(const iocshArgBuf *args) { - dbel(args[0].sval, args[1].ival); + iocshSetError(dbel(args[0].sval, args[1].ival)); } /* dba */ @@ -187,7 +189,7 @@ static const iocshFuncDef dbaFuncDef = {"dba",1,dbaArgs, "Print information in the dbAddr structure for a specific field.\n" "If no field is specified, VAL is assumed.\n\n" "Example: dba(\"aitest.HIGH\")\n"}; -static void dbaCallFunc(const iocshArgBuf *args) { dba(args[0].sval);} +static void dbaCallFunc(const iocshArgBuf *args) { iocshSetError(dba(args[0].sval));} /* dbl */ static const iocshArg dblArg0 = { "record type",iocshArgString}; @@ -204,7 +206,7 @@ static const iocshFuncDef dblFuncDef = {"dbl",2,dblArgs, " dbl(\"ai\",\"HIGH LOW VAL PREC\")\n"}; static void dblCallFunc(const iocshArgBuf *args) { - dbl(args[0].sval,args[1].sval); + iocshSetError(dbl(args[0].sval,args[1].sval)); } /* dbnr */ @@ -213,7 +215,7 @@ static const iocshArg * const dbnrArgs[1] = {&dbnrArg0}; static const iocshFuncDef dbnrFuncDef = {"dbnr",1,dbnrArgs, "List number of records and aliases by type.\n" "If verbose, list all record types regardless of being instanced\n"}; -static void dbnrCallFunc(const iocshArgBuf *args) { dbnr(args[0].ival);} +static void dbnrCallFunc(const iocshArgBuf *args) { iocshSetError(dbnr(args[0].ival));} /* dbli */ static const iocshArg dbliArg0 = { "pattern",iocshArgString}; @@ -221,7 +223,7 @@ static const iocshArg * const dbliArgs[1] = {&dbliArg0}; static const iocshFuncDef dbliFuncDef = {"dbli",1,dbliArgs, "List info() tags with names matching pattern.\n\n" "Example: dbli(\"autosave*\")\n"}; -static void dbliCallFunc(const iocshArgBuf *args) { dbli(args[0].sval);} +static void dbliCallFunc(const iocshArgBuf *args) { iocshSetError(dbli(args[0].sval));} /* dbla */ static const iocshArg dblaArg0 = { "pattern",iocshArgStringRecord}; @@ -229,7 +231,7 @@ static const iocshArg * const dblaArgs[1] = {&dblaArg0}; static const iocshFuncDef dblaFuncDef = {"dbla",1,dblaArgs, "List record alias()s by alias name pattern.\n\n" "Example: dbla(\"alia*\")\n"}; -static void dblaCallFunc(const iocshArgBuf *args) { dbla(args[0].sval);} +static void dblaCallFunc(const iocshArgBuf *args) { iocshSetError(dbla(args[0].sval));} /* dbgrep */ static const iocshArg dbgrepArg0 = { "pattern",iocshArgStringRecord}; @@ -240,7 +242,7 @@ static const iocshFuncDef dbgrepFuncDef = {"dbgrep",1,dbgrepArgs, " - \"?\", which matches 0 or one characters.\n" " - \"*\", which matches 0 or more characters.\n\n" "Example: dbgrep(\"*gpibAi*\")\n"}; -static void dbgrepCallFunc(const iocshArgBuf *args) { dbgrep(args[0].sval);} +static void dbgrepCallFunc(const iocshArgBuf *args) { iocshSetError(dbgrep(args[0].sval));} /* dbgf */ static const iocshArg dbgfArg0 = { "record name",iocshArgStringRecord}; @@ -250,7 +252,7 @@ static const iocshFuncDef dbgfFuncDef = {"dbgf",1,dbgfArgs, "Print current value of record field.\n" "If no field name is specified, VAL is assumed.\n\n" "Example: dbgf(\"aitest.VAL\")\n"}; -static void dbgfCallFunc(const iocshArgBuf *args) { dbgf(args[0].sval);} +static void dbgfCallFunc(const iocshArgBuf *args) { iocshSetError(dbgf(args[0].sval));} /* dbpf */ static const iocshArg dbpfArg0 = { "record name",iocshArgStringRecord}; @@ -261,7 +263,7 @@ static const iocshFuncDef dbpfFuncDef = {"dbpf",2,dbpfArgs, "Change value of record field and read it back with dbgf.\n" "If no field is specified, VAL is assumed\n"}; static void dbpfCallFunc(const iocshArgBuf *args) -{ dbpf(args[0].sval,args[1].sval);} +{ iocshSetError(dbpf(args[0].sval,args[1].sval));} /* dbpr */ static const iocshArg dbprArg0 = { "record name",iocshArgStringRecord}; @@ -279,14 +281,14 @@ static const iocshFuncDef dbprFuncDef = { "Example: dbpr aitest 3\n" }; static void dbprCallFunc(const iocshArgBuf *args) -{ dbpr(args[0].sval,args[1].ival);} +{ iocshSetError(dbpr(args[0].sval,args[1].ival));} /* dbtr */ static const iocshArg dbtrArg0 = { "record name",iocshArgStringRecord}; static const iocshArg * const dbtrArgs[1] = {&dbtrArg0}; static const iocshFuncDef dbtrFuncDef = {"dbtr",1,dbtrArgs, "Process record and then some fields.\n"}; -static void dbtrCallFunc(const iocshArgBuf *args) { dbtr(args[0].sval);} +static void dbtrCallFunc(const iocshArgBuf *args) { iocshSetError(dbtr(args[0].sval));} /* dbtgf */ static const iocshArg dbtgfArg0 = { "record name",iocshArgStringRecord}; @@ -296,7 +298,7 @@ static const iocshFuncDef dbtgfFuncDef = {"dbtgf",1,dbtgfArgs, "Get and print the specified field with all possible DBR_* types\n" "Example: dbtgf aitest\n" "Example: dbtgf aitest.VAL\n"}; -static void dbtgfCallFunc(const iocshArgBuf *args) { dbtgf(args[0].sval);} +static void dbtgfCallFunc(const iocshArgBuf *args) { iocshSetError(dbtgf(args[0].sval));} /* dbtpf */ static const iocshArg dbtpfArg0 = { "record name",iocshArgStringRecord}; @@ -308,7 +310,7 @@ static const iocshFuncDef dbtpfFuncDef = {"dbtpf",2,dbtpfArgs, "for all possible DBR_* types\n\n" "Example: dbtpf aitest 5.0\n"}; static void dbtpfCallFunc(const iocshArgBuf *args) -{ dbtpf(args[0].sval,args[1].sval);} +{ iocshSetError(dbtpf(args[0].sval,args[1].sval));} /* dbior */ static const iocshArg dbiorArg0 = { "driver name",iocshArgString}; @@ -317,7 +319,7 @@ static const iocshArg * const dbiorArgs[] = {&dbiorArg0,&dbiorArg1}; static const iocshFuncDef dbiorFuncDef = {"dbior",2,dbiorArgs, "Driver Report.\n"}; static void dbiorCallFunc(const iocshArgBuf *args) -{ dbior(args[0].sval,args[1].ival);} +{ iocshSetError(dbior(args[0].sval,args[1].ival));} /* dbhcr */ static const iocshFuncDef dbhcrFuncDef = {"dbhcr",0,0, @@ -327,7 +329,7 @@ static const iocshFuncDef dbhcrFuncDef = {"dbhcr",0,0, "Use the UNIX sort command:\n" "dbhcr > report\n" "sort report > report.sorted\n"}; -static void dbhcrCallFunc(const iocshArgBuf *args) { dbhcr();} +static void dbhcrCallFunc(const iocshArgBuf *args) { iocshSetError(dbhcr());} /* gft */ static const iocshArg gftArg0 = { "record name",iocshArgStringRecord}; @@ -336,7 +338,7 @@ static const iocshFuncDef gftFuncDef = {"gft",1,gftArgs, "Report dbChannel info and value.\n" "Example: gft aitest\n" "Example: gft aitest.VAL\n"}; -static void gftCallFunc(const iocshArgBuf *args) { gft(args[0].sval);} +static void gftCallFunc(const iocshArgBuf *args) { iocshSetError(gft(args[0].sval));} /* pft */ static const iocshArg pftArg0 = { "record name",iocshArgStringRecord}; @@ -346,7 +348,7 @@ static const iocshFuncDef pftFuncDef = {"pft",2,pftArgs, "dbChannel put value.\n" "Example: pft aitest 5.0\n"}; static void pftCallFunc(const iocshArgBuf *args) -{ pft(args[0].sval,args[1].sval);} +{ iocshSetError(pft(args[0].sval,args[1].sval));} /* dbtpn */ static const iocshArg dbtpnArg0 = { "record name",iocshArgStringRecord}; @@ -359,12 +361,12 @@ static const iocshFuncDef dbtpnFuncDef = {"dbtpn",2,dbtpnArgs, "Example: dbtpn aitest\n" "Example: dbtpn aitest 5.0\n"}; static void dbtpnCallFunc(const iocshArgBuf *args) -{ dbtpn(args[0].sval,args[1].sval);} +{ iocshSetError(dbtpn(args[0].sval,args[1].sval));} /* dbNotifyDump */ static const iocshFuncDef dbNotifyDumpFuncDef = {"dbNotifyDump",0,0, "Report status of any active async processing with completion notification.\n"}; -static void dbNotifyDumpCallFunc(const iocshArgBuf *args) { dbNotifyDump();} +static void dbNotifyDumpCallFunc(const iocshArgBuf *args) { iocshSetError(dbNotifyDump());} /* dbPutAttribute */ static const iocshArg dbPutAttrArg0 = { "record type",iocshArgString}; @@ -375,7 +377,7 @@ static const iocshArg * const dbPutAttrArgs[] = static const iocshFuncDef dbPutAttrFuncDef = {"dbPutAttribute",3,dbPutAttrArgs, "Set/Create record attribute.\n"}; static void dbPutAttrCallFunc(const iocshArgBuf *args) -{ dbPutAttribute(args[0].sval,args[1].sval,args[2].sval);} +{ iocshSetError(dbPutAttribute(args[0].sval,args[1].sval,args[2].sval));} /* tpn */ static const iocshArg tpnArg0 = { "record name",iocshArgStringRecord}; @@ -385,7 +387,7 @@ static const iocshFuncDef tpnFuncDef = {"tpn",2,tpnArgs, "Test Process Notify.\n\n" "Example: tpn aitest 5.0\n"}; static void tpnCallFunc(const iocshArgBuf *args) -{ tpn(args[0].sval,args[1].sval);} +{ iocshSetError(tpn(args[0].sval,args[1].sval));} /* dblsr */ static const iocshArg dblsrArg0 = { "record name",iocshArgStringRecord}; @@ -399,7 +401,7 @@ static const iocshFuncDef dblsrFuncDef = {"dblsr",2,dblsrArgs, " 2 - Show each record and all database links in the lock set.\n\n" "Example: dblsr aitest 2\n"}; static void dblsrCallFunc(const iocshArgBuf *args) -{ dblsr(args[0].sval,args[1].ival);} +{ iocshSetError(dblsr(args[0].sval,args[1].ival));} /* dbLockShowLocked */ static const iocshArg dbLockShowLockedArg0 = { "interest level",iocshArgInt}; @@ -412,7 +414,7 @@ static const iocshFuncDef dbLockShowLockedFuncDef = { "Example: dbLockShowLocked 0\n" }; static void dbLockShowLockedCallFunc(const iocshArgBuf *args) -{ dbLockShowLocked(args[0].ival);} +{ iocshSetError(dbLockShowLocked(args[0].ival));} /* scanOnceSetQueueSize */ static const iocshArg scanOnceSetQueueSizeArg0 = { "size",iocshArgInt}; @@ -423,7 +425,7 @@ static const iocshFuncDef scanOnceSetQueueSizeFuncDef = {"scanOnceSetQueueSize", "Must be called before iocInit().\n"}; static void scanOnceSetQueueSizeCallFunc(const iocshArgBuf *args) { - scanOnceSetQueueSize(args[0].ival); + iocshSetError(scanOnceSetQueueSize(args[0].ival)); } /* scanOnceQueueShow */ @@ -444,7 +446,7 @@ static const iocshFuncDef scanpplFuncDef = {"scanppl",1,scanpplArgs, "Print info for records with periodic scan.\n" "If rate == 0.0, all periods are shown.\n"}; static void scanpplCallFunc(const iocshArgBuf *args) -{ scanppl(args[0].dval);} +{ iocshSetError(scanppl(args[0].dval));} /* scanpel */ static const iocshArg scanpelArg0 = { "event name",iocshArgString}; @@ -452,7 +454,7 @@ static const iocshArg * const scanpelArgs[1] = {&scanpelArg0}; static const iocshFuncDef scanpelFuncDef = {"scanpel",1,scanpelArgs, "Print info for records with SCAN = \"Event\".\n"}; static void scanpelCallFunc(const iocshArgBuf *args) -{ scanpel(args[0].sval);} +{ iocshSetError(scanpel(args[0].sval));} /* postEvent */ static const iocshArg postEventArg0 = { "event name",iocshArgString}; @@ -468,7 +470,7 @@ static void postEventCallFunc(const iocshArgBuf *args) /* scanpiol */ static const iocshFuncDef scanpiolFuncDef = {"scanpiol",0,0, "Print info for records with SCAN = \"I/O Intr\".\n"}; -static void scanpiolCallFunc(const iocshArgBuf *args) { scanpiol();} +static void scanpiolCallFunc(const iocshArgBuf *args) { iocshSetError(scanpiol());} /* callbackSetQueueSize */ static const iocshArg callbackSetQueueSizeArg0 = { "bufsize",iocshArgInt}; @@ -479,7 +481,7 @@ static const iocshFuncDef callbackSetQueueSizeFuncDef = {"callbackSetQueueSize", "Must be called before iocInit().\n"}; static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args) { - callbackSetQueueSize(args[0].ival); + iocshSetError(callbackSetQueueSize(args[0].ival)); } /* callbackQueueShow */ @@ -504,7 +506,7 @@ static const iocshFuncDef callbackParallelThreadsFuncDef = {"callbackParallelThr "or one of LOW, MEDIUM, or HIGH.\n"}; static void callbackParallelThreadsCallFunc(const iocshArgBuf *args) { - callbackParallelThreads(args[0].ival, args[1].sval); + iocshSetError(callbackParallelThreads(args[0].ival, args[1].sval)); } /* dbStateCreate */ @@ -514,7 +516,8 @@ static const iocshFuncDef dbStateCreateFuncDef = {"dbStateCreate", 1, dbStateCre "Allocate new state name for \"state\" filter.\n"}; static void dbStateCreateCallFunc (const iocshArgBuf *args) { - dbStateCreate(args[0].sval); + if (!dbStateCreate(args[0].sval)) + iocshSetError(-1); } /* dbStateSet */ @@ -527,6 +530,8 @@ static void dbStateSetCallFunc (const iocshArgBuf *args) if (sid) dbStateSet(sid); + else + iocshSetError(-1); } /* dbStateClear */ @@ -539,6 +544,8 @@ static void dbStateClearCallFunc (const iocshArgBuf *args) if (sid) dbStateClear(sid); + else + iocshSetError(-1); } /* dbStateShow */ @@ -552,6 +559,8 @@ static void dbStateShowCallFunc (const iocshArgBuf *args) if (sid) dbStateShow(sid, args[1].ival); + else + iocshSetError(-1); } /* dbStateShowAll */ diff --git a/modules/database/src/ioc/db/dbLink.c b/modules/database/src/ioc/db/dbLink.c index f5d286447..a2819d553 100644 --- a/modules/database/src/ioc/db/dbLink.c +++ b/modules/database/src/ioc/db/dbLink.c @@ -133,7 +133,7 @@ void dbInitLink(struct link *plink, short dbfType) plink->value.pv_link.pvlMask |= pvlOptFWD; } else { - errlogPrintf("Forward-link uses Channel Access " + errlogPrintf(ERL_WARNING ": Forward-link uses Channel Access " "without pointing to PROC field\n" " %s.%s => %s\n", precord->name, dbLinkFieldName(plink), diff --git a/modules/database/src/ioc/db/dbLink.h b/modules/database/src/ioc/db/dbLink.h index 4e7862479..e53133541 100644 --- a/modules/database/src/ioc/db/dbLink.h +++ b/modules/database/src/ioc/db/dbLink.h @@ -307,7 +307,7 @@ typedef struct lset { * * @param plink the link * @param dbrType data type code - * @param pbuffer where to put the value + * @param pbuffer where the data is * @param nRequest number of elements to send * @returns status value */ @@ -324,7 +324,7 @@ typedef struct lset { * * @param plink the link * @param dbrType data type code - * @param pbuffer where to put the value + * @param pbuffer where the data is * @param nRequest number of elements to send * @returns status value */ @@ -391,6 +391,10 @@ typedef struct lset { #define dbGetSevr(link, sevr) \ dbGetAlarm(link, NULL, sevr) +/** @brief Lookup link field name from pointer. + * Returns only field name. aka. value of ``dbFldDes::name`` + * @since 3.16.2 + */ DBCORE_API const char * dbLinkFieldName(const struct link *plink); DBCORE_API void dbInitLink(struct link *plink, short dbfType); diff --git a/modules/database/src/ioc/db/dbLock.c b/modules/database/src/ioc/db/dbLock.c index 9a6b9efea..b6afbd629 100644 --- a/modules/database/src/ioc/db/dbLock.c +++ b/modules/database/src/ioc/db/dbLock.c @@ -918,7 +918,7 @@ long dblsr(char *recordname,int level) pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->link_ind[link]]; plink = (DBLINK *)((char *)precord + pdbFldDes->offset); if(plink->type != DB_LINK) continue; - pdbAddr = (DBADDR *)(plink->value.pv_link.pvt); + pdbAddr = &((dbChannel *)(plink->value.pv_link.pvt))->addr; printf("\t%s",pdbFldDes->name); if(pdbFldDes->field_type==DBF_INLINK) { printf("\t INLINK"); diff --git a/modules/database/src/ioc/db/dbPutNotifyBlocker.cpp b/modules/database/src/ioc/db/dbPutNotifyBlocker.cpp index fe42d094c..62fc44612 100644 --- a/modules/database/src/ioc/db/dbPutNotifyBlocker.cpp +++ b/modules/database/src/ioc/db/dbPutNotifyBlocker.cpp @@ -15,6 +15,8 @@ * 505 665 1831 */ +#define USE_TYPED_DBEVENT + #include #include diff --git a/modules/database/src/ioc/db/dbScan.c b/modules/database/src/ioc/db/dbScan.c index 6e8799784..97619ab83 100644 --- a/modules/database/src/ioc/db/dbScan.c +++ b/modules/database/src/ioc/db/dbScan.c @@ -155,7 +155,7 @@ void scanStop(void) { int i; - if (scanCtl == ctlExit) return; + if (scanCtl == ctlInit || scanCtl == ctlExit) return; scanCtl = ctlExit; interruptAccept = FALSE; @@ -264,7 +264,7 @@ void scanAdd(struct dbCommon *precord) } else if (scan == menuScanI_O_Intr) { ioscan_head *piosh = NULL; int prio; - DEVSUPFUN get_ioint_info; + long (*get_ioint_info)(int, struct dbCommon *, IOSCANPVT*); if (precord->dset == NULL){ recGblRecordError(-1, (void *)precord, @@ -332,7 +332,7 @@ void scanDelete(struct dbCommon *precord) } else if (scan == menuScanI_O_Intr) { ioscan_head *piosh = NULL; int prio; - DEVSUPFUN get_ioint_info; + long (*get_ioint_info)(int, struct dbCommon *, IOSCANPVT*); if (precord->dset==NULL) { recGblRecordError(-1, (void *)precord, diff --git a/modules/database/src/ioc/db/dbScan.h b/modules/database/src/ioc/db/dbScan.h index 1839ae321..7454dea75 100644 --- a/modules/database/src/ioc/db/dbScan.h +++ b/modules/database/src/ioc/db/dbScan.h @@ -62,8 +62,28 @@ DBCORE_API void post_event(int event); DBCORE_API void scanAdd(struct dbCommon *); DBCORE_API void scanDelete(struct dbCommon *); DBCORE_API double scanPeriod(int scan); -DBCORE_API int scanOnce(struct dbCommon *); -DBCORE_API int scanOnceCallback(struct dbCommon *, once_complete cb, void *usr); +/** Shorthand for scanOnceCallback(prec, NULL, NULL) + */ +DBCORE_API int scanOnce(struct dbCommon *prec); +/** @brief scanOnce Request immediate record processing from another thread. + * + * Queue a request for record processing from the dedicated "Once" thread. + * Request may fail if Once queue overflows. See scanOnceSetQueueSize() + * + * @param prec Record to process + * @param cb Function called after target record dbProcess() + * Does not wait for async record completion. + * @param usr Argumentfor cb + * @return Zero on success. Non-zero if the request could not be queued. + */ +DBCORE_API int scanOnceCallback(struct dbCommon *prec, once_complete cb, void *usr); +/** @brief Set Once queue size + * + * Must be called prior to iocInit() + * + * @param size New size. May be smaller + * @return Zero on success + */ DBCORE_API int scanOnceSetQueueSize(int size); DBCORE_API int scanOnceQueueStatus(const int reset, scanOnceQueueStats *result); DBCORE_API void scanOnceQueueShow(const int reset); @@ -77,9 +97,39 @@ DBCORE_API int scanpel(const char *event_name); /*print io_event list*/ DBCORE_API int scanpiol(void); +/** @brief Initialize "I/O Intr" source + * @param ppios Pointer to scan list to be initialized + * + * Afterwards this IOSCANPVT may be assigned during a get_ioint_info() callback. + * See typed_dset::get_ioint_info() + * + * @note There is currently no way to free this allocation. + */ DBCORE_API void scanIoInit(IOSCANPVT *ppios); +/** @brief Request processing of all associated records from callback threads + * @param pios The scan list + * @pre pios must be initialized by scanIoInit() + * @return + */ DBCORE_API unsigned int scanIoRequest(IOSCANPVT pios); +/** @brief Process all records on the scan list for the specificed priority. + * + * Also executes the callback set by scanIoSetComplete() + * + * @param pios The scan list + * @param prio one of priorityLow through priorityHigh (defined in callback.h). + * A value between 0 and NUM_CALLBACK_PRIORITIES-1 . + * @return Zero if the scan list was empty or 1< #include diff --git a/modules/database/src/ioc/db/dbTest.c b/modules/database/src/ioc/db/dbTest.c index 3c52f93e1..583eb9af3 100644 --- a/modules/database/src/ioc/db/dbTest.c +++ b/modules/database/src/ioc/db/dbTest.c @@ -680,7 +680,7 @@ long dbtpf(const char *pname, const char *pvalue) long dbior(const char *pdrvName,int interest_level) { drvSup *pdrvSup; - struct drvet *pdrvet; + drvet *pdrvet; dbRecordType *pdbRecordType; if (!pdbbase) { diff --git a/modules/database/src/ioc/db/dbUnitTest.c b/modules/database/src/ioc/db/dbUnitTest.c index bf4ec205b..5ac710f48 100644 --- a/modules/database/src/ioc/db/dbUnitTest.c +++ b/modules/database/src/ioc/db/dbUnitTest.c @@ -14,6 +14,7 @@ #include #define EPICS_PRIVATE_API +#define USE_TYPED_DBEVENT #include "dbmf.h" #include "epicsUnitTest.h" diff --git a/modules/database/src/ioc/db/menuYesNo.dbd.pod b/modules/database/src/ioc/db/menuYesNo.dbd.pod index d4237e7b8..0167ee699 100644 --- a/modules/database/src/ioc/db/menuYesNo.dbd.pod +++ b/modules/database/src/ioc/db/menuYesNo.dbd.pod @@ -17,7 +17,8 @@ C or C would not be accepted as choices for the field. Also, the choices C, C, and C are not valid choices since they don't match the case of C or C. The integer values C<0> and C<1> may often be used instead however, they are -used as an index into the choices so C<0> becomes C and C<1> becomes . +used as an index into the choices so C<0> becomes C and C<1> becomes +C. =menu menuYesNo diff --git a/modules/database/src/ioc/db/recGbl.c b/modules/database/src/ioc/db/recGbl.c index c9bb62fdb..002a7aebc 100644 --- a/modules/database/src/ioc/db/recGbl.c +++ b/modules/database/src/ioc/db/recGbl.c @@ -62,7 +62,7 @@ void recGblRecordError(long status, void *pdbc, dbCommon *precord = pdbc; char errMsg[256] = ""; - if (status) + if ( status>0 ) errSymLookup(status, errMsg, sizeof(errMsg)); errlogPrintf("recGblRecordError: %s %s PV: %s\n", @@ -190,6 +190,7 @@ unsigned short recGblResetAlarms(void *precord) if(strcmp(pdbc->namsg, pdbc->amsg)!=0) { strcpy(pdbc->amsg, pdbc->namsg); + pdbc->namsg[0] = '\0'; stat_mask = DBE_ALARM; } @@ -259,8 +260,8 @@ int recGblSetSevr(void *precord, epicsEnum16 new_stat, epicsEnum16 new_sevr) return recGblSetSevrMsg(precord, new_stat, new_sevr, NULL); } -void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat, - epicsEnum16 sevr) +void recGblInheritSevrMsg(int msMode, void *precord, epicsEnum16 stat, + epicsEnum16 sevr, const char *msg) { switch (msMode) { case pvlOptNMS: @@ -273,11 +274,17 @@ void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat, recGblSetSevr(precord, LINK_ALARM, sevr); break; case pvlOptMSS: - recGblSetSevr(precord, stat, sevr); + /* Only MSS inherits msg */ + recGblSetSevrMsg(precord, stat, sevr, "%s", msg); break; } } +void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat, + epicsEnum16 sevr) +{ + recGblInheritSevrMsg(msMode, precord, stat, sevr, NULL); +} void recGblFwdLink(void *precord) { diff --git a/modules/database/src/ioc/db/recGbl.h b/modules/database/src/ioc/db/recGbl.h index cf3db4bc5..4a21e7f34 100644 --- a/modules/database/src/ioc/db/recGbl.h +++ b/modules/database/src/ioc/db/recGbl.h @@ -73,6 +73,8 @@ DBCORE_API int recGblSetSevr(void *precord, epicsEnum16 new_stat, epicsEnum16 new_sevr); DBCORE_API void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat, epicsEnum16 sevr); +DBCORE_API void recGblInheritSevrMsg(int msMode, void *precord, epicsEnum16 stat, + epicsEnum16 sevr, const char *msg); DBCORE_API int recGblSetSevrMsg(void *precord, epicsEnum16 new_stat, epicsEnum16 new_sevr, EPICS_PRINTF_FMT(const char *msg), ...) EPICS_PRINTF_STYLE(4,5); diff --git a/modules/database/src/ioc/dbStatic/Makefile b/modules/database/src/ioc/dbStatic/Makefile index e9bd95b8d..bb178df41 100644 --- a/modules/database/src/ioc/dbStatic/Makefile +++ b/modules/database/src/ioc/dbStatic/Makefile @@ -11,6 +11,8 @@ SRC_DIRS += $(IOCDIR)/dbStatic +USR_CFLAGS += -DUSE_TYPED_DRVET + INC += dbBase.h INC += dbFldTypes.h INC += dbStaticLib.h diff --git a/modules/database/src/ioc/dbStatic/dbBase.h b/modules/database/src/ioc/dbStatic/dbBase.h index 9578d1fac..b69cceca0 100644 --- a/modules/database/src/ioc/dbStatic/dbBase.h +++ b/modules/database/src/ioc/dbStatic/dbBase.h @@ -21,6 +21,7 @@ #include "dbDefs.h" #include "recSup.h" #include "devSup.h" +#include "drvSup.h" typedef struct dbMenu { ELLNODE node; @@ -33,7 +34,7 @@ typedef struct dbMenu { typedef struct drvSup { ELLNODE node; char *name; - struct drvet *pdrvet; + drvet *pdrvet; }drvSup; typedef struct devSup { diff --git a/modules/database/src/ioc/dbStatic/dbLexRoutines.c b/modules/database/src/ioc/dbStatic/dbLexRoutines.c index cd3c6ff0d..301bb5b98 100644 --- a/modules/database/src/ioc/dbStatic/dbLexRoutines.c +++ b/modules/database/src/ioc/dbStatic/dbLexRoutines.c @@ -225,7 +225,7 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp, char **macPairs; if (ellCount(&tempList)) { - epicsPrintf("dbReadCOM: Parser stack dirty %d\n", ellCount(&tempList)); + fprintf(stderr, ERL_WARNING ": dbReadCOM: Parser stack dirty %d\n", ellCount(&tempList)); } if (getIocState() != iocVoid) { @@ -250,7 +250,7 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp, if (substitutions == NULL) substitutions = ""; if(macCreateHandle(&macHandle,NULL)) { - epicsPrintf("macCreateHandle error\n"); + fprintf(stderr, ERL_ERROR ": macCreateHandle failed\n"); status = -1; goto cleanup; } @@ -294,7 +294,7 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp, status = pvt_yy_parse(); if (ellCount(&tempList) && !yyAbort) - epicsPrintf("dbReadCOM: Parser stack dirty w/o error. %d\n", ellCount(&tempList)); + fprintf(stderr, ERL_WARNING ": dbReadCOM: Parser stack dirty w/o error. %d\n", ellCount(&tempList)); while (ellCount(&tempList)) popFirstTemp(); /* Memory leak on parser failure */ @@ -401,15 +401,15 @@ static void dbIncludePrint(void) inputFile *pinputFile = pinputFileNow; while (pinputFile) { - epicsPrintf(" in"); + fprintf(stderr, " in"); if (pinputFile->path) - epicsPrintf(" path \"%s\" ",pinputFile->path); + fprintf(stderr, " path \"%s\" ",pinputFile->path); if (pinputFile->filename) { - epicsPrintf(" file \"%s\"",pinputFile->filename); + fprintf(stderr, " file \"%s\"",pinputFile->filename); } else { - epicsPrintf(" standard input"); + fprintf(stderr, " standard input"); } - epicsPrintf(" line %d\n",pinputFile->line_num); + fprintf(stderr, " line %d\n",pinputFile->line_num); pinputFile = (inputFile *)ellPrevious(&pinputFile->node); } return; @@ -434,7 +434,7 @@ static void dbIncludeNew(char *filename) pinputFile->filename = macEnvExpand(filename); pinputFile->path = dbOpenFile(savedPdbbase, pinputFile->filename, &fp); if (!fp) { - epicsPrintf("Can't open include file \"%s\"\n", filename); + fprintf(stderr, "Can't open include file \"%s\"\n", filename); yyerror(NULL); free((void *)pinputFile->filename); free((void *)pinputFile); @@ -696,7 +696,7 @@ static void dbRecordtypeEmpty(void) ptempListNode = (tempListNode *)ellFirst(&tempList); pdbRecordType = ptempListNode->item; - epicsPrintf("Declaration of recordtype(%s) preceeded full definition.\n", + fprintf(stderr, "Declaration of recordtype(%s) preceeded full definition.\n", pdbRecordType->name); yyerrorAbort(NULL); } @@ -801,7 +801,7 @@ static void dbDevice(char *recordtype,char *linktype, int i,link_type; pgphentry = gphFind(savedPdbbase->pgpHash,recordtype,&savedPdbbase->recordTypeList); if(!pgphentry) { - epicsPrintf("Record type \"%s\" not found for device \"%s\"\n", + fprintf(stderr, "Record type \"%s\" not found for device \"%s\"\n", recordtype, choicestring); yyerror(NULL); return; @@ -814,7 +814,7 @@ static void dbDevice(char *recordtype,char *linktype, } } if(link_type==-1) { - epicsPrintf("Bad link type \"%s\" for device \"%s\"\n", + fprintf(stderr, "Bad link type \"%s\" for device \"%s\"\n", linktype, choicestring); yyerror(NULL); return; @@ -1077,16 +1077,16 @@ int dbRecordNameValidate(const char *name) if(i==0) { /* first character restrictions */ if(c=='-' || c=='+' || c=='[' || c=='{') { - errlogPrintf("Warning: Record/Alias name '%s' should not begin with '%c'\n", name, c); + fprintf(stderr, "Warning: Record/Alias name '%s' should not begin with '%c'\n", name, c); } } /* any character restrictions */ if(c < ' ') { - errlogPrintf("Warning: Record/Alias name '%s' should not contain non-printable 0x%02x\n", + fprintf(stderr, "Warning: Record/Alias name '%s' should not contain non-printable 0x%02x\n", name, c); } else if(c==' ' || c=='\t' || c=='"' || c=='\'' || c=='.' || c=='$') { - epicsPrintf(ERL_ERROR ": Bad character '%c' in Record/Alias name \"%s\"\n", + fprintf(stderr, ERL_ERROR ": Bad character '%c' in Record/Alias name \"%s\"\n", c, name); yyerrorAbort(NULL); return 1; @@ -1113,15 +1113,29 @@ static void dbRecordHead(char *recordType, char *name, int visible) status = dbFindRecord(pdbentry, name); if (status == 0) return; /* done */ - epicsPrintf(ERL_ERROR ": Record \"%s\" not found\n", name); + fprintf(stderr, ERL_ERROR ": Record \"%s\" not found\n", name); yyerror(NULL); duplicate = TRUE; return; } + if (recordType[0] == '#' && recordType[1] == 0) { + status = dbFindRecord(pdbentry, name); + if (status == 0) { + dbDeleteRecord(pdbentry); + } else { + fprintf(stderr, ERL_WARNING ": Unable to delete record \"%s\". Not found.\n" + " at file %s line %d\n", + name, pinputFileNow->filename, pinputFileNow->line_num); + } + popFirstTemp(); + dbFreeEntry(pdbentry); + duplicate = TRUE; + return; + } status = dbFindRecordType(pdbentry, recordType); if (status) { - epicsPrintf("Record \"%s\" is of unknown type \"%s\"\n", + fprintf(stderr, "Record \"%s\" is of unknown type \"%s\"\n", name, recordType); yyerrorAbort(NULL); return; @@ -1132,14 +1146,14 @@ static void dbRecordHead(char *recordType, char *name, int visible) status = dbCreateRecord(pdbentry,name); if (status == S_dbLib_recExists) { if (strcmp(recordType, dbGetRecordTypeName(pdbentry)) != 0) { - epicsPrintf(ERL_ERROR ": Record \"%s\" of type \"%s\" redefined with new type " + fprintf(stderr, ERL_ERROR ": Record \"%s\" of type \"%s\" redefined with new type " "\"%s\"\n", name, dbGetRecordTypeName(pdbentry), recordType); yyerror(NULL); duplicate = TRUE; return; } else if (dbRecordsOnceOnly) { - epicsPrintf(ERL_ERROR ": Record \"%s\" already defined and dbRecordsOnceOnly set.\n" + fprintf(stderr, ERL_ERROR ": Record \"%s\" already defined and dbRecordsOnceOnly set.\n" "Used record type \"*\" to append.\n", name); yyerror(NULL); @@ -1147,7 +1161,7 @@ static void dbRecordHead(char *recordType, char *name, int visible) } } else if (status) { - epicsPrintf("Can't create record \"%s\" of type \"%s\"\n", + fprintf(stderr, "Can't create record \"%s\" of type \"%s\"\n", name, recordType); yyerrorAbort(NULL); } @@ -1167,7 +1181,7 @@ static void dbRecordField(char *name,char *value) pdbentry = ptempListNode->item; status = dbFindField(pdbentry,name); if (status) { - epicsPrintf("%s Record \"%s\" does not have a field \"%s\"\n", + fprintf(stderr, "%s Record \"%s\" does not have a field \"%s\"\n", dbGetRecordTypeName(pdbentry), dbGetRecordName(pdbentry), name); if(dbGetRecordName(pdbentry)) { DBENTRY temp; @@ -1183,17 +1197,17 @@ static void dbRecordField(char *name,char *value) } dbFinishEntry(&temp); if(bestSim>0.0) { - epicsPrintf(" Did you mean \"%s\"?", bestFld->name); + fprintf(stderr, " Did you mean \"%s\"?", bestFld->name); if(bestFld->prompt) - epicsPrintf(" (%s)", bestFld->prompt); - epicsPrintf("\n"); + fprintf(stderr, " (%s)", bestFld->prompt); + fprintf(stderr, "\n"); } } yyerror(NULL); return; } if (pdbentry->indfield == 0) { - epicsPrintf("Can't set \"NAME\" field of record \"%s\"\n", + fprintf(stderr, "Can't set \"NAME\" field of record \"%s\"\n", dbGetRecordName(pdbentry)); yyerror(NULL); return; @@ -1211,7 +1225,7 @@ static void dbRecordField(char *name,char *value) char msg[128]; errSymLookup(status, msg, sizeof(msg)); - epicsPrintf("Can't set \"%s.%s\" to \"%s\" %s : %s\n", + fprintf(stderr, "Can't set \"%s.%s\" to \"%s\" %s : %s\n", dbGetRecordName(pdbentry), name, value, pdbentry->message ? pdbentry->message : "", msg); dbPutStringSuggest(pdbentry, value); yyerror(NULL); @@ -1242,31 +1256,57 @@ static void dbRecordInfo(char *name, char *value) status = dbPutInfo(pdbentry,name,value); if (status) { - epicsPrintf("Can't set \"%s\" info \"%s\" to \"%s\"\n", + fprintf(stderr, "Can't set \"%s\" info \"%s\" to \"%s\"\n", dbGetRecordName(pdbentry), name, value); yyerror(NULL); return; } } +static long createAlias(DBENTRY *pdbentry, const char *alias) +{ + DBENTRY tempEntry; + long status; + dbRecordNode *precnode = pdbentry->precnode; + + if (precnode->aliasedRecnode) precnode = precnode->aliasedRecnode; + dbInitEntry(pdbentry->pdbbase, &tempEntry); + status = dbFindRecord(&tempEntry, alias); + if (status == 0) { + if (tempEntry.precnode->aliasedRecnode != precnode) { + if (tempEntry.precnode->aliasedRecnode) + fprintf(stderr, ERL_ERROR ": Alias \"%s\" for \"%s\": name already used by an alias for \"%s\"\n", + alias, dbGetRecordName(pdbentry), + tempEntry.precnode->aliasedRecnode->recordname); + else + fprintf(stderr, ERL_ERROR ": Alias \"%s\" for \"%s\": name already used by a record\n", + alias, dbGetRecordName(pdbentry)); + status = S_dbLib_recExists; + } else if (dbRecordsOnceOnly) { + fprintf(stderr, ERL_ERROR ": Alias \"%s\" already defined and dbRecordsOnceOnly set.\n", + alias); + status = S_dbLib_recExists; + } + } else { + status = dbCreateAlias(pdbentry, alias); + } + dbFinishEntry(&tempEntry); + return status; +} + static void dbRecordAlias(char *name) { DBENTRY *pdbentry; tempListNode *ptempListNode; - long status; - if(dbRecordNameValidate(name)) return; if (duplicate) return; ptempListNode = (tempListNode *)ellFirst(&tempList); pdbentry = ptempListNode->item; - status = dbCreateAlias(pdbentry, name); - if (status) { - epicsPrintf("Can't create alias \"%s\" for \"%s\"\n", - name, dbGetRecordName(pdbentry)); + + if (createAlias(pdbentry, name) != 0) { yyerror(NULL); - return; } } @@ -1280,13 +1320,11 @@ static void dbAlias(char *name, char *alias) dbInitEntry(savedPdbbase, pdbEntry); if (dbFindRecord(pdbEntry, name)) { - epicsPrintf("Alias \"%s\" refers to unknown record \"%s\"\n", + fprintf(stderr, "Alias \"%s\" refers to unknown record \"%s\"\n", alias, name); yyerror(NULL); } - else if (dbCreateAlias(pdbEntry, alias)) { - epicsPrintf("Can't create alias \"%s\" referring to \"%s\"\n", - alias, name); + else if (createAlias(pdbEntry, alias) != 0) { yyerror(NULL); } dbFinishEntry(pdbEntry); diff --git a/modules/database/src/ioc/dbStatic/dbStaticIocRegister.c b/modules/database/src/ioc/dbStatic/dbStaticIocRegister.c index ff9aafc78..21a34e857 100644 --- a/modules/database/src/ioc/dbStatic/dbStaticIocRegister.c +++ b/modules/database/src/ioc/dbStatic/dbStaticIocRegister.c @@ -207,7 +207,7 @@ static const iocshFuncDef dbPvdTableSizeFuncDef = { }; static void dbPvdTableSizeCallFunc(const iocshArgBuf *args) { - dbPvdTableSize(args[0].ival); + iocshSetError(dbPvdTableSize(args[0].ival)); } /* dbReportDeviceConfig */ diff --git a/modules/database/src/ioc/dbStatic/dbStaticLib.c b/modules/database/src/ioc/dbStatic/dbStaticLib.c index fda3cbf51..5315f83f8 100644 --- a/modules/database/src/ioc/dbStatic/dbStaticLib.c +++ b/modules/database/src/ioc/dbStatic/dbStaticLib.c @@ -1219,7 +1219,9 @@ long dbNextRecordType(DBENTRY *pdbentry) char * dbGetRecordTypeName(DBENTRY *pdbentry) { - return(pdbentry->precordType->name); + dbRecordType *pdbRecordType = pdbentry->precordType; + if(!pdbRecordType) return NULL; + return(pdbRecordType->name); } int dbGetNRecordTypes(DBENTRY *pdbentry) @@ -1478,6 +1480,17 @@ long dbDeleteAliases(DBENTRY *pdbentry) return 0; } +static void dbDeleteRecordLinks(dbRecordType *rtyp, struct dbCommon *prec) +{ + short i; + + for (i=0; ino_links; i++) { + dbFldDes *pflddes = rtyp->papFldDes[rtyp->link_ind[i]]; + DBLINK *plink = (DBLINK *)(((char *)prec) + pflddes->offset); + dbFreeLinkContents(plink); + } +} + long dbDeleteRecord(DBENTRY *pdbentry) { dbBase *pdbbase = pdbentry->pdbbase; @@ -1493,6 +1506,7 @@ long dbDeleteRecord(DBENTRY *pdbentry) preclist = &precordType->recList; ellDelete(preclist, &precnode->node); dbPvdDelete(pdbbase, precnode); + dbDeleteRecordLinks(precordType, precnode->precord); while (!dbFirstInfo(pdbentry)) { dbDeleteInfo(pdbentry); } @@ -2206,12 +2220,12 @@ long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec) */ } else if(dbCanSetLink(plink, &link_info, devsup)!=0) { - errlogPrintf(ERL_ERROR ": %s.%s: can't initialize link type %d with \"%s\" (type %d)\n", - prec->name, pflddes->name, plink->type, plink->text, link_info.ltype); + errlogPrintf(ERL_ERROR ": %s.%s: can't initialize link type %s with \"%s\" (type %s)\n", + prec->name, pflddes->name, pamaplinkType[plink->type].strvalue, plink->text, pamaplinkType[link_info.ltype].strvalue); } else if(dbSetLink(plink, &link_info, devsup)) { - errlogPrintf(ERL_ERROR ": %s.%s: failed to initialize link type %d with \"%s\" (type %d)\n", - prec->name, pflddes->name, plink->type, plink->text, link_info.ltype); + errlogPrintf(ERL_ERROR ": %s.%s: failed to initialize link type %s with \"%s\" (type %s)\n", + prec->name, pflddes->name, pamaplinkType[plink->type].strvalue, plink->text, pamaplinkType[link_info.ltype].strvalue); } free(plink->text); plink->text = NULL; diff --git a/modules/database/src/ioc/dbStatic/dbStaticRun.c b/modules/database/src/ioc/dbStatic/dbStaticRun.c index ee9480c54..a4e93d5ca 100644 --- a/modules/database/src/ioc/dbStatic/dbStaticRun.c +++ b/modules/database/src/ioc/dbStatic/dbStaticRun.c @@ -66,7 +66,17 @@ void devExtend(dsxt *pdsxt) pthisDevSup->pdsxt = pdsxt; } } - + +/* Ensure that: sizeof(dbCommonPvt) + pdbRecordType->rec_size + * accounts for necessary alignment of record type struct + */ +typedef struct { + dbCommonPvt prefix; + /* ensure no padding needed... */ + dbCommon suffix; +} dbCommonPvtAlignmentTest; +STATIC_ASSERT(offsetof(dbCommonPvtAlignmentTest, suffix)==sizeof(dbCommonPvt)); + long dbAllocRecord(DBENTRY *pdbentry,const char *precordName) { dbRecordType *pdbRecordType = pdbentry->precordType; @@ -90,8 +100,8 @@ long dbAllocRecord(DBENTRY *pdbentry,const char *precordName) precordName, pdbRecordType->name, pdbRecordType->rec_size); return(S_dbLib_noRecSup); } - ppvt = dbCalloc(1, offsetof(dbCommonPvt, common) + pdbRecordType->rec_size); - precord = &ppvt->common; + ppvt = dbCalloc(1, sizeof(dbCommonPvt) + pdbRecordType->rec_size); + precord = dbPvt2Rec(ppvt); ppvt->recnode = precnode; precord->rdes = pdbRecordType; precnode->precord = precord; diff --git a/modules/database/src/ioc/dbStatic/dbYacc.y b/modules/database/src/ioc/dbStatic/dbYacc.y index 369fd914f..aad7ca33d 100644 --- a/modules/database/src/ioc/dbStatic/dbYacc.y +++ b/modules/database/src/ioc/dbStatic/dbYacc.y @@ -8,7 +8,7 @@ * in file LICENSE that is included with this distribution. \*************************************************************************/ %{ -static int yyerror(); +static int yyerror(char *str); static int yy_start; static long pvt_yy_parse(void); static int yyFailed = 0; @@ -370,14 +370,15 @@ json_value: jsonNULL { $$ = dbmfStrdup("null"); } static int yyerror(char *str) { if (str) - epicsPrintf(ERL_ERROR ": %s\n", str); + fprintf(stderr, ERL_ERROR ": %s\n", str); else - epicsPrintf(ERL_ERROR ""); + fprintf(stderr, ERL_ERROR ": "); if (!yyFailed) { /* Only print this stuff once */ - epicsPrintf(" at or before '%s'", yytext); + fprintf(stderr, " at or before '%s'", yytext); dbIncludePrint(); yyFailed = TRUE; } + fprintf(stderr, "\n %d | %s\n", pinputFileNow->line_num, my_buffer); return(0); } static long pvt_yy_parse(void) diff --git a/modules/database/src/ioc/dbStatic/link.h b/modules/database/src/ioc/dbStatic/link.h index 6488e8caa..6cd3ccd92 100644 --- a/modules/database/src/ioc/dbStatic/link.h +++ b/modules/database/src/ioc/dbStatic/link.h @@ -72,19 +72,20 @@ DBCORE_API extern const maplinkType pamaplinkType[LINK_NTYPES]; /* DBLINK Flag bits */ #define DBLINK_FLAG_INITIALIZED 1 /* dbInitLink() called */ #define DBLINK_FLAG_TSELisTIME 2 /* Use TSEL to get timeStamp */ +#define DBLINK_FLAG_VISITED 4 /* Used in loop detection */ struct macro_link { char *macroStr; }; -struct dbCommon; -typedef long (*LINKCVT)(); +struct dbAddr; +typedef long (*FASTCONVERTFUNC)(const void *from, void *to, const struct dbAddr *paddr); struct pv_link { ELLNODE backlinknode; char *pvname; /* pvname link points to */ void *pvt; /* CA or DB private */ - LINKCVT getCvt; /* input conversion function */ + FASTCONVERTFUNC getCvt; /* input conversion function */ short pvlMask; /* Options mask */ short lastGetdbrType; /* last dbrType for DB or CA get */ }; @@ -188,8 +189,8 @@ union value { struct vxiio vxiio; /* vxi io */ }; +struct dbCommon; struct lset; - struct link { struct dbCommon *precord; /* Pointer to record owning link */ short type; diff --git a/modules/database/src/ioc/dbtemplate/Makefile b/modules/database/src/ioc/dbtemplate/Makefile index 13b4f11fb..1013c0abf 100644 --- a/modules/database/src/ioc/dbtemplate/Makefile +++ b/modules/database/src/ioc/dbtemplate/Makefile @@ -14,8 +14,8 @@ SRC_DIRS += $(IOCDIR)/dbtemplate PROD_HOST += msi msi_SRCS = msi.cpp -msi_LIBS += Com -HTMLS += msi.html +msi_SYS_LIBS_WIN32 = shlwapi +DOCS += msi.md INC += dbLoadTemplate.h INC += dbtoolsIocRegister.h diff --git a/modules/database/src/ioc/dbtemplate/dbLoadTemplate.y b/modules/database/src/ioc/dbtemplate/dbLoadTemplate.y index a382459c1..f2fca00f8 100644 --- a/modules/database/src/ioc/dbtemplate/dbLoadTemplate.y +++ b/modules/database/src/ioc/dbtemplate/dbLoadTemplate.y @@ -41,6 +41,17 @@ static int var_count, sub_count; int dbTemplateMaxVars = 100; epicsExportAddress(int, dbTemplateMaxVars); +static +int msiLoadRecords(const char *fname, const char *subs) +{ + int ret = dbLoadRecords(fname, subs); + if(ret) { + fprintf(stderr, "dbLoadRecords(\"%s\", %s)\n", fname, subs); + yyerror("Error while reading included file"); + } + return ret; +} + %} %start substitution_file @@ -167,7 +178,7 @@ pattern_definition: global_definitions fprintf(stderr, "pattern_definition: pattern_values empty\n"); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); #endif - dbLoadRecords(db_file_name, sub_collect+1); + if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT; } | O_BRACE pattern_values C_BRACE { @@ -175,7 +186,7 @@ pattern_definition: global_definitions fprintf(stderr, "pattern_definition:\n"); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); #endif - dbLoadRecords(db_file_name, sub_collect+1); + if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT; *sub_locals = '\0'; sub_count = 0; } @@ -190,7 +201,7 @@ pattern_definition: global_definitions fprintf(stderr, "pattern_definition:\n"); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); #endif - dbLoadRecords(db_file_name, sub_collect+1); + if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT; dbmfFree($1); *sub_locals = '\0'; sub_count = 0; @@ -250,7 +261,7 @@ variable_substitution: global_definitions fprintf(stderr, "variable_substitution: variable_definitions empty\n"); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); #endif - dbLoadRecords(db_file_name, sub_collect+1); + if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT; } | O_BRACE variable_definitions C_BRACE { @@ -258,7 +269,7 @@ variable_substitution: global_definitions fprintf(stderr, "variable_substitution:\n"); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); #endif - dbLoadRecords(db_file_name, sub_collect+1); + if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT; *sub_locals = '\0'; } | WORD O_BRACE variable_definitions C_BRACE @@ -272,7 +283,7 @@ variable_substitution: global_definitions fprintf(stderr, "variable_substitution:\n"); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); #endif - dbLoadRecords(db_file_name, sub_collect+1); + if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT; dbmfFree($1); *sub_locals = '\0'; } @@ -328,6 +339,7 @@ int dbLoadTemplate(const char *sub_file, const char *cmd_collect) { FILE *fp; int i; + int err; line_num = 1; @@ -377,7 +389,7 @@ int dbLoadTemplate(const char *sub_file, const char *cmd_collect) yyrestart(fp); } - yyparse(); + err = yyparse(); for (i = 0; i < var_count; i++) { dbmfFree(vars[i]); @@ -390,5 +402,5 @@ int dbLoadTemplate(const char *sub_file, const char *cmd_collect) dbmfFree(db_file_name); db_file_name = NULL; } - return 0; + return err; } diff --git a/modules/database/src/ioc/dbtemplate/msi.cpp b/modules/database/src/ioc/dbtemplate/msi.cpp index 88c5d160d..0da488cba 100644 --- a/modules/database/src/ioc/dbtemplate/msi.cpp +++ b/modules/database/src/ioc/dbtemplate/msi.cpp @@ -27,6 +27,10 @@ #include #include +#ifdef _WIN32 +#include +#endif + #define MAX_BUFFER_SIZE 4096 #define MAX_DEPS 1024 @@ -61,7 +65,7 @@ typedef struct inputData inputData; static void inputConstruct(inputData **ppvt); static void inputDestruct(inputData * const pvt); static void inputAddPath(inputData * const pvt, const char * const pval); -static void inputBegin(inputData * const pvt, const char * const fileName); +static void inputBegin(inputData * const pvt, const char * const fileName, bool fromCmdLine); static char *inputNextLine(inputData * const pvt); static void inputNewIncludeFile(inputData * const pvt, const char * const name); static void inputErrPrint(const inputData * const pvt); @@ -83,7 +87,8 @@ static void addMacroReplacements(MAC_HANDLE * const macPvt, const char * const pval); static void makeSubstitutions(inputData * const inputPvt, MAC_HANDLE * const macPvt, - const char * const templateName); + const char * const templateName, + bool fromCmdLine); /*Global variables */ static int opt_V = 0; @@ -171,7 +176,7 @@ int main(int argc,char **argv) if (substitutionName.empty()) { STEP("Single template+substitutions file"); - makeSubstitutions(inputPvt, macPvt, templateName); + makeSubstitutions(inputPvt, macPvt, templateName, true); } else { subInfo *substitutePvt; @@ -203,7 +208,7 @@ int main(int argc,char **argv) macPushScope(macPvt); addMacroReplacements(macPvt, macStr); - makeSubstitutions(inputPvt, macPvt, filename); + makeSubstitutions(inputPvt, macPvt, filename, false); if (localScope) macPopScope(macPvt); @@ -276,14 +281,15 @@ static const char *cmdNames[] = {"include","substitute"}; static void makeSubstitutions(inputData * const inputPvt, MAC_HANDLE * const macPvt, - const char * const templateName) + const char * const templateName, + bool fromCmdLine) { char *input; static char buffer[MAX_BUFFER_SIZE]; int n; ENTER; - inputBegin(inputPvt, templateName); + inputBegin(inputPvt, templateName, fromCmdLine); while ((input = inputNextLine(inputPvt))) { int expand=1; char *p; @@ -378,7 +384,7 @@ struct inputData { inputData() { memset(inputBuffer, 0, sizeof(inputBuffer) * sizeof(inputBuffer[0])); }; }; -static void inputOpenFile(inputData *pinputData, const char * const filename); +static void inputOpenFile(inputData *pinputData, const char * const filename, bool fromCmdLine); static void inputCloseFile(inputData *pinputData); static void inputCloseAllFiles(inputData *pinputData); @@ -431,11 +437,11 @@ static void inputAddPath(inputData * const pinputData, const char * const path) EXIT; } -static void inputBegin(inputData * const pinputData, const char * const fileName) +static void inputBegin(inputData * const pinputData, const char * const fileName, bool fromCmdLine) { ENTER; inputCloseAllFiles(pinputData); - inputOpenFile(pinputData, fileName); + inputOpenFile(pinputData, fileName, fromCmdLine); EXIT; } @@ -462,7 +468,7 @@ static void inputNewIncludeFile(inputData * const pinputData, const char * const name) { ENTER; - inputOpenFile(pinputData,name); + inputOpenFile(pinputData, name, false); EXIT; } @@ -493,7 +499,15 @@ static void inputErrPrint(const inputData *const pinputData) EXIT; } -static void inputOpenFile(inputData *pinputData, const char * const filename) +static int isPathRelative(const char * const path) { +#ifdef _WIN32 + return path && PathIsRelativeA(path); +#else + return path && path[0] != '/'; +#endif +} + +static void inputOpenFile(inputData *pinputData, const char * const filename, bool fromCmdLine) { std::list& pathList = pinputData->pathList; std::list::iterator pathIt = pathList.end(); @@ -505,7 +519,7 @@ static void inputOpenFile(inputData *pinputData, const char * const filename) STEP("Using stdin"); fp = stdin; } - else if (pathList.empty() || strchr(filename, '/')){ + else if (fromCmdLine || pathList.empty() || !isPathRelative(filename)){ STEPS("Opening ", filename); fp = fopen(filename, "r"); } diff --git a/modules/database/src/ioc/dbtemplate/msi.html b/modules/database/src/ioc/dbtemplate/msi.html deleted file mode 100644 index b5929fc79..000000000 --- a/modules/database/src/ioc/dbtemplate/msi.html +++ /dev/null @@ -1,445 +0,0 @@ - - - - - - - - - -

    msi: Macro Substitution and Include Tool

    - -

    Introduction

    - -

    msi is a general purpose macro substitution/include tool. It accepts as input -an ascii template file. It looks for lines containing two reserved command -names: include and substitute. It also looks for and performs -substitutions on macros of the form $(var) and ${var}. It uses the macLib -routines from EPICS Base to perform the substitutions, so it also accepts the -default value and value definition syntax that macLib implements.

    - -

    msi also allows substitutions to be specified via a separate substitution -file. This substitution file allows the same format as the substitution files -accepted by the EPICS IOC's dbLoadTemplate command.

    - -

    Command Syntax:

    - -
    msi -V -g -D -ooutfile -Idir -Msubs -Ssubfile template
    - -

    All parameters are optional. The -o, -I, -M, and -S switches may be -separated from their associated value string by spaces if desired. Output will -be written to stdout unless the -o option is given.

    - -

    Switches have the following meanings:

    - -
    -
    -V
    -
    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.
    - -
    -g
    -
    When this flag is given all macros defined in a substitution file will - have global scope and thus their values will persist until a new value is - given for this macro. This flag is provided for backwards compatibility as - this was the behavior of previous versions of msi, but it does not follow - common scoping rules and is discouraged.
    - -
    -D
    -
    Output dependency information suitable for including by a Makefile to - stdout instead of performing the macro substitutions. The -o option - must be given to specify the target name for the dependency rules. Other - options should be given exactly as will be used in the macro substitution - process.
    - -
    -o file
    -
    Output will be written to the specifed file rather than to the standard - output.
    - -
    -I dir
    -
    This parameter, which may be repeated or contain a colon-separated (or - semi-colon separated on Windows) list of directory paths, specifies a search - path for include commands. For example: - -
    -
    msi -I /home/mrk/examples:. -I.. template
    -
    - - specifies that all named files should be searched for in the following - locations in the order given: - -
      -
    1. /home/mrk/examples
    2. -
    3. . (the current directory)
    4. -
    5. .. (the parent of the current directory)
    6. -
    -
    - -
    -M substitutions
    -
    This parameter specifies macro values for the template instance. - Multiple macro values can be specified in one substitution parameter, or in - multiple -M parameters. For example: - -
    -
    msi -M "a=aval,b=bval" -Mc=cval template
    -
    - - specifies that in the template file each occurrence of: - -
    -
    $(a) or ${a} is replaced by aval
    -
    $(b) or ${b} is replaced by bval
    -
    $(c) or ${c} is replaced by cval
    -
    -
    - -
    -S subfile
    -
    The substitution file. See below for format.
    - -
    template
    -
    The input file. If no file is specified then input is taken from - stdin, i.e. msi can be used as a filter. See below for a description of - commands that can be embedded in the template file.
    -
    - -

    It is not possible to display usage by just typing msi since -executing the command with no arguments is a valid command. To show usage -specify an illegal switch, e.g.

    - -
    -
    msi -help
    -
    - -

    Exit Status

    - -
    -
    0
    Success. -
    1
    Can't open/create file, or other I/O error. -
    2
    Undefined macros encountered with the -V option specified. -
    - -

    Template File Format

    - -

    This file contains the text to be read and written to the output after macro -substitution is performed. If no file is given then input is read from stdin. -Variable instances to be substituted by macro values are expressed in the -template using the syntax $(name) or -${name}. The template can also provide default values -to be used when a macro has not been given a value, using the syntax -$(name=default) or -${name=default}.

    - -

    For example, using the command

    - -
    -
    msi -M name=Marty template
    -
    - -

    where the file template contains

    - -
    -
    My name is $(name)
    -My age is $(age=none of your business)
    -
    - -

    results in this output:

    - -
    -
    My name is Marty
    -My age is none of your business
    -
    - -

    Macro variables and their default values can be expressed in terms of other -macros if necessary, to almost any level of complexity. Recursive definitions -will generate warning messages on stderr and result in undefined output.

    - -

    The template file is read and processed one line at a time, where the -maximum length of a line before and/or after macro expansion is 1023 characters -— longer input or output lines will cause msi to fail. Within the context -of a single line, macro expansion does not occur when the variable instance -appears inside a single-quoted string, or where the dollar sign $ is -preceded by a back-slash character \, but as with the standard Unix -shells, variables inside double quoted strings are expanded properly.

    - -

    However neither back-slash characters nor quotes of either variety are -removed when generating the output file, so depending on what is being output -the single quote behaviour may not be useful and may even be a hinderance. It -cannot be disabled in the current version of msi.

    - -

    Template file commands

    - -

    In addition to the regular text and variable instances described above, the -template file may also contain commands which allow the insertion of other -template files and the ability to set macro values inside the template file -itself. These commands are:

    - -
    -
    include "file"
    -substitute "var=value,var=value,..."
    -
    - -

    Lines containing commands must be in one of these forms:

    - -
      -
    • include "filename"
    • -
    • substitute "name1=value1, name2=value2, ..."
    • -
    - -

    White space is allowed before and after the command verb, and after the -quoted string. If embedded quotes are needed, the backslash character -\ can be used as an escape character. For example

    - -
    -
    substitute "a=\"val\""
    -
    - -

    specifies that (unless a is subsequently redefined) wherever a -$(a) macro appears in the template below this point, the text -"val" (including the double quote characters) will appear in the -output instead.

    - -

    If a line does match either syntax above it is just passed to macLib for -processing without any notification. Thus the input line:

    - -
    -
    include "myfile" #include file
    -
    - -

    would just be passed to macLib, i.e. it would not be considered an -include command.

    - -

    As an example of these commands, let the Unix command be:

    - -
    -
    msi template
    -
    - -

    and file includeFile contain:

    - -
    -
    first name is ${first}
    -family name is ${family}
    -
    - -

    and template is

    - -
    -
    substitute "first=Marty,family=Kraimer"
    -include "includeFile"
    -substitute "first=Irma,family=Kraimer"
    -include "includeFile"
    -
    - -

    then the following is written to the output.

    - -
    -
    first name is Marty
    -family name is Kraimer
    -first name is Irma
    -family name is Kraimer
    -
    - -

    Note that the IOC's dbLoadTemplate command does not support the -substitute syntax in template files, although the include -syntax is supported.

    - -

    Substitution File Format

    - -

    The optional substitution file has three formats: regular, pattern, and -dbTemplate format. We will discuss each separately.

    - -

    Regular format

    - -
    -
    global {gbl_var1=gbl_val1, gbl_var2=gbl_val2, ...}
    -{var1=set1_val1, var2=set1_val2, ...}
    -{var2=set2_val2, var1=set2_val1, ...}
    -global {gbl_var1=gbl_val3, gbl_var2=gbl_val4, ...}
    -{var1=set3_val1, var2=set3_val2, ...}
    -{var2=set4_val2, var1=set4_val1, ...}
    -
    - -

    The template file is output with macro substitutions performed once for each -set of braces containing macro replacement values.

    - -

    Pattern format

    - -
    -
    global {gbl_var1=gbl_val1, gbl_var2=gbl_val2, ...}
    -pattern {var1, var2, ...}
    -{set1_val1, set1_val2, ...}
    -{set2_val1, set2_val2, ...}
    -pattern {var2, var1, ...}
    -global {gbl_var1=gbl_val3, gbl_var2=gbl_val4, ...}
    -{set3_val2, set3_val1, ...}
    -{set4_val2, set4_val2, ...}
    -
    - -

    This produces the same result as the regular format example above.

    - -

    dbLoadTemplate Format

    - -

    This format is an extension of the format accepted by the EPICS IOC command -dbLoadTemplate, and allows templates to be expanded on the host rather -by using dbLoadTemplate at IOC boot time.

    - -
    -
    global {gbl_var1=gbl_val1, gbl_var2=gbl_val2, ...}
    -file templatefile {
    -    pattern format or regular format
    -}
    -file "${WHERE}/template2" {
    -    pattern format or regular format
    -}
    -
    - -

    For the dbTemplate format, the template filename does not have to be given -on the command line, and is usually specified in the substitutions file -instead. If a template filename is given on the command line it will override -the filenames listed in the substitutions files.

    - -

    Syntax for all formats

    - -

    A comment line may appear anywhere in a substitution file, and will be -ignored. A comment line is any line beginning with the character #, -which must be the very first character on the line.

    - -

    Global definitions may supplement or override the macro values supplied on -the command-line using the -M switch, and set default values that will -survive for the remainder of the file unless another global definition of the -same macro changes it.

    - -

    For definitions within braces given in any of the file formats, a separator -must be given between items. A separator is either a comma, or one or more of -the standard white space characters (space, formfeed, newline, carriage return, -tab or vertical tab).

    - -

    Each item within braces can be an alphanumeric token, or a double-quoted -string. A back-slash character \ can be used to escape a quote -character needed inside a quoted string. These three sets of substitutions are -all equivalent:

    - -
    -
    {a=aa b=bb c="\"cc\""}
    -{b="bb",a=aa,c="\"cc\""}
    -{
    -    c="\"cc\""
    -    b=bb
    -    a="aa"
    -}
    -
    - -

    Within a substitutions file, the file name may appear inside double quotation -marks; these are required if the name contains certain characters or environment -variable macros of the form ${ENV_VAR} or $(ENV_VAR), which will be expanded -before the file is opened.

    - -

    Regular substitution example

    - -

    Let the command be:

    - -
    -
    msi -S substitute template
    -
    - -

    The file template contains

    - -
    -
    first name is ${first}
    -family name is ${family}
    -
    - -

    and the file substitute is

    - -
    -
    global {family=Kraimer}
    -{first=Marty}
    -{first=Irma}
    -
    - -

    The following is the output produced:

    - -
    -
    first name is Marty
    -family name is Kraimer
    -first name is Irma
    -family name is Kraimer
    -
    - -

    Pattern substitution example

    - -

    Let the command be:

    - -
    -
    msi -S pattern template
    -
    - -

    The file pattern contains

    - -
    -
    pattern {first,last}
    -{Marty,Kraimer}
    -{Irma,Kraimer}
    -
    - -

    and template is the same as the previous example:

    - -
    -
    first name is ${first}
    -family name is ${family}
    -
    - -

    This is the output:

    - -
    -
    first name is Marty
    -family name is Kraimer
    -first name is Irma
    -family name is Kraimer
    -
    - -

    dbTemplate example

    -Let the command be - -
    -
    msi -S xxx.substitutions
    -
    - -xxx.substitutions is - -
    -
    file template {
    -pattern {first,last}
    -{Marty,Kraimer}
    -{Irma,Kraimer}
    -pattern {last,first}
    -{Smith,Bill}
    -{Smith,Mary}
    -}
    -file template {
    -{first=Marty,last=Kraimer}
    -{first=Irma,last=Kraimer}
    -}
    -
    -template is the same as in the previous example.. - -

    The following is written to the output

    - -
    -
    first name is Marty
    -family name is Kraimer
    -first name is Irma
    -family name is Kraimer
    -first name is Bill
    -last name is Smith
    -first name is Mary
    -last name is Smith
    -first name is Marty
    -family name is Kraimer
    -first name is Irma
    -family name is Kraimer
    -
    - - - diff --git a/modules/database/src/ioc/dbtemplate/msi.md b/modules/database/src/ioc/dbtemplate/msi.md new file mode 100644 index 000000000..bbe5273ef --- /dev/null +++ b/modules/database/src/ioc/dbtemplate/msi.md @@ -0,0 +1,396 @@ +# msi: Macro Substitution and Include Tool + +(msitool)= +## Introduction + +msi is a general purpose macro substitution/include tool. +It accepts as input an ascii template file. It looks for lines containing two reserved +command names: `include` and `substitute`. It also looks for and performs +substitutions on macros of the form `$(var)` and `${var}`. It uses the +macLib routines from EPICS Base to perform the substitutions, so it also +accepts the default value and value definition syntax that macLib +implements. + +msi also allows substitutions to be specified via a separate +substitution file. This substitution file allows the same format as the +substitution files accepted by the EPICS IOC's dbLoadTemplate command. + + +## Command Syntax + +`msi -V -g -o outfile -I dir -M subs -S subfile template` + +All parameters are optional. The -o, -I, -M, and -S switches may be +separated from their associated value string by spaces if desired. +Output will be written to stdout unless the -o option is given. + +Switches have the following meanings: + +- **-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. +- **-g** + + When this flag is given all macros defined in a substitution file + will have global scope and thus their values will persist until a + new value is given for this macro. This flag is provided for + backwards compatibility as this was the behavior of previous + versions of msi, but it does not follow common scoping rules and is + discouraged. +- **-o _file_** + + Output will be written to the specifed file rather than to the + standard output. +- **-I _dir_** + + This parameter, which may be repeated or contain a colon-separated + (or semi-colon separated on Windows) list of directory paths, + specifies a search path for include commands. For example: + + msi -I /home/mrk/examples:. -I.. template + + specifies that all named files should be searched for in the following locations, + in the order given: + + 1. /home/mrk/examples + 2. . (the current directory) + 3. .. (the parent of the current directory) + + Note that relative path searching is handled as + + $ cat foo.substitutions + file rel/path/bar.template { + # contents + } + $ msi -I . -I /some/path foo.substitutions + + which will try to find `bar.template` at the path `./rel/path/` followed by + `/some/path/rel/path`. + + +- **-M _substitutions_** + + This parameter specifies macro values for the template instance. + Multiple macro values can be specified in one substitution + parameter, or in multiple -M parameters. For example: + + msi -M "a=aval,b=bval" -Mc=cval template + + specifies that in the template file each occurrence of: + + - `$(a)` or `${a}` is replaced by _aval_ + - `$(b)` or `${b}` is replaced by _bval_ + - `$(c)` or `${c}` is replaced by _cval_ + +- **-S _subfile_** + + The substitution file. See below for format. +- **_template_** + + The input file. If no file is specified then input is taken from + stdin, i.e. msi can be used as a filter. See below for a description + of commands that can be embedded in the template file. + +It is not possible to display usage by just typing msi since executing +the command with no arguments is a valid command. To show usage specify +an illegal switch, e.g. + + msi -help + + +## Exit Status + +- **0** + Success. +- **1** + Can't open/create file, or other I/O error. +- **2** + Undefined macros encountered with the -V option specified. + + +## Template File Format + +This file contains the text to be read and written to the output after +macro substitution is performed. If no file is given then input is read +from stdin. Variable instances to be substituted by macro values are +expressed in the template using the syntax \$(_name_) or \${_name_}. The +template can also provide default values to be used when a macro has not +been given a value, using the syntax \$(_name_=_default_) or \${_name_=_default_}. + +For example, using the command + + msi -M name=Marty template + +where the file template contains + + My name is $(name) + My age is $(age=none of your business) + +results in this output: + + My name is Marty + My age is none of your business + +Macro variables and their default values can be expressed in terms of +other macros if necessary, to almost any level of complexity. Recursive +definitions will generate warning messages on stderr and result in +undefined output. + +The template file is read and processed one line at a time, where the +maximum length of a line before and/or after macro expansion is 1023 +characters; longer input or output lines will cause msi to fail. Within +the context of a single line, macro expansion does not occur when the +variable instance appears inside a single-quoted string, or where the +dollar sign $ is preceded by a back-slash character \, but as with the +standard Unix shells, variables inside double quoted strings are +expanded properly. + +However neither back-slash characters nor quotes of either variety are +removed when generating the output file, so depending on what is being +output the single quote behaviour may not be useful and may even be a +hinderance. It cannot be disabled in the current version of msi. + + +### Template file commands + +In addition to the regular text and variable instances described above, +the template file may also contain commands which allow the insertion of +other template files and the ability to set macro values inside the +template file itself. These commands are: + + include "file" + substitute "var=value,var=value,..." + +Lines containing commands must be in one of these forms: + + - include "_filename_" + + - substitute "_name1=value1, name2=value2, ..._" + +White space is allowed before and after the command verb, and after the +quoted string. If embedded quotes are needed, the backslash character \ +can be used as an escape character. For example + + substitute "a=\"val\"" + +specifies that (unless a is subsequently redefined) wherever a $(a) +macro appears in the template below this point, the text +"val" (including the double quote characters) will appear in the output +instead. + +If a line does match either syntax above it is just passed to macLib for +processing without any notification. Thus the input line: + + include "myfile" #include file + +would just be passed to macLib, i.e. it would _not_ be considered an +include command. + +As an example of these commands, let the Unix command be: + + msi template + +and file includeFile contain: + + first name is ${first} + family name is ${family} + +and template is + + substitute "first=Marty,family=Kraimer" + include "includeFile" + substitute "first=Irma,family=Kraimer" + include "includeFile" + +then the following is written to the output. + + first name is Marty + family name is Kraimer + first name is Irma + family name is Kraimer + +Note that the IOC's dbLoadTemplate command does not support the +substitute syntax in template files, although the include syntax is +supported. + + +## Substitution File Format + +The optional substitution file has three formats: regular, pattern, and +dbTemplate format. We will discuss each separately. + + +### Regular format + + global {gbl_var1=gbl_val1, gbl_var2=gbl_val2, ...} + {var1=set1_val1, var2=set1_val2, ...} + {var2=set2_val2, var1=set2_val1, ...} + global {gbl_var1=gbl_val3, gbl_var2=gbl_val4, ...} + {var1=set3_val1, var2=set3_val2, ...} + {var2=set4_val2, var1=set4_val1, ...} + +The template file is output with macro substitutions performed once for +each set of braces containing macro replacement values. + + +### Pattern format + + global {gbl_var1=gbl_val1, gbl_var2=gbl_val2, ...} + pattern {var1, var2, ...} + {set1_val1, set1_val2, ...} + {set2_val1, set2_val2, ...} + pattern {var2, var1, ...} + global {gbl_var1=gbl_val3, gbl_var2=gbl_val4, ...} + {set3_val2, set3_val1, ...} + {set4_val2, set4_val2, ...} + +This produces the same result as the regular format example above. + + +### dbLoadTemplate Format + +This format is an extension of the format accepted by the EPICS IOC +command dbLoadTemplate, and allows templates to be expanded on the host +rather by using dbLoadTemplate at IOC boot time. + + global {gbl_var1=gbl_val1, gbl_var2=gbl_val2, ...} + file templatefile { + /pattern format or regular format/ + } + file "${WHERE}/template2" { + /pattern format or regular format/ + } + +For the dbTemplate format, the template filename does not have to be +given on the command line, and is usually specified in the substitutions +file instead. If a template filename is given on the command line it +will override the filenames listed in the substitutions files. + + +### Syntax for all formats + +A comment line may appear anywhere in a substitution file, and will be +ignored. A comment line is any line beginning with the character #, +which must be the very first character on the line. + +Global definitions may supplement or override the macro values supplied +on the command-line using the -M switch, and set default values that +will survive for the remainder of the file unless another global +definition of the same macro changes it. + +For definitions within braces given in any of the file formats, a +separator must be given between items. A separator is either a comma, or +one or more of the standard white space characters (space, formfeed, +newline, carriage return, tab or vertical tab). + +Each item within braces can be an alphanumeric token, or a double-quoted +string. A back-slash character \ can be used to escape a quote character +needed inside a quoted string. These three sets of substitutions are all +equivalent: + + {a=aa b=bb c="\"cc\""} + {b="bb",a=aa,c="\"cc\""} + { + c="\"cc\"" + b=bb + a="aa" + } + +Within a substitutions file, the file name may appear inside double +quotation marks; these are required if the name contains certain +characters or environment variable macros of the form `${ENV_VAR}` or +`$(ENV_VAR)`, which will be expanded before the file is opened. + + +### Regular substitution example + +Let the command be: + + msi -S substitute template + +The file template contains + + first name is ${first} + family name is ${family} + +and the file `substitute` is + + global {family=Kraimer} + {first=Marty} + {first=Irma} + +The following is the output produced: + + first name is Marty + family name is Kraimer + first name is Irma + family name is Kraimer + + +### Pattern substitution example + +Let the command be: + + msi -S pattern template + +The file pattern contains + + pattern {first,last} + {Marty,Kraimer} + {Irma,Kraimer} + +and template is the same as the previous example: + + first name is ${first} + family name is ${family} + +This is the output: + + first name is Marty + family name is Kraimer + first name is Irma + family name is Kraimer + + +### dbTemplate example + +Let the command be + + msi -S xxx.substitutions + +`xxx.substitutions` is + + file template { + pattern {first,last} + {Marty,Kraimer} + {Irma,Kraimer} + pattern {last,first} + {Smith,Bill} + {Smith,Mary} + } + file template { + {first=Marty,last=Kraimer} + {first=Irma,last=Kraimer} + } + +`template` is the same as in the previous example. + +The following is written to the output + + first name is Marty + family name is Kraimer + first name is Irma + family name is Kraimer + first name is Bill + last name is Smith + first name is Mary + last name is Smith + first name is Marty + family name is Kraimer + first name is Irma + family name is Kraimer + diff --git a/modules/database/src/ioc/misc/dlload.c b/modules/database/src/ioc/misc/dlload.c index 8b2a76371..8aea72cb5 100644 --- a/modules/database/src/ioc/misc/dlload.c +++ b/modules/database/src/ioc/misc/dlload.c @@ -10,11 +10,13 @@ #include "iocsh.h" #include "epicsExport.h" -IOCSH_STATIC_FUNC void dlload(const char* name) +IOCSH_STATIC_FUNC int dlload(const char* name) { if (!epicsLoadLibrary(name)) { printf("epicsLoadLibrary failed: %s\n", epicsLoadError()); + return -1; } + return 0; } static const iocshArg dlloadArg0 = { "path/library.so", iocshArgStringPath}; @@ -28,7 +30,7 @@ static const iocshFuncDef dlloadFuncDef = { }; static void dlloadCallFunc(const iocshArgBuf *args) { - dlload(args[0].sval); + iocshSetError(dlload(args[0].sval)); } static void dlloadRegistar(void) { diff --git a/modules/database/src/ioc/misc/iocInit.c b/modules/database/src/ioc/misc/iocInit.c index 1b1d4fc0b..ac89495f0 100644 --- a/modules/database/src/ioc/misc/iocInit.c +++ b/modules/database/src/ioc/misc/iocInit.c @@ -114,7 +114,7 @@ int iocInit(void) static int iocBuild_1(void) { if (iocState != iocVoid) { - errlogPrintf("iocBuild: IOC can only be initialized from uninitialized or stopped state\n"); + errlogPrintf("iocBuild: " ERL_ERROR " IOC can only be initialized from uninitialized or stopped state\n"); return -1; } errlogInit(0); @@ -126,7 +126,7 @@ static int iocBuild_1(void) errlogPrintf("Starting iocInit\n"); if (checkDatabase(pdbbase)) { - errlogPrintf("iocBuild: Aborting, bad database definition (DBD)!\n"); + errlogPrintf("iocBuild: " ERL_ERROR " Aborting, bad database definition (DBD)!\n"); return -1; } epicsSignalInstallSigHupIgnore(); @@ -237,7 +237,7 @@ int iocBuildIsolated(void) int iocRun(void) { if (iocState != iocPaused && iocState != iocBuilt) { - errlogPrintf("iocRun: IOC not paused\n"); + errlogPrintf("iocRun: " ERL_WARNING " IOC not paused\n"); return -1; } initHookAnnounce(initHookAtIocRun); @@ -268,7 +268,7 @@ int iocRun(void) int iocPause(void) { if (iocState != iocRunning) { - errlogPrintf("iocPause: IOC not running\n"); + errlogPrintf("iocPause: " ERL_WARNING " IOC not running\n"); return -1; } initHookAnnounce(initHookAtIocPause); @@ -306,59 +306,59 @@ static int checkDatabase(dbBase *pdbbase) const dbMenu *pMenu; if (!pdbbase) { - errlogPrintf("checkDatabase: No database definitions loaded.\n"); + errlogPrintf("checkDatabase: " ERL_ERROR " No database definitions loaded.\n"); return -1; } pMenu = dbFindMenu(pdbbase, "menuConvert"); if (!pMenu) { - errlogPrintf("checkDatabase: menuConvert not defined.\n"); + errlogPrintf("checkDatabase: " ERL_ERROR " menuConvert not defined.\n"); return -1; } if (pMenu->nChoice <= menuConvertLINEAR) { - errlogPrintf("checkDatabase: menuConvert has too few choices.\n"); + errlogPrintf("checkDatabase: " ERL_ERROR " menuConvert has too few choices.\n"); return -1; } if (strcmp(pMenu->papChoiceName[menuConvertNO_CONVERSION], "menuConvertNO_CONVERSION")) { - errlogPrintf("checkDatabase: menuConvertNO_CONVERSION doesn't match.\n"); + errlogPrintf("checkDatabase: " ERL_ERROR " menuConvertNO_CONVERSION doesn't match.\n"); return -1; } if (strcmp(pMenu->papChoiceName[menuConvertSLOPE], "menuConvertSLOPE")) { - errlogPrintf("checkDatabase: menuConvertSLOPE doesn't match.\n"); + errlogPrintf("checkDatabase: " ERL_ERROR " menuConvertSLOPE doesn't match.\n"); return -1; } if (strcmp(pMenu->papChoiceName[menuConvertLINEAR], "menuConvertLINEAR")) { - errlogPrintf("checkDatabase: menuConvertLINEAR doesn't match.\n"); + errlogPrintf("checkDatabase: " ERL_ERROR " menuConvertLINEAR doesn't match.\n"); return -1; } pMenu = dbFindMenu(pdbbase, "menuScan"); if (!pMenu) { - errlogPrintf("checkDatabase: menuScan not defined.\n"); + errlogPrintf("checkDatabase: " ERL_ERROR " menuScan not defined.\n"); return -1; } if (pMenu->nChoice <= menuScanI_O_Intr) { - errlogPrintf("checkDatabase: menuScan has too few choices.\n"); + errlogPrintf("checkDatabase: " ERL_ERROR " menuScan has too few choices.\n"); return -1; } if (strcmp(pMenu->papChoiceName[menuScanPassive], "menuScanPassive")) { - errlogPrintf("checkDatabase: menuScanPassive doesn't match.\n"); + errlogPrintf("checkDatabase: " ERL_ERROR " menuScanPassive doesn't match.\n"); return -1; } if (strcmp(pMenu->papChoiceName[menuScanEvent], "menuScanEvent")) { - errlogPrintf("checkDatabase: menuScanEvent doesn't match.\n"); + errlogPrintf("checkDatabase: " ERL_ERROR " menuScanEvent doesn't match.\n"); return -1; } if (strcmp(pMenu->papChoiceName[menuScanI_O_Intr], "menuScanI_O_Intr")) { - errlogPrintf("checkDatabase: menuScanI_O_Intr doesn't match.\n"); + errlogPrintf("checkDatabase: " ERL_ERROR " menuScanI_O_Intr doesn't match.\n"); return -1; } if (pMenu->nChoice <= SCAN_1ST_PERIODIC) { - errlogPrintf("checkDatabase: menuScan has no periodic choices.\n"); + errlogPrintf("checkDatabase: " ERL_ERROR " menuScan has no periodic choices.\n"); return -1; } @@ -388,7 +388,7 @@ static void initDrvSup(void) /* Locate all driver support entry tables */ for (pdrvSup = (drvSup *)ellFirst(&pdbbase->drvList); pdrvSup; pdrvSup = (drvSup *)ellNext(&pdrvSup->node)) { - struct drvet *pdrvet = registryDriverSupportFind(pdrvSup->name); + drvet *pdrvet = registryDriverSupportFind(pdrvSup->name); if (!pdrvet) { errlogPrintf("iocInit: driver %s not found\n", pdrvSup->name); diff --git a/modules/database/src/ioc/misc/iocInit.h b/modules/database/src/ioc/misc/iocInit.h index 6b4c944d4..1acd8ee8f 100644 --- a/modules/database/src/ioc/misc/iocInit.h +++ b/modules/database/src/ioc/misc/iocInit.h @@ -22,6 +22,9 @@ enum iocStateEnum { extern "C" { #endif +/** Query present IOC run state + * @since 3.15.8 + */ DBCORE_API enum iocStateEnum getIocState(void); DBCORE_API int iocInit(void); DBCORE_API int iocBuild(void); diff --git a/modules/database/src/ioc/misc/registerAllRecordDeviceDrivers.cpp b/modules/database/src/ioc/misc/registerAllRecordDeviceDrivers.cpp index f6a21dc77..7d1a1ed61 100644 --- a/modules/database/src/ioc/misc/registerAllRecordDeviceDrivers.cpp +++ b/modules/database/src/ioc/misc/registerAllRecordDeviceDrivers.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #define EPICS_PRIVATE_API @@ -32,11 +33,12 @@ namespace { struct compareLoc { bool operator()(const recordTypeLocation& lhs, const recordTypeLocation& rhs) const { - if(lhs.prsetrhs.prset) + if (lhs.prset > rhs.prset) return false; - return lhs.sizeOffset(lhs.sizeOffset) + < reinterpret_cast(rhs.sizeOffset); } }; diff --git a/modules/database/src/ioc/registry/registryCommon.c b/modules/database/src/ioc/registry/registryCommon.c index 273e4d5ec..be5c4e347 100644 --- a/modules/database/src/ioc/registry/registryCommon.c +++ b/modules/database/src/ioc/registry/registryCommon.c @@ -65,7 +65,7 @@ void registerDevices(DBBASE *pbase, int nDevices, } void registerDrivers(DBBASE *pbase, int nDrivers, - const char * const * driverSupportNames, struct drvet * const *drvsl) + const char * const * driverSupportNames, drvet * const *drvsl) { int i; for (i = 0; i < nDrivers; i++) { diff --git a/modules/database/src/ioc/registry/registryCommon.h b/modules/database/src/ioc/registry/registryCommon.h index b2ff5eb07..5f062a4a1 100644 --- a/modules/database/src/ioc/registry/registryCommon.h +++ b/modules/database/src/ioc/registry/registryCommon.h @@ -29,7 +29,7 @@ DBCORE_API void registerDevices( const char * const *deviceSupportNames, const dset * const *devsl); DBCORE_API void registerDrivers( DBBASE *pbase, int nDrivers, - const char * const *driverSupportNames, struct drvet * const *drvsl); + const char * const *driverSupportNames, drvet * const *drvsl); DBCORE_API void registerJLinks( DBBASE *pbase, int nDrivers, jlif * const *jlifsl); diff --git a/modules/database/src/ioc/registry/registryDriverSupport.c b/modules/database/src/ioc/registry/registryDriverSupport.c index 36ac0a82e..5fb84a48b 100644 --- a/modules/database/src/ioc/registry/registryDriverSupport.c +++ b/modules/database/src/ioc/registry/registryDriverSupport.c @@ -18,12 +18,12 @@ static void *registryID = "driver support"; DBCORE_API int registryDriverSupportAdd( - const char *name, struct drvet *pdrvet) + const char *name, drvet *pdrvet) { return registryAdd(registryID, name, pdrvet); } -DBCORE_API struct drvet * registryDriverSupportFind( +DBCORE_API drvet * registryDriverSupportFind( const char *name) { return registryFind(registryID, name); diff --git a/modules/database/src/ioc/registry/registryDriverSupport.h b/modules/database/src/ioc/registry/registryDriverSupport.h index 8857d97b2..90815e8ed 100644 --- a/modules/database/src/ioc/registry/registryDriverSupport.h +++ b/modules/database/src/ioc/registry/registryDriverSupport.h @@ -19,8 +19,8 @@ extern "C" { #endif DBCORE_API int registryDriverSupportAdd( - const char *name, struct drvet *pdrvet); -DBCORE_API struct drvet * registryDriverSupportFind( + const char *name, drvet *pdrvet); +DBCORE_API drvet * registryDriverSupportFind( const char *name); #ifdef __cplusplus diff --git a/modules/database/src/ioc/rsrv/camsgtask.c b/modules/database/src/ioc/rsrv/camsgtask.c index 24df11d39..b69a39458 100644 --- a/modules/database/src/ioc/rsrv/camsgtask.c +++ b/modules/database/src/ioc/rsrv/camsgtask.c @@ -163,9 +163,11 @@ void camsgtask ( void *pParm ) int casClientInitiatingCurrentThread ( char * pBuf, size_t bufSize ) { - struct client * pClient = ( struct client * ) - epicsThreadPrivateGet ( rsrvCurrentClient ); + struct client * pClient; + if ( ! rsrvCurrentClient ) + return RSRV_ERROR; /* not yet initialized, or disabled via dbServer */ + pClient = ( struct client * ) epicsThreadPrivateGet ( rsrvCurrentClient ); if ( ! pClient ) return RSRV_ERROR; diff --git a/modules/database/src/ioc/rsrv/caservertask.c b/modules/database/src/ioc/rsrv/caservertask.c index 7e3ad7673..a658c3796 100644 --- a/modules/database/src/ioc/rsrv/caservertask.c +++ b/modules/database/src/ioc/rsrv/caservertask.c @@ -590,8 +590,7 @@ void rsrv_init (void) errlogPrintf ( "cas " ERL_WARNING ": reachable with UDP unicast (a host's IP in EPICS_CA_ADDR_LIST)\n" ); } - epicsSnprintf(buf, sizeof(buf)-1u, "%u", ca_server_port); - buf[sizeof(buf)-1u] = '\0'; + epicsSnprintf(buf, sizeof(buf), "%u", ca_server_port); epicsEnvSet("RSRV_SERVER_PORT", buf); } @@ -1537,6 +1536,13 @@ struct client *create_tcp_client (SOCKET sock , const osiSockAddr *peerAddr) void casStatsFetch ( unsigned *pChanCount, unsigned *pCircuitCount ) { + if(!clientQlock) { /* not yet initialized, or disabled via dbServer */ + if(pChanCount) + *pChanCount = 0; + if(pCircuitCount) + *pCircuitCount = 0; + return; + } LOCK_CLIENTQ; { int circuitCount = ellCount ( &clientQ ); diff --git a/modules/database/src/ioc/rsrv/cast_server.c b/modules/database/src/ioc/rsrv/cast_server.c index a18c41015..e5a1cb402 100644 --- a/modules/database/src/ioc/rsrv/cast_server.c +++ b/modules/database/src/ioc/rsrv/cast_server.c @@ -98,13 +98,10 @@ static void clean_addrq(struct client *client) } epicsMutexUnlock ( client->chanListLock ); -# ifdef DEBUG - if(ndelete){ + if (CASDEBUG>1 && ndelete){ epicsPrintf ("CAS: %d CA channels have expired after %f sec\n", ndelete, maxdelay); } -# endif - } /* diff --git a/modules/database/src/std/ComponentReference.md b/modules/database/src/std/ComponentReference.md new file mode 100644 index 000000000..d58428c57 --- /dev/null +++ b/modules/database/src/std/ComponentReference.md @@ -0,0 +1,96 @@ +# IOC Component Reference + +This document provides reference information about the record types, +menus, link types and channel filters included with EPICS Base. + +## Introduction and IOC Concepts + +- [Introduction to EPICS](https://docs.epics-controls.org/en/latest/getting-started/EPICS_Intro.html) +- [Process Database Concepts](https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html) + +## Record Type Definitions + +These sections describe common aspects of the record types: + +```{toctree} +:titlesonly: + +dbCommonRecord +dbCommonInput +dbCommonOutput +``` + +These are the record types supplied with EPICS Base: + +```{toctree} +:titlesonly: + +aaiRecord +aaoRecord +aiRecord +aoRecord +aSubRecord +biRecord +boRecord +calcoutRecord +calcRecord +compressRecord +dfanoutRecord +eventRecord +fanoutRecord +histogramRecord +int64inRecord +int64outRecord +longinRecord +longoutRecord +lsiRecord +lsoRecord +mbbiDirectRecord +mbbiRecord +mbboDirectRecord +mbboRecord +permissiveRecord +printfRecord +selRecord +seqRecord +stateRecord +stringinRecord +stringoutRecord +subArrayRecord +subRecord +waveformRecord +``` + +## Menu Definitions + +Menu field choices are documented with the record type that defines them, or +here for the global menus that are used by multiple record types: + +```{toctree} +:titlesonly: + +menuAlarmSevr +menuAlarmStat +menuConvert +menuFtype +menuIvoa +menuOmsl +menuPini +menuPost +menuPriority +menuScan +menuSimm +menuYesNo +``` + +## Other Components + +EPICS Base also comes with extensible sets of server Channel Filters and IOC +Database Link types, which are documented here: + +```{toctree} +:titlesonly: + +filters +links +``` diff --git a/documentation/ComponentReference.pod b/modules/database/src/std/ComponentReference.pod similarity index 95% rename from documentation/ComponentReference.pod rename to modules/database/src/std/ComponentReference.pod index 95169598b..c43394d50 100644 --- a/documentation/ComponentReference.pod +++ b/modules/database/src/std/ComponentReference.pod @@ -163,8 +163,8 @@ Database Link types, which are documented here: Corrections to these documents can be submitted as patch files to the EPICS core developers, or as GitHub pull requests to the 7.0 branch of Base. -These document sources can be found under `modules/database/src` tree, mostly -in the `std/rec` and `ioc/db` directories in files with extension `.dbd.pod`. +These document sources can be found under C tree, mostly +in the C and C directories in files with extension C<.dbd.pod>. The documentation source format is a combination of the EPICS DBD file format -with an extended version of Perl's POD (plain old documentation); run `perldoc -pod` for details of POD. +with an extended version of Perl's POD (plain old documentation); run +C for details of POD. diff --git a/modules/database/src/std/Makefile b/modules/database/src/std/Makefile index 6ee4f1d77..f7acfb936 100644 --- a/modules/database/src/std/Makefile +++ b/modules/database/src/std/Makefile @@ -30,6 +30,9 @@ include $(STDDIR)/filters/Makefile include $(STDDIR)/link/Makefile include $(STDDIR)/softIoc/Makefile +DOCS += ComponentReference.md +HTMLS += ComponentReference.html + include $(TOP)/configure/RULES include $(STDDIR)/rec/RULES diff --git a/modules/database/src/std/filters/Makefile b/modules/database/src/std/filters/Makefile index b02999403..5be6ca13d 100644 --- a/modules/database/src/std/filters/Makefile +++ b/modules/database/src/std/filters/Makefile @@ -18,5 +18,5 @@ dbRecStd_SRCS += sync.c dbRecStd_SRCS += decimate.c dbRecStd_SRCS += utag.c +DOCS += filters.md HTMLS += filters.html - diff --git a/modules/database/src/std/link/Makefile b/modules/database/src/std/link/Makefile index 9b6abd705..cf6b4e7f2 100644 --- a/modules/database/src/std/link/Makefile +++ b/modules/database/src/std/link/Makefile @@ -16,4 +16,5 @@ dbRecStd_SRCS += lnkCalc.c dbRecStd_SRCS += lnkState.c dbRecStd_SRCS += lnkDebug.c +DOCS += links.md HTMLS += links.html diff --git a/modules/database/src/std/link/lnkCalc.c b/modules/database/src/std/link/lnkCalc.c index 46655850e..ed5796f8a 100644 --- a/modules/database/src/std/link/lnkCalc.c +++ b/modules/database/src/std/link/lnkCalc.c @@ -36,8 +36,6 @@ #include "epicsExport.h" -typedef long (*FASTCONVERT)(); - typedef struct calc_link { jlink jlink; /* embedded object */ int nArgs; @@ -558,7 +556,7 @@ static long lnkCalc_getValue(struct link *plink, short dbrType, void *pbuffer, dbCommon *prec = plink->precord; int i; long status; - FASTCONVERT conv; + FASTCONVERTFUNC conv; if(INVALID_DB_REQ(dbrType)) return S_db_badDbrtype; @@ -638,7 +636,7 @@ static long lnkCalc_putValue(struct link *plink, short dbrType, dbCommon *prec = plink->precord; int i; long status; - FASTCONVERT conv; + FASTCONVERTFUNC conv; if(INVALID_DB_REQ(dbrType)) return S_db_badDbrtype; diff --git a/modules/database/src/std/link/lnkConst.c b/modules/database/src/std/link/lnkConst.c index 4e15fa803..24158ef12 100644 --- a/modules/database/src/std/link/lnkConst.c +++ b/modules/database/src/std/link/lnkConst.c @@ -23,8 +23,6 @@ #include "epicsExport.h" -typedef long (*FASTCONVERT)(); - typedef struct const_link { jlink jlink; /* embedded object */ int nElems; @@ -458,7 +456,7 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer, short dbrSize; char *pdest = pbuffer; int nElems = clink->nElems; - FASTCONVERT conv; + FASTCONVERTFUNC conv; long status; if(INVALID_DB_REQ(dbrType)) @@ -495,9 +493,11 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer, } else { /* Long string conversion */ - strncpy(pbuffer, clink->value.scalar_string, *pnReq); - ((char *)pbuffer)[*pnReq] = 0; - nElems = strlen(pbuffer) + 1; + if (*pnReq > 0) { + strncpy(pbuffer, clink->value.scalar_string, *pnReq); + ((char *)pbuffer)[*pnReq - 1] = 0; + nElems = strlen(pbuffer) + 1; + } status = 0; } break; diff --git a/modules/database/src/std/link/lnkState.c b/modules/database/src/std/link/lnkState.c index 588214a43..931e2d49f 100644 --- a/modules/database/src/std/link/lnkState.c +++ b/modules/database/src/std/link/lnkState.c @@ -34,8 +34,6 @@ #include "epicsExport.h" -typedef long (*FASTCONVERT)(); - typedef struct state_link { jlink jlink; /* embedded object */ char *name; @@ -143,7 +141,7 @@ static long lnkState_getValue(struct link *plink, short dbrType, void *pbuffer, { state_link *slink = CONTAINER(plink->value.json.jlink, struct state_link, jlink); - FASTCONVERT conv; + FASTCONVERTFUNC conv; if(INVALID_DB_REQ(dbrType)) return S_db_badDbrtype; diff --git a/modules/database/src/std/rec/Makefile b/modules/database/src/std/rec/Makefile index 87021987e..4754f3914 100644 --- a/modules/database/src/std/rec/Makefile +++ b/modules/database/src/std/rec/Makefile @@ -54,8 +54,13 @@ stdRecords_DBD = $(patsubst %,%.dbd,$(stdRecords)) dbRecStd_SRCS += $(patsubst %,%.c,$(stdRecords)) +DOCS += $(patsubst %.dbd.pod,%.md,$(notdir $(wildcard ../rec/*Record.dbd.pod))) HTMLS += $(patsubst %.dbd.pod,%.html,$(notdir $(wildcard ../rec/*Record.dbd.pod))) vpath %.png $(SRC_DIRS) + +DOCS += image/compress-1.png +DOCS += image/compress-2.png + HTMLS += image/compress-1.png HTMLS += image/compress-2.png diff --git a/modules/database/src/std/rec/aSubRecord.dbd.pod b/modules/database/src/std/rec/aSubRecord.dbd.pod index 5eeb30d54..8c97117d7 100644 --- a/modules/database/src/std/rec/aSubRecord.dbd.pod +++ b/modules/database/src/std/rec/aSubRecord.dbd.pod @@ -16,7 +16,7 @@ has a number of additional features: =item * -It provides 20 different input and output fields which can hold array or +It provides 21 different input and output fields which can hold array or scalar values. The types and array capacities of these are user configurable, and they all have an associated input or output link. diff --git a/modules/database/src/std/rec/aiRecord.dbd.pod b/modules/database/src/std/rec/aiRecord.dbd.pod index 4515368b2..83a7c5c26 100644 --- a/modules/database/src/std/rec/aiRecord.dbd.pod +++ b/modules/database/src/std/rec/aiRecord.dbd.pod @@ -33,7 +33,7 @@ These fields control where the record will read data from when it is processed: The DTYP field selects which device support layer should be responsible for providing input data to the record. The ai device support layers provided by EPICS Base are documented in the -L section. +L section. External support modules may provide additional device support for this record type. If not set explicitly, the DTYP value defaults to the first device support that diff --git a/modules/database/src/std/rec/compressRecord.c b/modules/database/src/std/rec/compressRecord.c index 22e804915..844172bf8 100644 --- a/modules/database/src/std/rec/compressRecord.c +++ b/modules/database/src/std/rec/compressRecord.c @@ -384,6 +384,7 @@ static long special(DBADDR *paddr, int after) if (special_type == SPC_RESET) { reset(prec); + monitor(prec); return 0; } diff --git a/modules/database/src/std/rec/mbbiRecord.c b/modules/database/src/std/rec/mbbiRecord.c index 8bb14c647..018c861c1 100644 --- a/modules/database/src/std/rec/mbbiRecord.c +++ b/modules/database/src/std/rec/mbbiRecord.c @@ -220,12 +220,9 @@ static long special(DBADDR *paddr, int after) return 0; init_common(prec); /* Note: ZRVL..FFVL are also SPC_MOD */ - if (fieldIndex >= mbbiRecordZRST && fieldIndex <= mbbiRecordFFST) { - int event = DBE_PROPERTY; - - if (prec->val == fieldIndex - mbbiRecordZRST) - event |= DBE_VALUE | DBE_LOG; - db_post_events(prec, &prec->val, event); + if (fieldIndex >= mbbiRecordZRST && fieldIndex <= mbbiRecordFFST + && prec->val == fieldIndex - mbbiRecordZRST) { + db_post_events(prec, &prec->val, DBE_VALUE | DBE_LOG); } return 0; diff --git a/modules/database/src/std/rec/mbbiRecord.dbd.pod b/modules/database/src/std/rec/mbbiRecord.dbd.pod index b3c7628f3..56b0d42f2 100644 --- a/modules/database/src/std/rec/mbbiRecord.dbd.pod +++ b/modules/database/src/std/rec/mbbiRecord.dbd.pod @@ -276,6 +276,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(ONST,DBF_STRING) { prompt("One String") @@ -284,6 +285,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(TWST,DBF_STRING) { prompt("Two String") @@ -292,6 +294,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(THST,DBF_STRING) { prompt("Three String") @@ -300,6 +303,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(FRST,DBF_STRING) { prompt("Four String") @@ -308,6 +312,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(FVST,DBF_STRING) { prompt("Five String") @@ -316,6 +321,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(SXST,DBF_STRING) { prompt("Six String") @@ -324,6 +330,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(SVST,DBF_STRING) { prompt("Seven String") @@ -332,6 +339,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(EIST,DBF_STRING) { prompt("Eight String") @@ -340,6 +348,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(NIST,DBF_STRING) { prompt("Nine String") @@ -348,6 +357,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(TEST,DBF_STRING) { prompt("Ten String") @@ -356,6 +366,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(ELST,DBF_STRING) { prompt("Eleven String") @@ -364,6 +375,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(TVST,DBF_STRING) { prompt("Twelve String") @@ -372,6 +384,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(TTST,DBF_STRING) { prompt("Thirteen String") @@ -380,6 +393,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(FTST,DBF_STRING) { prompt("Fourteen String") @@ -388,6 +402,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(FFST,DBF_STRING) { prompt("Fifteen String") @@ -396,6 +411,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) + prop(YES) } =head3 Alarm Parameters diff --git a/modules/database/src/std/rec/mbboRecord.c b/modules/database/src/std/rec/mbboRecord.c index 1de5d62bd..5283d8df1 100644 --- a/modules/database/src/std/rec/mbboRecord.c +++ b/modules/database/src/std/rec/mbboRecord.c @@ -285,12 +285,9 @@ static long special(DBADDR *paddr, int after) return 0; init_common(prec); /* Note: ZRVL..FFVL are also SPC_MOD */ - if (fieldIndex >= mbboRecordZRST && fieldIndex <= mbboRecordFFST) { - int event = DBE_PROPERTY; - - if (prec->val == fieldIndex - mbboRecordZRST) - event |= DBE_VALUE | DBE_LOG; - db_post_events(prec, &prec->val, event); + if (fieldIndex >= mbboRecordZRST && fieldIndex <= mbboRecordFFST + && prec->val == fieldIndex - mbboRecordZRST) { + db_post_events(prec, &prec->val, DBE_VALUE | DBE_LOG); } return 0; diff --git a/modules/database/src/std/rec/mbboRecord.dbd.pod b/modules/database/src/std/rec/mbboRecord.dbd.pod index 29a267f08..3c3652a39 100644 --- a/modules/database/src/std/rec/mbboRecord.dbd.pod +++ b/modules/database/src/std/rec/mbboRecord.dbd.pod @@ -354,6 +354,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(ONST,DBF_STRING) { prompt("One String") @@ -362,6 +363,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(TWST,DBF_STRING) { prompt("Two String") @@ -370,6 +372,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(THST,DBF_STRING) { prompt("Three String") @@ -378,6 +381,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(FRST,DBF_STRING) { prompt("Four String") @@ -386,6 +390,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(FVST,DBF_STRING) { prompt("Five String") @@ -394,6 +399,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(SXST,DBF_STRING) { prompt("Six String") @@ -402,6 +408,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(SVST,DBF_STRING) { prompt("Seven String") @@ -410,6 +417,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(EIST,DBF_STRING) { prompt("Eight String") @@ -418,6 +426,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(NIST,DBF_STRING) { prompt("Nine String") @@ -426,6 +435,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(TEST,DBF_STRING) { prompt("Ten String") @@ -434,6 +444,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(ELST,DBF_STRING) { prompt("Eleven String") @@ -442,6 +453,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(TVST,DBF_STRING) { prompt("Twelve String") @@ -450,6 +462,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(TTST,DBF_STRING) { prompt("Thirteen String") @@ -458,6 +471,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(FTST,DBF_STRING) { prompt("Fourteen String") @@ -466,6 +480,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(FFST,DBF_STRING) { prompt("Fifteen String") @@ -474,6 +489,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) + prop(YES) } field(ZRSV,DBF_MENU) { prompt("State Zero Severity") diff --git a/modules/database/src/std/rec/seqRecord.c b/modules/database/src/std/rec/seqRecord.c index cd6212da1..44d8599c4 100644 --- a/modules/database/src/std/rec/seqRecord.c +++ b/modules/database/src/std/rec/seqRecord.c @@ -282,10 +282,10 @@ static void processCallback(epicsCallback *arg) static long get_units(DBADDR *paddr, char *units) { seqRecord *prec = (seqRecord *) paddr->precord; - int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1); + int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY0); if (fieldOffset >= 0) - switch (fieldOffset & 2) { + switch (fieldOffset & 3) { case 0: /* DLYn */ strcpy(units, "s"); break; @@ -299,11 +299,11 @@ static long get_units(DBADDR *paddr, char *units) static long get_precision(const DBADDR *paddr, long *pprecision) { seqRecord *prec = (seqRecord *) paddr->precord; - int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1); + int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY0); short precision; if (fieldOffset >= 0) - switch (fieldOffset & 2) { + switch (fieldOffset & 3) { case 0: /* DLYn */ *pprecision = seqDLYprecision; return 0; @@ -321,10 +321,10 @@ static long get_precision(const DBADDR *paddr, long *pprecision) static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) { seqRecord *prec = (seqRecord *) paddr->precord; - int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1); + int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY0); if (fieldOffset >= 0) - switch (fieldOffset & 2) { + switch (fieldOffset & 3) { case 0: /* DLYn */ pgd->lower_disp_limit = 0.0; pgd->lower_disp_limit = 10.0; @@ -341,9 +341,9 @@ static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd) { - int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1); + int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY0); - if (fieldOffset >= 0 && (fieldOffset & 2) == 0) { /* DLYn */ + if (fieldOffset >= 0 && (fieldOffset & 3) == 0) { /* DLYn */ pcd->lower_ctrl_limit = 0.0; pcd->upper_ctrl_limit = seqDLYlimit; } @@ -355,9 +355,9 @@ static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd) static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad) { seqRecord *prec = (seqRecord *) paddr->precord; - int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1); + int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY0); - if (fieldOffset >= 0 && (fieldOffset & 2) == 2) /* DOn */ + if (fieldOffset >= 0 && (fieldOffset & 3) == 2) /* DOn */ dbGetAlarmLimits(get_dol(prec, fieldOffset), &pad->lower_alarm_limit, &pad->lower_warning_limit, &pad->upper_warning_limit, &pad->upper_alarm_limit); diff --git a/modules/database/src/std/softIoc/softMain.cpp b/modules/database/src/std/softIoc/softMain.cpp index 11eb76120..4c1b49a2b 100644 --- a/modules/database/src/std/softIoc/softMain.cpp +++ b/modules/database/src/std/softIoc/softMain.cpp @@ -17,10 +17,12 @@ #include #include "registryFunction.h" +#include "errlog.h" #include "epicsThread.h" #include "epicsExit.h" #include "epicsStdio.h" #include "epicsString.h" +#include "errlog.h" #include "dbStaticLib.h" #include "subRecord.h" #include "dbAccess.h" @@ -92,7 +94,7 @@ void usage(const char *arg0, const std::string& base_dbd) { "interactive IOC shell.\n" "\n" "Compiled-in path to softIoc.dbd is:\n" - "\t"< diff --git a/modules/database/src/template/top/exampleApp/src/_APPNAME_Hello.dbd b/modules/database/src/template/top/exampleApp/src/_APPNAME_Hello.dbd index 64eb0389a..8a294e248 100644 --- a/modules/database/src/template/top/exampleApp/src/_APPNAME_Hello.dbd +++ b/modules/database/src/template/top/exampleApp/src/_APPNAME_Hello.dbd @@ -1 +1,5 @@ +# SPDX-FileCopyrightText: 2005 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + registrar(helloRegister) diff --git a/modules/database/src/template/top/exampleApp/src/_APPNAME_Main.cpp b/modules/database/src/template/top/exampleApp/src/_APPNAME_Main.cpp index fe4f105ed..d0b8c5725 100644 --- a/modules/database/src/template/top/exampleApp/src/_APPNAME_Main.cpp +++ b/modules/database/src/template/top/exampleApp/src/_APPNAME_Main.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2000 Argonne National Laboratory +// +// SPDX-License-Identifier: EPICS + /* _APPNAME_Main.cpp */ /* Author: Marty Kraimer Date: 17MAR2000 */ diff --git a/modules/database/src/template/top/exampleApp/src/dbSubExample.c b/modules/database/src/template/top/exampleApp/src/dbSubExample.c index 1cc748b12..8d0e454f9 100644 --- a/modules/database/src/template/top/exampleApp/src/dbSubExample.c +++ b/modules/database/src/template/top/exampleApp/src/dbSubExample.c @@ -1,3 +1,7 @@ +/* SPDX-FileCopyrightText: 2002 Argonne National Laboratory */ + +/* SPDX-License-Identifier: EPICS */ + #include #include diff --git a/modules/database/src/template/top/exampleApp/src/dbSubExample.dbd b/modules/database/src/template/top/exampleApp/src/dbSubExample.dbd index 5f6e40ac7..88ae19931 100644 --- a/modules/database/src/template/top/exampleApp/src/dbSubExample.dbd +++ b/modules/database/src/template/top/exampleApp/src/dbSubExample.dbd @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2004 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + variable(mySubDebug) function(mySubInit) function(mySubProcess) diff --git a/modules/database/src/template/top/exampleApp/src/devXxxSoft.c b/modules/database/src/template/top/exampleApp/src/devXxxSoft.c index add7d5202..b0348213e 100644 --- a/modules/database/src/template/top/exampleApp/src/devXxxSoft.c +++ b/modules/database/src/template/top/exampleApp/src/devXxxSoft.c @@ -1,3 +1,7 @@ +/* SPDX-FileCopyrightText: 1998 Argonne National Laboratory */ + +/* SPDX-License-Identifier: EPICS */ + /* devXxxSoft.c */ /* Example device support module */ diff --git a/modules/database/src/template/top/exampleApp/src/dev_APPNAME_Version.c b/modules/database/src/template/top/exampleApp/src/dev_APPNAME_Version.c index 9d482af25..b16c3c1d4 100644 --- a/modules/database/src/template/top/exampleApp/src/dev_APPNAME_Version.c +++ b/modules/database/src/template/top/exampleApp/src/dev_APPNAME_Version.c @@ -1,3 +1,7 @@ +/* SPDX-FileCopyrightText: 2015 Argonne National Laboratory */ + +/* SPDX-License-Identifier: EPICS */ + /* dev_APPNAME_Version.c */ /* Example device support for the lsi (long string input) record * providing the module version string as the value diff --git a/modules/database/src/template/top/exampleApp/src/dev_APPNAME_Version.dbd b/modules/database/src/template/top/exampleApp/src/dev_APPNAME_Version.dbd index 67295f3f0..ec978d768 100644 --- a/modules/database/src/template/top/exampleApp/src/dev_APPNAME_Version.dbd +++ b/modules/database/src/template/top/exampleApp/src/dev_APPNAME_Version.dbd @@ -1 +1,5 @@ +# SPDX-FileCopyrightText: 2015 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + device(lsi,INST_IO,dev_CSAFEAPPNAME_Version,"_APPNAME_ version") diff --git a/modules/database/src/template/top/exampleApp/src/initTrace.c b/modules/database/src/template/top/exampleApp/src/initTrace.c index 64bc9cff7..c9f371377 100644 --- a/modules/database/src/template/top/exampleApp/src/initTrace.c +++ b/modules/database/src/template/top/exampleApp/src/initTrace.c @@ -1,3 +1,7 @@ +/* SPDX-FileCopyrightText: 2008 Argonne National Laboratory */ + +/* SPDX-License-Identifier: EPICS */ + /* initTrace.c */ /* diff --git a/modules/database/src/template/top/exampleApp/src/initTrace.dbd b/modules/database/src/template/top/exampleApp/src/initTrace.dbd index 8083c0a50..682d92c19 100644 --- a/modules/database/src/template/top/exampleApp/src/initTrace.dbd +++ b/modules/database/src/template/top/exampleApp/src/initTrace.dbd @@ -1 +1,5 @@ +# SPDX-FileCopyrightText: 2008 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + registrar(initTraceRegister) diff --git a/modules/database/src/template/top/exampleApp/src/sncExample.dbd b/modules/database/src/template/top/exampleApp/src/sncExample.dbd index 80722a807..69f61a440 100644 --- a/modules/database/src/template/top/exampleApp/src/sncExample.dbd +++ b/modules/database/src/template/top/exampleApp/src/sncExample.dbd @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2004 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # The name below is derived from the name of the SNL program # inside the source file, not from its filename. Here the # program is called sncExample, but is compiled in both the diff --git a/modules/database/src/template/top/exampleApp/src/sncExample.stt b/modules/database/src/template/top/exampleApp/src/sncExample.stt index 5237618db..6ecb83ce7 100644 --- a/modules/database/src/template/top/exampleApp/src/sncExample.stt +++ b/modules/database/src/template/top/exampleApp/src/sncExample.stt @@ -20,3 +20,9 @@ ss ss1 { } state low } } + +/* +SPDX-FileCopyrightText: 1998 Argonne National Laboratory + +SPDX-License-Identifier: EPICS +*/ diff --git a/modules/database/src/template/top/exampleApp/src/sncProgram.st b/modules/database/src/template/top/exampleApp/src/sncProgram.st index 1ba29893e..393d8a6b8 100644 --- a/modules/database/src/template/top/exampleApp/src/sncProgram.st +++ b/modules/database/src/template/top/exampleApp/src/sncProgram.st @@ -1 +1,7 @@ #include "../sncExample.stt" + +/* +SPDX-FileCopyrightText: 2004 Argonne National Laboratory + +SPDX-License-Identifier: EPICS +*/ diff --git a/modules/database/src/template/top/exampleApp/src/xxxRecord.c b/modules/database/src/template/top/exampleApp/src/xxxRecord.c index 418ed2ba9..bf8c79982 100644 --- a/modules/database/src/template/top/exampleApp/src/xxxRecord.c +++ b/modules/database/src/template/top/exampleApp/src/xxxRecord.c @@ -1,3 +1,7 @@ +/* SPDX-FileCopyrightText: 1998 Argonne National Laboratory */ + +/* SPDX-License-Identifier: EPICS */ + /* xxxRecord.c */ /* Example record support module */ diff --git a/modules/database/src/template/top/exampleApp/src/xxxRecord.dbd b/modules/database/src/template/top/exampleApp/src/xxxRecord.dbd index 12c1d6202..af9e01050 100644 --- a/modules/database/src/template/top/exampleApp/src/xxxRecord.dbd +++ b/modules/database/src/template/top/exampleApp/src/xxxRecord.dbd @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 1998 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + recordtype(xxx) { include "dbCommon.dbd" field(VAL,DBF_DOUBLE) { diff --git a/modules/database/src/template/top/exampleApp/src/xxxSupport.dbd b/modules/database/src/template/top/exampleApp/src/xxxSupport.dbd index 64f2e3842..dea0eebbf 100644 --- a/modules/database/src/template/top/exampleApp/src/xxxSupport.dbd +++ b/modules/database/src/template/top/exampleApp/src/xxxSupport.dbd @@ -1,2 +1,6 @@ +# SPDX-FileCopyrightText: 2003 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + include "xxxRecord.dbd" device(xxx,CONSTANT,devXxxSoft,"Soft Channel") diff --git a/modules/database/src/template/top/exampleBoot/Makefile b/modules/database/src/template/top/exampleBoot/Makefile index 91e47d0b5..e7ec38ae6 100644 --- a/modules/database/src/template/top/exampleBoot/Makefile +++ b/modules/database/src/template/top/exampleBoot/Makefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 1997 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP = .. include $(TOP)/configure/CONFIG DIRS += $(wildcard *ioc*) diff --git a/modules/database/src/template/top/exampleBoot/ioc/Makefile@Common b/modules/database/src/template/top/exampleBoot/ioc/Makefile@Common index e1b9aa4ab..7734a4df3 100644 --- a/modules/database/src/template/top/exampleBoot/ioc/Makefile@Common +++ b/modules/database/src/template/top/exampleBoot/ioc/Makefile@Common @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 1998 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP = ../.. include $(TOP)/configure/CONFIG ARCH = $(EPICS_HOST_ARCH) diff --git a/modules/database/src/template/top/exampleBoot/ioc/Makefile@cygwin b/modules/database/src/template/top/exampleBoot/ioc/Makefile@cygwin index 77c3215d7..5853a35da 100644 --- a/modules/database/src/template/top/exampleBoot/ioc/Makefile@cygwin +++ b/modules/database/src/template/top/exampleBoot/ioc/Makefile@cygwin @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2014 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP = ../.. include $(TOP)/configure/CONFIG ARCH = _ARCH_ diff --git a/modules/database/src/template/top/exampleBoot/ioc/Makefile@vxWorks b/modules/database/src/template/top/exampleBoot/ioc/Makefile@vxWorks index 12ff7f494..8861917d6 100644 --- a/modules/database/src/template/top/exampleBoot/ioc/Makefile@vxWorks +++ b/modules/database/src/template/top/exampleBoot/ioc/Makefile@vxWorks @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 1998 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP = ../.. include $(TOP)/configure/CONFIG ARCH = _ARCH_ diff --git a/modules/database/src/template/top/exampleBoot/ioc/Makefile@win32 b/modules/database/src/template/top/exampleBoot/ioc/Makefile@win32 index 59b32d734..0db9a2739 100644 --- a/modules/database/src/template/top/exampleBoot/ioc/Makefile@win32 +++ b/modules/database/src/template/top/exampleBoot/ioc/Makefile@win32 @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2008 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP = ../.. include $(TOP)/configure/CONFIG ARCH = _ARCH_ diff --git a/modules/database/src/template/top/exampleBoot/ioc/Makefile@windows b/modules/database/src/template/top/exampleBoot/ioc/Makefile@windows index 59b32d734..0f4cbe813 100644 --- a/modules/database/src/template/top/exampleBoot/ioc/Makefile@windows +++ b/modules/database/src/template/top/exampleBoot/ioc/Makefile@windows @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2014 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP = ../.. include $(TOP)/configure/CONFIG ARCH = _ARCH_ diff --git a/modules/database/src/template/top/exampleBoot/ioc/README@Common b/modules/database/src/template/top/exampleBoot/ioc/README@Common index 572570df5..62ce09c2d 100644 --- a/modules/database/src/template/top/exampleBoot/ioc/README@Common +++ b/modules/database/src/template/top/exampleBoot/ioc/README@Common @@ -7,3 +7,7 @@ and check the executable name on the first line of the st.cmd file You may need to change the name of the .dbd file given in the st.cmd's dbLoadDatabase() command before starting the ioc. + + +SPDX-FileCopyrightText: 2001 Argonne National Laboratory +SPDX-License-Identifier: EPICS diff --git a/modules/database/src/template/top/exampleBoot/ioc/README@RTEMS b/modules/database/src/template/top/exampleBoot/ioc/README@RTEMS index b040e848c..0f8f25eb3 100644 --- a/modules/database/src/template/top/exampleBoot/ioc/README@RTEMS +++ b/modules/database/src/template/top/exampleBoot/ioc/README@RTEMS @@ -4,3 +4,7 @@ contents to Then load the executable into the IOC (floppy disk, network boot, debugger, etc.) and start it. + + +SPDX-FileCopyrightText: 2001 Argonne National Laboratory +SPDX-License-Identifier: EPICS diff --git a/modules/database/src/template/top/exampleBoot/ioc/st.cmd@Common b/modules/database/src/template/top/exampleBoot/ioc/st.cmd@Common index 81753860d..54a6084ea 100644 --- a/modules/database/src/template/top/exampleBoot/ioc/st.cmd@Common +++ b/modules/database/src/template/top/exampleBoot/ioc/st.cmd@Common @@ -1,5 +1,9 @@ #!../../bin/_ARCH_/_APPNAME_ +#- SPDX-FileCopyrightText: 2000 Argonne National Laboratory +#- +#- SPDX-License-Identifier: EPICS + #- You may have to change _APPNAME_ to something else #- everywhere it appears in this file diff --git a/modules/database/src/template/top/exampleBoot/ioc/st.cmd@RTEMS b/modules/database/src/template/top/exampleBoot/ioc/st.cmd@RTEMS index 54f2a74e7..c1e964e7e 100644 --- a/modules/database/src/template/top/exampleBoot/ioc/st.cmd@RTEMS +++ b/modules/database/src/template/top/exampleBoot/ioc/st.cmd@RTEMS @@ -1,3 +1,7 @@ +#- SPDX-FileCopyrightText: 2001 Argonne National Laboratory +#- +#- SPDX-License-Identifier: EPICS + #- Example RTEMS startup script #- You may have to change _APPNAME_ to something else diff --git a/modules/database/src/template/top/exampleBoot/ioc/st.cmd@vxWorks b/modules/database/src/template/top/exampleBoot/ioc/st.cmd@vxWorks index 1f390a862..33a06b5ee 100644 --- a/modules/database/src/template/top/exampleBoot/ioc/st.cmd@vxWorks +++ b/modules/database/src/template/top/exampleBoot/ioc/st.cmd@vxWorks @@ -1,3 +1,7 @@ +#- SPDX-FileCopyrightText: 1998 Argonne National Laboratory +#- +#- SPDX-License-Identifier: EPICS + #- Example vxWorks startup file #- The following is needed if your board support package doesn't at boot time diff --git a/modules/database/src/template/top/exampleBoot/nfsCommands@RTEMS b/modules/database/src/template/top/exampleBoot/nfsCommands@RTEMS index 18ae461fa..a3040d384 100644 --- a/modules/database/src/template/top/exampleBoot/nfsCommands@RTEMS +++ b/modules/database/src/template/top/exampleBoot/nfsCommands@RTEMS @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2005 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + #- Instructions for creating and using a real nfsCommands file #- #- in order to use nfs do the following: diff --git a/modules/database/src/template/top/exampleBoot/nfsCommands@vxWorks b/modules/database/src/template/top/exampleBoot/nfsCommands@vxWorks index eb302c569..a775f2b31 100644 --- a/modules/database/src/template/top/exampleBoot/nfsCommands@vxWorks +++ b/modules/database/src/template/top/exampleBoot/nfsCommands@vxWorks @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 1997 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + #- Instructions for creating and using a real nfsCommands file #- #- in order to use nfs do the following: diff --git a/modules/database/src/template/top/iocApp/Db/Makefile b/modules/database/src/template/top/iocApp/Db/Makefile index 8eb97279d..495627f58 100644 --- a/modules/database/src/template/top/iocApp/Db/Makefile +++ b/modules/database/src/template/top/iocApp/Db/Makefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 1998 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP=../.. include $(TOP)/configure/CONFIG #---------------------------------------- diff --git a/modules/database/src/template/top/iocApp/Makefile b/modules/database/src/template/top/iocApp/Makefile index 60ab8ae88..8315101cf 100644 --- a/modules/database/src/template/top/iocApp/Makefile +++ b/modules/database/src/template/top/iocApp/Makefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 1997 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # Makefile at top of application tree TOP = .. include $(TOP)/configure/CONFIG diff --git a/modules/database/src/template/top/iocApp/src/Makefile b/modules/database/src/template/top/iocApp/src/Makefile index de6b93a5b..535de549b 100644 --- a/modules/database/src/template/top/iocApp/src/Makefile +++ b/modules/database/src/template/top/iocApp/src/Makefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2003 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP=../.. include $(TOP)/configure/CONFIG diff --git a/modules/database/src/template/top/iocApp/src/_APPNAME_Main.cpp b/modules/database/src/template/top/iocApp/src/_APPNAME_Main.cpp index fe4f105ed..d0b8c5725 100644 --- a/modules/database/src/template/top/iocApp/src/_APPNAME_Main.cpp +++ b/modules/database/src/template/top/iocApp/src/_APPNAME_Main.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2000 Argonne National Laboratory +// +// SPDX-License-Identifier: EPICS + /* _APPNAME_Main.cpp */ /* Author: Marty Kraimer Date: 17MAR2000 */ diff --git a/modules/database/src/template/top/iocBoot/Makefile b/modules/database/src/template/top/iocBoot/Makefile index 91e47d0b5..e7ec38ae6 100644 --- a/modules/database/src/template/top/iocBoot/Makefile +++ b/modules/database/src/template/top/iocBoot/Makefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 1997 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP = .. include $(TOP)/configure/CONFIG DIRS += $(wildcard *ioc*) diff --git a/modules/database/src/template/top/iocBoot/ioc/Makefile@Common b/modules/database/src/template/top/iocBoot/ioc/Makefile@Common index e1b9aa4ab..7734a4df3 100644 --- a/modules/database/src/template/top/iocBoot/ioc/Makefile@Common +++ b/modules/database/src/template/top/iocBoot/ioc/Makefile@Common @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 1998 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP = ../.. include $(TOP)/configure/CONFIG ARCH = $(EPICS_HOST_ARCH) diff --git a/modules/database/src/template/top/iocBoot/ioc/Makefile@cygwin b/modules/database/src/template/top/iocBoot/ioc/Makefile@cygwin index 77c3215d7..5853a35da 100644 --- a/modules/database/src/template/top/iocBoot/ioc/Makefile@cygwin +++ b/modules/database/src/template/top/iocBoot/ioc/Makefile@cygwin @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2014 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP = ../.. include $(TOP)/configure/CONFIG ARCH = _ARCH_ diff --git a/modules/database/src/template/top/iocBoot/ioc/Makefile@vxWorks b/modules/database/src/template/top/iocBoot/ioc/Makefile@vxWorks index 12ff7f494..8861917d6 100644 --- a/modules/database/src/template/top/iocBoot/ioc/Makefile@vxWorks +++ b/modules/database/src/template/top/iocBoot/ioc/Makefile@vxWorks @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 1998 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP = ../.. include $(TOP)/configure/CONFIG ARCH = _ARCH_ diff --git a/modules/database/src/template/top/iocBoot/ioc/Makefile@win32 b/modules/database/src/template/top/iocBoot/ioc/Makefile@win32 index 59b32d734..0db9a2739 100644 --- a/modules/database/src/template/top/iocBoot/ioc/Makefile@win32 +++ b/modules/database/src/template/top/iocBoot/ioc/Makefile@win32 @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2008 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP = ../.. include $(TOP)/configure/CONFIG ARCH = _ARCH_ diff --git a/modules/database/src/template/top/iocBoot/ioc/Makefile@windows b/modules/database/src/template/top/iocBoot/ioc/Makefile@windows index 59b32d734..0f4cbe813 100644 --- a/modules/database/src/template/top/iocBoot/ioc/Makefile@windows +++ b/modules/database/src/template/top/iocBoot/ioc/Makefile@windows @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2014 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP = ../.. include $(TOP)/configure/CONFIG ARCH = _ARCH_ diff --git a/modules/database/src/template/top/iocBoot/ioc/st.cmd@Common b/modules/database/src/template/top/iocBoot/ioc/st.cmd@Common index 695b05a14..d26eec8ad 100644 --- a/modules/database/src/template/top/iocBoot/ioc/st.cmd@Common +++ b/modules/database/src/template/top/iocBoot/ioc/st.cmd@Common @@ -1,5 +1,9 @@ #!../../bin/_ARCH_/_APPNAME_ +#- SPDX-FileCopyrightText: 2003 Argonne National Laboratory +#- +#- SPDX-License-Identifier: EPICS + #- You may have to change _APPNAME_ to something else #- everywhere it appears in this file diff --git a/modules/database/src/template/top/iocBoot/ioc/st.cmd@Cross b/modules/database/src/template/top/iocBoot/ioc/st.cmd@Cross index 8303f99f5..996a7bfe6 100644 --- a/modules/database/src/template/top/iocBoot/ioc/st.cmd@Cross +++ b/modules/database/src/template/top/iocBoot/ioc/st.cmd@Cross @@ -1,5 +1,9 @@ #!../../bin/_ARCH_/_APPNAME_ +#- SPDX-FileCopyrightText: 2005 Argonne National Laboratory +#- +#- SPDX-License-Identifier: EPICS + #- You may have to change _APPNAME_ to something else #- everywhere it appears in this file diff --git a/modules/database/src/template/top/iocBoot/ioc/st.cmd@RTEMS b/modules/database/src/template/top/iocBoot/ioc/st.cmd@RTEMS index a7d08fbcf..087dead65 100644 --- a/modules/database/src/template/top/iocBoot/ioc/st.cmd@RTEMS +++ b/modules/database/src/template/top/iocBoot/ioc/st.cmd@RTEMS @@ -1,3 +1,7 @@ +#- SPDX-FileCopyrightText: 2001 Argonne National Laboratory +#- +#- SPDX-License-Identifier: EPICS + #- Example RTEMS startup script #- You may have to change _APPNAME_ to something else diff --git a/modules/database/src/template/top/iocBoot/ioc/st.cmd@vxWorks b/modules/database/src/template/top/iocBoot/ioc/st.cmd@vxWorks index 43287d68b..4a91f0436 100644 --- a/modules/database/src/template/top/iocBoot/ioc/st.cmd@vxWorks +++ b/modules/database/src/template/top/iocBoot/ioc/st.cmd@vxWorks @@ -1,3 +1,7 @@ +#- SPDX-FileCopyrightText: 1998 Argonne National Laboratory +#- +#- SPDX-License-Identifier: EPICS + #- Example vxWorks startup file #- The following is needed if your board support package doesn't at boot time diff --git a/modules/database/src/template/top/iocBoot/nfsCommands@RTEMS b/modules/database/src/template/top/iocBoot/nfsCommands@RTEMS index dd8811319..492165eae 100644 --- a/modules/database/src/template/top/iocBoot/nfsCommands@RTEMS +++ b/modules/database/src/template/top/iocBoot/nfsCommands@RTEMS @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2005 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + #- Instructions for creating and using a real nfsCommands file #- #- in order to use nfs do the following: diff --git a/modules/database/src/template/top/iocBoot/nfsCommands@vxWorks b/modules/database/src/template/top/iocBoot/nfsCommands@vxWorks index dd8811319..46560bfd9 100644 --- a/modules/database/src/template/top/iocBoot/nfsCommands@vxWorks +++ b/modules/database/src/template/top/iocBoot/nfsCommands@vxWorks @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 1997 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + #- Instructions for creating and using a real nfsCommands file #- #- in order to use nfs do the following: diff --git a/modules/database/src/template/top/supportApp/Db/Makefile b/modules/database/src/template/top/supportApp/Db/Makefile index 8eb97279d..495627f58 100644 --- a/modules/database/src/template/top/supportApp/Db/Makefile +++ b/modules/database/src/template/top/supportApp/Db/Makefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 1998 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP=../.. include $(TOP)/configure/CONFIG #---------------------------------------- diff --git a/modules/database/src/template/top/supportApp/Makefile b/modules/database/src/template/top/supportApp/Makefile index 60ab8ae88..8315101cf 100644 --- a/modules/database/src/template/top/supportApp/Makefile +++ b/modules/database/src/template/top/supportApp/Makefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 1997 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # Makefile at top of application tree TOP = .. include $(TOP)/configure/CONFIG diff --git a/modules/database/src/template/top/supportApp/src/Makefile b/modules/database/src/template/top/supportApp/src/Makefile index 941d94f8b..ccd3b57a6 100644 --- a/modules/database/src/template/top/supportApp/src/Makefile +++ b/modules/database/src/template/top/supportApp/src/Makefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2003 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP=../.. include $(TOP)/configure/CONFIG diff --git a/modules/database/src/template/top/supportApp/src/_APPNAME_.dbd b/modules/database/src/template/top/supportApp/src/_APPNAME_.dbd index e70223b19..6e1d95722 100644 --- a/modules/database/src/template/top/supportApp/src/_APPNAME_.dbd +++ b/modules/database/src/template/top/supportApp/src/_APPNAME_.dbd @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2003 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # provide definitions such as #include "xxxRecord.dbd" #device(xxx,CONSTANT,devXxxSoft,"SoftChannel") diff --git a/modules/database/src/tools/Makefile b/modules/database/src/tools/Makefile index 41bf086d3..84229dca8 100644 --- a/modules/database/src/tools/Makefile +++ b/modules/database/src/tools/Makefile @@ -34,6 +34,7 @@ PERL_SCRIPTS += dbdToRecordtypeH.pl PERL_SCRIPTS += dbdExpand.pl PERL_SCRIPTS += dbExpand.pl PERL_SCRIPTS += dbdToHtml.pl +PERL_SCRIPTS += dbdToMD.pl PERL_SCRIPTS += registerRecordDeviceDriver.pl HTMLS += dbdToHtml.html diff --git a/modules/database/src/tools/dbdToMD.pl b/modules/database/src/tools/dbdToMD.pl new file mode 100644 index 000000000..f1686a230 --- /dev/null +++ b/modules/database/src/tools/dbdToMD.pl @@ -0,0 +1,307 @@ +#!/usr/bin/env perl + +#************************************************************************* +# Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# SPDX-License-Identifier: EPICS +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +use strict; + + +use FindBin qw($Bin); +use lib ("$Bin/../../lib/perl"); + +use DBD; +use DBD::Parser; +use EPICS::Getopts; +use EPICS::macLib; +use EPICS::PodMD; +use EPICS::Readfile; + +use Pod::Usage; + +=head1 NAME + +dbdToMD.pl - Convert DBD file with POD to Markdown + +=head1 SYNOPSIS + +B [B<-h>] [B<-D>] [B<-I> dir] [B<-o> file] file.dbd.pod + +=head1 DESCRIPTION + +Generates MArkdown documentation from a B<.dbd.pod> file. + +=head1 OPTIONS + +B understands the following options: + +=over 4 + +=item B<-h> + +Help, display usage information. + +=item B<-H> + +Conversion help, display information about converting reference documentation +from the EPICS Wiki into a B<.dbd.pod> file for use with this tool. + +=item B<-I> + +Path to look for include files. + +=item B<-o> file + +Name of the output file to be created. + +=back + +If no output filename is set, the file created will be named after the input +file, removing any directory components in the path and replacing any +B<.dbd.pod> file extension with B<.md>. + +=cut + +our ($opt_h, $opt_H, @opt_I, $opt_o); + +my $tool = 'dbdToMD.pl'; + +getopts('hHI@o:') or + pod2usage(2); +pod2usage(-verbose => 2) if $opt_H; +pod2usage(1) if $opt_h; +pod2usage("$tool: No input file given.\n") if @ARGV != 1; + +my $dbd = DBD->new(); + +my $infile = shift @ARGV; +$infile =~ m/\.dbd.pod$/ or + pod2usage("$tool: Input file '$infile' must have '.dbd.pod' extension.\n"); + +ParseDBD($dbd, Readfile($infile, 0, \@opt_I)); + +if (!$opt_o) { + ($opt_o = $infile) =~ s/\.dbd\.pod$/.md/; + $opt_o =~ s/^.*\///; + $opt_o =~ s/dbCommonRecord/dbCommon/; +} + +(my $title = $opt_o) =~ s/\.md$//; + +open my $out, '>', $opt_o or + die "Can't create $opt_o: $!\n"; + +$SIG{__DIE__} = sub { + die @_ if $^S; # Ignore eval deaths + close $out; + unlink $opt_o; +}; + +sub make_fragment { + my $fragment = $_[1]; + $fragment =~ s/\W+/-/g; + $fragment = lc($fragment); + $_[1] = $fragment; +} + +my $podRst = EPICS::PodMD->new( + perldoc_url_prefix => '', + perldoc_fragment_format => make_fragment, + markdown_fragment_format => make_fragment, +); + +# Parse the Pod text from the root DBD object +my $pod = join "\n", + map { + # Handle a 'recordtype' Pod directive + if (m/^ =recordtype \s+ (\w+) /x) { + my $rn = $1; + my $rtyp = $dbd->recordtype($rn); + die "Unknown recordtype '$rn' in $infile POD directive\n" + unless $rtyp; + rtypeToMD($rtyp, $dbd); + } + # Handle a 'menu' Pod directive + elsif (m/^ =menu \s+ (\w+) /x) { + my $mn = $1; + my $menu = $dbd->menu($mn); + die "Unknown menu '$mn' in $infile POD directive\n" + unless $menu; + menuToMD($menu); + } + elsif (m/^ =title \s+ (.*)/x) { + $title = $1; + "=head1 $title"; + } + else { + $_; + } + } $dbd->pod; + +$podRst->output_fh($out); +$podRst->parse_string_document($pod); +close $out; + +sub menuToMD { + my ($menu) = @_; + my $index = 0; + return "| Index | Identifier | Choice String |", + "| ----- | ---------- | ------------- |", + map({choiceTableRow($_, $index++)} $menu->choices); +} + +sub choiceTableRow { + my ($ch, $index) = @_; + my ($id, $name) = @{$ch}; + return "| $index | $id | $name |"; +} + +sub rtypeToMD { + my ($rtyp, $dbd) = @_; + return map { + # Handle a 'fields' Pod directive + if (m/^ =fields \s+ (\w+ (?:\s* , \s* \w+ )* )/x) { + my @names = split /\s*,\s*/, $1; + # Look up the named fields + my @fields = map { + my $field = $rtyp->field($_); + die "Unknown field name '$_' in $infile POD\n" + unless $field; + $field; + } @names; + # Generate Pod for the table + "| Field | Summary | Type | DCT | Default | Read | Write | CA PP |", + "| ----- | ------- | ---- | --- | ------- | ---- | ----- | ----- |", + map({fieldTableRow($_, $dbd)} @fields); + } + # Handle a 'menu' Pod directive + elsif (m/^ =menu \s+ (\w+) /x) { + my $mn = $1; + my $menu = $dbd->menu($mn); + die "Unknown menu '$mn' in $infile POD directive\n" + unless $menu; + menuToMD($menu); + } + else { + # Raw text line + $_; + } + } $rtyp->pod; +} + +sub fieldTableRow { + my ($fld, $dbd) = @_; + my @md; + push @md, $fld->name, $fld->attribute('prompt'); + + my $type = $fld->public_type; + if ($type eq 'STRING') { + $type .= ' [' . $fld->attribute('size') . ']'; + } elsif ($type eq 'MENU') { + my $mn = $fld->attribute('menu'); + my $menu = $dbd->menu($mn); + my $mnl = lc($mn); + my $url = $menu ? "/menu-$mnl" : "${mn}.md"; + #just pass a L directive for the parser + $type .= " L<$mn|$url>"; + } + push @md, $type; + + push @md, $fld->attribute('promptgroup') ? 'Yes' : 'No'; + push @md, $fld->attribute('initial') || ' '; + push @md, $fld->readable; + push @md, $fld->writable; + push @md, $fld->attribute('pp') eq 'TRUE' ? 'Yes' : 'No'; + return '| ' . join(' | ', @md) . ' |'; +} + +# Native type presented to dbAccess users +sub DBD::Recfield::public_type { + my $fld = shift; + m/^ =type \s+ (.+) /x && return $1 for $fld->comments; + my $type = $fld->dbf_type; + $type =~ s/^DBF_//; + return $type; +} + +# Check if this field is readable +sub DBD::Recfield::readable { + my $fld = shift; + m/^ =read \s+ (?i) (Yes|No) /x && return $1 for $fld->comments; + return 'Probably' + if $fld->attribute('special') eq "SPC_DBADDR"; + return $fld->dbf_type eq 'DBF_NOACCESS' ? 'No' : 'Yes'; +} + +# Check if this field is writable +sub DBD::Recfield::writable { + my $fld = shift; + m/^ =write \s+ (?i) (Yes|No) /x && return $1 for $fld->comments; + my $special = $fld->attribute('special'); + return 'No' + if $special eq "SPC_NOMOD"; + return 'Maybe' + if $special eq "SPC_DBADDR"; + return $fld->dbf_type eq "DBF_NOACCESS" ? 'No' : 'Yes'; +} + +=pod + +=head1 Converting Wiki Record Reference to POD + +If you open the src/std/rec/aiRecord.dbd.pod file in your favourite plain text +editor you'll see what input was required to generate the aiRecord.html file. +The text markup language we're using is a standard called POD (Plain Old +Documentation) which is used by Perl developers, but you don't need to know Perl +at all to be able to use it. + +When we add POD markup to a record type, we rename its *Record.dbd file to +.dbd.pod in the src/std/rec directory; no other changes are needed for the build +system to find it by its new name. The POD content is effectively just a new +kind of comment that appears in .dbd.pod files, which the formatter knows how to +convert into Markdown. The build also generates a plain *Record.dbd file from this +same input file by stripping out all of the POD markup. + +Documentation for Perl's POD markup standard can be found online at +L or you may be able to type 'perldoc +perlpod' into a Linux command-line to see the same text. We added a few POD +keywords of our own to handle the table generation, and I'll cover those briefly +below. + +POD text can appear almost anywhere in a dbd.pod file. It always starts with a +line "=[keyword] [additional text...]" where [keyword] is "title", "head1" +through "head4" etc.. The POD text ends with a line "=cut". There must be a +blank line above every POD line, and in many cases below it as well. + +The POD keywords we have added are "title", "recordtype", "menu", "fields", +"type", "read" and "write". The last 3 are less common but are used in some of +the other record types such as the waveform and aSub records. + +The most interesting of our new keywords is "fields", which takes a list of +record field names on the same line after the keyword and generates an Markdown +Table describing those fields based on the field description found in the DBD +parts. In the ai documentation the first such table covers the DTYP and INP +fields. + +Note that the "=fields" line must appear inside the DBD's declaration of the +record type, i.e. after the line + + recordtype(ai) { + +The "type", "read" and "write" POD keywords are used inside an individual record +field declaration and provide information for the "Type", "Read" and "Write" +columns of the field's table output for fields where this information is +normally supplied by the record support code. Usage examples for these keywords +can be found in the aai and aSub record types. + +If you look at the L file you'll see that the POD there starts +by documenting a record-specific menu definition. The "menu" keyword generates a +table that lists all the choices found in the named menu. Any MENU fields in the +field tables that refer to a locally-defined menu will generate a link to a +document section which must be titled "Menu [menuName]". + +=cut diff --git a/modules/database/test/ioc/db/Makefile b/modules/database/test/ioc/db/Makefile index ad1d3d9b7..4395c291e 100644 --- a/modules/database/test/ioc/db/Makefile +++ b/modules/database/test/ioc/db/Makefile @@ -36,6 +36,7 @@ dbTestIoc_DBD += xLink.dbd dbTestIoc_DBD += devx.dbd dbTestIoc_DBD += jlinkz.dbd dbTestIoc_DBD += dbLinkdset.dbd +dbTestIoc_DBD += dbCore.dbd TESTFILES += $(COMMON_DIR)/dbTestIoc.dbd ../xRecord.db testHarness_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp @@ -183,6 +184,12 @@ testHarness_SRCS += dbStaticTest.c TESTFILES += ../dbStaticTest.db TESTFILES += ../dbStaticTestAlias1.db TESTFILES += ../dbStaticTestAlias2.db +TESTFILES += ../dbStaticTestAliasAgain1.db +TESTFILES += ../dbStaticTestAliasAgain2.db +TESTFILES += ../dbStaticTestAliasAgain3.db +TESTFILES += ../dbStaticTestAliasAgainError1.db +TESTFILES += ../dbStaticTestAliasAgainError2.db +TESTFILES += ../dbStaticTestRemove.db TESTS += dbStaticTest # This runs all the test programs in a known working order: diff --git a/modules/database/test/ioc/db/dbPutGetTest.c b/modules/database/test/ioc/db/dbPutGetTest.c index 3a5155661..6a2168e56 100644 --- a/modules/database/test/ioc/db/dbPutGetTest.c +++ b/modules/database/test/ioc/db/dbPutGetTest.c @@ -82,12 +82,12 @@ void checkDoubleGet(dbChannel *chan, db_field_log* pfl) testOk1(meta.status==UDF_ALARM); testOk1(meta.acks==MAJOR_ALARM); testOk1(meta.ackt==1); - testOk1(strncmp(meta.amsg, "oops", DB_UNITS_SIZE)==0); + testOk1(strncmp(meta.amsg, "oops", sizeof(meta.amsg))==0); testOk1(meta.time.secPastEpoch==0x12345678); testOk1(meta.time.nsec==0x90abcdef); testOk1(meta.utag==0x10203040); testOk1(meta.precision.dp==0x12345678); - testOk1(strncmp(meta.units, "arbitrary", DB_UNITS_SIZE)==0); + testOk1(strncmp(meta.units, "arbitrary", sizeof(meta.units))==0); #define limitEq(UL, FL, VAL) testOk(meta.UL ## _ ## FL ## _limit == (VAL), #UL "_" #FL "_limit (%f) == %f", meta.UL ## _ ## FL ## _limit, VAL) limitEq(lower, disp, 10000000.0-1.0); limitEq(upper, disp, 10000000.0+1.0); @@ -114,6 +114,8 @@ void testdbMetaDoubleGet(void) STATIC_ASSERT(sizeof(meta.amsg)==sizeof(prec->amsg)); STATIC_ASSERT(sizeof(meta.amsg)==sizeof(pfl->amsg)); + testDiag("testdbMetaDoubleGet"); + if(!chan) testAbort("Missing recmeta OTST"); if((status=dbChannelOpen(chan))!=0) @@ -163,6 +165,8 @@ typedef struct { DBRenumStrs } dbMetaEnum; +enum {dbMetaMetaMask = DBR_STATUS | DBR_AMSG | DBR_ENUM_STRS | DBR_TIME | DBR_UTAG}; + static void testdbMetaEnumSizes(void) { @@ -189,6 +193,66 @@ void testdbMetaEnumSizes(void) testOk(sizeof(dbMetaEnum)==pos, "sizeof(dbMetaEnum), %u == %u", (unsigned)sizeof(dbMetaEnum), (unsigned)pos); } +static +void checkEnumGet(dbChannel *chan, db_field_log* pfl) +{ + dbMetaEnum meta; + long options = (long)dbMetaMetaMask; + long nReq = 0; + long status; + + /* spoil */ + meta.no_str = -1; + meta.strs[0][0] = '!'; + + status=dbChannelGet(chan, DBF_DOUBLE, &meta, &options, &nReq, pfl); + testOk(status==0, "dbGet OTST : %ld", status); + + testOk1(meta.severity==INVALID_ALARM); + testOk1(meta.status==UDF_ALARM); + testOk1(meta.acks==MAJOR_ALARM); + testOk1(meta.ackt==1); + testOk1(strncmp(meta.amsg, "oops", sizeof(meta.amsg))==0); + testOk1(meta.time.secPastEpoch==0x12345678); + testOk1(meta.time.nsec==0x90abcdef); + testOk1(meta.utag==0x10203040); + if(testOk1(meta.no_str==3)) { + testOk1(strncmp(meta.strs[0], "Before", sizeof(meta.strs[0]))==0); + testOk1(strncmp(meta.strs[1], "After", sizeof(meta.strs[0]))==0); + testOk1(strncmp(meta.strs[2], "None", sizeof(meta.strs[0]))==0); + } +} + +void testdbMetaEnumGet(void) +{ + long status; + xRecord* prec = (xRecord*)testdbRecordPtr("recmeta"); + dbChannel *chan = dbChannelCreate("recmeta.SFX"); + + testDiag("testdbMetaEnumGet"); + + if(!chan) + testAbort("Missing recmeta SFX"); + if((status=dbChannelOpen(chan))!=0) + testAbort("can't open recmeta SFX : %ld", status); + + dbScanLock((dbCommon*)prec); + /* ensure that all meta-data has different non-zero values */ + prec->otst = 10000000.0; + prec->sevr = INVALID_ALARM; + prec->stat = UDF_ALARM; + strcpy(prec->amsg, "oops"); + prec->acks = MAJOR_ALARM; + prec->time.secPastEpoch = 0x12345678; + prec->time.nsec = 0x90abcdef; + prec->utag = 0x10203040; + + testDiag("dbGet directly from record"); + checkEnumGet(chan, NULL); + + dbScanUnlock((dbCommon*)prec); +} + static void testdbGetStringEqual(const char *pv, const char *expected) { @@ -347,7 +411,7 @@ void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); MAIN(dbPutGet) { - testPlan(124); + testPlan(137); testdbPrepare(); testdbMetaDoubleSizes(); @@ -366,6 +430,7 @@ MAIN(dbPutGet) eltc(1); testdbMetaDoubleGet(); + testdbMetaEnumGet(); testLongLink(); testLongAttr(); diff --git a/modules/database/test/ioc/db/dbPutLinkTest.c b/modules/database/test/ioc/db/dbPutLinkTest.c index db947b9eb..2b7d3a518 100644 --- a/modules/database/test/ioc/db/dbPutLinkTest.c +++ b/modules/database/test/ioc/db/dbPutLinkTest.c @@ -597,8 +597,12 @@ void testJLink(void) testNumZ(6); testdbPutFieldOk("j1.INP", DBF_STRING, "{\"z\":{\"good\":4}}"); + testdbPutFieldOk("j1.PHAS", DBF_LONG, 0); + testdbPutFieldOk("j1.OUTP", DBF_STRING, "{z:{good:99}}"); testdbPutFieldOk("j1.PROC", DBF_LONG, 1); testdbGetFieldEqual("j1.VAL", DBF_LONG, 4); + testdbGetFieldEqual("j1.PHAS", DBF_LONG, 4); + testdbPutFieldOk("j1.OUTP", DBF_STRING, ""); testdbPutFieldOk("j2.TSEL", DBF_STRING, "{'z':{good:0}}"); testdbPutFieldOk("j2.PROC", DBF_LONG, 1); @@ -701,7 +705,7 @@ void testTSEL(void) MAIN(dbPutLinkTest) { - testPlan(348); + testPlan(352); testLinkParse(); testLinkFailParse(); testCADBSet(); diff --git a/modules/database/test/ioc/db/dbStaticTest.c b/modules/database/test/ioc/db/dbStaticTest.c index d118c790a..695307659 100644 --- a/modules/database/test/ioc/db/dbStaticTest.c +++ b/modules/database/test/ioc/db/dbStaticTest.c @@ -13,6 +13,37 @@ #include #include #include +#include +#include + + +static void testEntryRemoved(const char *pv) +{ + DBENTRY entry; + + testDiag("testEntryRemoved(\"%s\")", pv); + + dbInitEntry(pdbbase, &entry); + + testOk(dbFindRecord(&entry, pv)==S_dbLib_recNotFound, + "Record '%s' not present", pv); + + dbFinishEntry(&entry); +} + +static void testEntryPresent(const char *pv) +{ + DBENTRY entry; + + testDiag("testEntryPresent(\"%s\")", pv); + + dbInitEntry(pdbbase, &entry); + + testOk(dbFindRecord(&entry, pv)==0, + "Record '%s' present", pv); + + dbFinishEntry(&entry); +} static void testEntry(const char *pv) { @@ -291,16 +322,19 @@ static void testDbVerify(const char *record) dbFinishEntry(&entry); } -static void testWrongAliasRecord(const char *filename) +static void testReadDatabase(const char *filename, int expectToFail) { FILE *fp = NULL; + long status; dbPath(pdbbase,"." OSI_PATH_LIST_SEPARATOR ".."); dbOpenFile(pdbbase, filename, &fp); if(!fp) { - testAbort("Unable to read %s", filename); + testAbort("Unable to open %s", filename); } - testOk(dbReadDatabaseFP(&pdbbase, fp, NULL, NULL) != 0, - "Wrong alias record in %s is expected to fail", filename); + status = dbReadDatabaseFP(&pdbbase, fp, NULL, NULL); + testOk(!status == !expectToFail, + "Reading %s%s", filename, + expectToFail ? " is expected to fail" : ""); } void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); @@ -308,9 +342,10 @@ void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); MAIN(dbStaticTest) { const char *ldir; + char *ldirDup; FILE *fp = NULL; - testPlan(312); + testPlan(350); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); @@ -320,13 +355,40 @@ MAIN(dbStaticTest) if(!fp) { testAbort("Unable to read dbStaticTest.db"); } + ldirDup = epicsStrDup(ldir); if(dbReadDatabaseFP(&pdbbase, fp, NULL, NULL)) { testAbort("Unable to load %s%sdbStaticTest.db", - ldir, OSI_PATH_LIST_SEPARATOR); + ldirDup, OSI_PATH_LIST_SEPARATOR); } + free(ldirDup); - testWrongAliasRecord("dbStaticTestAlias1.db"); - testWrongAliasRecord("dbStaticTestAlias2.db"); + dbPath(pdbbase,"." OSI_PATH_LIST_SEPARATOR ".."); + ldir = dbOpenFile(pdbbase, "dbStaticTestRemove.db", &fp); + if(!fp) { + testAbort("Unable to read dbStaticTestRemove.db"); + } + ldirDup = epicsStrDup(ldir); + if(dbReadDatabaseFP(&pdbbase, fp, NULL, NULL)) { + testAbort("Unable to load %s%sdbStaticTestRemove.db", + ldirDup, OSI_PATH_LIST_SEPARATOR); + } + free(ldirDup); + + testReadDatabase("dbStaticTestAlias1.db", 1); + testReadDatabase("dbStaticTestAlias2.db", 1); + + /* Test re-defining aliases */ + testReadDatabase("dbStaticTestAliasAgain1.db", 0); + testReadDatabase("dbStaticTestAliasAgain1.db", 0); + testReadDatabase("dbStaticTestAliasAgain2.db", 0); + testReadDatabase("dbStaticTestAliasAgain2.db", 0); + testReadDatabase("dbStaticTestAliasAgain3.db", 0); + testReadDatabase("dbStaticTestAliasAgain3.db", 0); + testReadDatabase("dbStaticTestAliasAgainError1.db", 1); + testReadDatabase("dbStaticTestAliasAgainError2.db", 1); + iocshCmd("var dbRecordsOnceOnly 1"); + testReadDatabase("dbStaticTestAliasAgain2.db", 1); + testReadDatabase("dbStaticTestAliasAgain3.db", 1); testEntry("testrec.VAL"); testEntry("testalias.VAL"); @@ -341,6 +403,21 @@ MAIN(dbStaticTest) testRec2Entry("testalias2"); testRec2Entry("testalias3"); + testEntryPresent("testdelrec"); + testEntryPresent("testdelrec6"); + testEntryPresent("testdelalias66"); + testEntryRemoved("testdelrec1"); + testEntryRemoved("testdelrec2"); + testEntryRemoved("testdelrec3"); + testEntryRemoved("testdelrec4"); + testEntryRemoved("testdelrec5"); + testEntryRemoved("testdelalias6"); + testEntryRemoved("testdelrec7"); + testEntryRemoved("testdelalias7"); + testEntryRemoved("testdelalias77"); + testEntryRemoved("testdelrec8"); + testEntryRemoved("testdelrec11"); + eltc(0); testIocInitOk(); eltc(1); @@ -358,6 +435,21 @@ MAIN(dbStaticTest) testRec2Entry("testalias2"); testRec2Entry("testalias3"); + testEntryPresent("testdelrec"); + testEntryPresent("testdelrec6"); + testEntryPresent("testdelalias66"); + testEntryRemoved("testdelrec1"); + testEntryRemoved("testdelrec2"); + testEntryRemoved("testdelrec3"); + testEntryRemoved("testdelrec4"); + testEntryRemoved("testdelrec5"); + testEntryRemoved("testdelalias6"); + testEntryRemoved("testdelrec7"); + testEntryRemoved("testdelalias7"); + testEntryRemoved("testdelalias77"); + testEntryRemoved("testdelrec8"); + testEntryRemoved("testdelrec11"); + testDbVerify("testrec"); testIocShutdownOk(); diff --git a/modules/database/test/ioc/db/dbStaticTestAliasAgain1.db b/modules/database/test/ioc/db/dbStaticTestAliasAgain1.db new file mode 100644 index 000000000..bc7cc1b65 --- /dev/null +++ b/modules/database/test/ioc/db/dbStaticTestAliasAgain1.db @@ -0,0 +1,4 @@ +# Test re-load alias in record +record(x, "testrecAgain") { + alias("testaliasAgain1") +} diff --git a/modules/database/test/ioc/db/dbStaticTestAliasAgain2.db b/modules/database/test/ioc/db/dbStaticTestAliasAgain2.db new file mode 100644 index 000000000..b6709f24f --- /dev/null +++ b/modules/database/test/ioc/db/dbStaticTestAliasAgain2.db @@ -0,0 +1,2 @@ +# Test re-load alias for record +alias("testrecAgain", "testaliasAgain2") diff --git a/modules/database/test/ioc/db/dbStaticTestAliasAgain3.db b/modules/database/test/ioc/db/dbStaticTestAliasAgain3.db new file mode 100644 index 000000000..8dcbc4057 --- /dev/null +++ b/modules/database/test/ioc/db/dbStaticTestAliasAgain3.db @@ -0,0 +1,2 @@ +# Test re-load alias for alias +alias("testaliasAgain2", "testaliasAgain3") diff --git a/modules/database/test/ioc/db/dbStaticTestAliasAgainError1.db b/modules/database/test/ioc/db/dbStaticTestAliasAgainError1.db new file mode 100644 index 000000000..150c09e20 --- /dev/null +++ b/modules/database/test/ioc/db/dbStaticTestAliasAgainError1.db @@ -0,0 +1,2 @@ +# ERROR: alias using name of exising alias for different record +alias("testrec", "testaliasAgain1") diff --git a/modules/database/test/ioc/db/dbStaticTestAliasAgainError2.db b/modules/database/test/ioc/db/dbStaticTestAliasAgainError2.db new file mode 100644 index 000000000..0fb844777 --- /dev/null +++ b/modules/database/test/ioc/db/dbStaticTestAliasAgainError2.db @@ -0,0 +1,2 @@ +# ERROR: alias using name of exising record fails +alias("testrecAgain", "testrec") diff --git a/modules/database/test/ioc/db/dbStaticTestRemove.db b/modules/database/test/ioc/db/dbStaticTestRemove.db new file mode 100644 index 000000000..21304bf4c --- /dev/null +++ b/modules/database/test/ioc/db/dbStaticTestRemove.db @@ -0,0 +1,55 @@ +record(x, "testdelrec") { } + +record(x, "testdelrec1") { } + +record(x, "testdelrec2") { + field("INP", "foobar") +} + +record(x, "testdelrec3") { + info("foo", "bar") +} + +record(x, "testdelrec4") { + field("INP", "testdelrec.VAL") +} + +record(x, "testdelrec5") { + field("FLNK", "testdelrec.VAL") +} + +record(x, "testdelrec6") { } + +alias("testdelrec6", "testdelalias6") +alias("testdelrec6", "testdelalias66") + + +record(x, "testdelrec7") { } + +alias("testdelrec7", "testdelalias7") +alias("testdelrec7", "testdelalias77") + + +record(x, "testdelrec8") { + field("INP", "{z:{good:1}}") +} + +record("#", "no:such:record") { } +record("#", "also:non:existant") { + field(FOO, "5") +} + +record("#", "testdelrec1") { } +record("#", "testdelrec2") { + field("INP", "foobar2") +} +record("#", "testdelrec3") { + field("INP", "foobar2") + field("VAL", "1") +} +record("#", "testdelrec4") { } +record("#", "testdelrec5") { } +record("#", "testdelalias6") { } +record("#", "testdelrec7") { } +record("#", "testdelrec8") { } +record("#", "testdelrec11") { } diff --git a/modules/database/test/ioc/db/dbStressLock.c b/modules/database/test/ioc/db/dbStressLock.c index 6cac59fe2..d71fca8ac 100644 --- a/modules/database/test/ioc/db/dbStressLock.c +++ b/modules/database/test/ioc/db/dbStressLock.c @@ -59,6 +59,9 @@ static unsigned int nrecords; #define MAXLOCK 20 +/* Verbose output from test if you set this to 1 */ +#define MULTI_DIAG 0 + static dbCommon **precords; typedef struct { @@ -120,6 +123,9 @@ void doMulti(workerPriv *p) } dbScanUnlockMany(locker); + if (MULTI_DIAG) + testDiag("sum = %d", sum); + dbLockerFree(locker); } diff --git a/modules/database/test/ioc/db/jlinkz.c b/modules/database/test/ioc/db/jlinkz.c index 087cc0520..673391617 100644 --- a/modules/database/test/ioc/db/jlinkz.c +++ b/modules/database/test/ioc/db/jlinkz.c @@ -35,7 +35,6 @@ void z_open(struct link *plink) if(priv->isopen) testDiag("lsetZ re-open"); priv->isopen = 1; - testDiag("Open jlinkz %p", priv); } static @@ -50,8 +49,6 @@ void z_remove(struct dbLocker *locker, struct link *plink) epicsMutexUnlock(priv->lock); - testDiag("Remove/free jlinkz %p", priv); - epicsAtomicDecrIntT(&numzalloc); epicsMutexDestroy(priv->lock); @@ -83,13 +80,13 @@ long z_getval(struct link *plink, short dbrType, void *pbuffer, long *pnRequest) { long ret; - long (*pconv)(const epicsInt32 *, void *, const dbAddr *) = dbFastGetConvertRoutine[DBF_LONG][dbrType]; + FASTCONVERTFUNC pconv = dbFastGetConvertRoutine[DBF_LONG][dbrType]; zpriv *priv = CONTAINER(plink->value.json.jlink, zpriv, base); if(pnRequest && *pnRequest==0) return 0; epicsMutexLock(priv->lock); - ret = (*pconv)(&priv->value, pbuffer, NULL); + ret = pconv(&priv->value, pbuffer, NULL); epicsMutexUnlock(priv->lock); if(ret==0 && pnRequest) *pnRequest = 1; return ret; @@ -118,19 +115,20 @@ long z_putval(struct link *plink, short dbrType, const void *pbuffer, long nRequest) { long ret; - long (*pconv)(epicsInt32 *, const void *, const dbAddr *); zpriv *priv = CONTAINER(plink->value.json.jlink, zpriv, base); + FASTCONVERTFUNC pconv; if(INVALID_DB_REQ(dbrType)) return S_db_badDbrtype; - pconv = dbFastPutConvertRoutine[DBF_LONG][dbrType]; + pconv = dbFastPutConvertRoutine[dbrType][DBF_LONG]; if(nRequest==0) return 0; epicsMutexLock(priv->lock); - ret = (*pconv)(&priv->value, pbuffer, NULL); + ret = pconv(pbuffer, &priv->value, NULL); epicsMutexUnlock(priv->lock); + plink->precord->phas = priv->value; return ret; } @@ -168,8 +166,6 @@ jlink* z_alloc(short dbfType) epicsAtomicIncrIntT(&numzalloc); - testDiag("Alloc jlinkz %p", priv); - return &priv->base; fail: if(priv && priv->lock) epicsMutexDestroy(priv->lock); @@ -185,8 +181,6 @@ void z_free(jlink *pj) if(priv->isopen) testDiag("lsetZ jlink free after open()"); - testDiag("Free jlinkz %p", priv); - epicsAtomicDecrIntT(&numzalloc); epicsMutexDestroy(priv->lock); diff --git a/modules/database/test/ioc/db/xRecord.c b/modules/database/test/ioc/db/xRecord.c index cd8fa74df..9f934746a 100644 --- a/modules/database/test/ioc/db/xRecord.c +++ b/modules/database/test/ioc/db/xRecord.c @@ -71,6 +71,7 @@ static long process(struct dbCommon *pcommon) ret = (*xset->process)(prec); monitor(prec); recGblGetTimeStamp(prec); + dbPutLink(&prec->outp, DBR_LONG, &prec->val, 1); recGblFwdLink(prec); prec->pact = FALSE; return ret; diff --git a/modules/database/test/ioc/db/xRecord.dbd b/modules/database/test/ioc/db/xRecord.dbd index 620deac61..494ea349a 100644 --- a/modules/database/test/ioc/db/xRecord.dbd +++ b/modules/database/test/ioc/db/xRecord.dbd @@ -49,6 +49,9 @@ recordtype(x) { prompt("Input Link") special(SPC_MOD) } + field(OUTP, DBF_OUTLINK) { + prompt("Output Link") + } field(CLBK, DBF_NOACCESS) { prompt("Processing callback") special(SPC_NOMOD) diff --git a/modules/database/test/ioc/dbtemplate/msi.plt b/modules/database/test/ioc/dbtemplate/msi.plt index dc466be1f..09835d3ac 100644 --- a/modules/database/test/ioc/dbtemplate/msi.plt +++ b/modules/database/test/ioc/dbtemplate/msi.plt @@ -12,7 +12,7 @@ use strict; use Test; -BEGIN {plan tests => 12} +BEGIN {plan tests => 14} # Check include/substitute command model ok(msi('-I .. ../t1-template.txt'), slurp('../t1-result.txt')); @@ -56,6 +56,12 @@ my %envs = (TEST_NO => 12, PREFIX => 't'); ok(msi('-I. -I.. -S ../t12-substitute.txt'), slurp('../t12-result.txt')); delete @ENV{ keys %envs }; # Not really needed +# Substitution file, relative path includes +ok(msi('-I @TOP@/modules -S ../t13-substitute.txt'), slurp('../t13-result.txt')); + +# Template file, relative path includes +ok(msi('-I @TOP@/modules ../t14-template.txt'), slurp('../t14-result.txt')); + # Test support routines sub slurp { diff --git a/modules/database/test/ioc/dbtemplate/t13-result.txt b/modules/database/test/ioc/dbtemplate/t13-result.txt new file mode 100644 index 000000000..6cfcc57da --- /dev/null +++ b/modules/database/test/ioc/dbtemplate/t13-result.txt @@ -0,0 +1,2 @@ +# comment line +a=foo diff --git a/modules/database/test/ioc/dbtemplate/t13-substitute.txt b/modules/database/test/ioc/dbtemplate/t13-substitute.txt new file mode 100644 index 000000000..63d3fca1f --- /dev/null +++ b/modules/database/test/ioc/dbtemplate/t13-substitute.txt @@ -0,0 +1,3 @@ +file database/test/ioc/dbtemplate/t13-template.txt { + { a=foo } +} diff --git a/modules/database/test/ioc/dbtemplate/t13-template.txt b/modules/database/test/ioc/dbtemplate/t13-template.txt new file mode 100644 index 000000000..7958885a7 --- /dev/null +++ b/modules/database/test/ioc/dbtemplate/t13-template.txt @@ -0,0 +1,2 @@ +# comment line +a=$(a) diff --git a/modules/database/test/ioc/dbtemplate/t14-include.txt b/modules/database/test/ioc/dbtemplate/t14-include.txt new file mode 100644 index 000000000..7f69fca81 --- /dev/null +++ b/modules/database/test/ioc/dbtemplate/t14-include.txt @@ -0,0 +1 @@ +I'm a file! diff --git a/modules/database/test/ioc/dbtemplate/t14-result.txt b/modules/database/test/ioc/dbtemplate/t14-result.txt new file mode 100644 index 000000000..5d5dbda1b --- /dev/null +++ b/modules/database/test/ioc/dbtemplate/t14-result.txt @@ -0,0 +1,5 @@ +This is t14-template.txt + +I'm a file! + +End of t14-template.txt diff --git a/modules/database/test/ioc/dbtemplate/t14-template.txt b/modules/database/test/ioc/dbtemplate/t14-template.txt new file mode 100644 index 000000000..276663f7a --- /dev/null +++ b/modules/database/test/ioc/dbtemplate/t14-template.txt @@ -0,0 +1,5 @@ +This is t14-template.txt + +include "database/test/ioc/dbtemplate/t14-include.txt" + +End of t14-template.txt diff --git a/modules/database/test/std/rec/Makefile b/modules/database/test/std/rec/Makefile index a717b769e..ebc4ac3d0 100644 --- a/modules/database/test/std/rec/Makefile +++ b/modules/database/test/std/rec/Makefile @@ -21,7 +21,9 @@ dbRecStdTest_LIBS += dbRecStd dbCore ca Com PROD_LIBS = dbRecStdTest dbRecStd dbCore ca Com TARGETS += $(COMMON_DIR)/recTestIoc.dbd +ifneq (inc,$(strip $(MAKECMDGOALS))) DBDDEPENDS_FILES += recTestIoc.dbd$(DEP) +endif recTestIoc_DBD = base.dbd recTestIoc_DBD += bptTypeKdegC.dbd recTestIoc_DBD += bptTypeKdegF.dbd @@ -174,7 +176,9 @@ TESTS += regressTest TARGETS += $(COMMON_DIR)/simmTest.dbd TARGETS += $(COMMON_DIR)/simmTest.db DBDDEPENDS_FILES += simmTest.dbd$(DEP) +ifneq (inc,$(strip $(MAKECMDGOALS))) DBDDEPENDS_FILES += simmTest.db$(DEP) +endif simmTest_DBD += base.dbd TESTPROD_HOST += simmTest simmTest_SRCS += simmTest.c diff --git a/modules/libcom/RTEMS/posix/rtems_config.c b/modules/libcom/RTEMS/posix/rtems_config.c index e4f882355..b3c42097f 100644 --- a/modules/libcom/RTEMS/posix/rtems_config.c +++ b/modules/libcom/RTEMS/posix/rtems_config.c @@ -2,7 +2,7 @@ * Copyright (c) 2002 The University of Saskatchewan * 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. \*************************************************************************/ /* * RTEMS configuration for EPICS @@ -26,7 +26,14 @@ extern void *POSIX_Init(void *argument); #define CONFIGURE_POSIX_INIT_THREAD_STACK_SIZE (64*1024) #define CONFIGURE_MAXIMUM_PERIODS 5 +/* + * Override in configure/CONFIG_SITE.local with: + * + * OP_SYS_CFLAGS += -DCONFIGURE_MICROSECONDS_PER_TICK=1000 + */ +#ifndef CONFIGURE_MICROSECONDS_PER_TICK #define CONFIGURE_MICROSECONDS_PER_TICK 10000 +#endif #define CONFIGURE_MALLOC_STATISTICS 1 /* MINIMUM_STACK_SIZE == 8K */ #define CONFIGURE_EXTRA_TASK_STACKS (4000 * RTEMS_MINIMUM_STACK_SIZE) @@ -121,16 +128,17 @@ extern void *POSIX_Init(void *argument); &rtems_shell_SYSCTL_Command #else // LEGACY_STACK: #define CONFIGURE_SHELL_USER_COMMANDS \ - &bsp_interrupt_shell_command, \ - &rtems_shell_PING_Command, \ - &rtems_shell_ROUTE_Command, \ - &rtems_shell_IFCONFIG_Command -#endif + &bsp_interrupt_shell_command +#endif // not LEGACY_STACK #define CONFIGURE_SHELL_COMMAND_CPUUSE #define CONFIGURE_SHELL_COMMAND_PERIODUSE #define CONFIGURE_SHELL_COMMAND_STACKUSE #define CONFIGURE_SHELL_COMMAND_PROFREPORT +#define CONFIGURE_SHELL_COMMAND_TOP +#define CONFIGURE_SHELL_COMMAND_RTEMS + +#define CONFIGURE_SHELL_COMMANDS_ALL_NETWORKING #define CONFIGURE_SHELL_COMMAND_CP #define CONFIGURE_SHELL_COMMAND_PWD @@ -148,14 +156,15 @@ extern void *POSIX_Init(void *argument); #define CONFIGURE_SHELL_COMMAND_SHUTDOWN #include + #define RTEMS_BSD_CONFIG_BSP_CONFIG #define RTEMS_BSD_CONFIG_SERVICE_TELNETD #define RTEMS_BSD_CONFIG_TELNETD_STACK_SIZE (16 * 1024) #define RTEMS_BSD_CONFIG_SERVICE_FTPD #define RTEMS_BSD_CONFIG_FIREWALL_PF -#else +#else // __RTEMS_MAJOR__ > 4 #include -#endif // not LEGACY_STACK +#endif // __RTEMS_MAJOR__ > 4 #if __RTEMS_MAJOR__ < 5 // still needed in Version 4? #define CONFIGURE_MAXIMUM_TASKS rtems_resource_unlimited(30) diff --git a/modules/libcom/RTEMS/posix/rtems_init.c b/modules/libcom/RTEMS/posix/rtems_init.c index 403cd0785..0af4fa78f 100644 --- a/modules/libcom/RTEMS/posix/rtems_init.c +++ b/modules/libcom/RTEMS/posix/rtems_init.c @@ -102,9 +102,9 @@ char bootp_server_name_init[128] = "1001.1001@10.0.5.1:/epics"; char bootp_boot_file_name_init[128] = "/epics/myExample/bin/RTEMS-beatnik/myExample.boot"; char bootp_cmdline_init[128] = "/epics/myExample/iocBoot/iocmyExample/st.cmd"; -struct in_addr rtems_bsdnet_bootp_server_address; /* TODO check rtems_bsdnet_bootp_cmdline */ #ifndef RTEMS_LEGACY_STACK +struct in_addr rtems_bsdnet_bootp_server_address; char *rtems_bsdnet_bootp_server_name = bootp_server_name_init; char *rtems_bsdnet_bootp_boot_file_name = bootp_boot_file_name_init; char *rtems_bsdnet_bootp_cmdline = bootp_cmdline_init; @@ -510,6 +510,7 @@ static void rtshellCallFunc(const iocshArgBuf *args) if (!cmd) { fprintf(stderr, "ERR: No such command\n"); + iocshSetError(-1); } else { fflush(stdout); @@ -517,6 +518,7 @@ static void rtshellCallFunc(const iocshArgBuf *args) ret = (*cmd->command)(args[1].aval.ac,args[1].aval.av); fflush(stdout); fflush(stderr); + iocshSetError(ret); if(ret) fprintf(stderr, "ERR: %d\n",ret); } @@ -611,18 +613,27 @@ static void nfsMountCallFunc(const iocshArgBuf *args) } *cp = '/'; } - nfsMount(args[0].sval, args[1].sval, args[2].sval); + iocshSetError(nfsMount(args[0].sval, args[1].sval, args[2].sval)); } #endif -void zoneset(const char *zone) +int zoneset(const char *zone) { - if(zone) - setenv("TZ", zone, 1); - else - unsetenv("TZ"); + int ret; + if(zone) { + if ((ret = setenv("TZ", zone, 1)) < 0) + return ret; + } + #if defined( __NEWLIB_MINOR__ ) /* Added in newlib 2.2.0 */ + else if ((ret = unsetenv("TZ")) < 0) + return ret; + #else + else + unsetenv("TZ"); + #endif tzset(); + return 0; } static const iocshArg zonesetArg0 = {"zone string", iocshArgString}; @@ -634,7 +645,7 @@ static const iocshFuncDef zonesetFuncDef = {"zoneset",1,zonesetArgs }; static void zonesetCallFunc(const iocshArgBuf *args) { - zoneset(args[0].sval); + iocshSetError(zoneset(args[0].sval)); } #ifndef RTEMS_LEGACY_STACK @@ -667,6 +678,7 @@ static void setlogmaskCallFunc(const iocshArgBuf *args) return; } printf("Error: unknown log level.\n"); + iocshSetError(-1); } } static const iocshArg setlogmaskArg0 = {"level name", iocshArgString}; diff --git a/modules/libcom/RTEMS/score/rtems_init.c b/modules/libcom/RTEMS/score/rtems_init.c index f8cd62d5d..d291ea1f9 100644 --- a/modules/libcom/RTEMS/score/rtems_init.c +++ b/modules/libcom/RTEMS/score/rtems_init.c @@ -483,18 +483,27 @@ static void nfsMountCallFunc(const iocshArgBuf *args) } *cp = '/'; } - nfsMount(args[0].sval, args[1].sval, args[2].sval); + iocshSetError(nfsMount(args[0].sval, args[1].sval, args[2].sval)); } #endif -void zoneset(const char *zone) +int zoneset(const char *zone) { - if(zone) - setenv("TZ", zone, 1); - else - unsetenv("TZ"); + int ret; + if(zone) { + if ((ret = setenv("TZ", zone, 1)) < 0) + return ret; + } + #if defined( __NEWLIB_MINOR__ ) /* Added in newlib 2.2.0 */ + else if ((ret = unsetenv("TZ")) < 0) + return ret; + #else + else + unsetenv("TZ"); + #endif tzset(); + return 0; } static const iocshArg zonesetArg0 = {"zone string", iocshArgString}; @@ -502,7 +511,7 @@ static const iocshArg * const zonesetArgs[1] = {&zonesetArg0}; static const iocshFuncDef zonesetFuncDef = {"zoneset",1,zonesetArgs}; static void zonesetCallFunc(const iocshArgBuf *args) { - zoneset(args[0].sval); + iocshSetError(zoneset(args[0].sval)); } diff --git a/modules/libcom/src/Makefile b/modules/libcom/src/Makefile index ffc36445e..198d64a61 100644 --- a/modules/libcom/src/Makefile +++ b/modules/libcom/src/Makefile @@ -54,6 +54,8 @@ include $(LIBCOM)/yajl/Makefile # Generate library API header file API_HEADER = libComAPI.h Com_API = libCom +# Generate early to allow a sneak peak at dbChannel.h +API_HEADER += dbCoreAPI.h # Library to build: LIBRARY=Com diff --git a/modules/libcom/src/as/Makefile b/modules/libcom/src/as/Makefile index 65f752018..4d17a623a 100644 --- a/modules/libcom/src/as/Makefile +++ b/modules/libcom/src/as/Makefile @@ -19,4 +19,8 @@ INC += asTrapWrite.h Com_SRCS += asLib.c Com_SRCS += asTrapWrite.c +# Allow early access to dbChannel.h +asTrapWrite_CPPFLAGS = -I$(LIBCOM)/../../database/src/ioc/db + + CLEANS += asLib.c asLib_lex.c diff --git a/modules/libcom/src/as/asLib.h b/modules/libcom/src/as/asLib.h index 9c47790eb..415d98d1a 100644 --- a/modules/libcom/src/as/asLib.h +++ b/modules/libcom/src/as/asLib.h @@ -22,6 +22,8 @@ extern "C" { #endif +struct dbChannel; + /* 0 - Use (unverified) client provided host name string. * 1 - Use actual client IP address. HAG() are resolved to IPs at ACF load time. */ @@ -121,7 +123,7 @@ LIBCOM_API int epicsStdCall asDumpHash(void); LIBCOM_API int epicsStdCall asDumpHashFP(FILE *fp); LIBCOM_API void * epicsStdCall asTrapWriteBeforeWithData( - const char *userid, const char *hostid, void *addr, + const char *userid, const char *hostid, struct dbChannel *addr, int dbrType, int no_elements, void *data); LIBCOM_API void epicsStdCall asTrapWriteAfterWrite(void *pvt); diff --git a/modules/libcom/src/as/asTrapWrite.c b/modules/libcom/src/as/asTrapWrite.c index a3f390847..ec5f0d147 100644 --- a/modules/libcom/src/as/asTrapWrite.c +++ b/modules/libcom/src/as/asTrapWrite.c @@ -22,10 +22,10 @@ #include "ellLib.h" #include "freeList.h" -#include "epicsStdio.h" #include "cantProceed.h" #include "epicsMutex.h" #include "ellLib.h" +#include #include "asLib.h" #include "asTrapWrite.h" @@ -112,11 +112,12 @@ void epicsStdCall asTrapWriteUnregisterListener(asTrapWriteId id) } void * epicsStdCall asTrapWriteBeforeWithData( - const char *userid, const char *hostid, void *addr, + const char *userid, const char *hostid, dbChannel *chan, int dbrType, int no_elements, void *data) { writeMessage *pwriteMessage; listener *plistener; + void *pfieldsave; if (pasTrapWritePvt == 0 || ellCount(&pasTrapWritePvt->listenerList) <= 0) return 0; @@ -125,13 +126,14 @@ void * epicsStdCall asTrapWriteBeforeWithData( pasTrapWritePvt->freeListWriteMessage); pwriteMessage->message.userid = userid; pwriteMessage->message.hostid = hostid; - pwriteMessage->message.serverSpecific = addr; + pwriteMessage->message.serverSpecific = chan; pwriteMessage->message.dbrType = dbrType; pwriteMessage->message.no_elements = no_elements; pwriteMessage->message.data = data; ellInit(&pwriteMessage->listenerPvtList); epicsMutexMustLock(pasTrapWritePvt->lock); + pfieldsave = chan->addr.pfield; ellAdd(&pasTrapWritePvt->writeMessageList, &pwriteMessage->node); plistener = (listener *)ellFirst(&pasTrapWritePvt->listenerList); while (plistener) { @@ -141,6 +143,7 @@ void * epicsStdCall asTrapWriteBeforeWithData( plistenerPvt->plistener = plistener; pwriteMessage->message.userPvt = 0; plistener->func(&pwriteMessage->message, 0); + chan->addr.pfield = pfieldsave; plistenerPvt->userPvt = pwriteMessage->message.userPvt; ellAdd(&pwriteMessage->listenerPvtList, &plistenerPvt->node); plistener = (listener *)ellNext(&plistener->node); @@ -153,11 +156,15 @@ void epicsStdCall asTrapWriteAfterWrite(void *pvt) { writeMessage *pwriteMessage = (writeMessage *)pvt; listenerPvt *plistenerPvt; + dbChannel *chan; + void *pfieldsave; if (pwriteMessage == 0 || pasTrapWritePvt == 0) return; epicsMutexMustLock(pasTrapWritePvt->lock); + chan = pwriteMessage->message.serverSpecific; + pfieldsave = chan->addr.pfield; plistenerPvt = (listenerPvt *)ellFirst(&pwriteMessage->listenerPvtList); while (plistenerPvt) { listenerPvt *pnext = (listenerPvt *)ellNext(&plistenerPvt->node); @@ -165,6 +172,7 @@ void epicsStdCall asTrapWriteAfterWrite(void *pvt) pwriteMessage->message.userPvt = plistenerPvt->userPvt; plistener->func(&pwriteMessage->message, 1); + chan->addr.pfield = pfieldsave; ellDelete(&pwriteMessage->listenerPvtList, &plistenerPvt->node); freeListFree(pasTrapWritePvt->freeListListenerPvt, plistenerPvt); plistenerPvt = pnext; diff --git a/modules/libcom/src/as/asTrapWrite.h b/modules/libcom/src/as/asTrapWrite.h index 0d9f4d99b..d64e24b18 100644 --- a/modules/libcom/src/as/asTrapWrite.h +++ b/modules/libcom/src/as/asTrapWrite.h @@ -26,6 +26,8 @@ extern "C" { #endif +struct dbChannel; + /** * \brief The message passed to registered listeners. */ @@ -39,7 +41,7 @@ typedef struct asTrapWriteMessage { * the value the server provides to asTrapWriteWithData(), which * for RSRV is the dbChannel pointer for the target field. */ - void *serverSpecific; + struct dbChannel *serverSpecific; /** \brief A field for use by the \ref asTrapWriteListener. * * When the listener is called before the write, this has the diff --git a/modules/libcom/src/cxxTemplates/tsSLList.h b/modules/libcom/src/cxxTemplates/tsSLList.h index 6d375af55..895ec7dde 100644 --- a/modules/libcom/src/cxxTemplates/tsSLList.h +++ b/modules/libcom/src/cxxTemplates/tsSLList.h @@ -311,13 +311,13 @@ inline bool tsSLIterConst::valid () const template < class T > inline bool tsSLIterConst::operator == ( const tsSLIterConst &rhs ) const { - return this->pEntry == rhs.pConstEntry; + return this->pEntry == rhs.pEntry; } template < class T > inline bool tsSLIterConst::operator != (const tsSLIterConst &rhs) const { - return this->pEntry != rhs.pConstEntry; + return this->pEntry != rhs.pEntry; } template < class T > diff --git a/modules/libcom/src/env/envDefs.h b/modules/libcom/src/env/envDefs.h index 3bf64c861..c54a781c7 100644 --- a/modules/libcom/src/env/envDefs.h +++ b/modules/libcom/src/env/envDefs.h @@ -76,6 +76,7 @@ LIBCOM_API extern const ENV_PARAM EPICS_IOC_LOG_FILE_COMMAND; LIBCOM_API extern const ENV_PARAM IOCSH_PS1; LIBCOM_API extern const ENV_PARAM IOCSH_HISTSIZE; LIBCOM_API extern const ENV_PARAM IOCSH_HISTEDIT_DISABLE; +LIBCOM_API extern const ENV_PARAM EPICS_ABORT_ON_ASSERT; LIBCOM_API extern const ENV_PARAM *env_param_list[]; struct in_addr; diff --git a/modules/libcom/src/error/errlog.c b/modules/libcom/src/error/errlog.c index 480375ea4..75c6e63c1 100644 --- a/modules/libcom/src/error/errlog.c +++ b/modules/libcom/src/error/errlog.c @@ -500,7 +500,7 @@ void errPrintf(long status, const char *pFileName, int lineno, errSymLookup(status, name, sizeof(name)); } - nchar = epicsSnprintf(buf, pvt.maxMsgSize, "%s%sfilename=\"%s\" line number=%d", + nchar = epicsSnprintf(buf, pvt.maxMsgSize, "%s%sfilename=\"%s\" line number=%d ", name, status ? " " : "", pFileName, lineno); if(nchar < pvt.maxMsgSize) nchar += epicsVsnprintf(buf + nchar, pvt.maxMsgSize - nchar, pformat, pvar); diff --git a/modules/libcom/src/iocsh/iocsh.cpp b/modules/libcom/src/iocsh/iocsh.cpp index 39effd9b9..ae013f8a0 100644 --- a/modules/libcom/src/iocsh/iocsh.cpp +++ b/modules/libcom/src/iocsh/iocsh.cpp @@ -1325,7 +1325,7 @@ iocshCmd (const char *cmd) int epicsStdCall iocshLoad(const char *pathname, const char *macros) { - if (pathname) + if (pathname && !getenv("IOCSH_STARTUP_SCRIPT")) epicsEnvSet("IOCSH_STARTUP_SCRIPT", pathname); return iocshBody(pathname, NULL, macros); } @@ -1433,13 +1433,16 @@ static void varCallFunc(const iocshArgBuf *args) varHandler(v->pVarDef, NULL); found = 1; } - if (!found && name != NULL) + if (!found && name != NULL) { fprintf(epicsGetStderr(), ANSI_RED("No var matching") " %s found.\n", name); + iocshSetError(1); + } } else { v = (iocshVariable *)registryFind(iocshVarID, args[0].sval); if (v == NULL) { fprintf(epicsGetStderr(), "Var %s " ANSI_RED("not found.") "\n", name); + iocshSetError(1); } else { varHandler(v->pVarDef, value); @@ -1456,7 +1459,7 @@ static const iocshFuncDef iocshCmdFuncDef = {"iocshCmd",1,iocshCmdArgs, " from vxWorks or RTEMS startup script (or command line)\n"}; static void iocshCmdCallFunc(const iocshArgBuf *args) { - iocshCmd(args[0].sval); + iocshSetError(iocshCmd(args[0].sval)); } /* iocshLoad */ @@ -1468,7 +1471,7 @@ static const iocshFuncDef iocshLoadFuncDef = {"iocshLoad",2,iocshLoadArgs, " * (optional) replace macros within the file with provided values\n"}; static void iocshLoadCallFunc(const iocshArgBuf *args) { - iocshLoad(args[0].sval, args[1].sval); + iocshSetError(iocshLoad(args[0].sval, args[1].sval)); } /* iocshRun */ @@ -1481,7 +1484,7 @@ static const iocshFuncDef iocshRunFuncDef = {"iocshRun",2,iocshRunArgs, " from vxWorks or RTEMS startup script (or command line)\n"}; static void iocshRunCallFunc(const iocshArgBuf *args) { - iocshRun(args[0].sval, args[1].sval); + iocshSetError(iocshRun(args[0].sval, args[1].sval)); } /* on */ diff --git a/modules/libcom/src/iocsh/libComRegister.c b/modules/libcom/src/iocsh/libComRegister.c index 5751ad070..9fbf6b850 100644 --- a/modules/libcom/src/iocsh/libComRegister.c +++ b/modules/libcom/src/iocsh/libComRegister.c @@ -139,10 +139,12 @@ static void epicsEnvSetCallFunc(const iocshArgBuf *args) if (name == NULL) { fprintf(stderr, "Missing environment variable name argument.\n"); + iocshSetError(-1); return; } if (value == NULL) { fprintf(stderr, "Missing environment variable value argument.\n"); + iocshSetError(-1); return; } epicsEnvSet (name, value); @@ -159,6 +161,7 @@ static void epicsEnvUnsetCallFunc(const iocshArgBuf *args) if (name == NULL) { fprintf(stderr, "Missing environment variable name argument.\n"); + iocshSetError(-1); return; } epicsEnvUnset (name); @@ -215,7 +218,7 @@ static const iocshFuncDef iocLogInitFuncDef = {"iocLogInit",0,0, " see 'setIocLogDisable' command\n"}; static void iocLogInitCallFunc(const iocshArgBuf *args) { - iocLogInit (); + iocshSetError(iocLogInit ()); } /* iocLogDisable */ @@ -354,6 +357,7 @@ static void threadCallFunc(const iocshArgBuf *args) tid = epicsThreadGetId (cp); if (!tid) { fprintf(stderr, "\t'%s' is not a known thread name\n", cp); + iocshSetError(-1); continue; } } @@ -429,6 +433,7 @@ static void epicsThreadResumeCallFunc(const iocshArgBuf *args) tid = epicsThreadGetId(cp); if (!tid) { fprintf(stderr, "'%s' is not a valid thread name\n", cp); + iocshSetError(-1); continue; } } @@ -437,12 +442,14 @@ static void epicsThreadResumeCallFunc(const iocshArgBuf *args) epicsThreadGetName(tid, nameBuf, sizeof nameBuf); if (nameBuf[0] == '\0') { fprintf(stderr, "'%s' is not a valid thread id\n", cp); + iocshSetError(-1); continue; } } if (!epicsThreadIsSuspended(tid)) { fprintf(stderr, "Thread %s is not suspended\n", cp); + iocshSetError(-1); continue; } epicsThreadResume(tid); @@ -458,7 +465,7 @@ static const iocshFuncDef generalTimeReportFuncDef = {"generalTimeReport",1,gene " 1 - Additionally show current time obtained from each provider.\n"}; static void generalTimeReportCallFunc(const iocshArgBuf *args) { - generalTimeReport(args[0].ival); + iocshSetError(generalTimeReport(args[0].ival)); } /* installLastResortEventProvider */ @@ -467,7 +474,7 @@ static const iocshFuncDef installLastResortEventProviderFuncDef = {"installLastR "which returns the current time for every event number\n"}; static void installLastResortEventProviderCallFunc(const iocshArgBuf *args) { - installLastResortEventProvider(); + iocshSetError(installLastResortEventProvider()); } static iocshVarDef comDefs[] = { diff --git a/modules/libcom/src/misc/cantProceed.c b/modules/libcom/src/misc/cantProceed.c index 80e26f8f6..f66d61bc2 100644 --- a/modules/libcom/src/misc/cantProceed.c +++ b/modules/libcom/src/misc/cantProceed.c @@ -24,7 +24,7 @@ LIBCOM_API void * callocMustSucceed(size_t count, size_t size, const char *msg) void * mem = NULL; if (count > 0 && size > 0) { while ((mem = calloc(count, size)) == NULL) { - errlogPrintf("%s: callocMustSucceed(%lu, %lu) - calloc failed\n", + errlogPrintf("%s: callocMustSucceed(%lu, %lu) - " ERL_ERROR " calloc failed\n", msg, (unsigned long)count, (unsigned long)size); errlogPrintf("Thread %s (%p) suspending.\n", epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf()); @@ -40,7 +40,7 @@ LIBCOM_API void * mallocMustSucceed(size_t size, const char *msg) void * mem = NULL; if (size > 0) { while ((mem = malloc(size)) == NULL) { - errlogPrintf("%s: mallocMustSucceed(%lu) - malloc failed\n", + errlogPrintf("%s: mallocMustSucceed(%lu) - " ERL_ERROR " malloc failed\n", msg, (unsigned long)size); errlogPrintf("Thread %s (%p) suspending.\n", epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf()); @@ -59,7 +59,7 @@ LIBCOM_API void cantProceed(const char *msg, ...) errlogVprintf(msg, pvar); va_end(pvar); - errlogPrintf("Thread %s (%p) can't proceed, suspending.\n", + errlogPrintf(ANSI_RED("CRITICAL ERROR") " Thread %s (%p) can't proceed, suspending.\n", epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf()); epicsStackTrace(); diff --git a/modules/libcom/src/misc/cantProceed.h b/modules/libcom/src/misc/cantProceed.h index 0232a8611..f9559b14c 100644 --- a/modules/libcom/src/misc/cantProceed.h +++ b/modules/libcom/src/misc/cantProceed.h @@ -56,18 +56,24 @@ LIBCOM_API void cantProceed( * gracefully when memory runs out. */ /** @{ */ -/** \brief A calloc() that never returns NULL. +/** \brief A calloc() which suspends on error. * \param count Number of objects. * \param size Size of each object. - * \param errorMessage What this memory is needed for. - * \return Pointer to zeroed allocated memory. + * \param errorMessage Context added to logged error message + * \return Pointer to zeroed allocated memory. Should later be free() d + * + * Will always return NULL for a zero length allocation. + * Will never return NULL otherwise. */ LIBCOM_API void * callocMustSucceed(size_t count, size_t size, const char *errorMessage); -/** \brief A malloc() that never returns NULL. +/** \brief A malloc() which suspends on error. * \param size Size of block to allocate. - * \param errorMessage What this memory is needed for. - * \return Pointer to allocated memory. + * \param errorMessage Context added to logged error message + * \return Pointer to allocated memory. Should later be free() d + * + * Will always return NULL for a zero length allocation. + * Will never return NULL otherwise. */ LIBCOM_API void * mallocMustSucceed(size_t size, const char *errorMessage); /** @} */ diff --git a/modules/libcom/src/misc/epicsExit.c b/modules/libcom/src/misc/epicsExit.c index 4e22fd81d..a7e1e4757 100644 --- a/modules/libcom/src/misc/epicsExit.c +++ b/modules/libcom/src/misc/epicsExit.c @@ -35,8 +35,6 @@ #include "cantProceed.h" #include "epicsExit.h" -void epicsMutexCleanup(void); - typedef struct exitNode { ELLNODE node; epicsExitFunc func; @@ -115,8 +113,6 @@ LIBCOM_API void epicsExitCallAtExits(void) epicsExitCallAtExitsPvt ( pep ); destroyExitPvt ( pep ); } - /* Handle specially to avoid circular reference */ - epicsMutexCleanup(); } LIBCOM_API void epicsExitCallAtThreadExits(void) diff --git a/modules/libcom/src/osi/epicsMutex.cpp b/modules/libcom/src/osi/epicsMutex.cpp index 0a63a6f33..a97de6067 100644 --- a/modules/libcom/src/osi/epicsMutex.cpp +++ b/modules/libcom/src/osi/epicsMutex.cpp @@ -26,30 +26,23 @@ #include #include +#include "dbDefs.h" #include "epicsStdio.h" #include "epicsThread.h" -#include "valgrind/valgrind.h" #include "ellLib.h" #include "errlog.h" #include "epicsMutex.h" +#include "epicsMutexImpl.h" #include "epicsThread.h" +#include "cantProceed.h" -static epicsThreadOnceId epicsMutexOsiOnce = EPICS_THREAD_ONCE_INIT; -static ELLLIST mutexList; -static ELLLIST freeList; - -struct epicsMutexParm { - ELLNODE node; - epicsMutexOSD * id; -# ifdef LOG_LAST_OWNER - epicsThreadId lastOwner; -# endif - const char *pFileName; - int lineno; -}; - -static epicsMutexOSD * epicsMutexGlobalLock; +static ELLLIST mutexList = ELLLIST_INIT; +/* Specially initialized to bootstrap initialization. + * When supported (posix and !rtems) use statically initiallized mutex. + * Otherwise, initialize via epicsMutexOsdSetup(). + */ +struct epicsMutexParm epicsMutexGlobalLock = {ELLNODE_INIT, __FILE__, __LINE__}; // vxWorks 5.4 gcc fails during compile when I use std::exception using namespace std; @@ -76,176 +69,82 @@ const char * epicsMutex::invalidMutex::what () const throw () return "epicsMutex::invalidMutex()"; } -static void epicsMutexOsiInit(void *) { - ellInit(&mutexList); - ellInit(&freeList); - VALGRIND_CREATE_MEMPOOL(&freeList, 0, 0); - epicsMutexGlobalLock = epicsMutexOsdCreate(); -} - epicsMutexId epicsStdCall epicsMutexOsiCreate( const char *pFileName,int lineno) { - epicsMutexOSD * id; + epicsMutexOsdSetup(); - epicsThreadOnce(&epicsMutexOsiOnce, epicsMutexOsiInit, NULL); + epicsMutexId ret = (epicsMutexId)calloc(1, sizeof(*ret)); + if(ret) { + ret->pFileName = pFileName; + ret->lineno = lineno; + + if(!epicsMutexOsdPrepare(ret)) { + epicsMutexMustLock(&epicsMutexGlobalLock); + ellAdd(&mutexList, &ret->node); + (void)epicsMutexUnlock(&epicsMutexGlobalLock); + + } else { + free(ret); + ret = NULL; + } - id = epicsMutexOsdCreate(); - if(!id) { - return 0; - } - epicsMutexLockStatus lockStat = - epicsMutexOsdLock(epicsMutexGlobalLock); - assert ( lockStat == epicsMutexLockOK ); - epicsMutexParm *pmutexNode = - reinterpret_cast < epicsMutexParm * > ( ellFirst(&freeList) ); - if(pmutexNode) { - ellDelete(&freeList,&pmutexNode->node); - VALGRIND_MEMPOOL_FREE(&freeList, pmutexNode); - } else { - pmutexNode = static_cast < epicsMutexParm * > ( calloc(1,sizeof(epicsMutexParm)) ); } - VALGRIND_MEMPOOL_ALLOC(&freeList, pmutexNode, sizeof(epicsMutexParm)); - pmutexNode->id = id; -# ifdef LOG_LAST_OWNER - pmutexNode->lastOwner = 0; -# endif - pmutexNode->pFileName = pFileName; - pmutexNode->lineno = lineno; - ellAdd(&mutexList,&pmutexNode->node); - epicsMutexOsdUnlock(epicsMutexGlobalLock); - return(pmutexNode); + return ret; } epicsMutexId epicsStdCall epicsMutexOsiMustCreate( const char *pFileName,int lineno) { epicsMutexId id = epicsMutexOsiCreate(pFileName,lineno); - assert(id); - return(id ); + if(!id) { + cantProceed("epicsMutexOsiMustCreate() fails at %s:%d\n", + pFileName, lineno); + } + return id; } void epicsStdCall epicsMutexDestroy(epicsMutexId pmutexNode) { - epicsMutexLockStatus lockStat = - epicsMutexOsdLock(epicsMutexGlobalLock); - assert ( lockStat == epicsMutexLockOK ); - ellDelete(&mutexList,&pmutexNode->node); - epicsMutexOsdDestroy(pmutexNode->id); - VALGRIND_MEMPOOL_FREE(&freeList, pmutexNode); - VALGRIND_MEMPOOL_ALLOC(&freeList, &pmutexNode->node, sizeof(pmutexNode->node)); - ellAdd(&freeList,&pmutexNode->node); - epicsMutexOsdUnlock(epicsMutexGlobalLock); -} - -void epicsStdCall epicsMutexUnlock(epicsMutexId pmutexNode) -{ - epicsMutexOsdUnlock(pmutexNode->id); -} - -epicsMutexLockStatus epicsStdCall epicsMutexLock( - epicsMutexId pmutexNode) -{ - epicsMutexLockStatus status = - epicsMutexOsdLock(pmutexNode->id); -# ifdef LOG_LAST_OWNER - if ( status == epicsMutexLockOK ) { - pmutexNode->lastOwner = epicsThreadGetIdSelf(); - } -# endif - return status; -} - -epicsMutexLockStatus epicsStdCall epicsMutexTryLock( - epicsMutexId pmutexNode) -{ - epicsMutexLockStatus status = - epicsMutexOsdTryLock(pmutexNode->id); -# ifdef LOG_LAST_OWNER - if ( status == epicsMutexLockOK ) { - pmutexNode->lastOwner = epicsThreadGetIdSelf(); - } -# endif - return status; -} - -/* Empty the freeList. - * Called from epicsExit.c, but not via epicsAtExit() - * to avoid the possibility of a circular reference. - */ -extern "C" -void epicsMutexCleanup(void) -{ - ELLNODE *cur; - epicsMutexLockStatus lockStat = - epicsMutexOsdLock(epicsMutexGlobalLock); - assert ( lockStat == epicsMutexLockOK ); - - while((cur=ellGet(&freeList))!=NULL) { - VALGRIND_MEMPOOL_FREE(&freeList, cur); - free(cur); + if(pmutexNode) { + epicsMutexMustLock(&epicsMutexGlobalLock); + ellDelete(&mutexList, &pmutexNode->node); + (void)epicsMutexUnlock(&epicsMutexGlobalLock); + epicsMutexOsdCleanup(pmutexNode); + free(pmutexNode); } - - epicsMutexOsdUnlock(epicsMutexGlobalLock); } void epicsStdCall epicsMutexShow( epicsMutexId pmutexNode, unsigned int level) { -# ifdef LOG_LAST_OWNER - char threadName [255]; - if ( pmutexNode->lastOwner ) { -# error currently not safe to fetch name for stale thread - epicsThreadGetName ( pmutexNode->lastOwner, - threadName, sizeof ( threadName ) ); - } - else { - strcpy ( threadName, "" ); - } - printf("epicsMutexId %p last owner \"%s\" source %s line %d\n", - (void *)pmutexNode, threadName, - pmutexNode->pFileName, pmutexNode->lineno); -# else - printf("epicsMutexId %p source %s line %d\n", - (void *)pmutexNode, pmutexNode->pFileName, - pmutexNode->lineno); -# endif + printf("epicsMutexId %p source %s line %d\n", + (void *)pmutexNode, pmutexNode->pFileName, + pmutexNode->lineno); if ( level > 0 ) { - epicsMutexOsdShow(pmutexNode->id,level-1); + epicsMutexOsdShow(pmutexNode,level-1); } } void epicsStdCall epicsMutexShowAll(int onlyLocked,unsigned int level) { - epicsMutexParm *pmutexNode; - - if (epicsMutexOsiOnce == EPICS_THREAD_ONCE_INIT) - return; + epicsMutexOsdSetup(); - printf("ellCount(&mutexList) %d ellCount(&freeList) %d\n", - ellCount(&mutexList),ellCount(&freeList)); + printf("ellCount(&mutexList) %d\n", ellCount(&mutexList)); epicsMutexOsdShowAll(); - epicsMutexLockStatus lockStat = - epicsMutexOsdLock(epicsMutexGlobalLock); - assert ( lockStat == epicsMutexLockOK ); - pmutexNode = reinterpret_cast < epicsMutexParm * > ( ellFirst(&mutexList) ); - while(pmutexNode) { + epicsMutexMustLock(&epicsMutexGlobalLock); + for(ELLNODE *cur =ellFirst(&mutexList); cur; cur = ellNext(cur)) { + epicsMutexParm *lock = CONTAINER(cur, epicsMutexParm, node); if(onlyLocked) { - epicsMutexLockStatus status; - status = epicsMutexOsdTryLock(pmutexNode->id); - if(status==epicsMutexLockOK) { - epicsMutexOsdUnlock(pmutexNode->id); - pmutexNode = - reinterpret_cast < epicsMutexParm * > - ( ellNext(&pmutexNode->node) ); - continue; + // cycle through to test state + if(epicsMutexTryLock(lock)==epicsMutexLockOK) { + epicsMutexUnlock(lock); + continue; // was not locked, skip } } - epicsMutexShow(pmutexNode, level); - pmutexNode = - reinterpret_cast < epicsMutexParm * > ( ellNext(&pmutexNode->node) ); + epicsMutexShow(lock, level); } - epicsMutexOsdUnlock(epicsMutexGlobalLock); + epicsMutexUnlock(&epicsMutexGlobalLock); } #if !defined(__GNUC__) || __GNUC__<4 || (__GNUC__==4 && __GNUC_MINOR__<8) diff --git a/modules/libcom/src/osi/epicsMutex.h b/modules/libcom/src/osi/epicsMutex.h index 41f73a4bc..117767a48 100644 --- a/modules/libcom/src/osi/epicsMutex.h +++ b/modules/libcom/src/osi/epicsMutex.h @@ -247,21 +247,6 @@ LIBCOM_API void epicsStdCall epicsMutexShow( LIBCOM_API void epicsStdCall epicsMutexShowAll( int onlyLocked,unsigned int level); -/**@privatesection - * The following are interfaces to the OS dependent - * implementation and should NOT be called directly by - * user code. - */ -struct epicsMutexOSD * epicsMutexOsdCreate(void); -void epicsMutexOsdDestroy(struct epicsMutexOSD *); -void epicsMutexOsdUnlock(struct epicsMutexOSD *); -epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD *); -epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD *); -void epicsMutexOsdShow(struct epicsMutexOSD *,unsigned int level); -#ifdef EPICS_PRIVATE_API -void epicsMutexOsdShowAll(void); -#endif - #ifdef __cplusplus } #endif diff --git a/modules/libcom/src/osi/epicsMutexImpl.h b/modules/libcom/src/osi/epicsMutexImpl.h new file mode 100644 index 000000000..91079e8c5 --- /dev/null +++ b/modules/libcom/src/osi/epicsMutexImpl.h @@ -0,0 +1,66 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* Copyright (c) 2023 Michael Davidsaver +* SPDX-License-Identifier: EPICS +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Only include from osdMutex.c */ + +#ifndef epicsMutexImpl_H +#define epicsMutexImpl_H + +#if defined(vxWorks) +# include +# include +#elif defined(_WIN32) +# define VC_EXTRALEAN +# define WIN32_LEAN_AND_MEAN +# include +#elif defined(__rtems__) +# include +# include +#else +# include +#endif + +#include "ellLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct epicsMutexParm { + /* global list of mutex */ + ELLNODE node; + /* location where mutex was allocated */ + const char *pFileName; + int lineno; +#if defined(vxWorks) + SEM_ID osd; +#elif defined(_WIN32) + CRITICAL_SECTION osd; +#elif defined(__RTEMS_MAJOR__) && __RTEMS_MAJOR__<5 + Semaphore_Control *osd; +#else + pthread_mutex_t osd; +#endif +}; + +void epicsMutexOsdSetup(void); +long epicsMutexOsdPrepare(struct epicsMutexParm *); +void epicsMutexOsdCleanup(struct epicsMutexParm *); +void epicsMutexOsdShow(struct epicsMutexParm *,unsigned int level); +void epicsMutexOsdShowAll(void); + +extern struct epicsMutexParm epicsMutexGlobalLock; + +#ifdef __cplusplus +} // extern "C +#endif + +#endif // epicsMutexImpl_H diff --git a/modules/libcom/src/osi/epicsThread.cpp b/modules/libcom/src/osi/epicsThread.cpp index b67c8bf29..b1273fd55 100644 --- a/modules/libcom/src/osi/epicsThread.cpp +++ b/modules/libcom/src/osi/epicsThread.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -78,10 +79,8 @@ void epicsThread :: printLastChanceExceptionMessage ( "with type \"%s\" in thread \"%s\" at %s\n", pExceptionContext, pExceptionTypeName, name, date ); errlogFlush (); - // This behavior matches the C++ implementation when an exception - // isn't handled by the thread code. Users can install their own - // application-specific unexpected handler if preferred. - std::unexpected (); + + abort(); } extern "C" void epicsThreadCallEntryPoint ( void * pPvt ) diff --git a/modules/libcom/src/osi/os/RTEMS-posix/osdThreadExtra.c b/modules/libcom/src/osi/os/RTEMS-posix/osdThreadExtra.c new file mode 100755 index 000000000..73cde7ed5 --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS-posix/osdThreadExtra.c @@ -0,0 +1,52 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* SPDX-License-Identifier: EPICS +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Author: Marty Kraimer Date: 18JAN2000 */ +/* Author: Chris Johns Date: 21JUL2023 */ + +/* This is part of the posix implementation of epicsThread */ + +#include "epicsStdio.h" +#include "ellLib.h" +#include "epicsEvent.h" +#include "epicsThread.h" + +#include + +void epicsThreadShowInfo(epicsThreadOSD *pthreadInfo, unsigned int level) +{ + if(!pthreadInfo) { + fprintf(epicsGetStdout()," NAME EPICS ID " + "PTHREAD ID OSIPRI OSSPRI STATE\n"); + } else { + struct sched_param param; + int policy; + int priority = 0; + + if(pthreadInfo->tid) { + int status; + status = pthread_getschedparam(pthreadInfo->tid,&policy,¶m); + if(!status) priority = param.sched_priority; + } + fprintf(epicsGetStdout(),"%16.16s %14p %12lu %3d%8d %8.8s\n", + pthreadInfo->name,(void *) + pthreadInfo,(unsigned long)pthreadInfo->tid, + pthreadInfo->osiPriority,priority, + pthreadInfo->isSuspended?"SUSPEND":"OK"); + } +} + +static void thread_hook(epicsThreadId pthreadInfo) +{ + pthread_setname_np(pthreadInfo->tid, pthreadInfo->name); +} + +EPICS_THREAD_HOOK_ROUTINE epicsThreadHookDefault = thread_hook; +EPICS_THREAD_HOOK_ROUTINE epicsThreadHookMain = thread_hook; diff --git a/modules/libcom/src/osi/os/RTEMS-score/osdEvent.c b/modules/libcom/src/osi/os/RTEMS-score/osdEvent.c index 277e7882f..9c726ad59 100644 --- a/modules/libcom/src/osi/os/RTEMS-score/osdEvent.c +++ b/modules/libcom/src/osi/os/RTEMS-score/osdEvent.c @@ -26,6 +26,7 @@ #include "epicsEvent.h" #include "epicsThread.h" +#include "rtemsNamePvt.h" #include "errlog.h" /* #define EPICS_RTEMS_SEMAPHORE_STATS */ @@ -47,12 +48,9 @@ epicsEventCreate(epicsEventInitialState initialState) { rtems_status_code sc; rtems_id sid; - rtems_interrupt_level level; - static char c1 = 'a'; - static char c2 = 'a'; - static char c3 = 'a'; + static uint32_t name; - sc = rtems_semaphore_create (rtems_build_name ('B', c3, c2, c1), + sc = rtems_semaphore_create (next_rtems_name ('B', &name), initialState, RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, @@ -62,26 +60,6 @@ epicsEventCreate(epicsEventInitialState initialState) errlogPrintf ("Can't create binary semaphore: %s\n", rtems_status_text (sc)); return NULL; } - rtems_interrupt_disable (level); - if (c1 == 'z') { - if (c2 == 'z') { - if (c3 == 'z') { - c3 = 'a'; - } - else { - c3++; - } - c2 = 'a'; - } - else { - c2++; - } - c1 = 'a'; - } - else { - c1++; - } - rtems_interrupt_enable (level); return (epicsEventId)sid; } diff --git a/modules/libcom/src/osi/os/RTEMS-score/osdMutex.c b/modules/libcom/src/osi/os/RTEMS-score/osdMutex.c index 44adf2f6e..74fad8f81 100644 --- a/modules/libcom/src/osi/os/RTEMS-score/osdMutex.c +++ b/modules/libcom/src/osi/os/RTEMS-score/osdMutex.c @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -26,97 +27,84 @@ #include "epicsStdio.h" #include "epicsMutex.h" +#include "epicsMutexImpl.h" +#include "rtemsNamePvt.h" #include "epicsEvent.h" #include "errlog.h" -#define RTEMS_FAST_MUTEX -/* #define EPICS_RTEMS_SEMAPHORE_STATS */ -/* - * Some performance tuning instrumentation - */ -#ifdef EPICS_RTEMS_SEMAPHORE_STATS -unsigned long semMstat[4]; -#define SEMSTAT(i) semMstat[i]++; -#else -#define SEMSTAT(i) -#endif - -struct epicsMutexOSD * -epicsMutexOsdCreate(void) +uint32_t next_rtems_name(char prefix, uint32_t* counter) +{ + uint32_t next; + rtems_interrupt_level level; + char a, b, c; + + rtems_interrupt_disable (level); + next = *counter; + *counter = (next+1)%(26u*26u*26u); + rtems_interrupt_enable (level); + + a = 'a' + (next % 26u); + next /= 26u; + b = 'a' + (next % 26u); + next /= 26u; + c = 'a' + (next % 26u); // modulo should be redundant, but ... paranoia + + return rtems_build_name(prefix, a, b, c); +} + +void epicsMutexOsdSetup(void) +{ + // TODO: use RTEMS_SYSINIT_ITEM() ? + if(!epicsMutexGlobalLock.osd) { + epicsMutexOsdPrepare(&epicsMutexGlobalLock); + } +} + +long epicsMutexOsdPrepare(struct epicsMutexParm *mutex) { rtems_status_code sc; rtems_id sid; rtems_interrupt_level level; - static char c1 = 'a'; - static char c2 = 'a'; - static char c3 = 'a'; + static uint32_t name; - sc = rtems_semaphore_create (rtems_build_name ('M', c3, c2, c1), + sc = rtems_semaphore_create (next_rtems_name ('M', &name), 1, RTEMS_PRIORITY|RTEMS_BINARY_SEMAPHORE|RTEMS_INHERIT_PRIORITY|RTEMS_NO_PRIORITY_CEILING|RTEMS_LOCAL, 0, &sid); if (sc != RTEMS_SUCCESSFUL) { errlogPrintf ("Can't create mutex semaphore: %s\n", rtems_status_text (sc)); - return NULL; + return ENOMEM; } - rtems_interrupt_disable (level); - if (c1 == 'z') { - if (c2 == 'z') { - if (c3 == 'z') { - c3 = 'a'; - } - else { - c3++; - } - c2 = 'a'; - } - else { - c2++; - } - c1 = 'a'; - } - else { - c1++; - } - rtems_interrupt_enable (level); -#ifdef RTEMS_FAST_MUTEX { - Semaphore_Control *the_semaphore; - Objects_Locations location; + Objects_Locations location; - the_semaphore = _Semaphore_Get( sid, &location ); - _Thread_Enable_dispatch(); + mutex->osd = _Semaphore_Get( sid, &location ); + _Thread_Enable_dispatch(); /* _Semaphore_Get() disables */ - return (struct epicsMutexOSD *)the_semaphore; + return 0; } -#endif - return (struct epicsMutexOSD *)sid; } -void epicsMutexOsdDestroy(struct epicsMutexOSD * id) +void epicsMutexOsdCleanup(struct epicsMutexParm *mutex) { rtems_status_code sc; rtems_id sid; -#ifdef RTEMS_FAST_MUTEX - Semaphore_Control *the_semaphore = (Semaphore_Control *)id; + Semaphore_Control *the_semaphore = mutex->osd; sid = the_semaphore->Object.id; -#else - sid = (rtems_id)id; -#endif sc = rtems_semaphore_delete (sid); if (sc == RTEMS_RESOURCE_IN_USE) { rtems_semaphore_release (sid); sc = rtems_semaphore_delete (sid); } if (sc != RTEMS_SUCCESSFUL) - errlogPrintf ("Can't destroy semaphore %p (%lx): %s\n", id, (unsigned long)sid, rtems_status_text (sc)); + errlogPrintf ("Can't destroy semaphore %p (%lx): %s\n", + mutex, (unsigned long)sid, rtems_status_text (sc)); } -void epicsMutexOsdUnlock(struct epicsMutexOSD * id) +void epicsMutexUnlock(struct epicsMutexParm *mutex) { -#ifdef RTEMS_FAST_MUTEX - Semaphore_Control *the_semaphore = (Semaphore_Control *)id; + Semaphore_Control *the_semaphore = mutex->osd; _Thread_Disable_dispatch(); _CORE_mutex_Surrender ( &the_semaphore->Core_control.mutex, @@ -124,18 +112,13 @@ void epicsMutexOsdUnlock(struct epicsMutexOSD * id) NULL ); _Thread_Enable_dispatch(); -#else - epicsEventSignal (id); -#endif } -epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD * id) +epicsMutexLockStatus epicsMutexLock(struct epicsMutexParm *mutex) { -#ifdef RTEMS_FAST_MUTEX - Semaphore_Control *the_semaphore = (Semaphore_Control *)id; + Semaphore_Control *the_semaphore = mutex->osd; ISR_Level level; - SEMSTAT(0) _ISR_Disable( level ); _CORE_mutex_Seize( &the_semaphore->Core_control.mutex, @@ -148,19 +131,12 @@ epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD * id) return epicsMutexLockOK; else return epicsMutexLockError; -#else - SEMSTAT(0) - return((epicsEventWait (id) == epicsEventWaitOK) - ?epicsMutexLockOK : epicsMutexLockError); -#endif } -epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * id) +epicsMutexLockStatus epicsMutexTryLock(struct epicsMutexParm *mutex) { -#ifdef RTEMS_FAST_MUTEX - Semaphore_Control *the_semaphore = (Semaphore_Control *)id; + Semaphore_Control *the_semaphore = mutex->osd; ISR_Level level; - SEMSTAT(2) _ISR_Disable( level ); _CORE_mutex_Seize( &the_semaphore->Core_control.mutex, @@ -175,25 +151,12 @@ epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * id) return epicsMutexLockTimeout; else return epicsMutexLockError; -#else - epicsEventWaitStatus status; - SEMSTAT(2) - status = epicsEventTryWait(id); - return((status==epicsEventWaitOK - ? epicsMutexLockOK - : (status==epicsEventWaitTimeout) - ? epicsMutexLockTimeout - : epicsMutexLockError)); -#endif } -LIBCOM_API void epicsMutexOsdShow(struct epicsMutexOSD * id,unsigned int level) +LIBCOM_API void epicsMutexOsdShow(struct epicsMutexParm *mutex,unsigned int level) { -#ifdef RTEMS_FAST_MUTEX - Semaphore_Control *the_semaphore = (Semaphore_Control *)id; - id = (struct epicsMutexOSD *)the_semaphore->Object.id; -#endif - epicsEventShow ((epicsEventId)id,level); + Semaphore_Control *the_semaphore = mutex->osd; + epicsEventShow ((epicsEventId)the_semaphore->Object.id,level); } void epicsMutexOsdShowAll(void) {} diff --git a/modules/libcom/src/osi/os/RTEMS-score/osdThread.c b/modules/libcom/src/osi/os/RTEMS-score/osdThread.c index 7d37bbe48..e34717a36 100644 --- a/modules/libcom/src/osi/os/RTEMS-score/osdThread.c +++ b/modules/libcom/src/osi/os/RTEMS-score/osdThread.c @@ -31,6 +31,7 @@ #include "epicsStdio.h" #include "errlog.h" #include "epicsMutex.h" +#include "epicsMutexImpl.h" #include "epicsString.h" #include "epicsThread.h" #include "cantProceed.h" @@ -59,7 +60,7 @@ struct taskVar { unsigned int threadVariableCapacity; void **threadVariables; }; -static struct epicsMutexOSD *taskVarMutex; +static struct epicsMutexParm taskVarMutex = {ELLNODE_INIT, __FILE__, __LINE__}; static struct taskVar *taskVarHead; #define RTEMS_NOTEPAD_TASKVAR 11 @@ -67,15 +68,7 @@ static struct taskVar *taskVarHead; * Support for `once-only' execution */ static volatile int initialized = 0; /* strictly speaking 'volatile' is not enough here, but it shouldn't hurt */ -static struct epicsMutexOSD *onceMutex; - -static -void epicsMutexOsdMustLock(struct epicsMutexOSD * L) -{ - while(epicsMutexOsdLock(L)!=epicsMutexLockOK) { - cantProceed("epicsThreadOnce() mutex error"); - } -} +static struct epicsMutexParm onceMutex = {ELLNODE_INIT, __FILE__, __LINE__}; /* * Just map osi 0 to 99 into RTEMS 199 to 100 @@ -161,13 +154,13 @@ epicsThreadGetStackSize (epicsThreadStackSizeClass size) static void taskVarLock (void) { - epicsMutexOsdMustLock (taskVarMutex); + epicsMutexMustLock (&taskVarMutex); } static void taskVarUnlock (void) { - epicsMutexOsdUnlock (taskVarMutex); + epicsMutexUnlock (&taskVarMutex); } static @@ -243,7 +236,7 @@ setThreadInfo(rtems_id tid, const char *name, EPICSTHREADFUNC funptr, v->threadVariables = NULL; v->isRunning = 1; if (joinable) { - char c[3]; + char c[3] = {0,0,0}; strncpy(c, v->name, 3); sc = rtems_barrier_create(rtems_build_name('~', c[0], c[1], c[2]), RTEMS_BARRIER_AUTOMATIC_RELEASE | RTEMS_LOCAL, @@ -288,10 +281,8 @@ epicsThreadInit (void) rtems_task_priority old; rtems_task_set_priority (RTEMS_SELF, epicsThreadGetOssPriorityValue(99), &old); - onceMutex = epicsMutexOsdCreate(); - taskVarMutex = epicsMutexOsdCreate(); - if (!onceMutex || !taskVarMutex) - cantProceed("epicsThreadInit() can't create global mutexes\n"); + epicsMutexOsdPrepare(&taskVarMutex); + epicsMutexOsdPrepare(&onceMutex); rtems_task_ident (RTEMS_SELF, 0, &tid); if(setThreadInfo (tid, "_main_", NULL, NULL, 0) != RTEMS_SUCCESSFUL) cantProceed("epicsThreadInit() unable to setup _main_"); @@ -317,7 +308,7 @@ epicsThreadCreateOpt ( unsigned int stackSize; rtems_id tid; rtems_status_code sc; - char c[4]; + char c[4] = {0,0,0,0}; if (!initialized) epicsThreadInit(); @@ -612,26 +603,26 @@ void epicsThreadOnce(epicsThreadOnceId *id, void(*func)(void *), void *arg) #define EPICS_THREAD_ONCE_DONE (epicsThreadId) 1 if (!initialized) epicsThreadInit(); - epicsMutexOsdMustLock(onceMutex); + epicsMutexMustLock(&onceMutex); if (*id != EPICS_THREAD_ONCE_DONE) { if (*id == EPICS_THREAD_ONCE_INIT) { /* first call */ *id = epicsThreadGetIdSelf(); /* mark active */ - epicsMutexOsdUnlock(onceMutex); + epicsMutexUnlock(&onceMutex); func(arg); - epicsMutexOsdMustLock(onceMutex); + epicsMutexMustLock(&onceMutex); *id = EPICS_THREAD_ONCE_DONE; /* mark done */ } else if (*id == epicsThreadGetIdSelf()) { - epicsMutexOsdUnlock(onceMutex); + epicsMutexUnlock(&onceMutex); cantProceed("Recursive epicsThreadOnce() initialization\n"); } else while (*id != EPICS_THREAD_ONCE_DONE) { /* Another thread is in the above func(arg) call. */ - epicsMutexOsdUnlock(onceMutex); + epicsMutexUnlock(&onceMutex); epicsThreadSleep(epicsThreadSleepQuantum()); - epicsMutexOsdMustLock(onceMutex); + epicsMutexMustLock(&onceMutex); } } - epicsMutexOsdUnlock(onceMutex); + epicsMutexUnlock(&onceMutex); } /* diff --git a/modules/libcom/src/osi/os/RTEMS-score/rtemsNamePvt.h b/modules/libcom/src/osi/os/RTEMS-score/rtemsNamePvt.h new file mode 100644 index 000000000..436728902 --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS-score/rtemsNamePvt.h @@ -0,0 +1,20 @@ +/*************************************************************************\ +* Copyright (c) 2023 Michael Davidsaver +* SPDX-License-Identifier: EPICS +* EPICS Base is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef RTEMSNAMEPVT_H +#define RTEMSNAMEPVT_H + +#include + +/* Compute rtems_build_name(prefix, A, B, C) where A, B, C are the letters a-z. + * eg. "Qaaa" + * + * 'counter' is incremented atomically during each call. + */ +uint32_t next_rtems_name(char prefix, uint32_t* counter); + +#endif // RTEMSNAMEPVT_H diff --git a/modules/libcom/src/osi/os/WIN32/epicsMath.h b/modules/libcom/src/osi/os/WIN32/epicsMath.h index 883cd9b0b..d0d02330f 100644 --- a/modules/libcom/src/osi/os/WIN32/epicsMath.h +++ b/modules/libcom/src/osi/os/WIN32/epicsMath.h @@ -19,6 +19,15 @@ #define finite(D) _finite(D) #endif +#if !defined(__cplusplus) || (defined(_MSC_VER) && (_MSC_VER < 1800)) + +/* these are macros in C but usually inline functions in C++ + * so macro ifndef does not work as a check in c++ + * In a recent VS2022 update redefining as macros causes a conflict and + * compile time error. It looks like VS2013 and above supply + * isnan and isinf functions for in C++ so no need to create them + */ + #ifndef isnan #define isnan(D) _isnan(D) #endif @@ -27,6 +36,8 @@ #define isinf(D) ( !_finite(D) && !_isnan(D) ) #endif +#endif /* ifndef __cplusplus */ + #ifdef __cplusplus extern "C" { #endif diff --git a/modules/libcom/src/osi/os/WIN32/osdMutex.c b/modules/libcom/src/osi/os/WIN32/osdMutex.c index b18e849f4..ce2c615eb 100644 --- a/modules/libcom/src/osi/os/WIN32/osdMutex.c +++ b/modules/libcom/src/osi/os/WIN32/osdMutex.c @@ -20,147 +20,59 @@ #include #include -#define VC_EXTRALEAN -#define STRICT -#include -#if _WIN32_WINNT < 0x0501 -# error Minimum supported is Windows XP -#endif - #define EPICS_PRIVATE_API #include "libComAPI.h" #include "epicsMutex.h" +#include "epicsMutexImpl.h" +#include "epicsThread.h" #include "epicsAssert.h" #include "epicsStdio.h" -typedef struct epicsMutexOSD { - union { - HANDLE mutex; - CRITICAL_SECTION criticalSection; - } os; -} epicsMutexOSD; - -static BOOL thisIsNT = FALSE; -static LONG weHaveInitialized = 0; - -/* - * epicsMutexCreate () - */ -epicsMutexOSD * epicsMutexOsdCreate ( void ) +static epicsThreadOnceId epicsMutexOsdOnce = EPICS_THREAD_ONCE_INIT; +static void epicsMutexOsdInit(void* unused) { - epicsMutexOSD * pSem; + (void)unused; + InitializeCriticalSection(&epicsMutexGlobalLock.osd); +} - if ( ! weHaveInitialized ) { - BOOL status; - OSVERSIONINFO osInfo; - osInfo.dwOSVersionInfoSize = sizeof ( OSVERSIONINFO ); - status = GetVersionEx ( & osInfo ); - thisIsNT = status && ( osInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ); - weHaveInitialized = 1; - } +void epicsMutexOsdSetup() +{ + epicsThreadOnce(&epicsMutexOsdOnce, &epicsMutexOsdInit, NULL); +} - pSem = malloc ( sizeof (*pSem) ); - if ( pSem ) { - if ( thisIsNT ) { - InitializeCriticalSection ( &pSem->os.criticalSection ); - } - else { - pSem->os.mutex = CreateMutex ( NULL, FALSE, NULL ); - if ( pSem->os.mutex == 0 ) { - free ( pSem ); - pSem = 0; - } - } - } - return pSem; +long epicsMutexOsdPrepare(struct epicsMutexParm *mutex) +{ + InitializeCriticalSection(&mutex->osd); + return 0; } -/* - * epicsMutexOsdDestroy () - */ -void epicsMutexOsdDestroy ( epicsMutexOSD * pSem ) +void epicsMutexOsdCleanup(struct epicsMutexParm *mutex) { - if ( thisIsNT ) { - DeleteCriticalSection ( &pSem->os.criticalSection ); - } - else { - CloseHandle ( pSem->os.mutex ); - } - free ( pSem ); + DeleteCriticalSection(&mutex->osd); } -/* - * epicsMutexOsdUnlock () - */ -void epicsMutexOsdUnlock ( epicsMutexOSD * pSem ) +void epicsStdCall epicsMutexUnlock ( struct epicsMutexParm *mutex ) { - if ( thisIsNT ) { - LeaveCriticalSection ( &pSem->os.criticalSection ); - } - else { - BOOL success = ReleaseMutex ( pSem->os.mutex ); - assert ( success ); - } + LeaveCriticalSection ( &mutex->osd ); } -/* - * epicsMutexOsdLock () - */ -epicsMutexLockStatus epicsMutexOsdLock ( epicsMutexOSD * pSem ) +epicsMutexLockStatus epicsStdCall epicsMutexLock ( struct epicsMutexParm *mutex ) { - if ( thisIsNT ) { - EnterCriticalSection ( &pSem->os.criticalSection ); - } - else { - DWORD status = WaitForSingleObject ( pSem->os.mutex, INFINITE ); - if ( status != WAIT_OBJECT_0 ) { - return epicsMutexLockError; - } - } + EnterCriticalSection ( &mutex->osd ); return epicsMutexLockOK; } -/* - * epicsMutexOsdTryLock () - */ -epicsMutexLockStatus epicsMutexOsdTryLock ( epicsMutexOSD * pSem ) +epicsMutexLockStatus epicsStdCall epicsMutexTryLock ( struct epicsMutexParm *mutex ) { - if ( thisIsNT ) { - if ( TryEnterCriticalSection ( &pSem->os.criticalSection ) ) { - return epicsMutexLockOK; - } - else { - return epicsMutexLockTimeout; - } - } - else { - DWORD status = WaitForSingleObject ( pSem->os.mutex, 0 ); - if ( status != WAIT_OBJECT_0 ) { - if (status == WAIT_TIMEOUT) { - return epicsMutexLockTimeout; - } - else { - return epicsMutexLockError; - } - } - } - return epicsMutexLockOK; + return TryEnterCriticalSection ( &mutex->osd ) ? epicsMutexLockOK : epicsMutexLockTimeout; } -/* - * epicsMutexOsdShow () - */ -void epicsMutexOsdShow ( epicsMutexOSD * pSem, unsigned level ) +void epicsMutexOsdShow ( struct epicsMutexParm *mutex, unsigned level ) { - if ( thisIsNT ) { - printf ("epicsMutex: win32 critical section at %p\n", - (void * ) & pSem->os.criticalSection ); - } - else { - printf ( "epicsMutex: win32 mutex at %p\n", - ( void * ) pSem->os.mutex ); - } + (void)level; + printf ("epicsMutex: win32 critical section at %p\n", + (void * ) & mutex->osd ); } void epicsMutexOsdShowAll(void) {} diff --git a/modules/libcom/src/osi/os/default/epicsMMIODef.h b/modules/libcom/src/osi/os/default/epicsMMIODef.h index 7de7b65de..5aa2d9161 100644 --- a/modules/libcom/src/osi/os/default/epicsMMIODef.h +++ b/modules/libcom/src/osi/os/default/epicsMMIODef.h @@ -139,8 +139,12 @@ bswap32(epicsUInt32 value) /* hton* is optimized or a builtin for most compilers * so use it if possible */ +#ifndef bswap16 #define bswap16(v) htons(v) +#endif +#ifndef bswap32 #define bswap32(v) htonl(v) +#endif # define be_ioread16(A) bswap16(nat_ioread16(A)) # define be_ioread32(A) bswap32(nat_ioread32(A)) diff --git a/modules/libcom/src/osi/os/default/gnuReadline.c b/modules/libcom/src/osi/os/default/gnuReadline.c index 11551586e..243b6828a 100644 --- a/modules/libcom/src/osi/os/default/gnuReadline.c +++ b/modules/libcom/src/osi/os/default/gnuReadline.c @@ -109,7 +109,7 @@ osdReadline (const char *prompt, struct readlineContext *context) line[linelen] = '\0'; } context->line = line; - if (line && *line) + if (line && *line && !context->in) add_history(line); return line; } diff --git a/modules/libcom/src/osi/os/default/osdAssert.c b/modules/libcom/src/osi/os/default/osdAssert.c index 2d62bc820..23263df4e 100644 --- a/modules/libcom/src/osi/os/default/osdAssert.c +++ b/modules/libcom/src/osi/os/default/osdAssert.c @@ -20,12 +20,14 @@ #include "epicsTime.h" #include "cantProceed.h" #include "epicsStackTrace.h" +#include "envDefs.h" void epicsAssert (const char *pFile, const unsigned line, const char *pExp, const char *pAuthorName) { epicsTimeStamp current; + int shouldAbort = 0; errlogPrintf("\n\n\n" "A call to 'assert(%s)'\n" @@ -50,6 +52,13 @@ void epicsAssert (const char *pFile, const unsigned line, errlogPrintf("Please E-mail this message to %s or to tech-talk@aps.anl.gov\n", pAuthorName); - errlogPrintf("Calling epicsThreadSuspendSelf()\n"); - epicsThreadSuspendSelf (); + if (envGetBoolConfigParam(&EPICS_ABORT_ON_ASSERT, &shouldAbort) == 0 && shouldAbort) { + errlogPrintf("Calling abort()\n"); + errlogFlush(); + abort(); + } + else { + errlogPrintf("Calling epicsThreadSuspendSelf()\n"); + epicsThreadSuspendSelf (); + } } diff --git a/modules/libcom/src/osi/os/posix/osdMutex.c b/modules/libcom/src/osi/os/posix/osdMutex.c index 85040c5f4..4bd87cf88 100644 --- a/modules/libcom/src/osi/os/posix/osdMutex.c +++ b/modules/libcom/src/osi/os/posix/osdMutex.c @@ -25,14 +25,15 @@ #include #define EPICS_PRIVATE_API +#define epicsStdioStdStreams +#define epicsStdioStdPrintfEtc #include "epicsMutex.h" +#include "epicsMutexImpl.h" #include "osdPosixMutexPriv.h" #include "cantProceed.h" -#include "epicsTime.h" #include "errlog.h" #include "epicsStdio.h" -#include "epicsAssert.h" #define checkStatus(status,message) \ if((status)) { \ @@ -119,62 +120,62 @@ static int mutexLock(pthread_mutex_t *id) return status; } -typedef struct epicsMutexOSD { - pthread_mutex_t lock; -} epicsMutexOSD; +/* used if OS does not support statically allocated mutex */ +static pthread_once_t epicsMutexOsdOnce = PTHREAD_ONCE_INIT; -epicsMutexOSD * epicsMutexOsdCreate(void) { - epicsMutexOSD *pmutex; - int status; - - pmutex = calloc(1, sizeof(*pmutex)); - if(!pmutex) - return NULL; +static void epicsMutexOsdInit(void) +{ + int ret = pthread_mutex_init(&epicsMutexGlobalLock.osd, NULL); + if(ret) { + /* something has gone wrong early. Not much can be done...*/ + fprintf(stderr, "osdMutex early init failure %d.\n", ret); + abort(); + } +} - status = osdPosixMutexInit(&pmutex->lock, PTHREAD_MUTEX_RECURSIVE); - if (!status) - return pmutex; +void epicsMutexOsdSetup() +{ + int ret = pthread_once(&epicsMutexOsdOnce, &epicsMutexOsdInit); + if(ret) { + /* ditto...*/ + fprintf(stderr, "osdMutex early once failure %d.\n", ret); + abort(); + } +} - free(pmutex); - return NULL; +long epicsMutexOsdPrepare(struct epicsMutexParm *pmutex) { + return osdPosixMutexInit(&pmutex->osd, PTHREAD_MUTEX_RECURSIVE); } -void epicsMutexOsdDestroy(struct epicsMutexOSD * pmutex) +void epicsMutexOsdCleanup(struct epicsMutexParm *pmutex) { - int status; - - status = pthread_mutex_destroy(&pmutex->lock); + int status = pthread_mutex_destroy(&pmutex->osd); checkStatus(status, "pthread_mutex_destroy"); - free(pmutex); } -void epicsMutexOsdUnlock(struct epicsMutexOSD * pmutex) +void epicsMutexUnlock(struct epicsMutexParm * pmutex) { - int status; - - status = pthread_mutex_unlock(&pmutex->lock); + int status = pthread_mutex_unlock(&pmutex->osd); checkStatus(status, "pthread_mutex_unlock epicsMutexOsdUnlock"); } -epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD * pmutex) +epicsMutexLockStatus epicsMutexLock(struct epicsMutexParm * pmutex) { - int status; - - status = mutexLock(&pmutex->lock); + int status = mutexLock(&pmutex->osd); if (status == EINVAL) return epicsMutexLockError; if(status) { - errlogMessage("epicsMutex pthread_mutex_lock failed: error epicsMutexOsdLock\n"); + errlogMessage("epicsMutex pthread_mutex_lock failed: error epicsMutexLock\n"); return epicsMutexLockError; } return epicsMutexLockOK; } -epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * pmutex) +epicsMutexLockStatus epicsMutexTryLock(struct epicsMutexParm * pmutex) { int status; if (!pmutex) return epicsMutexLockError; - status = pthread_mutex_trylock(&pmutex->lock); + status = pthread_mutex_trylock(&pmutex->osd); if (status == EINVAL) return epicsMutexLockError; if (status == EBUSY) return epicsMutexLockTimeout; if(status) { @@ -184,12 +185,13 @@ epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * pmutex) return epicsMutexLockOK; } -void epicsMutexOsdShow(struct epicsMutexOSD * pmutex, unsigned int level) +void epicsMutexOsdShow(struct epicsMutexParm * pmutex, unsigned int level) { + (void)level; /* GLIBC w/ NTPL is passing the &lock.__data.__lock as the first argument (UADDR) * of the futex() syscall. __lock is at offset 0 of the enclosing structures. */ - printf(" pthread_mutex_t* uaddr=%p\n", &pmutex->lock); + epicsStdoutPrintf(" pthread_mutex_t* uaddr=%p\n", &pmutex->osd); } void epicsMutexOsdShowAll(void) @@ -198,11 +200,11 @@ void epicsMutexOsdShowAll(void) int proto = -1; int ret = pthread_mutexattr_getprotocol(&globalAttrRecursive, &proto); if(ret) { - printf("PI maybe not enabled: %d\n", ret); + epicsStdoutPrintf("PI maybe not enabled: %d\n", ret); } else { - printf("PI is%s enabled\n", proto==PTHREAD_PRIO_INHERIT ? "" : " not"); + epicsStdoutPrintf("PI is%s enabled\n", proto==PTHREAD_PRIO_INHERIT ? "" : " not"); } #else - printf("PI not supported\n"); + epicsStdoutPrintf("PI not supported\n"); #endif } diff --git a/modules/libcom/src/osi/os/posix/osdThread.c b/modules/libcom/src/osi/os/posix/osdThread.c index 6142e73c5..e9ea2dfbc 100644 --- a/modules/libcom/src/osi/os/posix/osdThread.c +++ b/modules/libcom/src/osi/os/posix/osdThread.c @@ -615,6 +615,10 @@ epicsThreadCreateOpt(const char * name, start_routine, pthreadInfo); if (status==EPERM) { /* Try again without SCHED_FIFO*/ + if (pthreadInfo->joinable) { + int cnt = epicsAtomicDecrIntT(&pthreadInfo->refcnt); + assert(cnt==1); + } free_threadInfo(pthreadInfo); pthreadInfo = init_threadInfo(name, opts->priority, stackSize, @@ -630,12 +634,15 @@ epicsThreadCreateOpt(const char * name, if (status) { if (pthreadInfo->joinable) { /* release extra ref which would have been for epicsThreadMustJoin() */ - epicsAtomicDecrIntT(&pthreadInfo->refcnt); + int cnt = epicsAtomicDecrIntT(&pthreadInfo->refcnt); + assert(cnt==1); } - free_threadInfo(pthreadInfo); return 0; } + /* New thread starting and is now responsible for one free_threadInfo(). + * If joinable, then caller responsible for arranging one epicsThreadMustJoin() + */ status = pthread_sigmask(SIG_SETMASK, &oldSig, NULL); checkStatusOnce(status, "pthread_sigmask"); @@ -680,34 +687,54 @@ static epicsThreadOSD *createImplicit(void) void epicsThreadMustJoin(epicsThreadId id) { - void *ret = NULL; int status; + int prev; + epicsThreadId self; - if(!id) { + if(!id) return; - } else if(epicsAtomicCmpAndSwapIntT(&id->joinable, 1, 0)!=1) { - if(epicsThreadGetIdSelf()==id) { - errlogPrintf("Warning: %s thread self-join of unjoinable\n", id->name); + + prev = epicsAtomicCmpAndSwapIntT(&id->joinable, 1, 0); + self = epicsThreadGetIdSelf(); + + if(prev==0) { + /* join of unjoinable */ + if(self==id) { + errlogPrintf(ERL_WARNING ": %s thread self-join of unjoinable\n", id->name); + return; } else { /* try to error nicely, however in all likelihood de-ref of * 'id' has already caused SIGSEGV as we are racing thread exit, * which free's 'id'. */ - cantProceed("Error: %s thread not joinable.\n", id->name); + cantProceed(ERL_ERROR ": %s can't join unjoinable %s\n", self->name, id->name); } - return; + + } else if(prev!=1) { /* also not 0, the only other allowed value. */ + cantProceed(ERL_ERROR ": %s joins corrupt thread handle\n", self->name); } - status = pthread_join(id->tid, &ret); - if(status == EDEADLK) { - /* Thread can't join itself (directly or indirectly) - * so we detach instead. + /* from this point, we are responsible to either detach or join (or leak memory) */ + + if(self!=id) { + status = pthread_join(id->tid, NULL); + checkStatusOnce(status, "pthread_join"); + /* on error, continue and attempt to detach */ + + } else { + /* Thread self pthread_join() isn't portable. + * We choose to allow, and treat as detach. */ + status = EDEADLK; + } + + if(status) { status = pthread_detach(id->tid); checkStatusOnce(status, "pthread_detach"); - } else checkStatusOnce(status, "pthread_join"); - free_threadInfo(id); + } + + free_threadInfo(id); /* release joinable reference */ } LIBCOM_API void epicsStdCall epicsThreadSuspendSelf(void) diff --git a/modules/libcom/src/osi/os/vxWorks/osdMutex.c b/modules/libcom/src/osi/os/vxWorks/osdMutex.c index d772bf2dd..680f9e663 100644 --- a/modules/libcom/src/osi/os/vxWorks/osdMutex.c +++ b/modules/libcom/src/osi/os/vxWorks/osdMutex.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -23,30 +24,47 @@ int sysClkRateGet(void); #define EPICS_PRIVATE_API #include "epicsMutex.h" - -struct epicsMutexOSD * epicsMutexOsdCreate(void) +#include "epicsMutexImpl.h" + +void epicsMutexOsdSetup(void) +{ + if(!epicsMutexGlobalLock.osd) { + epicsMutexOsdPrepare(&epicsMutexGlobalLock); + } +} + +long epicsMutexOsdPrepare(struct epicsMutexParm *mutex) +{ + mutex->osd = semMCreate(SEM_DELETE_SAFE|SEM_INVERSION_SAFE|SEM_Q_PRIORITY); + return mutex->osd ? 0 : ENOMEM; +} + +void epicsMutexOsdCleanup(struct epicsMutexParm *mutex) { - return((struct epicsMutexOSD *) - semMCreate(SEM_DELETE_SAFE|SEM_INVERSION_SAFE|SEM_Q_PRIORITY)); + semDelete(mutex->osd); } -void epicsMutexOsdDestroy(struct epicsMutexOSD * id) +epicsMutexLockStatus epicsMutexLock(struct epicsMutexParm *mutex) { - semDelete((SEM_ID)id); + return semTake(mutex->osd,WAIT_FOREVER)==OK ? epicsMutexLockOK : epicsMutexLockError; } -epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * id) +epicsMutexLockStatus epicsMutexTryLock(struct epicsMutexParm *mutex) { - int status; - status = semTake((SEM_ID)id,NO_WAIT); + int status = semTake(mutex->osd,NO_WAIT); if(status==OK) return(epicsMutexLockOK); if(errno==S_objLib_OBJ_UNAVAILABLE) return(epicsMutexLockTimeout); return(epicsMutexLockError); } -void epicsMutexOsdShow(struct epicsMutexOSD * id,unsigned int level) +void epicsMutexUnlock(struct epicsMutexParm *mutex) +{ + semGive(mutex->osd); +} + +void epicsMutexOsdShow(struct epicsMutexParm *mutex,unsigned int level) { - semShow((SEM_ID)id,level); + semShow(mutex->osd,level); } void epicsMutexOsdShowAll(void) {} diff --git a/modules/libcom/src/osi/os/vxWorks/osdMutex.h b/modules/libcom/src/osi/os/vxWorks/osdMutex.h index 2eb8b1ba2..f3f663622 100644 --- a/modules/libcom/src/osi/os/vxWorks/osdMutex.h +++ b/modules/libcom/src/osi/os/vxWorks/osdMutex.h @@ -13,13 +13,3 @@ #include #include - -/* If the macro is replaced by inline it is necessary to say - static __inline__ - but then a warning message appears everywhere osdMutex.h is included -*/ - -#define epicsMutexOsdUnlock(ID) semGive((SEM_ID)(ID)) - -#define epicsMutexOsdLock(ID) \ -(semTake((SEM_ID)(ID),WAIT_FOREVER)==OK ? epicsMutexLockOK : epicsMutexLockError) diff --git a/modules/libcom/test/yajlTestConverter.pl b/modules/libcom/test/yajlTestConverter.pl old mode 100644 new mode 100755 diff --git a/modules/pvAccess/documentation/commit-gh.sh b/modules/pvAccess/documentation/commit-gh.sh old mode 100644 new mode 100755 diff --git a/modules/pvAccess/runTestServer b/modules/pvAccess/runTestServer old mode 100644 new mode 100755 diff --git a/modules/pvAccess/scripts/gcovr b/modules/pvAccess/scripts/gcovr old mode 100644 new mode 100755 diff --git a/modules/pvDatabase/Doxyfile b/modules/pvDatabase/Doxyfile index d34ed3e83..7c2c2cd2b 100644 --- a/modules/pvDatabase/Doxyfile +++ b/modules/pvDatabase/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = pvDatabaseCPP # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 4.7.1 +PROJECT_NUMBER = 4.7.2 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/modules/pvDatabase/configure/CONFIG_PVDATABASE_VERSION b/modules/pvDatabase/configure/CONFIG_PVDATABASE_VERSION index 77c0f5448..8b281237e 100644 --- a/modules/pvDatabase/configure/CONFIG_PVDATABASE_VERSION +++ b/modules/pvDatabase/configure/CONFIG_PVDATABASE_VERSION @@ -2,7 +2,7 @@ EPICS_PVDATABASE_MAJOR_VERSION = 4 EPICS_PVDATABASE_MINOR_VERSION = 7 -EPICS_PVDATABASE_MAINTENANCE_VERSION = 1 +EPICS_PVDATABASE_MAINTENANCE_VERSION = 2 # Development flag, set to zero for release versions diff --git a/modules/pvDatabase/documentation/RELEASE_NOTES.md b/modules/pvDatabase/documentation/RELEASE_NOTES.md index e3cea46a0..eaa58b59f 100644 --- a/modules/pvDatabase/documentation/RELEASE_NOTES.md +++ b/modules/pvDatabase/documentation/RELEASE_NOTES.md @@ -2,6 +2,13 @@ This document summarizes the changes to the module between releases. +## Release 4.7.2 (EPICS 7.0.9, Feb 2025) + +* Resolved issue with changed field set in the case where the top level (master) +field ("_") is not requested by the client, but the master field callback causes +all fields to be marked as updated, rather than only those fields that have +actually been modified. + ## Release 4.7.1 (EPICS 7.0.8, Dec 2023) * Added data distributor plugin which can be used for distributing data between diff --git a/modules/pvDatabase/src/copy/pvCopy.cpp b/modules/pvDatabase/src/copy/pvCopy.cpp index 7694ecf78..0916878ed 100644 --- a/modules/pvDatabase/src/copy/pvCopy.cpp +++ b/modules/pvDatabase/src/copy/pvCopy.cpp @@ -432,6 +432,7 @@ bool PVCopy::init(epics::pvData::PVStructurePtr const &pvRequest) PVStructurePtr pvMasterStructure = pvMaster; size_t len = pvRequest->getPVFields().size(); bool entireMaster = false; + requestHasMasterField = false; PVStructurePtr pvOptions; if(len==0) { entireMaster = true; @@ -441,6 +442,9 @@ bool PVCopy::init(epics::pvData::PVStructurePtr const &pvRequest) // then assume the top level PV structure is requested PVStructurePtr masterFieldPtr = pvMaster->getSubField("_"); PVStructurePtr requestFieldPtr = pvRequest->getSubField("_"); + if (requestFieldPtr) { + requestHasMasterField = true; + } if (!masterFieldPtr && requestFieldPtr) { entireMaster = true; pvOptions = requestFieldPtr->getSubField("_options"); diff --git a/modules/pvDatabase/src/database/pvRecord.cpp b/modules/pvDatabase/src/database/pvRecord.cpp index 5357fc86f..e3b72251d 100644 --- a/modules/pvDatabase/src/database/pvRecord.cpp +++ b/modules/pvDatabase/src/database/pvRecord.cpp @@ -481,27 +481,30 @@ void PVRecordStructure::init() for(size_t i=0; igetField()->getType()==structure) { - PVStructurePtr xxx = static_pointer_cast(pvField); - PVRecordStructurePtr pvRecordStructure( + PVStructurePtr xxx = static_pointer_cast(pvField); + PVRecordStructurePtr pvRecordStructure( new PVRecordStructure(xxx,self,pvRecord)); - pvRecordFields->push_back(pvRecordStructure); - pvRecordStructure->init(); + pvRecordFields->push_back(pvRecordStructure); + pvRecordStructure->init(); } else { - PVRecordFieldPtr pvRecordField( + PVRecordFieldPtr pvRecordField( new PVRecordField(pvField,self,pvRecord)); - pvRecordFields->push_back(pvRecordField); - pvRecordField->init(); - // Master field listeners will be called before - // calling listeners for the first subfield - if (!masterFieldCallbackSet) { - masterFieldCallbackSet = true; - // Find master field - PVRecordStructurePtr p = pvRecordField->parent.lock(); - while (p) { - pvRecordField->master = p; - p = p->parent.lock(); - } - } + pvRecordFields->push_back(pvRecordField); + pvRecordField->init(); + // Master field listeners will be called before + // calling listeners for the first subfield + if (!masterFieldCallbackSet) { + masterFieldCallbackSet = true; + // Find master field + PVRecordStructurePtr p = pvRecordField->parent.lock(); + while (p) { + PVRecordStructurePtr p2 = p->parent.lock(); + if (!p2) { + pvRecordField->master = p; + } + p = p2; + } + } } } } diff --git a/modules/pvDatabase/src/pv/pvStructureCopy.h b/modules/pvDatabase/src/pv/pvStructureCopy.h index 3ae37a3a6..8bce17069 100644 --- a/modules/pvDatabase/src/pv/pvStructureCopy.h +++ b/modules/pvDatabase/src/pv/pvStructureCopy.h @@ -167,6 +167,10 @@ class epicsShareClass PVCopy : * name is the subField name and value is the subField value. */ epics::pvData::PVStructurePtr getOptions(std::size_t fieldOffset); + /** + * Is master field requested? + */ + bool isMasterFieldRequested() const {return requestHasMasterField;} /** * For debugging. */ @@ -183,6 +187,7 @@ class epicsShareClass PVCopy : CopyNodePtr headNode; epics::pvData::PVStructurePtr cacheInitStructure; epics::pvData::BitSetPtr ignorechangeBitSet; + bool requestHasMasterField; void traverseMaster( CopyNodePtr const &node, diff --git a/modules/pvDatabase/src/pvAccess/monitorFactory.cpp b/modules/pvDatabase/src/pvAccess/monitorFactory.cpp index c467c5009..2e9e7e584 100644 --- a/modules/pvDatabase/src/pvAccess/monitorFactory.cpp +++ b/modules/pvDatabase/src/pvAccess/monitorFactory.cpp @@ -292,6 +292,12 @@ void MonitorLocal::dataPut(PVRecordFieldPtr const & pvRecordField) { cout << "MonitorLocal::dataPut(pvRecordField)" << endl; } + // If this record field is the master field, and the master field was not + // requested, we do not proceed with copy + bool isMasterField = pvRecordField->getPVRecord()->getPVStructure()->getFieldOffset()==0; + if (isMasterField && !pvCopy->isMasterFieldRequested()) { + return; + } if(state!=active) return; { Lock xx(mutex); diff --git a/modules/pvDatabase/test/src/Makefile b/modules/pvDatabase/test/src/Makefile index a7eb5e398..57ee58ee0 100644 --- a/modules/pvDatabase/test/src/Makefile +++ b/modules/pvDatabase/test/src/Makefile @@ -33,3 +33,8 @@ TESTPROD_HOST += testPVAServer testPVAServer_SRCS += testPVAServer.cpp testHarness_SRCS += testPVAServer.cpp TESTS += testPVAServer + +TESTPROD_HOST += testChannelMonitor +testChannelMonitor_SRCS += testChannelMonitor.cpp +testHarness_SRCS += testChannelMonitor.cpp +TESTS += testChannelMonitor diff --git a/modules/pvDatabase/test/src/testChannelMonitor.cpp b/modules/pvDatabase/test/src/testChannelMonitor.cpp new file mode 100644 index 000000000..3d63278e4 --- /dev/null +++ b/modules/pvDatabase/test/src/testChannelMonitor.cpp @@ -0,0 +1,312 @@ +/* testChannelMonitor.cpp */ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * EPICS pvData is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using std::tr1::static_pointer_cast; +using namespace epics::pvData; +using namespace epics::pvAccess; +using namespace epics::pvDatabase; +namespace TR1 = std::tr1; + +static bool debug = true; + +PVStructurePtr createTestPvStructure() +{ + FieldCreatePtr fieldCreate = getFieldCreate(); + StandardFieldPtr standardField = getStandardField() +; + PVDataCreatePtr pvDataCreate = getPVDataCreate(); + + return pvDataCreate->createPVStructure( + fieldCreate->createFieldBuilder()-> + add("id",pvInt) -> + add("x",pvInt) -> + add("y",pvInt) -> + add("z",pvInt) -> + add("alarm",standardField->alarm()) -> + add("timeStamp",standardField->timeStamp()) -> + createStructure()); +} + +class ChannelMonitorRequesterImpl : public MonitorRequester +{ +public: + + ChannelMonitorRequesterImpl(const std::string& channelName_) + : channelName(channelName_) + , lastReceivedPvStructure(createTestPvStructure()) + , lastReceivedBitSet() + { + } + + virtual string getRequesterName() + { + return "ChannelMonitorRequesterImpl"; + } + + virtual void message(const std::string& message, MessageType messageType) + { + cout << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")" << endl; + } + + virtual void monitorConnect(const epics::pvData::Status& status, const Monitor::shared_pointer& /*monitor*/, const Structure::const_shared_pointer& /*structure*/) + { + if (status.isSuccess()) { + // show warning + if (!status.isOK()) { + cout << "[" << channelName << "] channel monitor create: " << status << endl; + } + connectionEvent.signal(); + } + else { + cout << "[" << channelName << "] failed to create channel monitor: " << status << endl; + } + } + + virtual void monitorEvent(const Monitor::shared_pointer& monitor) + { + MonitorElement::shared_pointer element; + while ((element = monitor->poll())) { + cout << "changed/overrun " << *element->changedBitSet << '/' << *element->overrunBitSet << endl; + if (!lastReceivedBitSet) { + lastReceivedBitSet = BitSet::create(element->changedBitSet->size()); + } + lastReceivedBitSet->clear(); + *lastReceivedBitSet |= *element->changedBitSet; + lastReceivedPvStructure->copyUnchecked(*element->pvStructurePtr); + monitor->release(element); + } + } + + virtual void unlisten(const Monitor::shared_pointer& /*monitor*/) + { + } + + bool waitUntilConnected(double timeOut) + { + return connectionEvent.wait(timeOut); + } + + PVStructurePtr getLastReceivedPvStructure() + { + return lastReceivedPvStructure; + } + + BitSetPtr getLastReceivedBitSet() + { + return lastReceivedBitSet; + } + +private: + Event event; + Event connectionEvent; + string channelName; + PVStructurePtr lastReceivedPvStructure; + BitSetPtr lastReceivedBitSet; +}; + +class ChannelRequesterImpl : public ChannelRequester +{ +private: + Event event; + +public: + + virtual string getRequesterName() + { + return "ChannelRequesterImpl"; + }; + + virtual void message(const std::string& message, MessageType messageType) + { + cout << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")" << endl; + } + + virtual void channelCreated(const Status& status, const Channel::shared_pointer& channel) + { + if (status.isSuccess()) { + // show warning + if (!status.isOK()) { + cout << "[" << channel->getChannelName() << "] channel create: " << status << endl; + } + } + else { + cout << "[" << channel->getChannelName() << "] failed to create a channel: " << status << endl; + } + } + + virtual void channelStateChange(const Channel::shared_pointer& /*channel*/, Channel::ConnectionState connectionState) + { + if (connectionState == Channel::CONNECTED) { + event.signal(); + } + else { + cout << Channel::ConnectionStateNames[connectionState] << endl; + exit(3); + } + } + + bool waitUntilConnected(double timeOut) + { + return event.wait(timeOut); + } +}; + + +static void test() +{ + PVDatabasePtr master = PVDatabase::getMaster(); + ChannelProviderLocalPtr channelProvider = getChannelProviderLocal(); + string recordName = "positions"; + PVStructurePtr pvStructure = createTestPvStructure(); + PVRecordPtr pvRecord = PVRecord::create(recordName,pvStructure); + master->addRecord(pvRecord); + pvRecord = master->findRecord(recordName); + { + pvRecord->lock(); + pvRecord->process(); + pvRecord->unlock(); + } + if(debug) {cout << "processed positions" << endl; } + ServerContext::shared_pointer ctx = startPVAServer("local",0,true,true); + testOk1(ctx.get() != 0); + + ClientFactory::start(); + ChannelProvider::shared_pointer provider = ChannelProviderRegistry::clients()->getProvider("pva"); + + cout << "creating channel: " << recordName << endl; + TR1::shared_ptr channelRequesterImpl(new ChannelRequesterImpl()); + + Channel::shared_pointer channel = provider->createChannel(recordName, channelRequesterImpl); + bool channelConnected = channelRequesterImpl->waitUntilConnected(1.0); + testOk1(channelConnected); + if (channelConnected) { + string remoteAddress = channel->getRemoteAddress(); + cout << "remote address: " << remoteAddress << endl; + } + + string request = ""; + PVStructure::shared_pointer pvRequest = CreateRequest::create()->createRequest(request); + TR1::shared_ptr cmRequesterImpl(new ChannelMonitorRequesterImpl(channel->getChannelName())); + Monitor::shared_pointer monitor = channel->createMonitor(cmRequesterImpl, pvRequest); + bool monitorConnected = cmRequesterImpl->waitUntilConnected(1.0); + testOk1(monitorConnected); + Status status = monitor->start(); + testOk1(status.isOK()); + epicsThreadSleep(1); + + // Set id, x + { + pvRecord->beginGroupPut(); + PVIntPtr id = pvStructure->getSubField("id"); + id->put(1); + PVIntPtr x = pvStructure->getSubField("x"); + x->put(1); + pvRecord->endGroupPut(); + } + epicsThreadSleep(1); + + // Changed set for (id,x): 0 unset, 1 set, 2 set, 3 unset, 4 unset + BitSetPtr changedSet = cmRequesterImpl->getLastReceivedBitSet(); + testOk1(!changedSet->get(0) && changedSet->get(1) && changedSet->get(2) && !changedSet->get(3) && !changedSet->get(4)); + testOk1(*pvStructure == *cmRequesterImpl->getLastReceivedPvStructure()); + + // Set id, y + { + pvRecord->beginGroupPut(); + PVIntPtr id = pvStructure->getSubField("id"); + id->put(2); + PVIntPtr y = pvStructure->getSubField("y"); + y->put(2); + pvRecord->endGroupPut(); + } + epicsThreadSleep(1); + + // Changed set for (id,y): 0 unset, 1 set, 2 unset, 3 set, 4 unset + changedSet = cmRequesterImpl->getLastReceivedBitSet(); + testOk1(!changedSet->get(0) && changedSet->get(1) && !changedSet->get(2) && changedSet->get(3) && !changedSet->get(4)); + testOk1(*pvStructure == *cmRequesterImpl->getLastReceivedPvStructure()); + + // Set id, z + { + pvRecord->beginGroupPut(); + PVIntPtr id = pvStructure->getSubField("id"); + id->put(3); + PVIntPtr z = pvStructure->getSubField("z"); + z->put(3); + pvRecord->endGroupPut(); + } + epicsThreadSleep(1); + + // Changed set for (id,z): 0 unset, 1 set, 2 unset, 3 unset, 4 set + changedSet = cmRequesterImpl->getLastReceivedBitSet(); + testOk1(!changedSet->get(0) && changedSet->get(1) && !changedSet->get(2) && !changedSet->get(3) && changedSet->get(4)); + testOk1(*pvStructure == *cmRequesterImpl->getLastReceivedPvStructure()); + + status = monitor->stop(); + testOk1(status.isOK()); + + // Test master field + request = "field(_)"; + pvRequest = CreateRequest::create()->createRequest(request); + cmRequesterImpl = TR1::shared_ptr(new ChannelMonitorRequesterImpl(channel->getChannelName())); + monitor = channel->createMonitor(cmRequesterImpl, pvRequest); + monitorConnected = cmRequesterImpl->waitUntilConnected(1.0); + testOk1(monitorConnected); + status = monitor->start(); + testOk1(status.isOK()); + epicsThreadSleep(1); + { + pvRecord->beginGroupPut(); + PVIntPtr id = pvStructure->getSubField("id"); + id->put(4); + PVIntPtr x = pvStructure->getSubField("x"); + x->put(4); + pvRecord->endGroupPut(); + } + epicsThreadSleep(1); + // Changed set with master field requested: 0 set + changedSet = cmRequesterImpl->getLastReceivedBitSet(); + testOk1(changedSet->get(0)); + testOk1(*pvStructure == *cmRequesterImpl->getLastReceivedPvStructure()); + + status = monitor->stop(); + testOk1(status.isOK()); + +} + +MAIN(testChannelMonitor) +{ + testPlan(16); + test(); + return 0; +} diff --git a/modules/pvDatabase/test/src/testPVCopy.cpp b/modules/pvDatabase/test/src/testPVCopy.cpp index a86493f9e..9719eb1e6 100644 --- a/modules/pvDatabase/test/src/testPVCopy.cpp +++ b/modules/pvDatabase/test/src/testPVCopy.cpp @@ -289,6 +289,7 @@ static void testMasterField(PVRecordPtr const& pvRecord) cout << "Master PV structure from copy" << endl << *pvMasterField << endl; cout << "Master PV structure from copy offset " << pvMasterField->getFieldOffset() << endl; } + testOk1(pvCopy->isMasterFieldRequested()); testOk1(pvMasterField->getNumberFields() == pvStructureRecord->getNumberFields()); testOk1(pvMasterField->getFieldOffset() == 0); PVStructurePtr pvStructureCopy = pvCopy->createPVStructure(); @@ -431,7 +432,7 @@ static void masterFieldTest() MAIN(testPVCopy) { - testPlan(70); + testPlan(71); scalarTest(); arrayTest(); powerSupplyTest(); diff --git a/src/template/base/Makefile b/src/template/base/Makefile index f1ebe9e41..cd7a1cdb2 100644 --- a/src/template/base/Makefile +++ b/src/template/base/Makefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 1997 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP=../../.. include $(TOP)/configure/CONFIG diff --git a/src/template/base/makeBaseApp.pl b/src/template/base/makeBaseApp.pl index 4512e0107..7cc9a302e 100644 --- a/src/template/base/makeBaseApp.pl +++ b/src/template/base/makeBaseApp.pl @@ -1,5 +1,9 @@ #!/usr/bin/env perl +# SPDX-FileCopyrightText: 1997 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # Authors: Ralph Lange, Marty Kraimer, Andrew Johnson and Janet Anderson use FindBin qw($RealBin); diff --git a/src/template/base/top/.gitignore b/src/template/base/top/.gitignore index 8b27edce1..a3960f2d2 100644 --- a/src/template/base/top/.gitignore +++ b/src/template/base/top/.gitignore @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2022 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # Install directories /bin/ /cfg/ diff --git a/src/template/base/top/Makefile b/src/template/base/top/Makefile index 19c9068d1..d78acc549 100644 --- a/src/template/base/top/Makefile +++ b/src/template/base/top/Makefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 1997 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # Makefile at top of application tree TOP = . include $(TOP)/configure/CONFIG diff --git a/src/template/base/top/configure/CONFIG b/src/template/base/top/configure/CONFIG index 34ace5775..752b62740 100644 --- a/src/template/base/top/configure/CONFIG +++ b/src/template/base/top/configure/CONFIG @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 1997 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # CONFIG - Load build configuration data # # Do not make changes to this file! diff --git a/src/template/base/top/configure/CONFIG_SITE b/src/template/base/top/configure/CONFIG_SITE index 212485ebe..76326286f 100644 --- a/src/template/base/top/configure/CONFIG_SITE +++ b/src/template/base/top/configure/CONFIG_SITE @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2006 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # CONFIG_SITE # Make any application-specific changes to the EPICS build diff --git a/src/template/base/top/configure/Makefile b/src/template/base/top/configure/Makefile index 925430940..e004cd71b 100644 --- a/src/template/base/top/configure/Makefile +++ b/src/template/base/top/configure/Makefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 1999 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP=.. include $(TOP)/configure/CONFIG diff --git a/src/template/base/top/configure/RELEASE b/src/template/base/top/configure/RELEASE index a44ff608c..64a5811de 100644 --- a/src/template/base/top/configure/RELEASE +++ b/src/template/base/top/configure/RELEASE @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 1997 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # RELEASE - Location of external support modules # # IF YOU CHANGE ANY PATHS in this file or make API changes to diff --git a/src/template/base/top/configure/RULES b/src/template/base/top/configure/RULES index 6d56e14e8..8041da67c 100644 --- a/src/template/base/top/configure/RULES +++ b/src/template/base/top/configure/RULES @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2000 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # RULES include $(CONFIG)/RULES diff --git a/src/template/base/top/configure/RULES.ioc b/src/template/base/top/configure/RULES.ioc index 901987c6c..1b271be95 100644 --- a/src/template/base/top/configure/RULES.ioc +++ b/src/template/base/top/configure/RULES.ioc @@ -1,2 +1,6 @@ +# SPDX-FileCopyrightText: 1999 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + #RULES.ioc include $(CONFIG)/RULES.ioc diff --git a/src/template/base/top/configure/RULES_DIRS b/src/template/base/top/configure/RULES_DIRS index 3ba269dcc..2ad98ddb6 100644 --- a/src/template/base/top/configure/RULES_DIRS +++ b/src/template/base/top/configure/RULES_DIRS @@ -1,2 +1,6 @@ +# SPDX-FileCopyrightText: 1999 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + #RULES_DIRS include $(CONFIG)/RULES_DIRS diff --git a/src/template/base/top/configure/RULES_TOP b/src/template/base/top/configure/RULES_TOP index d09d668d5..3087a853a 100644 --- a/src/template/base/top/configure/RULES_TOP +++ b/src/template/base/top/configure/RULES_TOP @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 1997 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + #RULES_TOP include $(CONFIG)/RULES_TOP diff --git a/src/template/ext/Makefile b/src/template/ext/Makefile index 768062b13..5d4c09b33 100644 --- a/src/template/ext/Makefile +++ b/src/template/ext/Makefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2000 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP=../../.. include $(TOP)/configure/CONFIG diff --git a/src/template/ext/makeBaseExt.pl b/src/template/ext/makeBaseExt.pl index a3bf895d8..a097c153a 100644 --- a/src/template/ext/makeBaseExt.pl +++ b/src/template/ext/makeBaseExt.pl @@ -1,5 +1,9 @@ #!/usr/bin/env perl +# SPDX-FileCopyrightText: 2000 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # Authors: Ralph Lange, Marty Kraimer, Andrew Johnson and Janet Anderson use Cwd; diff --git a/src/template/ext/top/.gitignore b/src/template/ext/top/.gitignore index 06106f4fe..51edcf53e 100644 --- a/src/template/ext/top/.gitignore +++ b/src/template/ext/top/.gitignore @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2022 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # Install directories /bin/ /cfg/ diff --git a/src/template/ext/top/Makefile b/src/template/ext/top/Makefile index 91e678167..3d5365923 100644 --- a/src/template/ext/top/Makefile +++ b/src/template/ext/top/Makefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2000 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # Makefile at the top of an extensions tree TOP = . diff --git a/src/template/ext/top/README b/src/template/ext/top/README index c0e91eb9c..b99847ce4 100644 --- a/src/template/ext/top/README +++ b/src/template/ext/top/README @@ -3,3 +3,6 @@ Notes: Each time you add a new extension in the src directory you must add the extension directory name to src/Makefile. + +SPDX-FileCopyrightText: 2000 Argonne National laboratory +SPDX-License-Identifier: EPICS diff --git a/src/template/ext/top/configure/CONFIG b/src/template/ext/top/configure/CONFIG index 0c239bf18..f3fcc3c13 100644 --- a/src/template/ext/top/configure/CONFIG +++ b/src/template/ext/top/configure/CONFIG @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2000 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # CONFIG - Load build configuration data # # Do not make changes to this file! diff --git a/src/template/ext/top/configure/CONFIG_SITE b/src/template/ext/top/configure/CONFIG_SITE index 4ef88cedf..7aa4fb770 100644 --- a/src/template/ext/top/configure/CONFIG_SITE +++ b/src/template/ext/top/configure/CONFIG_SITE @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2006 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # CONFIG_SITE # # Make any extensions-specific changes to the EPICS build diff --git a/src/template/ext/top/configure/Makefile b/src/template/ext/top/configure/Makefile index 6e6609842..7ecbe1be3 100644 --- a/src/template/ext/top/configure/Makefile +++ b/src/template/ext/top/configure/Makefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2000 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # Makefile in extensions/configure directory TOP=.. diff --git a/src/template/ext/top/configure/RELEASE b/src/template/ext/top/configure/RELEASE index 2a7ad2714..26ce656cd 100644 --- a/src/template/ext/top/configure/RELEASE +++ b/src/template/ext/top/configure/RELEASE @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2000 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # RELEASE Locations of external modules # # NOTE: The build does not check dependancies on files diff --git a/src/template/ext/top/configure/RULES b/src/template/ext/top/configure/RULES index f58520704..5cc414f2c 100644 --- a/src/template/ext/top/configure/RULES +++ b/src/template/ext/top/configure/RULES @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2000 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # extensions/configure/RULES include $(CONFIG)/RULES diff --git a/src/template/ext/top/configure/RULES_DIRS b/src/template/ext/top/configure/RULES_DIRS index dd6904367..f1702f7f7 100644 --- a/src/template/ext/top/configure/RULES_DIRS +++ b/src/template/ext/top/configure/RULES_DIRS @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2000 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # extensions/configure/RULES_DIRS include $(CONFIG)/RULES_DIRS diff --git a/src/template/ext/top/configure/RULES_IDL b/src/template/ext/top/configure/RULES_IDL index 3610bcc21..0ee7693b0 100644 --- a/src/template/ext/top/configure/RULES_IDL +++ b/src/template/ext/top/configure/RULES_IDL @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2004 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # extensions/configure/RULES_IDL ifdef T_A diff --git a/src/template/ext/top/configure/RULES_JAVA b/src/template/ext/top/configure/RULES_JAVA index e33ab3ce2..88d397917 100644 --- a/src/template/ext/top/configure/RULES_JAVA +++ b/src/template/ext/top/configure/RULES_JAVA @@ -1,13 +1,6 @@ -#************************************************************************* -# Copyright (c) 2002 The University of Chicago, as Operator of Argonne -# National Laboratory. -# Copyright (c) 2002 The Regents of the University of California, as -# Operator of Los Alamos National Laboratory. +# SPDX-FileCopyrightText: 2000 Argonne National Laboratory +# # SPDX-License-Identifier: EPICS -# EPICS Base is distributed subject to a Software License Agreement found -# in file LICENSE that is included with this distribution. -#************************************************************************* - ifeq ($(BUILD_CLASS),HOST) diff --git a/src/template/ext/top/configure/RULES_TOP b/src/template/ext/top/configure/RULES_TOP index 1f81d3d39..7203c3dc8 100644 --- a/src/template/ext/top/configure/RULES_TOP +++ b/src/template/ext/top/configure/RULES_TOP @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2000 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # extensions/configure/RULES_TOP include $(CONFIG)/RULES_TOP diff --git a/src/template/ext/top/configure/os/CONFIG_SITE.darwin-aarch64.darwin-aarch64 b/src/template/ext/top/configure/os/CONFIG_SITE.darwin-aarch64.darwin-aarch64 index d8dd8bd73..db773b63a 100644 --- a/src/template/ext/top/configure/os/CONFIG_SITE.darwin-aarch64.darwin-aarch64 +++ b/src/template/ext/top/configure/os/CONFIG_SITE.darwin-aarch64.darwin-aarch64 @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2022 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # # Site Specific Configuration Information diff --git a/src/template/ext/top/configure/os/CONFIG_SITE.darwin-x86.darwin-x86 b/src/template/ext/top/configure/os/CONFIG_SITE.darwin-x86.darwin-x86 index d8dd8bd73..fe7a36159 100644 --- a/src/template/ext/top/configure/os/CONFIG_SITE.darwin-x86.darwin-x86 +++ b/src/template/ext/top/configure/os/CONFIG_SITE.darwin-x86.darwin-x86 @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2006 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # # Site Specific Configuration Information diff --git a/src/template/ext/top/configure/os/CONFIG_SITE.freebsd-x86_64.freebsd-x86_64 b/src/template/ext/top/configure/os/CONFIG_SITE.freebsd-x86_64.freebsd-x86_64 index c4d1d5670..f1a75dc36 100644 --- a/src/template/ext/top/configure/os/CONFIG_SITE.freebsd-x86_64.freebsd-x86_64 +++ b/src/template/ext/top/configure/os/CONFIG_SITE.freebsd-x86_64.freebsd-x86_64 @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2006 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # # Site Specific Configuration Information # Only the local epics system manager should modify this file diff --git a/src/template/ext/top/configure/os/CONFIG_SITE.linux-aarch64.linux-aarch64 b/src/template/ext/top/configure/os/CONFIG_SITE.linux-aarch64.linux-aarch64 index 118c42d8b..38a595cb5 100644 --- a/src/template/ext/top/configure/os/CONFIG_SITE.linux-aarch64.linux-aarch64 +++ b/src/template/ext/top/configure/os/CONFIG_SITE.linux-aarch64.linux-aarch64 @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2022 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # Site Specific Configuration Information X11_LIB=/usr/lib/aarch64-linux-gnu diff --git a/src/template/ext/top/configure/os/CONFIG_SITE.linux-ppc.linux-ppc b/src/template/ext/top/configure/os/CONFIG_SITE.linux-ppc.linux-ppc index 4d78b29d7..5234ebb78 100644 --- a/src/template/ext/top/configure/os/CONFIG_SITE.linux-ppc.linux-ppc +++ b/src/template/ext/top/configure/os/CONFIG_SITE.linux-ppc.linux-ppc @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2006 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # # Site Specific Configuration Information # Only the local epics system manager should modify this file diff --git a/src/template/ext/top/configure/os/CONFIG_SITE.linux-x86-debug.linux-x86-debug b/src/template/ext/top/configure/os/CONFIG_SITE.linux-x86-debug.linux-x86-debug index 4d78b29d7..5234ebb78 100644 --- a/src/template/ext/top/configure/os/CONFIG_SITE.linux-x86-debug.linux-x86-debug +++ b/src/template/ext/top/configure/os/CONFIG_SITE.linux-x86-debug.linux-x86-debug @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2006 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # # Site Specific Configuration Information # Only the local epics system manager should modify this file diff --git a/src/template/ext/top/configure/os/CONFIG_SITE.linux-x86.linux-x86 b/src/template/ext/top/configure/os/CONFIG_SITE.linux-x86.linux-x86 index 067e33731..7a47243e6 100644 --- a/src/template/ext/top/configure/os/CONFIG_SITE.linux-x86.linux-x86 +++ b/src/template/ext/top/configure/os/CONFIG_SITE.linux-x86.linux-x86 @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2000 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # # Site Specific Configuration Information # Only the local epics system manager should modify this file diff --git a/src/template/ext/top/configure/os/CONFIG_SITE.linux-x86_64-debug.linux-x86_64-debug b/src/template/ext/top/configure/os/CONFIG_SITE.linux-x86_64-debug.linux-x86_64-debug index 4d78b29d7..5234ebb78 100644 --- a/src/template/ext/top/configure/os/CONFIG_SITE.linux-x86_64-debug.linux-x86_64-debug +++ b/src/template/ext/top/configure/os/CONFIG_SITE.linux-x86_64-debug.linux-x86_64-debug @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2006 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # # Site Specific Configuration Information # Only the local epics system manager should modify this file diff --git a/src/template/ext/top/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 b/src/template/ext/top/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 index 221d6a6f8..7d643d91e 100644 --- a/src/template/ext/top/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 +++ b/src/template/ext/top/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2006 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # # Site Specific Configuration Information # Only the local epics system manager should modify this file diff --git a/src/template/ext/top/configure/os/CONFIG_SITE.win32-x86-debug.win32-x86-debug b/src/template/ext/top/configure/os/CONFIG_SITE.win32-x86-debug.win32-x86-debug index ebc1b8238..35ffa582d 100644 --- a/src/template/ext/top/configure/os/CONFIG_SITE.win32-x86-debug.win32-x86-debug +++ b/src/template/ext/top/configure/os/CONFIG_SITE.win32-x86-debug.win32-x86-debug @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2006 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # # Site Specific Configuration Information # Only the local epics system manager should modify this file diff --git a/src/template/ext/top/configure/os/CONFIG_SITE.win32-x86.win32-x86 b/src/template/ext/top/configure/os/CONFIG_SITE.win32-x86.win32-x86 index 712d17ae8..f0c1c35aa 100644 --- a/src/template/ext/top/configure/os/CONFIG_SITE.win32-x86.win32-x86 +++ b/src/template/ext/top/configure/os/CONFIG_SITE.win32-x86.win32-x86 @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2000 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # # CONFIG_SITE.win32-x86.win32-x86,v 1.3 2003/09/03 19:06:10 jba Exp # diff --git a/src/template/ext/top/configure/os/CONFIG_SITE.windows-x64.windows-x64 b/src/template/ext/top/configure/os/CONFIG_SITE.windows-x64.windows-x64 index 68a0c149a..fdb7310cf 100644 --- a/src/template/ext/top/configure/os/CONFIG_SITE.windows-x64.windows-x64 +++ b/src/template/ext/top/configure/os/CONFIG_SITE.windows-x64.windows-x64 @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2010 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + # # Site Specific Configuration Information # Only the local epics system manager should modify this file diff --git a/src/template/ext/top/exampleExt/Makefile b/src/template/ext/top/exampleExt/Makefile index 618991fa7..8af9480e5 100644 --- a/src/template/ext/top/exampleExt/Makefile +++ b/src/template/ext/top/exampleExt/Makefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2000 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP=../.. include $(TOP)/configure/CONFIG diff --git a/src/template/ext/top/exampleExt/RELEASE_NOTES.HTM b/src/template/ext/top/exampleExt/RELEASE_NOTES.HTM index 56dce9346..c82291815 100644 --- a/src/template/ext/top/exampleExt/RELEASE_NOTES.HTM +++ b/src/template/ext/top/exampleExt/RELEASE_NOTES.HTM @@ -1,3 +1,9 @@ + + diff --git a/src/template/ext/top/exampleExt/caExample.c b/src/template/ext/top/exampleExt/caExample.c index cc342e237..dcda8e3af 100644 --- a/src/template/ext/top/exampleExt/caExample.c +++ b/src/template/ext/top/exampleExt/caExample.c @@ -1,3 +1,7 @@ +/* SPDX-FileCopyrightText: 1998 Argonne National Laboratory */ + +/* SPDX-License-Identifier: EPICS */ + /*caExample.c*/ #include #include diff --git a/src/template/ext/top/simpleExt/Makefile b/src/template/ext/top/simpleExt/Makefile index 52cfac525..1de42bac5 100644 --- a/src/template/ext/top/simpleExt/Makefile +++ b/src/template/ext/top/simpleExt/Makefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2000 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP=../.. include $(TOP)/configure/CONFIG diff --git a/src/template/ext/top/src/Makefile b/src/template/ext/top/src/Makefile index d672087ca..374670e57 100644 --- a/src/template/ext/top/src/Makefile +++ b/src/template/ext/top/src/Makefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2000 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP = .. include $(TOP)/configure/CONFIG diff --git a/src/tools/EPICS/PodMD.pm b/src/tools/EPICS/PodMD.pm new file mode 100644 index 000000000..e2017854b --- /dev/null +++ b/src/tools/EPICS/PodMD.pm @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: EPICS + +package EPICS::PodMD; + +use strict; +use warnings; + +use base 'Pod::Markdown'; + +# Translate L +# into link text# +# This is for Sphinx processing on Readthedocs. Sphinx converts +# links to .md into .html automatically in the processing. + +sub format_perldoc_url { + my ($self, $name, $section) = @_; + + my $url_prefix = $self->perldoc_url_prefix; + if ( + defined($name) + && $self->is_local_module($name) + && defined($self->local_module_url_prefix) + ) { + $url_prefix = $self->local_module_url_prefix; + } + + my $url = ''; + + # If the link is to another module (external link). + if (defined($name)) { + $name .= ".md" unless $name =~ m/\.md$/; + $url = $url_prefix . + ($self->escape_url ? URI::Escape::uri_escape($name) : $name); + } + + # See https://rt.cpan.org/Ticket/Display.html?id=57776 + # for a discussion on the need to mangle the section. + if ($section){ + + my $method = $url + # If we already have a prefix on the url it's external. + ? $self->perldoc_fragment_format + # Else an internal link points to this markdown doc. + : $self->markdown_fragment_format; + + $method = 'format_fragment_' . $method + unless ref($method); + + { + # Set topic to enable code refs to be simple. + local $_ = $section; + $section = $self->$method($section); + } + + $url .= '#' . $section; + } + + return $url; +} + +1; diff --git a/src/tools/EpicsHostArch.pl b/src/tools/EpicsHostArch.pl index abff49007..dee1ffe03 100644 --- a/src/tools/EpicsHostArch.pl +++ b/src/tools/EpicsHostArch.pl @@ -43,6 +43,8 @@ sub HostArch { return "cygwin-x86" if m/^i[3-6]86-cygwin/; return 'solaris-sparc' if m/^sun4-solaris/; return 'solaris-x86' if m/^i86pc-solaris/; + return 'freebsd-x86_64' if m/^x86_64-freebsd/; + return 'freebsd-x86_64' if m/^amd64-freebsd/; my ($kernel, $hostname, $release, $version, $cpu) = uname; if (m/^darwin/) { @@ -52,7 +54,13 @@ sub HostArch { } die "$0: macOS CPU type '$cpu' not recognized\n"; } - + # mingw64 has 32bit and 64bit build shells which give same arch result + if (m/^(x86_64|i686)-msys/) { + die "$0: Architecture '$arch' is unclear,\n". + "try again after explicitly setting the EPICS_HOST_ARCH environment variable.\n". + "Use EPICS_HOST_ARCH=windows-x64-mingw for a 64bit MinGW build, or\n". + "EPICS_HOST_ARCH=win32-x86-mingw for a 32bit MinGW build.\n" + } die "$0: Architecture '$arch' not recognized\n"; } } diff --git a/src/tools/Makefile b/src/tools/Makefile index db7a99fe2..bb47f37a0 100644 --- a/src/tools/Makefile +++ b/src/tools/Makefile @@ -17,7 +17,10 @@ PERL_MODULES += EPICS/Release.pm PERL_MODULES += EPICS/Readfile.pm PERL_MODULES += EPICS/Getopts.pm PERL_MODULES += EPICS/PodHtml.pm +PERL_MODULES += EPICS/PodMD.pm PERL_MODULES += EPICS/PodXHtml.pm +PERL_MODULES += Pod/Markdown.pm +PERL_MODULES += URI/Escape.pm # This goes into lib/perl, not bin/ PERL_MODULES += EpicsHostArch.pl @@ -29,6 +32,7 @@ PERL_SCRIPTS += depclean.pl PERL_SCRIPTS += dos2unix.pl PERL_SCRIPTS += epicsProve.pl PERL_SCRIPTS += expandVars.pl +PERL_SCRIPTS += filterMakeflags.pl PERL_SCRIPTS += fullPathName.pl PERL_SCRIPTS += installEpics.pl PERL_SCRIPTS += makeAPIheader.pl @@ -36,6 +40,7 @@ PERL_SCRIPTS += makeMakefile.pl PERL_SCRIPTS += makeTestfile.pl PERL_SCRIPTS += mkmf.pl PERL_SCRIPTS += munch.pl +PERL_SCRIPTS += podToMD.pl PERL_SCRIPTS += podToHtml.pl PERL_SCRIPTS += podRemove.pl PERL_SCRIPTS += replaceVAR.pl @@ -54,6 +59,11 @@ HTMLS += fullPathName.html HTMLS += makeAPIheader.html HTMLS += munch.html HTMLS += podToHtml.html +HTMLS += podToMD.html HTMLS += podRemove.html +# The above Perl program documentation (modelled after Man pages) +# doesn't work well in Doxygen's "Related Pages" area, the titles +# for all doc's are "NAME", so don't convert these to Markdown. + include $(TOP)/configure/RULES diff --git a/src/tools/Pod/Markdown.pm b/src/tools/Pod/Markdown.pm new file mode 100644 index 000000000..1874f232f --- /dev/null +++ b/src/tools/Pod/Markdown.pm @@ -0,0 +1,1698 @@ +# vim: set ts=2 sts=2 sw=2 expandtab smarttab: +# +# This file is part of Pod-Markdown +# +# This software is copyright (c) 2011 by Randy Stauner. +# +# This is free software; you can redistribute it and/or modify it under +# the same terms as the Perl 5 programming language system itself. +# SPDX-License-Identifier: Artistic-2.0 +# +use 5.008; +use strict; +use warnings; + +package Pod::Markdown; +# git description: v3.300-3-gb01c18d + +our $AUTHORITY = 'cpan:RWSTAUNER'; +# ABSTRACT: Convert POD to Markdown +$Pod::Markdown::VERSION = '3.400'; +use Pod::Simple 3.27 (); # detected_encoding and keep_encoding bug fix +use parent qw(Pod::Simple::Methody); +use Encode (); +use URI::Escape (); + +our %URL_PREFIXES = ( + sco => 'http://search.cpan.org/perldoc?', + metacpan => 'https://metacpan.org/pod/', + man => 'http://man.he.net/man', +); +$URL_PREFIXES{perldoc} = $URL_PREFIXES{metacpan}; + +our $LOCAL_MODULE_RE = qr/^(Local::|\w*?_\w*)/; + +## no critic +#{ + our $HAS_HTML_ENTITIES; + + # Stolen from Pod::Simple::XHTML 3.28. {{{ + + BEGIN { + $HAS_HTML_ENTITIES = eval "require HTML::Entities; 1"; + } + + my %entities = ( + q{>} => 'gt', + q{<} => 'lt', + q{'} => '#39', + q{"} => 'quot', + q{&} => 'amp', + ); + + sub encode_entities { + my $self = shift; + my $ents = $self->html_encode_chars; + return HTML::Entities::encode_entities( $_[0], $ents ) if $HAS_HTML_ENTITIES; + if (defined $ents) { + $ents =~ s,(? 'nbsp', + chr(utf8::unicode_to_native(0xA9)) => 'copy', + %entities + ); + + sub __entity_encode_ord_he { + my $chr = chr $_[0]; + # Skip the encode_entities() logic and go straight for the substitution + # since we already have the char we know we want replaced. + # Both the hash and the function are documented as exportable (so should be reliable). + return $HTML::Entities::char2entity{ $chr } || HTML::Entities::num_entity( $chr ); + } + sub __entity_encode_ord_basic { + return '&' . ($entities{chr $_[0]} || sprintf '#x%X', $_[0]) . ';'; + } + + # From HTML::Entities 3.69 + my $DEFAULT_ENTITY_CHARS = '^\n\r\t !\#\$%\(-;=?-~'; + +#} +## use critic + +# Use hash for simple "exists" check in `new` (much more accurate than `->can`). +my %attributes = map { ($_ => 1) } + qw( + html_encode_chars + match_encoding + output_encoding + local_module_re + local_module_url_prefix + man_url_prefix + perldoc_url_prefix + perldoc_fragment_format + markdown_fragment_format + include_meta_tags + escape_url + ); + + +sub new { + my $class = shift; + my %args = @_; + + my $self = $class->SUPER::new(); + $self->preserve_whitespace(1); + $self->nbsp_for_S(1); + $self->accept_targets(qw( markdown html )); + $self->escape_url(1); + + # Default to the global, but allow it to be overwritten in args. + $self->local_module_re($LOCAL_MODULE_RE); + + for my $type ( qw( perldoc man ) ){ + my $attr = $type . '_url_prefix'; + # Initialize to the alias. + $self->$attr($type); + } + + while( my ($attr, $val) = each %args ){ + # NOTE: Checking exists on a private var means we don't allow Pod::Simple + # attributes to be set this way. It's not very consistent, but I think + # I'm ok with that for now since there probably aren't many Pod::Simple attributes + # being changed besides `output_*` which feel like API rather than attributes. + # We'll see. + # This is currently backward-compatible as we previously just put the attribute + # into the private stash so anything unknown was silently ignored. + # We could open this up to `$self->can($attr)` in the future if that seems better + # but it tricked me when I was testing a misspelled attribute name + # which also happened to be a Pod::Simple method. + + exists $attributes{ $attr } or + # Provide a more descriptive message than "Can't locate object method". + warn("Unknown argument to ${class}->new(): '$attr'"), next; + + # Call setter. + $self->$attr($val); + } + + # TODO: call from the setters. + $self->_prepare_fragment_formats; + + if(defined $self->local_module_url_prefix && $self->local_module_url_prefix eq '' && !$self->escape_url) { + warn("turning escape_url with an empty local_module_url_prefix is not recommended as relative URLs could be confused for IPv6 addresses"); + } + + return $self; +} + +for my $type ( qw( local_module perldoc man ) ){ + my $attr = $type . '_url_prefix'; + no strict 'refs'; ## no critic + *$attr = sub { + my $self = shift; + if (@_) { + $self->{$attr} = $URL_PREFIXES{ $_[0] } || $_[0]; + } + else { + return $self->{$attr}; + } + } +} + +## Attribute accessors ## + + +sub html_encode_chars { + my $self = shift; + my $stash = $self->_private; + + # Setter. + if( @_ ){ + # If false ('', 0, undef), disable. + if( !$_[0] ){ + delete $stash->{html_encode_chars}; + $stash->{encode_amp} = 1; + $stash->{encode_lt} = 1; + } + else { + # Special case boolean '1' to mean "all". + # If we have HTML::Entities, undef will use the default. + # Without it, we need to specify so that we use the same list (for consistency). + $stash->{html_encode_chars} = $_[0] eq '1' ? ($HAS_HTML_ENTITIES ? undef : $DEFAULT_ENTITY_CHARS) : $_[0]; + + # If [char] doesn't get encoded, we need to do it ourselves. + $stash->{encode_amp} = ($self->encode_entities('&') eq '&'); + $stash->{encode_lt} = ($self->encode_entities('<') eq '<'); + } + return; + } + + # Getter. + return $stash->{html_encode_chars}; +} + + +# I prefer ro-accessors (immutability!) but it can be confusing +# to not support the same API as other Pod::Simple classes. + +# NOTE: Pod::Simple::_accessorize is not a documented public API. +# Skip any that have already been defined. +__PACKAGE__->_accessorize(grep { !__PACKAGE__->can($_) } keys %attributes); + +sub _prepare_fragment_formats { + my ($self) = @_; + + foreach my $attr ( keys %attributes ){ + next unless $attr =~ /^(\w+)_fragment_format/; + my $type = $1; + my $format = $self->$attr; + + # If one was provided. + if( $format ){ + # If the attribute is a coderef just use it. + next if ref($format) eq 'CODE'; + } + # Else determine a default. + else { + if( $type eq 'perldoc' ){ + # Choose a default that matches the destination url. + my $target = $self->perldoc_url_prefix; + foreach my $alias ( qw( metacpan sco ) ){ + if( $target eq $URL_PREFIXES{ $alias } ){ + $format = $alias; + } + } + # This seems like a reasonable fallback. + $format ||= 'pod_simple_xhtml'; + } + else { + $format = $type; + } + } + + # The short name should become a method name with the prefix prepended. + my $prefix = 'format_fragment_'; + $format =~ s/^$prefix//; + die "Unknown fragment format '$format'" + unless $self->can($prefix . $format); + + # Save it. + $self->$attr($format); + } + + return; +} + +## Backward compatible API ## + +# For backward compatibility (previously based on Pod::Parser): +# While Pod::Simple provides a parse_from_file() method +# it's primarily for Pod::Parser compatibility. +# When called without an output handle it will print to STDOUT +# but the old Pod::Markdown never printed to a handle +# so we don't want to start now. +sub parse_from_file { + my ($self, $file) = @_; + + # TODO: Check that all dependent cpan modules use the Pod::Simple API + # then add a deprecation warning here to avoid confusion. + + $self->output_string(\($self->{_as_markdown_})); + $self->parse_file($file); +} + +# Likewise, though Pod::Simple doesn't define this method at all. +sub parse_from_filehandle { shift->parse_from_file(@_) } + + +## Document state ## + +sub _private { + my ($self) = @_; + $self->{_Pod_Markdown_} ||= { + indent => 0, + stacks => [], + states => [{}], + link => [], + encode_amp => 1, + encode_lt => 1, + }; +} + +sub _increase_indent { + ++$_[0]->_private->{indent} >= 1 + or die 'Invalid state: indent < 0'; +} +sub _decrease_indent { + --$_[0]->_private->{indent} >= 0 + or die 'Invalid state: indent < 0'; +} + +sub _new_stack { + push @{ $_[0]->_private->{stacks} }, []; + push @{ $_[0]->_private->{states} }, {}; +} + +sub _last_string { + $_[0]->_private->{stacks}->[-1][-1]; +} + +sub _pop_stack_text { + $_[0]->_private->{last_state} = pop @{ $_[0]->_private->{states} }; + join '', @{ pop @{ $_[0]->_private->{stacks} } }; +} + +sub _stack_state { + $_[0]->_private->{states}->[-1]; +} + +sub _save { + my ($self, $text) = @_; + push @{ $self->_private->{stacks}->[-1] }, $text; + # return $text; # DEBUG +} + +sub _save_line { + my ($self, $text) = @_; + + $text = $self->_process_escapes($text); + + $self->_save($text . $/); +} + +# For paragraphs, etc. +sub _save_block { + my ($self, $text) = @_; + + $self->_stack_state->{blocks}++; + + $self->_save_line($self->_indent($text) . $/); +} + +## Formatting ## + +sub _chomp_all { + my ($self, $text) = @_; + 1 while chomp $text; + return $text; +} + +sub _indent { + my ($self, $text) = @_; + my $level = $self->_private->{indent}; + + if( $level ){ + my $indent = ' ' x ($level * 4); + + # Capture text on the line so that we don't indent blank lines (/^\x20{4}$/). + $text =~ s/^(.+)/$indent$1/mg; + } + + return $text; +} + +# as_markdown() exists solely for backward compatibility +# and requires having called parse_from_file() to be useful. + + +sub as_markdown { + my ($parser, %args) = @_; + my @header; + # Don't add meta tags again if we've already done it. + if( $args{with_meta} && !$parser->include_meta_tags ){ + @header = $parser->_build_markdown_head; + } + return join("\n" x 2, @header, $parser->{_as_markdown_}); +} + +sub _build_markdown_head { + my $parser = shift; + my $data = $parser->_private; + return join "\n", + map { qq![[meta \l$_="$data->{$_}"]]! } + grep { defined $data->{$_} } + qw( Title Author ); +} + +## Escaping ## + +# http://daringfireball.net/projects/markdown/syntax#backslash +# Markdown provides backslash escapes for the following characters: +# +# \ backslash +# ` backtick +# * asterisk +# _ underscore +# {} curly braces +# [] square brackets +# () parentheses +# # hash mark +# + plus sign +# - minus sign (hyphen) +# . dot +# ! exclamation mark + +# However some of those only need to be escaped in certain places: +# * Backslashes *do* need to be escaped or they may be swallowed by markdown. +# * Word-surrounding characters (/[`*_]/) *do* need to be escaped mid-word +# because the markdown spec explicitly allows mid-word em*pha*sis. +# * I don't actually see anything that curly braces are used for. +# * Escaping square brackets is enough to avoid accidentally +# creating links and images (so we don't need to escape plain parentheses +# or exclamation points as that would generate a lot of unnecesary noise). +# Parentheses will be escaped in urls (&end_L) to avoid premature termination. +# * We don't need a backslash for every hash mark or every hyphen found mid-word, +# just the ones that start a line (likewise for plus and dot). +# (Those will all be handled by _escape_paragraph_markdown). + + +# Backslash escape markdown characters to avoid having them interpreted. +sub _escape_inline_markdown { + local $_ = $_[1]; + +# s/([\\`*_{}\[\]()#+-.!])/\\$1/g; # See comments above. + s/([\\`*_\[\]])/\\$1/g; + + return $_; +} + +# Escape markdown characters that would be interpreted +# at the start of a line. +sub _escape_paragraph_markdown { + local $_ = $_[1]; + + # Escape headings, horizontal rules, (unordered) lists, and blockquotes. + s/^([-+#>])/\\$1/mg; + + # Markdown doesn't support backslash escapes for equal signs + # even though they can be used to underline a header. + # So use html to escape them to avoid having them interpreted. + s/^([=])/sprintf '&#x%x;', ord($1)/mge; + + # Escape the dots that would wrongfully create numbered lists. + s/^( (?:>\s+)? \d+ ) (\.\x20)/$1\\$2/xgm; + + return $_; +} + + +# Additionally Markdown allows inline html so we need to escape things that look like it. +# While _some_ Markdown processors handle backslash-escaped html, +# [Daring Fireball](http://daringfireball.net/projects/markdown/syntax) states distinctly: +# > In HTML, there are two characters that demand special treatment: < and &... +# > If you want to use them as literal characters, you must escape them as entities, e.g. <, and &. + +# It goes on to say: +# > Markdown allows you to use these characters naturally, +# > taking care of all the necessary escaping for you. +# > If you use an ampersand as part of an HTML entity, +# > it remains unchanged; otherwise it will be translated into &. +# > Similarly, because Markdown supports inline HTML, +# > if you use angle brackets as delimiters for HTML tags, Markdown will treat them as such. + +# In order to only encode the occurrences that require it (something that +# could be interpreted as an entity) we escape them all so that we can do the +# suffix test later after the string is complete (since we don't know what +# strings might come after this one). + +my %_escape = + map { + my ($k, $v) = split /:/; + # Put the "code" marker before the char instead of after so that it doesn't + # get confused as the $2 (which is what requires us to entity-encode it). + # ( "XsX", "XcsX", "X(c?)sX" ) + my ($s, $code, $re) = map { "\0$_$v\0" } '', map { ($_, '('.$_.'?)') } 'c'; + + ( + $k => $s, + $k.'_code' => $code, + $k.'_re' => qr/$re/, + ) + } + qw( amp:& lt:< ); + +# Make the values of this private var available to the tests. +sub __escape_sequences { %_escape } + + +# HTML-entity encode any characters configured by the user. +# If that doesn't include [&<] then we escape those chars so we can decide +# later if we will entity-encode them or put them back verbatim. +sub _encode_or_escape_entities { + my $self = $_[0]; + my $stash = $self->_private; + local $_ = $_[1]; + + if( $stash->{encode_amp} ){ + if( exists($stash->{html_encode_chars}) ){ + # Escape all amps for later processing. + # Pass intermediate strings to entity encoder so that it doesn't + # process any of the characters of our escape sequences. + # Use -1 to get "as many fields as possible" so that we keep leading and + # trailing (possibly empty) fields. + $_ = join $_escape{amp}, map { $self->encode_entities($_) } split /&/, $_, -1; + } + else { + s/&/$_escape{amp}/g; + } + } + elsif( exists($stash->{html_encode_chars}) ){ + $_ = $self->encode_entities($_); + } + + s/{encode_lt}; + + return $_; +} + +# From Markdown.pl version 1.0.1 line 1172 (_DoAutoLinks). +my $EMAIL_MARKER = qr{ +# < # Opening token is in parent regexp. + (?:mailto:)? + ( + [-.\w]+ + \@ + [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+ + ) + > +}x; + +# Process any escapes we put in the text earlier, +# now that the text is complete (end of a block). +sub _process_escapes { + my $self = $_[0]; + my $stash = $self->_private; + local $_ = $_[1]; + + # The patterns below are taken from Markdown.pl 1.0.1 _EncodeAmpsAndAngles(). + # In this case we only want to encode the ones that Markdown won't. + # This is overkill but produces nicer looking text (less escaped entities). + # If it proves insufficent then we'll just encode them all. + + # $1: If the escape was in a code sequence, simply replace the original. + # $2: If the unescaped value would be followed by characters + # that could be interpreted as html, entity-encode it. + # else: The character is safe to leave bare. + + # Neither currently allows $2 to contain '0' so bool tests are sufficient. + + if( $stash->{encode_amp} ){ + # Encode & if succeeded by chars that look like an html entity. + s,$_escape{amp_re}((?:#?[xX]?(?:[0-9a-fA-F]+|\w+);)?), + $1 ? '&'.$2 : $2 ? '&'.$2 : '&',egos; + } + + if( $stash->{encode_lt} ){ + # Encode < if succeeded by chars that look like an html tag. + # Leave email addresses () for Markdown to process. + s,$_escape{lt_re}((?=$EMAIL_MARKER)|(?:[a-z/?\$!])?), + $1 ? '<'.$2 : $2 ? '<'.$2 : '<',egos; + } + + return $_; +} + + +## Parsing ## + +sub handle_text { + my $self = $_[0]; + my $stash = $self->_private; + local $_ = $_[1]; + + # Unless we're in a code span, verbatim block, or formatted region. + unless( $stash->{no_escape} ){ + + # We could, in theory, alter what gets escaped according to context + # (for example, escape square brackets (but not parens) inside link text). + # The markdown produced might look slightly nicer but either way you're + # at the whim of the markdown processor to interpret things correctly. + # For now just escape everything. + + # Don't let literal characters be interpreted as markdown. + $_ = $self->_escape_inline_markdown($_); + + # Entity-encode (or escape for later processing) necessary/desired chars. + $_ = $self->_encode_or_escape_entities($_); + + } + # If this _is_ a code section, do limited/specific handling. + else { + # Always escaping these chars ensures that we won't mangle the text + # in the unlikely event that a sequence matching our escape occurred in the + # input stream (since we're going to escape it and then unescape it). + s/&/$_escape{amp_code}/gos if $stash->{encode_amp}; + s/{encode_lt}; + } + + $self->_save($_); +} + +sub start_Document { + my ($self) = @_; + $self->_new_stack; +} + +sub end_Document { + my ($self) = @_; + $self->_check_search_header; + my $end = pop @{ $self->_private->{stacks} }; + + @{ $self->_private->{stacks} } == 0 + or die 'Document ended with stacks remaining'; + + my @doc = $self->_chomp_all(join('', @$end)) . $/; + + if( $self->include_meta_tags ){ + unshift @doc, $self->_build_markdown_head, ($/ x 2); + } + + if( my $encoding = $self->_get_output_encoding ){ + # Do the check outside the loop(s) for efficiency. + my $ents = $HAS_HTML_ENTITIES ? \&__entity_encode_ord_he : \&__entity_encode_ord_basic; + # Iterate indices to avoid copying large strings. + for my $i ( 0 .. $#doc ){ + print { $self->{output_fh} } Encode::encode($encoding, $doc[$i], $ents); + } + } + else { + print { $self->{output_fh} } @doc; + } +} + +sub _get_output_encoding { + my ($self) = @_; + + # If 'match_encoding' is set we need to return an encoding. + # If pod has no =encoding, Pod::Simple will guess if it sees a high-bit char. + # If there are no high-bit chars, encoding is undef. + # Use detected_encoding() rather than encoding() because if Pod::Simple + # can't use whatever encoding was specified, we probably can't either. + # Fallback to 'o_e' if no match is found. This gives the user the choice, + # since otherwise there would be no reason to specify 'o_e' *and* 'm_e'. + # Fallback to UTF-8 since it is a reasonable default these days. + + return $self->detected_encoding || $self->output_encoding || 'UTF-8' + if $self->match_encoding; + + # If output encoding wasn't specified, return false. + return $self->output_encoding; +} + +## Blocks ## + +sub start_Verbatim { + my ($self) = @_; + $self->_new_stack; + $self->_private->{no_escape} = 1; +} + +sub end_Verbatim { + my ($self) = @_; + + my $text = $self->_pop_stack_text; + + $text = $self->_indent_verbatim($text); + + $self->_private->{no_escape} = 0; + + # Verbatim blocks do not generate a separate "Para" event. + $self->_save_block($text); +} + +sub _indent_verbatim { + my ($self, $paragraph) = @_; + + # NOTE: Pod::Simple expands the tabs for us (as suggested by perlpodspec). + # Pod::Simple also has a 'strip_verbatim_indent' attribute + # but it doesn't sound like it gains us anything over this method. + + # POD verbatim can start with any number of spaces (or tabs) + # markdown should be 4 spaces (or a tab) + # so indent any paragraphs so that all lines start with at least 4 spaces + my @lines = split /\n/, $paragraph; + my $indent = ' ' x 4; + foreach my $line ( @lines ){ + next unless $line =~ m/^( +)/; + # find the smallest indentation + $indent = $1 if length($1) < length($indent); + } + if( (my $smallest = length($indent)) < 4 ){ + # invert to get what needs to be prepended + $indent = ' ' x (4 - $smallest); + + # Prepend indent to each line. + # We could check /\S/ to only indent non-blank lines, + # but it's backward compatible to respect the whitespace. + # Additionally, both pod and markdown say they ignore blank lines + # so it shouldn't hurt to leave them in. + $paragraph = join "\n", map { length($_) ? $indent . $_ : '' } @lines; + } + + return $paragraph; +} + +sub start_Para { + $_[0]->_new_stack; +} + +sub end_Para { + my ($self) = @_; + my $text = $self->_pop_stack_text; + + $text = $self->_escape_paragraph_markdown($text); + + $self->_save_block($text); +} + + +## Headings ## + +sub start_head1 { $_[0]->_start_head(1) } +sub end_head1 { $_[0]->_end_head(1) } +sub start_head2 { $_[0]->_start_head(2) } +sub end_head2 { $_[0]->_end_head(2) } +sub start_head3 { $_[0]->_start_head(3) } +sub end_head3 { $_[0]->_end_head(3) } +sub start_head4 { $_[0]->_start_head(4) } +sub end_head4 { $_[0]->_end_head(4) } + +sub _check_search_header { + my ($self) = @_; + # Save the text since the last heading if we want it for metadata. + if( my $last = $self->_private->{search_header} ){ + for( $self->_private->{$last} = $self->_last_string ){ + s/\A\s+//; + s/\s+\z//; + } + } +} +sub _start_head { + my ($self) = @_; + $self->_check_search_header; + $self->_new_stack; +} + +sub _end_head { + my ($self, $num) = @_; + my $h = '#' x $num; + + my $text = $self->_pop_stack_text; + $self->_private->{search_header} = + $text =~ /NAME/ ? 'Title' + : $text =~ /AUTHOR/ ? 'Author' + : undef; + + # TODO: option for $h suffix + # TODO: put a name="" if $self->{embed_anchor_tags}; ? + # https://rt.cpan.org/Ticket/Display.html?id=57776 + $self->_save_block(join(' ', $h, $text)); +} + +## Lists ## + +# With Pod::Simple->parse_empty_lists(1) there could be an over_empty event, +# but what would you do with that? + +sub _start_list { + my ($self) = @_; + $self->_new_stack; + + # Nest again b/c start_item will pop this to look for preceding content. + $self->_increase_indent; + $self->_new_stack; +} + +sub _end_list { + my ($self) = @_; + $self->_handle_between_item_content; + + # Finish the list. + + # All the child elements should be blocks, + # but don't end with a double newline. + my $text = $self->_chomp_all($self->_pop_stack_text); + + $_[0]->_save_line($text . $/); +} + +sub _handle_between_item_content { + my ($self) = @_; + + # This might be empty (if the list item had no additional content). + if( my $text = $self->_pop_stack_text ){ + # Else it's a sub-document. + # If there are blocks we need to separate with blank lines. + if( $self->_private->{last_state}->{blocks} ){ + $text = $/ . $text; + } + # If not, we can condense the text. + # In this module's history there was a patch contributed to specifically + # produce "huddled" lists so we'll try to maintain that functionality. + else { + $text = $self->_chomp_all($text) . $/; + } + $self->_save($text) + } + + $self->_decrease_indent; +} + +sub _start_item { + my ($self) = @_; + $self->_handle_between_item_content; + $self->_new_stack; +} + +sub _end_item { + my ($self, $marker) = @_; + my $text = $self->_pop_stack_text; + $self->_save_line($self->_indent($marker . + # Add a space only if there is text after the marker. + (defined($text) && length($text) ? ' ' . $text : '') + )); + + # Store any possible contents in a new stack (like a sub-document). + $self->_increase_indent; + $self->_new_stack; +} + +sub start_over_bullet { $_[0]->_start_list } +sub end_over_bullet { $_[0]->_end_list } + +sub start_item_bullet { $_[0]->_start_item } +sub end_item_bullet { $_[0]->_end_item('-') } + +sub start_over_number { $_[0]->_start_list } +sub end_over_number { $_[0]->_end_list } + +sub start_item_number { + $_[0]->_start_item; + # It seems like this should be a stack, + # but from testing it appears that the corresponding 'end' event + # comes right after the text (it doesn't surround any embedded content). + # See t/nested.t which shows start-item, text, end-item, para, start-item.... + $_[0]->_private->{item_number} = $_[1]->{number}; +} + +sub end_item_number { + my ($self) = @_; + $self->_end_item($self->_private->{item_number} . '.'); +} + +# Markdown doesn't support definition lists +# so do regular (unordered) lists with indented paragraphs. +sub start_over_text { $_[0]->_start_list } +sub end_over_text { $_[0]->_end_list } + +sub start_item_text { $_[0]->_start_item } +sub end_item_text { $_[0]->_end_item('-')} + + +# perlpodspec equates an over/back region with no items to a blockquote. +sub start_over_block { + # NOTE: We don't actually need to indent for a blockquote. + $_[0]->_new_stack; +} + +sub end_over_block { + my ($self) = @_; + + # Chomp first to avoid prefixing a blank line with a `>`. + my $text = $self->_chomp_all($self->_pop_stack_text); + + # NOTE: Paragraphs will already be escaped. + + # I don't really like either of these implementations + # but the join/map/split seems a little better and benches a little faster. + # You would lose the last newline but we've already chomped. + #$text =~ s{^(.)?}{'>' . (defined($1) && length($1) ? (' ' . $1) : '')}mge; + $text = join $/, map { length($_) ? '> ' . $_ : '>' } split qr-$/-, $text; + + $self->_save_block($text); +} + +## Custom Formats ## + +sub start_for { + my ($self, $attr) = @_; + $self->_new_stack; + + if( $attr->{target} eq 'html' ){ + # Use another stack so we can indent + # (not syntactily necessary but seems appropriate). + $self->_new_stack; + $self->_increase_indent; + $self->_private->{no_escape} = 1; + # Mark this so we know to undo it. + $self->_stack_state->{for_html} = 1; + } +} + +sub end_for { + my ($self) = @_; + # Data gets saved as a block (which will handle indents), + # but if there was html we'll alter this, so chomp and save a block again. + my $text = $self->_chomp_all($self->_pop_stack_text); + + if( $self->_private->{last_state}->{for_html} ){ + $self->_private->{no_escape} = 0; + # Save it to the next stack up so we can pop it again (we made two stacks). + $self->_save($text); + $self->_decrease_indent; + $text = join "\n", '
    ', $self->_chomp_all($self->_pop_stack_text), '
    '; + } + + $self->_save_block($text); +} + +# Data events will be emitted for any formatted regions that have been enabled +# (by default, `markdown` and `html`). + +sub start_Data { + my ($self) = @_; + # TODO: limit this to what's in attr? + $self->_private->{no_escape}++; + $self->_new_stack; +} + +sub end_Data { + my ($self) = @_; + my $text = $self->_pop_stack_text; + $self->_private->{no_escape}--; + $self->_save_block($text); +} + +## Codes ## + +sub start_B { $_[0]->_save('**') } +sub end_B { $_[0]->start_B() } + +sub start_I { $_[0]->_save('_') } +sub end_I { $_[0]->start_I() } + +sub start_C { + my ($self) = @_; + $self->_new_stack; + $self->_private->{no_escape}++; +} + +sub end_C { + my ($self) = @_; + $self->_private->{no_escape}--; + $self->_save( $self->_wrap_code_span($self->_pop_stack_text) ); +} + +# Use code spans for F<>. +sub start_F { shift->start_C(@_); } +sub end_F { shift ->end_C(@_); } + +sub start_L { + my ($self, $flags) = @_; + $self->_new_stack; + push @{ $self->_private->{link} }, $flags; +} + +sub end_L { + my ($self) = @_; + my $flags = pop @{ $self->_private->{link} } + or die 'Invalid state: link end with no link start'; + + my ($type, $to, $section) = @{$flags}{qw( type to section )}; + + my $url = ( + $type eq 'url' ? $to + : $type eq 'man' ? $self->format_man_url($to, $section) + : $type eq 'pod' ? $self->format_perldoc_url($to, $section) + : undef + ); + + my $text = $self->_pop_stack_text; + + # NOTE: I don't think the perlpodspec says what to do with L<|blah> + # but it seems like a blank link text just doesn't make sense + if( !length($text) ){ + $text = + $section ? + $to ? sprintf('"%s" in %s', $section, $to) + : ('"' . $section . '"') + : $to; + } + + # FIXME: What does Pod::Simple::X?HTML do for this? + # if we don't know how to handle the url just print the pod back out + if (!$url) { + $self->_save(sprintf 'L<%s>', $flags->{raw}); + return; + } + + # In the url we need to escape quotes and parentheses lest markdown + # break the url (cut it short and/or wrongfully interpret a title). + + # Backslash escapes do not work for the space and quotes. + # URL-encoding the space is not sufficient + # (the quotes confuse some parsers and produce invalid html). + # I've arbitratily chosen HTML encoding to hide them from markdown + # while mangling the url as litle as possible. + $url =~ s/([ '"])/sprintf '&#x%x;', ord($1)/ge; + + # We also need to double any backslashes that may be present + # (lest they be swallowed up) and stop parens from breaking the url. + $url =~ s/([\\()])/\\$1/g; + + # TODO: put section name in title if not the same as $text + $self->_save('[' . $text . '](' . $url . ')'); +} + +sub start_X { + $_[0]->_new_stack; +} + +sub end_X { + my ($self) = @_; + my $text = $self->_pop_stack_text; + # TODO: mangle $text? + # TODO: put if configured +} + +# A code span can be delimited by multiple backticks (and a space) +# similar to pod codes (C<< code >>), so ensure we use a big enough +# delimiter to not have it broken by embedded backticks. +sub _wrap_code_span { + my ($self, $arg) = @_; + my $longest = 0; + while( $arg =~ /([`]+)/g ){ + my $len = length($1); + $longest = $len if $longest < $len; + } + my $delim = '`' x ($longest + 1); + my $pad = $longest > 0 ? ' ' : ''; + return $delim . $pad . $arg . $pad . $delim; +} + +## Link Formatting (TODO: Move this to another module) ## + + +sub format_man_url { + my ($self, $to) = @_; + my ($page, $part) = ($to =~ /^ ([^(]+) (?: \( (\S+) \) )? /x); + return $self->man_url_prefix . ($part || 1) . '/' . ($page || $to); +} + + +sub format_perldoc_url { + my ($self, $name, $section) = @_; + + my $url_prefix = $self->perldoc_url_prefix; + if ( + defined($name) + && $self->is_local_module($name) + && defined($self->local_module_url_prefix) + ) { + $url_prefix = $self->local_module_url_prefix; + } + + my $url = ''; + + # If the link is to another module (external link). + if ($name) { + $url = $url_prefix . ($self->escape_url ? URI::Escape::uri_escape($name) : $name); + } + + # See https://rt.cpan.org/Ticket/Display.html?id=57776 + # for a discussion on the need to mangle the section. + if ($section){ + + my $method = $url + # If we already have a prefix on the url it's external. + ? $self->perldoc_fragment_format + # Else an internal link points to this markdown doc. + : $self->markdown_fragment_format; + + $method = 'format_fragment_' . $method + unless ref($method); + + { + # Set topic to enable code refs to be simple. + local $_ = $section; + $section = $self->$method($section); + } + + $url .= '#' . $section; + } + + return $url; +} + + +# TODO: simple, pandoc, etc? + +sub format_fragment_markdown { + my ($self, $section) = @_; + + # If this is an internal link (to another section in this doc) + # we can't be sure what the heading id's will look like + # (it depends on what is rendering the markdown to html) + # but we can try to follow popular conventions. + + # http://johnmacfarlane.net/pandoc/demo/example9/pandocs-markdown.html#header-identifiers-in-html-latex-and-context + #$section =~ s/(?![-_.])[[:punct:]]//g; + #$section =~ s/\s+/-/g; + $section =~ s/\W+/-/g; + $section =~ s/-+$//; + $section =~ s/^-+//; + $section = lc $section; + #$section =~ s/^[^a-z]+//; + $section ||= 'section'; + + return $section; +} + + +{ + # From Pod::Simple::XHTML 3.28. + # The strings gets passed through encode_entities() before idify(). + # If we don't do it here the substitutions below won't operate consistently. + + sub format_fragment_pod_simple_xhtml { + my ($self, $t) = @_; + + # encode_entities { + # We need to use the defaults in case html_encode_chars has been customized + # (since the purpose is to match what external sources are doing). + + local $self->_private->{html_encode_chars}; + $t = $self->encode_entities($t); + # } + + # idify { + for ($t) { + s/<[^>]+>//g; # Strip HTML. + s/&[^;]+;//g; # Strip entities. + s/^\s+//; s/\s+$//; # Strip white space. + s/^([^a-zA-Z]+)$/pod$1/; # Prepend "pod" if no valid chars. + s/^[^a-zA-Z]+//; # First char must be a letter. + s/[^-a-zA-Z0-9_:.]+/-/g; # All other chars must be valid. + s/[-:.]+$//; # Strip trailing punctuation. + } + # } + + return $t; + } +} + + +sub format_fragment_pod_simple_html { + my ($self, $section) = @_; + + # From Pod::Simple::HTML 3.28. + + # section_name_tidy { + $section =~ s/^\s+//; + $section =~ s/\s+$//; + $section =~ tr/ /_/; + $section =~ tr/\x00-\x1F\x80-\x9F//d if 'A' eq chr(65); # drop crazy characters + + #$section = $self->unicode_escape_url($section); + # unicode_escape_url { + $section =~ s/([^\x00-\xFF])/'('.ord($1).')'/eg; + # Turn char 1234 into "(1234)" + # } + + $section = '_' unless length $section; + return $section; + # } +} + + +sub format_fragment_metacpan { shift->format_fragment_pod_simple_xhtml(@_); } +sub format_fragment_sco { shift->format_fragment_pod_simple_html(@_); } + + +sub is_local_module { + my ($self, $name) = @_; + + return ($name =~ $self->local_module_re); +} + +1; + +__END__ + +=pod + +=encoding UTF-8 + +=for :stopwords Marcel Gruenauer Victor Moral Ryan C. Thompson Aristotle Pagaltzis Randy Stauner ACKNOWLEDGEMENTS html cpan +testmatrix url bugtracker rt cpants kwalitee diff irc mailto metadata +placeholders metacpan + +=head1 NAME + +Pod::Markdown - Convert POD to Markdown + +=head1 VERSION + +version 3.400 + +=for test_synopsis my ($pod_string); + +=head1 SYNOPSIS + + # Pod::Simple API is supported. + + # Command line usage: Parse a pod file and print to STDOUT: + # $ perl -MPod::Markdown -e 'Pod::Markdown->new->filter(@ARGV)' path/to/POD/file > README.md + + # Work with strings: + my $markdown; + my $parser = Pod::Markdown->new; + $parser->output_string(\$markdown); + $parser->parse_string_document($pod_string); + + # See Pod::Simple docs for more. + +=head1 DESCRIPTION + +This module uses L to convert POD to Markdown. + +Literal characters in Pod that are special in Markdown +(like *asterisks*) are backslash-escaped when appropriate. + +By default C and C formatted regions are accepted. +Regions of C will be passed through unchanged. +Regions of C will be placed inside a C<< EdivE >> tag +so that markdown characters won't be processed. +Regions of C<:markdown> or C<:html> will be processed as POD and included. +To change which regions are accepted use the L API: + + my $parser = Pod::Markdown->new; + $parser->unaccept_targets(qw( markdown html )); + +=head2 A note on encoding and escaping + +The common L API returns a character string. +If you want Pod::Markdown to return encoded octets, there are two attributes +to assist: L and L. + +When an output encoding is requested any characters that are not valid +for that encoding will be escaped as HTML entities. + +This is not 100% safe, however. + +Markdown escapes all ampersands inside of code spans, so escaping a character +as an HTML entity inside of a code span will not be correct. +However, with pod's C and C sequences it is possible +to end up with high-bit characters inside of code spans. + +So, while C<< output_encoding => 'ascii' >> can work, it is not recommended. +For these reasons (and more), C is the default, fallback encoding (when one is required). + +If you prefer HTML entities over literal characters you can use +L which will only operate outside of code spans (where it is safe). + +=head1 METHODS + +=head2 new + + Pod::Markdown->new(%options); + +The constructor accepts the following named arguments: + +=over 4 + +=item * + +C + +Alters the perldoc urls that are created from C<< LEE >> codes +when the module is a "local" module (C<"Local::*"> or C<"Foo_Corp::*"> (see L)). + +The default is to use C. + +=item * + +C + +Alternate regular expression for determining "local" modules. +Default is C<< our $LOCAL_MODULE_RE = qr/^(Local::|\w*?_\w*)/ >>. + +=item * + +C + +Alters the man page urls that are created from C<< LEE >> codes. + +The default is C. + +=item * + +C + +Alters the perldoc urls that are created from C<< LEE >> codes. +Can be: + +=over 4 + +=item * + +C (shortcut for C) + +=item * + +C (shortcut for C) + +=item * + +any url + +=back + +The default is C. + + Pod::Markdown->new(perldoc_url_prefix => 'http://localhost/perl/pod'); + +=item * + +C + +Alters the format of the url fragment for any C<< LEE >> links +that point to a section of an external document (C<< L >>). +The default will be chosen according to the destination L. +Alternatively you can specify one of the following: + +=over 4 + +=item * + +C + +=item * + +C + +=item * + +C + +=item * + +C + +=item * + +A code ref + +=back + +The code ref can expect to receive two arguments: +the parser object (C<$self>) and the section text. +For convenience the topic variable (C<$_>) is also set to the section text: + + perldoc_fragment_format => sub { s/\W+/-/g; } + +=item * + +C + +Alters the format of the url fragment for any C<< LEE >> links +that point to an internal section of this document (C<< L >>). + +Unfortunately the format of the id attributes produced +by whatever system translates the markdown into html is unknown at the time +the markdown is generated so we do some simple clean up. + +B C and C accept +the same values: a (shortcut to a) method name or a code ref. + +=item * + +C + +Specifies whether or not to print author/title meta tags at the top of the document. +Default is false. + +=item * + +C + +Specifies whether or not to escape URLs. Default is true. It is not recommended +to turn this off with an empty local_module_url_prefix, as the resulting local +module URLs can be confused with IPv6 addresses by web browsers. + +=back + +=head2 html_encode_chars + +A string of characters to encode as html entities +(using L if available, falling back to numeric entities if not). + +Possible values: + +=over 4 + +=item * + +A value of C<1> will use the default set of characters from L (control chars, high-bit chars, and C<< <&>"' >>). + +=item * + +A false value will disable. + +=item * + +Any other value is used as a string of characters (like a regular expression character class). + +=back + +By default this is disabled and literal characters will be in the output stream. +If you specify a desired L any characters not valid for that encoding will be HTML entity encoded. + +B that Markdown requires ampersands (C<< & >>) and left angle brackets (C<< < >>) +to be entity-encoded if they could otherwise be interpreted as html entities. +If this attribute is configured to encode those characters, they will always be encoded. +If not, the module will make an effort to only encode the ones required, +so there will be less html noise in the output. + +=head2 match_encoding + +Boolean: If true, use the C<< =encoding >> of the input pod +as the encoding for the output. + +If no encoding is specified, L will guess the encoding +if it sees a high-bit character. + +If no encoding is guessed (or the specified encoding is unusable), +L will be used if it was specified. +Otherwise C will be used. + +This attribute is not recommended +but is provided for consistency with other pod converters. + +Defaults to false. + +=head2 output_encoding + +The encoding to use when writing to the output file handle. + +If neither this nor L are specified, +a character string will be returned in whatever L output method you specified. + +=head2 local_module_re + +Returns the regular expression used to determine local modules. + +=head2 local_module_url_prefix + +Returns the url prefix in use for local modules. + +=head2 man_url_prefix + +Returns the url prefix in use for man pages. + +=head2 perldoc_url_prefix + +Returns the url prefix in use (after resolving shortcuts to urls). + +=head2 perldoc_fragment_format + +Returns the coderef or format name used to format a url fragment +to a section in an external document. + +=head2 markdown_fragment_format + +Returns the coderef or format name used to format a url fragment +to an internal section in this document. + +=head2 include_meta_tags + +Returns the boolean value indicating +whether or not meta tags will be printed. + +=head2 escape_url + +Returns the boolean value indicating +whether or not URLs should be escaped. + +=head2 format_man_url + +Used internally to create a url (using L) +from a string like C. + +=head2 format_perldoc_url + + # With $name and section being the two parts of L. + my $url = $parser->format_perldoc_url($name, $section); + +Used internally to create a url from +the name (of a module or script) +and a possible section (heading). + +The format of the url fragment (when pointing to a section in a document) +varies depending on the destination url +so L is used (which can be customized). + +If the module name portion of the link is blank +then the section is treated as an internal fragment link +(to a section of the generated markdown document) +and L is used (which can be customized). + +=head2 format_fragment_markdown + +Format url fragment for an internal link +by replacing non-word characters with dashes. + +=head2 format_fragment_pod_simple_xhtml + +Format url fragment like L. + +=head2 format_fragment_pod_simple_html + +Format url fragment like L. + +=head2 format_fragment_metacpan + +Format fragment for L +(uses L). + +=head2 format_fragment_sco + +Format fragment for L +(uses L). + +=head2 is_local_module + +Uses C to determine if passed module is a "local" module. + +=for Pod::Coverage parse_from_file +parse_from_filehandle + +=for Pod::Coverage as_markdown + +=for Pod::Coverage handle_text +end_.+ +start_.+ +encode_entities + +=head1 SEE ALSO + +=over 4 + +=item * + +L - script included for command line usage + +=item * + +L - Super class that handles Pod parsing + +=item * + +L - For writing POD + +=item * + +L - For parsing POD + +=item * + +L - Markdown spec + +=back + +=head1 SUPPORT + +=head2 Perldoc + +You can find documentation for this module with the perldoc command. + + perldoc Pod::Markdown + +=head2 Websites + +The following websites have more information about this module, and may be of help to you. As always, +in addition to those websites please use your favorite search engine to discover more resources. + +=over 4 + +=item * + +MetaCPAN + +A modern, open-source CPAN search engine, useful to view POD in HTML format. + +L + +=back + +=head2 Bugs / Feature Requests + +Please report any bugs or feature requests by email to C, or through +the web interface at L. You will be automatically notified of any +progress on the request by the system. + +=head2 Source Code + + +L + + git clone https://github.com/rwstauner/Pod-Markdown.git + +=head1 AUTHORS + +=over 4 + +=item * + +Marcel Gruenauer + +=item * + +Victor Moral + +=item * + +Ryan C. Thompson + +=item * + +Aristotle Pagaltzis + +=item * + +Randy Stauner + +=back + +=head1 CONTRIBUTORS + +=for stopwords Aristotle Pagaltzis Cindy Wang (CindyLinz) Graham Ollis Johannes Schauer Marin Rodrigues Mike Covington motemen moznion Peter Vereshagin Ryan C. Thompson Yasutaka ATARASHI + +=over 4 + +=item * + +Aristotle Pagaltzis + +=item * + +Cindy Wang (CindyLinz) + +=item * + +Graham Ollis + +=item * + +Johannes Schauer Marin Rodrigues + +=item * + +Mike Covington + +=item * + +motemen + +=item * + +moznion + +=item * + +Peter Vereshagin + +=item * + +Ryan C. Thompson + +=item * + +Yasutaka ATARASHI + +=back + +=head1 COPYRIGHT AND LICENSE + +This software is copyright (c) 2011 by Randy Stauner. + +This is free software; you can redistribute it and/or modify it under +the same terms as the Perl 5 programming language system itself. + +=cut diff --git a/src/tools/URI/Escape.pm b/src/tools/URI/Escape.pm new file mode 100644 index 000000000..f5082a2dc --- /dev/null +++ b/src/tools/URI/Escape.pm @@ -0,0 +1,249 @@ +package URI::Escape; + +use strict; +use warnings; + +=head1 NAME + +URI::Escape - Percent-encode and percent-decode unsafe characters + +=head1 SYNOPSIS + + use URI::Escape; + $safe = uri_escape("10% is enough\n"); + $verysafe = uri_escape("foo", "\0-\377"); + $str = uri_unescape($safe); + +=head1 DESCRIPTION + +This module provides functions to percent-encode and percent-decode URI strings as +defined by RFC 3986. Percent-encoding URI's is informally called "URI escaping". +This is the terminology used by this module, which predates the formalization of the +terms by the RFC by several years. + +A URI consists of a restricted set of characters. The restricted set +of characters consists of digits, letters, and a few graphic symbols +chosen from those common to most of the character encodings and input +facilities available to Internet users. They are made up of the +"unreserved" and "reserved" character sets as defined in RFC 3986. + + unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + reserved = ":" / "/" / "?" / "#" / "[" / "]" / "@" + "!" / "$" / "&" / "'" / "(" / ")" + / "*" / "+" / "," / ";" / "=" + +In addition, any byte (octet) can be represented in a URI by an escape +sequence: a triplet consisting of the character "%" followed by two +hexadecimal digits. A byte can also be represented directly by a +character, using the US-ASCII character for that octet. + +Some of the characters are I for use as delimiters or as +part of certain URI components. These must be escaped if they are to +be treated as ordinary data. Read RFC 3986 for further details. + +The functions provided (and exported by default) from this module are: + +=over 4 + +=item uri_escape( $string ) + +=item uri_escape( $string, $unsafe ) + +Replaces each unsafe character in the $string with the corresponding +escape sequence and returns the result. The $string argument should +be a string of bytes. The uri_escape() function will croak if given a +characters with code above 255. Use uri_escape_utf8() if you know you +have such chars or/and want chars in the 128 .. 255 range treated as +UTF-8. + +The uri_escape() function takes an optional second argument that +overrides the set of characters that are to be escaped. The set is +specified as a string that can be used in a regular expression +character class (between [ ]). E.g.: + + "\x00-\x1f\x7f-\xff" # all control and hi-bit characters + "a-z" # all lower case characters + "^A-Za-z" # everything not a letter + +The default set of characters to be escaped is all those which are +I part of the C character class shown above as well +as the reserved characters. I.e. the default is: + + "^A-Za-z0-9\-\._~" + +The second argument can also be specified as a regular expression object: + + qr/[^A-Za-z]/ + +Any strings matched by this regular expression will have all of their +characters escaped. + +=item uri_escape_utf8( $string ) + +=item uri_escape_utf8( $string, $unsafe ) + +Works like uri_escape(), but will encode chars as UTF-8 before +escaping them. This makes this function able to deal with characters +with code above 255 in $string. Note that chars in the 128 .. 255 +range will be escaped differently by this function compared to what +uri_escape() would. For chars in the 0 .. 127 range there is no +difference. + +Equivalent to: + + utf8::encode($string); + my $uri = uri_escape($string); + +Note: JavaScript has a function called escape() that produces the +sequence "%uXXXX" for chars in the 256 .. 65535 range. This function +has really nothing to do with URI escaping but some folks got confused +since it "does the right thing" in the 0 .. 255 range. Because of +this you sometimes see "URIs" with these kind of escapes. The +JavaScript encodeURIComponent() function is similar to uri_escape_utf8(). + +=item uri_unescape($string,...) + +Returns a string with each %XX sequence replaced with the actual byte +(octet). + +This does the same as: + + $string =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg; + +but does not modify the string in-place as this RE would. Using the +uri_unescape() function instead of the RE might make the code look +cleaner and is a few characters less to type. + +In a simple benchmark test I did, +calling the function (instead of the inline RE above) if a few chars +were unescaped was something like 40% slower, and something like 700% slower if none were. If +you are going to unescape a lot of times it might be a good idea to +inline the RE. + +If the uri_unescape() function is passed multiple strings, then each +one is returned unescaped. + +=back + +The module can also export the C<%escapes> hash, which contains the +mapping from all 256 bytes to the corresponding escape codes. Lookup +in this hash is faster than evaluating C +each time. + +=head1 SEE ALSO + +L + + +=head1 COPYRIGHT + +Copyright 1995-2004 Gisle Aas. + +This program is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + +SPDX-License-Identifier: Artistic-2.0 +=cut + +use Exporter 5.57 'import'; +our %escapes; +our @EXPORT = qw(uri_escape uri_unescape uri_escape_utf8); +our @EXPORT_OK = qw(%escapes); +our $VERSION = '5.28'; + +use Carp (); + +# Build a char->hex map +for (0..255) { + $escapes{chr($_)} = sprintf("%%%02X", $_); +} + +my %subst; # compiled patterns + +my %Unsafe = ( + RFC2732 => qr/[^A-Za-z0-9\-_.!~*'()]/, + RFC3986 => qr/[^A-Za-z0-9\-\._~]/, +); + +sub uri_escape { + my($text, $patn) = @_; + return undef unless defined $text; + my $re; + if (defined $patn){ + if (ref $patn eq 'Regexp') { + $text =~ s{($patn)}{ + join('', map +($escapes{$_} || _fail_hi($_)), split //, "$1") + }ge; + return $text; + } + $re = $subst{$patn}; + if (!defined $re) { + $re = $patn; + # we need to escape the [] characters, except for those used in + # posix classes. if they are prefixed by a backslash, allow them + # through unmodified. + $re =~ s{(\[:\w+:\])|(\\)?([\[\]]|\\\z)}{ + defined $1 ? $1 : defined $2 ? "$2$3" : "\\$3" + }ge; + eval { + # disable the warnings here, since they will trigger later + # when used, and we only want them to appear once per call, + # but every time the same pattern is used. + no warnings 'regexp'; + $re = $subst{$patn} = qr{[$re]}; + 1; + } or Carp::croak("uri_escape: $@"); + } + } + else { + $re = $Unsafe{RFC3986}; + } + $text =~ s/($re)/$escapes{$1} || _fail_hi($1)/ge; + $text; +} + +sub _fail_hi { + my $chr = shift; + Carp::croak(sprintf "Can't escape \\x{%04X}, try uri_escape_utf8() instead", ord($chr)); +} + +sub uri_escape_utf8 { + my $text = shift; + return undef unless defined $text; + utf8::encode($text); + return uri_escape($text, @_); +} + +sub uri_unescape { + # Note from RFC1630: "Sequences which start with a percent sign + # but are not followed by two hexadecimal characters are reserved + # for future extension" + my $str = shift; + if (@_ && wantarray) { + # not executed for the common case of a single argument + my @str = ($str, @_); # need to copy + for (@str) { + s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg; + } + return @str; + } + $str =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg if defined $str; + $str; +} + +# XXX FIXME escape_char is buggy as it assigns meaning to the string's storage format. +sub escape_char { + # Old versions of utf8::is_utf8() didn't properly handle magical vars (e.g. $1). + # The following forces a fetch to occur beforehand. + my $dummy = substr($_[0], 0, 0); + + if (utf8::is_utf8($_[0])) { + my $s = shift; + utf8::encode($s); + unshift(@_, $s); + } + + return join '', @URI::Escape::escapes{split //, $_[0]}; +} + +1; diff --git a/src/tools/filterMakeflags.pl b/src/tools/filterMakeflags.pl new file mode 100644 index 000000000..93a2f8e89 --- /dev/null +++ b/src/tools/filterMakeflags.pl @@ -0,0 +1,15 @@ +#!/bin/env perl +# +# Filter all versions of GNU Make's MAKEFLAGS variable to return +# only the single-letter flags. The content differed slightly +# between 3.81, 3.82 and 4.0; Apple still ship 3.81. + +use strict; + +my @flags; +foreach (@ARGV) { + last if m/^--$/ or m/\w+=/; + next if m/^--/ or m/^-$/; + push @flags, $_; +}; +print join(' ', @flags); diff --git a/src/tools/genVersionHeader.pl b/src/tools/genVersionHeader.pl index 371b8db63..a305619a7 100644 --- a/src/tools/genVersionHeader.pl +++ b/src/tools/genVersionHeader.pl @@ -86,7 +86,7 @@ } $cv = `hg log -l1 --template '{date|isodate}'` } -elsif (-d '.git') { # Git +elsif (-d '.git' || -f '.git') { # Git print "== Found /.git directory\n" if $opt_v; # v1-4-abcdef-dirty # is 4 commits after tag 'v1' with short hash abcdef diff --git a/src/tools/podToMD.pl b/src/tools/podToMD.pl new file mode 100644 index 000000000..8f0ba0ec9 --- /dev/null +++ b/src/tools/podToMD.pl @@ -0,0 +1,103 @@ +#!/usr/bin/env perl +#************************************************************************* +# Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# SPDX-License-Identifier: EPICS +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +use strict; +use warnings; + +# To find the EPICS::Getopts module used below we need to add our lib/perl to +# the lib search path. If the script is running from the src/tools directory +# before everything has been installed though, the search path must include +# our source directory (i.e. $Bin), so we add both here. +use FindBin qw($Bin); +use lib ("$Bin/../../lib/perl", $Bin); + +use EPICS::Getopts; + +use EPICS::PodMD; + +use Pod::Usage; + +=head1 NAME + +podToMD.pl - Convert EPICS .pod files to .md + +=head1 SYNOPSIS + +B [B<-h>] [B<-o> file.md] file.pod + +=head1 DESCRIPTION + +Converts files from Perl's POD format into Markdown format. + +=head1 OPTIONS + +B understands the following options: + +=over 4 + +=item B<-h> + +Help, display this document as text. + +=item B<-o> file.md + +Name of the Markdown output file to be created. + +=back + +If no output filename is set, the file created will be named after the input +file, removing any directory components in the path and replacing any file +extension with .md. + +=cut + +our ($opt_o, $opt_h); + +sub HELP_MESSAGE { + pod2usage(-exitval => 2, -verbose => $opt_h); +} + +HELP_MESSAGE() if !getopts('ho:') || $opt_h || @ARGV != 1; + +my $infile = shift @ARGV; + +my @inpath = split /\//, $infile; +my $file = pop @inpath; + +if (!$opt_o) { + ($opt_o = $file) =~ s/\. \w+ $/.md/x; +} + +open my $out, '>', $opt_o or + die "Can't create $opt_o: $!\n"; + +$SIG{__DIE__} = sub { + die @_ if $^S; # Ignore eval deaths + close $out; + unlink $opt_o; +}; + +my $podMD = EPICS::PodMD->new(); + +$podMD->perldoc_url_prefix(''); +# $podMD->perldoc_url_postfix('.md'); +$podMD->output_string(\my $md); +$podMD->parse_file($infile); + +print $out $md; +close $out; + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2013 UChicago Argonne LLC, as Operator of Argonne National +Laboratory. + +This software is distributed under the terms of the EPICS Open License. + +=cut diff --git a/startup/EpicsHostArch b/startup/EpicsHostArch old mode 100644 new mode 100755 diff --git a/startup/win32.bat b/startup/win32.bat old mode 100755 new mode 100644